Правильная регистрация 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;
}