1
votes

I am experimenting with linux device drivers. I'm attempting to build an OV2680 driver for my laptop, a Lenovo Miix 510. On that platform the sensor sits behind an INT3472 PMIC, and accessing the driver requires controlling the GPIO pins of the INT3472. The INT3472 has a driver, and a corresponding MFD Driver which didn't work out of the box but which I have altered to work (my laptop's ACPI tables don't define an I2cSerialBus2 for the INT3472, so I just had to add an ic2_device_id table and create the i2c device with echo INT3472 0x48 | sudo tee /sys/bus/i2c/devices/i2c-7/new_device - this creates a gpiochip1 with the 10 GPIO lanes defined in the GPIO driver, so it seems to be working.

I can set and get values for those pins in a terminal using the tools libgpiod provides. For example sudo gpioset gpiochip1 1 1 sets lane 1 high.

My question is; what is the correct way to control the 10 GPIO pins provided by the INT3472 in my camera driver? I need, for example, to be able to pull a pin low/high to trigger the camera's software standby. I guess the obvious answer is "use libgpiod", but if that's the case, how do I identify the correct "gpiochipN" file in /dev to open, given there's two INT3470's plus the main gpiochip0 in my laptop.

1
This question is misguided. You messed up provider and consumer thingy. First part about PMIC being consumer, while second part of your question is discussing how to access PMIC GPIO which it provides. They are completely different GPIO chips!0andriy
@0andriy sorry; I'm confused. I thought the PMIC I'm interfacing with in the first snippet is the same TPS68470 that would be driven by the drivers you linked, and you were suggesting that I should use the API that the driver for that chip is providing rather than the hacky method. Did I get the wrong impression?Dan Scally
You put your gpiod_get_index() inside elixir.bootlin.com/linux/latest/source/drivers/mfd/… tps68470_probe(). This is how to get this right. In some (rare or broken) cases it might be that current way is plausible.0andriy
@0andriy Thanks for your patience, I appreciate this a lot; reading tends to only get me so far! So trying this again; given the GPIOs defined in the DSDT are actually consumed by this device, what I'm actually doing by turning those on is turning the tps68470 itself on, rather than the ov2680? That does make a lot of sense, given the other i2c device that shows up when I do that is at 0x48 which is the first of the possible addresses for the tps68470. So I need to alter the tps68470_probe() function to get those GPIOs and turn them on when the function's called (i.e. at boot I think)Dan Scally
This sounds right, but I'm not familiar with the device. It would be nice if you can get schematics of something similar to see how actually this camera PMIC(s) is(are) connected to the SoC. And also datasheet for the PMIC may help a lot.0andriy

1 Answers

0
votes

I think I came across the right way to do it here. In short, you can map the GPIO pins to the device like so in the camera driver (as a part of the .probe function):

    static struct gpiod_lookup_table ov2680_gpios = {
        .dev_id = "i2c-OVTI2680:00",
        .table = {
            GPIO_LOOKUP_IDX("tps68470-gpio", 7, "s_enable", 0, GPIO_ACTIVE_HIGH),
            GPIO_LOOKUP_IDX("tps68470-gpio", 8, "s_idle", 0, GPIO_ACTIVE_HIGH),
            GPIO_LOOKUP_IDX("tps68470-gpio", 9, "s_resetn", 0, GPIO_ACTIVE_HIGH),
            { },
        },
    };

    gpiod_add_lookup_table(&ov2680_gpios);

The .dev_id member matches the device name. GPIO_LOOKUP_IDX is a macro that takes the label of the GPIO chip (tps68470-gpio), the index of the pin in the chip (given by the PMIC's GPIO driver here, plus a function name, index in the function and some flags. Once the lookup table is built it can be registered with gpiod_add_lookup_table(). Once that's done you can fetch the pins using gpiod_get...:

/* ov2680 is a struct ov2680_device containing, amongst other things... */
struct ov2680_device {
    gpio_desc            *s_enable;
    struct i2c_device    *client;
}; 

ov2680->s_idle = gpiod_get_index(&ov2680->client->dev, "s_idle", 0, GPIOD_OUT_HIGH);

I think this is right; but I'm going to leave this open for a while in case a better answer comes along.