arranque del controlador i2c - raspbian


8

Soy relativamente nuevo en controladores de dispositivos en Linux. Lo que estoy tratando de lograr es que al arrancar mi Raspberry, un controlador RGB externo recibirá un comando i2c para que pueda ver un LED iluminado en el arranque.

Mi enfoque está tratando de lograr esto a través de un módulo de kernel que se cargará en el arranque. Intenté muchas cosas para lograr esto, pero en este momento siento que tengo una brecha de conocimiento. Tal vez alguien me puede ayudar? (tenga en cuenta que no es un problema de hardware, desde el espacio del usuario puedo enviar comandos al dispositivo).

El código de mi módulo de kernel es el siguiente:

    #include <linux/i2c.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/regmap.h>


MODULE_AUTHOR ("Niels");
MODULE_DESCRIPTION("driver rgb led");
MODULE_LICENSE("GPL");

/*CAT3626 control registers*/
#define CAT3626_ADDRESS     0x66
#define CAT3626_ENA         0x03
#define CAT3626_REGA        0x00
#define CAT3626_REGB        0x01
#define CAT3626_REGC        0x02

struct cat3626 {
    struct device *dev;
    struct regmap * regmap;
};


enum {
    cat3626, 
};

static const struct of_device_id cat3626_dt_ids[] = {
    { .compatible = "onsemi,cat3626", .data = (void *)cat3626},
    { }
};

MODULE_DEVICE_TABLE(of, cat3626_dt_ids);


static const struct i2c_device_id cat3626_id[] = {
    {"cat3626",cat3626},
    { }
};

MODULE_DEVICE_TABLE(i2c, cat3626_id);

static const struct regmap_config regmap_config = {
    .reg_bits = 8,
    .val_bits = 8,
};

static int cat3626_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
    struct cat3626 *cat3626;
    const struct of_device_id *match;
    int ret;

    cat3626 = devm_kzalloc(&client->dev, sizeof(struct cat3626), GFP_KERNEL);
    if (!cat3626){
        return -ENOMEM;
    }

    dev_set_drvdata(&client->dev, cat3626);
    cat3626->dev = &client->dev;

    cat3626->regmap = devm_regmap_init_i2c(client, &regmap_config);
    if (IS_ERR(cat3626->regmap)) {
        dev_err(cat3626->dev, "regmap allocation failed\n");
        return PTR_ERR(cat3626->regmap);
    }

    i2c_set_clientdata(client, cat3626);

    match = of_match_device(cat3626_dt_ids, &client->dev);
        if (!match) {
        dev_err(&client->dev, "unknown device model\n");
        return -ENODEV;
    }

    ret = i2c_smbus_write_byte_data(client, CAT3626_ENA, 0x30);   /* write LED C on*/
    ret = i2c_smbus_write_byte_data(client, CAT3626_REGC, 19);    /* write mA*/

    return ret;
}

static struct i2c_driver cat3626_driver = {
    .driver = {
        .name = "cat3626",
        .owner = THIS_MODULE,
        .of_match_table = of_match_ptr(cat3626_dt_ids),
    },
    .probe = cat3626_probe,
    .remove = cat3626_remove,
    .id_table = cat3626_id,
};

module_i2c_driver(cat3626_driver);

Aquí está el archivo MAKE:

ifneq ($(KERNELRELEASE),)
    obj-m := hiber_rgb_driver.o

else
    KERNELDIR ?= \
    /lib/modules/`uname -r`/build/
    PWD := `pwd`

default:
    $(MAKE) -C $(KERNELDIR) \
    M=$(PWD) modules

endif

clean:
    rm -f *.ko *.o Module* *mod*

En el archivo /boot/config.txt he agregado esto:

dtoverlay = i2c-gpio, bus = 80, i2c_gpio_delay_us = 2, i2c_gpio_sda = 44, i2c_gpio_scl = 45.

Además hice un dtoverlay personalizado:

/dts-v1/;
/plugin/;

/ {
    fragment@0 {
        target = <&i2c80>;
        __overlay__ {
            status = "okay";
            #address-cells = <1>;
            #size-cells = <0>;

            cat3626: cat3626@66 {
                compatible = "onsemi,cat3626";
                reg = <0x66>;
                clock-frequency = <400000>;
            };
        };
    };
};

Desafortunadamente en el arranque no pasa nada. Todo lo que obtengo del dmesg de arranque es lo siguiente:

rgb_driver: loading out-of-tree module taints kernel

¿Alguien puede darme alguna ayuda, o tal vez un enfoque diferente para lograr mi objetivo?

¡Gracias por adelantado!


@shellter Se puede
tripleee

Cuando dice que desea enviar un comando i2c para encender un LED en el arranque, ¿quiere decir que durante el proceso de arranque o después de que el proceso de arranque se haya completado y sea posible iniciar sesión?
rnorris

Durante el proceso de arranque
Nelizzsan

Dependiendo de qué tan pronto desee hacer esto en el proceso de arranque, puede valer la pena considerar implementar esto como un script bajo /etc/init.d/o similar en lugar de un módulo del núcleo.
rnorris

Respuestas:


4

Un par de cosas a tener en cuenta: un kernel contaminado a menudo tiene una función reducida y probablemente no desee ir allí si no tiene que hacerlo. Intentaría resolver el problema de la contaminación. He creado módulos de kernel como independientes y no he tocado el problema de la contaminación. Es posible que desee volver a visitar su archivo MAKE, este es un archivo MAKE más estándar de construcción de módulos con un par de arrugas, ya que, por supuesto, está cruzando la compilación:

PWD = $(shell pwd)
obj-m += hiber_rgb_driver.o

all:
    make ARCH=arm CROSS_COMPILE=$(CROSS) -C $(KERNEL) SUBDIRS=$(PWD) modules

clean:
    make -C $(KERNEL) SUBDIRS=$(PWD) clean

y construirlo con algo como:

make KERNEL=<LINUX_SOURCE_DIR> CROSS=<TOOLCHAIN_DIR>/bin/arm-linux-gnueabihf-

Entonces ahí está eso.

A continuación, las cosas de la sonda de su dispositivo se ven interesantes. No tengo tiempo para depurarlo, pero sugeriría agregar algunos printk para verificar que la sonda está siendo golpeada. Si es así, genial, es solo una cuestión de averiguar por qué no estás 'haciendo coincidir'. Si no está siendo golpeado, sigue leyendo ...

Como probablemente sepa, los buses i2c son un poco especiales cuando se trata de sondeo de dispositivos. No existe un sondeo real automatizado o mágico que normalmente sucedería en un bus PCI. En su lugar, debe construir un árbol de dispositivos que el kernel pueda recorrer en el momento del arranque para completar todas las sondas.

Veo que has creado un fragmento de superposición. Debe asegurarse de que se compila en un binario de código de bytes '.dtb' que el kernel puede analizar y luego colocar en el lugar correcto en su medio de arranque donde grub puede encontrarlo.

También es posible que deba actualizar la dtb maestra de su dispositivo para hacer referencia a esta superposición, de modo que el núcleo sepa a dónde podría ir. Piense en la dtb del dispositivo como un árbol de navidad artificial, y en la superposición como una extremidad que podría unirse en algún momento en el futuro; deberá especificar los puntos de conexión en la dtb del dispositivo. Ojalá pudiera ser más preciso aquí, pero la esperanza te pone en la dirección correcta al menos en este punto.


@Nelizzsan: ¿esta respuesta te ha ayudado significativamente?
Shellter

@ Andrew Atrens: ¡Gracias por tu respuesta! Confirmaste todas mis suposiciones: P. Estoy de acuerdo, el problema probablemente se deba a un problema del árbol de dispositivos. De hecho, lo tuve trabajando en el Rpi normal. Entonces debería comenzar a cavar en el árbol de dispositivos.
Nelizzsan
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.