Inicialização do driver i2c - raspbian

8

Eu sou relativamente novo para drivers de dispositivo no Linux. O que estou tentando alcançar é que, na inicialização do meu Raspberry, um driver RGB externo receberá um comando i2c para que você possa ver um LED acender na inicialização.

Minha abordagem está tentando fazer isso por meio de um módulo do kernel que será carregado na inicialização. Eu tentei muitas coisas para conseguir isso, mas no momento sinto que tenho uma lacuna de conhecimento. Talvez alguém possa me ajudar? (observe que não é um problema de hardware, no espaço do usuário posso enviar comandos para o dispositivo.)

O código do meu módulo do kernel é o seguinte:

    #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);

Aqui está o makefile:

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*

No arquivo /boot/config.txt, adicionei isso:

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

Além disso, criei um 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>;
            };
        };
    };
};

Infelizmente, na inicialização, nada acontece. Tudo o que recebo do dmesg de inicialização é o seguinte:

rgb_driver: loading out-of-tree module taints kernel

Alguém pode me dar alguma ajuda, ou talvez uma abordagem diferente para alcançar meu objetivo?

Desde já, obrigado!

Nelizzsan
fonte
Quando você diz que deseja enviar um comando i2c para acender um LED na inicialização, você quer dizer durante o processo de inicialização ou após a conclusão do processo de inicialização e é possível efetuar login?
rnorris 19/02
Durante o processo de inicialização
Nelizzsan
Dependendo de quanto tempo você deseja fazer isso no processo de inicialização, pode valer a pena implementá-lo como um script sob /etc/init.d/ou semelhante, em vez de um módulo do kernel.
rnorris 21/02

Respostas:

4

Algumas coisas a serem observadas - um kernel contaminado geralmente é reduzido de recursos e você provavelmente não quer ir para lá se não precisar. Eu tentaria resolver o problema de contaminação. Criei módulos do kernel como autônomos e não atingi o problema de contaminação. Convém revisitar seu makefile. Esse é um makefile de construção de módulo mais padrão com algumas rugas, pois, é claro, você está cruzando a compilação -

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

e construa com algo como -

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

Então existe isso.

Em seguida, o material do probe do dispositivo parece interessante. Não tenho tempo para depurá-lo para você, mas sugiro adicionar alguns printk para verificar se o probe está sendo atingido. Se for, então ótimo, é apenas uma questão de descobrir por que você não está 'combinando'. Se não for atingido, continue a ler ..

Como você provavelmente sabe, os barramentos i2c são um pouco especiais quando se trata de detecção de dispositivos. Não há nenhuma investigação automática ou mágica real que normalmente aconteceria em um barramento PCI. Em vez disso, você precisa criar uma árvore de dispositivos que o kernel possa percorrer no momento da inicialização para concluir todas as análises.

Vejo que você criou um snippet de sobreposição. Você precisa ter certeza de que tudo está compilado em um binário de código de bytes '.dtb' que o kernel pode analisar e depois colocar no local correto na mídia de inicialização onde o grub pode encontrá-lo.

Você também pode precisar atualizar o dtb principal do seu dispositivo para se referir a essa sobreposição, para que o kernel saiba para onde pode ir. Pense no dtb do dispositivo como uma árvore de natal artificial e a sobreposição como um membro que pode ser anexado em algum momento no futuro - você precisará especificar os pontos de anexo no dtb do dispositivo. Eu gostaria de poder ser mais preciso aqui, mas a esperança coloca você na direção correta neste ponto, pelo menos.

Andrew Atrens
fonte
@Nizzizzan: esta resposta ajudou você significativamente?
shellter 24/02
@ Andrew Atrens: Obrigado pela resposta! Você confirmou todas as minhas suposições: P. Concordo que o problema provavelmente se deve a um problema na árvore de dispositivos. Na verdade, eu estava trabalhando no Rpi normal. Então eu deveria começar a cavar na árvore de dispositivos.
Nelizzsan