Правильная регистрация SPI-устройства в Device Tree на OrangePi Zero 3

Я пишу драйвер SPI-устройства для OrangePi Zero 3 с установленной ОС Armbian. В написании драйвера используется API шины SPI в ядре Linux. У меня возникла проблема с активацией шины SPI. Для добавления устройства в Device Tree я использую overlay следующего содержания:

/dts-v1/;
/plugin/;

/ {
    compatible = "allwinner,sun50i-h616";

    fragment@0 {
        target = <&pio>;
        __overlay__ {
            ssd1306_dc_pin: ssd1306-dc-pin {
                pins = "PC7";
                function = "gpio_out";
                output-low; // Начальное состояние (по умолчанию 0)
            };
        };
    };

    fragment@1 {
        target = <&pio>;
        __overlay__ {
            ssd1306_res_pin: ssd1306-res-pin {
                pins = "PC10";
                function = "gpio_out";
                output-high;
            };
        };
    };

    fragment@2 {
        target = <&spi0>; // Указываем целевую шину (SPI0)
        __overlay__ {
            status = "okay";
            #address-cells = <1>;
            #size-cells = <0>;

            display: ssd1306@0 {
                compatible = "duel,ssd1306";
                reg = <0>;          // CS0
                spi-max-frequency = <400000>;
                dc-gpios = <&pio 2 7 0>;
                res-gpios = <&pio 2 10 1>;
            };
        };
    };
};

Данный overlay успешно компилируется, и при подключении модуля происходит вызов соответствующей функции probe, но шина SPI, предположительно, не активируется. Данное предположение вынесено на основе проверки светодиодом: пин CS всегда находится в состоянии 0, пин CLK также всегда находится в этом состоянии (Для проверки CLK в overlay понижалась частота до 4000).

Дополнительные сведения

Для установки overlay используются следующие Bash-команды:

dtc -@ -I dts -O dtb -o ssd1306.dtbo ssd1306-overlay.dts
sudo cp ssd1306.dtbo /boot/dtb/allwinner/overlay/sun50i-h616-ssd1306.dtbo

В armbianEnv.txt при этом добавляется строка: overlays=ssd1306.

Сокращённый исходный код функции probe (в данной версии я ещё не обеспечиваю потокобезопасность):

static int  duel_ssd1306_probe(struct spi_device* spi) {
    struct duel_ssd1306_drvdata* drvdata;
    ssize_t spi_status;
    int result;

    PDEBUG("Duel: probe.\n");
    drvdata = kmalloc(sizeof(struct duel_ssd1306_drvdata), GFP_KERNEL);
    if (!drvdata) {
        printk(KERN_WARNING "Duel: out of memory.\n");
        return -ENOMEM;
    }
    spi_set_drvdata(spi, drvdata);

    result = duel_ssd1306_init_drvdata(drvdata, &spi->dev);
    if (result) {
        duel_ssd1306_free_drvdata(drvdata);
        kfree(drvdata);
        return result;
    }

    PDEBUG("Duel: ssd1306 startup.\n");
    //Эта функция два раза с задержками переключает пин RES.
    duel_ssd1306_hard_reset(drvdata);

    /*
    Здесь также осуществляется инициализация устройства посредством направления серии команд, описанных в datasheet.
    */

    return 0;
}

Ответы (1 шт):

Автор решения: Cof. Cof.

Проблема

Данная проблема является типичной для OrangePi Zero 3, использующих ОС Armbian, и характерна она даже для программ, использующих более высокоуровневые абстракции над SPI (spidev). Это объясняется тем, что существующие overlay для spidev1_0, spidev1_1, spi-spidev нуждаются в правках (как и overlay из вопроса). Проблемой стандартных overlay является заблокированный для шины SPI1 пин CS1.

Решение

Для правки своего overlay я руководствовался следующими ресурсами:

  1. Исправленные стандартные overlay для Zero 3: https://forum.armbian.com/topic/48480-orange-pi-zero-3-spi-cs-pin/

  2. Изменение значения pinctrl-0 на магические числа, равные phandle для spi1_pins и spi1_cs1_pin в декомпилированном DT для Zero 3 (В оригинале процесс описан для модели Orange Pi 3 LTS): https://forum.armbian.com/topic/28425-spi-problem-with-orange-pi-3-lts/

Обновлённый overlay из вопроса

Приведённый далее overlay был протестирован на Armbian Linux 6.6.62-current-sunxi64.

/dts-v1/;
/plugin/;

/ {
    compatible = "allwinner,sun50i-h616";

    fragment@0 {
        target-path = "/aliases";
            __overlay__ {
                spi1 = "/soc/spi@5011000";
            };
        };

    fragment@1 {
        target = <&pio>;
        __overlay__ {
            ssd1306_dc_pin: ssd1306-dc-pin {
                pins = "PC7";
                function = "gpio_out";
                output-low; // Начальное состояние (по умолчанию 0)
                bias-pull-down;
            };
        };
    };

    fragment@2 {
        target = <&pio>;
        __overlay__ {
            ssd1306_res_pin: ssd1306-res-pin {
                pins = "PC10";
                function = "gpio_out";
                output-high;
                bias-pull-up;
            };
        };
    };

    fragment@3 {
        target = <&spi1>; // Указываем целевую шину (SPI1)
        __overlay__ {
            status = "okay";
            #address-cells = <1>;
            #size-cells = <0>;
            pinctrl-names = "default";
            pinctrl-0 = <0x22 0x4f>;

            display: ssd1306@1 {
                compatible = "duel,ssd1306";
                reg = <1>;          // CS1
                spi-max-frequency = <400000>;
                dc-gpios = <&pio 2 7 0>;
                res-gpios = <&pio 2 10 1>;
            };
        };
    };
};
→ Ссылка