Как использовать драйвер spi-gpio в Linux?

У меня есть устройство, работающее на 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- gpio.c» и «spi-bitbang-txrx,h»). Кроме того, это могло быть теоретически возможно в 2009 году, но я не знаю, применимо ли это до сих пор, тем более что эти макросы нигде в исходном коде не используются.

Ian Abbott 23.08.2024 12:21

В общем случае вам необходимо написать драйвер устройства SPI для клиентов и позволить модулю «spi-gpio» позаботиться о передаче. Для каждого из клиентов будет вызываться функция «зонда» драйвера. Подсистема «iio» (промышленный ввод-вывод) может подойти, поскольку вы можете использовать триггеры hrtimer, если ядро ​​настроено на их поддержку.

Ian Abbott 23.08.2024 12:33

Вам вообще не следует использовать второй подход. Это наследие и довольно устарело. В первом случае все должно работать, если у вас есть драйвер для ваших целевых устройств SPI (чипов памяти?).

0andriy 23.08.2024 23:48
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
3
50
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Итак, поэкспериментировав, я сделал это так, как было задумано. Дерево устройств выглядит так:

// 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.

Другие вопросы по теме