У меня есть устройство, работающее на Linux, и мне нужно написать модуль ядра, который будет опрашивать два внешних устройства через шину SPI. Эта шина основана на GPIO процессора, поэтому стратегия состоит в том, чтобы использовать внешние устройства для получения данных. Чтобы избежать дальнейших вопросов: я не могу изменить архитектуру, она должна быть побитовой и это должен быть встроенный модуль ядра.
Проблема в том, что непонятно, как использовать драйвер spi-gpio (src). В коде драйвера есть пояснение:
/*
* Because the overhead of going through four GPIO procedure calls
* per transferred bit can make performance a problem, this code
* is set up so that you can use it in either of two ways:
*
* - The slow generic way: set up platform_data to hold the GPIO
* numbers used for MISO/MOSI/SCK, and issue procedure calls for
* each of them. This driver can handle several such busses.
*
* - The quicker inlined way: only helps with platform GPIO code
* that inlines operations for constant GPIOs. This can give
* you tight (fast!) inner loops, but each such bus needs a
* new driver. You'll define a new C file, with Makefile and
* Kconfig support; the C code can be a total of six lines:
*
* #define DRIVER_NAME "myboard_spi2"
* #define SPI_MISO_GPIO 119
* #define SPI_MOSI_GPIO 120
* #define SPI_SCK_GPIO 121
* #define SPI_N_CHIPSEL 4
* #include "spi-gpio.c"
*/
Я добавил некоторые данные в Devicetree:
spi-gpio {
compatible = "spi-gpio";
#address-cells = <0x1>;
ranges;
status = "okay";
sck-gpios = <&pio 4 9 GPIO_ACTIVE_HIGH>; // PE9
mosi-gpios = <&pio 4 6 GPIO_ACTIVE_HIGH>; // PE6
miso-gpios = <&pio 4 8 GPIO_ACTIVE_HIGH>; // PE8
cs-gpios = <&pio 4 4 GPIO_ACTIVE_HIGH>, // PE4
<&pio 4 17 GPIO_ACTIVE_HIGH>; // PE17
num-chipselects = <2>;
/* Clients */
m90e32@0 {
reg = <0>;
compatible = "atmel,m90e32";
spi-max-frequency = <950000>;
};
m90e32@1 {
reg = <1>;
compatible = "atmel,m90e32";
spi-max-frequency = <950000>;
};
};
Если я использую второй метод, описанный в spi-gpio.c, напишу еще один модуль ядра следующим образом:
#define DRIVER_NAME "myboard_spi2"
#define SPI_MISO_GPIO 119
#define SPI_MOSI_GPIO 120
#define SPI_SCK_GPIO 121
#define SPI_N_CHIPSEL 4
#include "spi-gpio.c"
Как добавить цикл опроса в этот код? Или мне нужно написать оверлей для spi-gpio.c и добавить туда задачу опроса? Лично я хочу пойти первым путем и просто использовать нужные мне процедуры и конструкции из spi-gpio.c. Но очень неясно, как это сделать правильно, и я не могу найти примеров.
Какие методы из этого модуля и в каком порядке мне нужно использовать, чтобы создать в моем модуле два устройства SPI для работы с ними?
В общем случае вам необходимо написать драйвер устройства SPI для клиентов и позволить модулю «spi-gpio» позаботиться о передаче. Для каждого из клиентов будет вызываться функция «зонда» драйвера. Подсистема «iio» (промышленный ввод-вывод) может подойти, поскольку вы можете использовать триггеры hrtimer, если ядро настроено на их поддержку.
Вам вообще не следует использовать второй подход. Это наследие и довольно устарело. В первом случае все должно работать, если у вас есть драйвер для ваших целевых устройств SPI (чипов памяти?).





Итак, поэкспериментировав, я сделал это так, как было задумано. Дерево устройств выглядит так:
// SPI E-Meter Bus
spi-gpio {
compatible = "spi-gpio";
#address-cells = <0x1>;
ranges;
status = "okay";
sck-gpios = <&pio 4 9 GPIO_ACTIVE_HIGH>; // PE9
mosi-gpios = <&pio 4 6 GPIO_ACTIVE_HIGH>; // PE6
miso-gpios = <&pio 4 8 GPIO_ACTIVE_HIGH>; // PE8
cs-gpios = <&pio 4 4 GPIO_ACTIVE_HIGH>, // PE4
<&pio 4 17 GPIO_ACTIVE_HIGH>; // PE17
num-chipselects = <2>;
/* Clients */
m90e32@0 {
reg = <0>;
compatible = "atmel,m90e32";
spi-max-frequency = <100000>;
};
m90e32@1 {
reg = <1>;
compatible = "atmel,m90e32";
spi-max-frequency = <100000>;
};
};
Затем я написал spi_driver (не драйвер платформы):
static const struct of_device_id m90e32_spi_gpio_ids[] = {
{ .compatible = "atmel,m90e32" },
{}
};
MODULE_DEVICE_TABLE(of, m90e32_spi_gpio_ids);
static struct spi_driver m90e32_driver = {
.driver = {
.name = SPI_DRIVER_NAME,
.of_match_table = of_match_ptr(m90e32_spi_gpio_ids),
},
.probe = m90e32as_probe,
.remove = m90e32as_remove,
};
module_spi_driver(m90e32_driver);
Функция зонда вызывается дважды, по одному разу для каждого устройства m90e32. Однако существует только один экземпляр драйвера. Там я могу прочитать информацию о выборе чипов, чтобы отличить их друг от друга, и сохранить указатель to spi_device для использования в будущем. Это помогает, когда я хочу использовать функцию spi_write_**. Модуль spi-gpio работает сам по себе, вам не нужно явно вызывать его для передачи сообщений spi.
Я не знал, с чего начать со «второго метода», особенно для модуля ядра вне дерева (особенно при сборке с использованием пакета заголовков ядра, а не с полным исходным кодом ядра — для этого потребуются локальные копии «spi- gpio.c» и «spi-bitbang-txrx,h»). Кроме того, это могло быть теоретически возможно в 2009 году, но я не знаю, применимо ли это до сих пор, тем более что эти макросы нигде в исходном коде не используются.