i.MX7d процессор. Как написать драйвер под линукс для управления gpio.

Немного про i.MX7d. Все было бы очень хорошо, если бы не было так плохо. В определенных компаниях давно работают с продукцией Freescale и временами очень довольны. Достаточно интересный в своем составе чип из этой серии содержит на кристалле еще и М4 ядро. Все очень хорошо за исключением нескольких моментов.

i.mx7d
i.mx7d

Да, это супер, встроить M4 ядро рядом с ARMv7, даже не с одним, а двумя. Которые и содержит в себе вышеупомянутый чип i.MX7d процессора. Для STM32 процессоров написано много, временами даже очень много. Несмотря на то, что в нем так и не реализована правильная многозадачность на аппаратном уровне, а все возможные решение это чисто софт. Но фан клуб его много разработчиков объединил.

Достаточно лениво переходить на какой нибудь другой чип, если и так все ок. Вот и получается что огроомный плюс в его внутреннем М4 ядре. Народ любит такие чудеса.

Весьма знаменательно, что вместе с новым чипом ребята сделали и новую систему сборки вместо buildroot. Называется Yocto Project и его главная черта это надстройка из pyton над обычным gcc и Makefile. То есть ценность так себе, средненько.

Однако мы не привыкли отступать и в результате с удивлением обнаружили ядро версии 6.2 для этого процессора. Что удачно и собрали после некоторых ухищрений. Естественно захотелось большего и кроме стандартного buildroot взлетел и обычный ubuntu for arm.

Разработчик ( а писали ему письма крупным шрифтом ) и далеко не один раз, многократно повторял заученную фразу. В примерном переводе «я не знаю как управлять GPIO из ядра линукса, используйте M4 ядро» . Завелось и это. И как водится на одном из этапов появился некий HelloWorld для конкретной реализации на весьма конкретном процессоре i.MX7. Тем самым лишний раз доказав в том числе и разработчику, что в софте невозможного нет. В общем нет. Или совсем нет.

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/timer.h>

#define DEVICE_NAME "pbus_device"
#define CLASS_NAME "pbus"
#define GPIO3_BASE_ADDR 0x30230000
#define GPIO3_DR_OFFSET 0x00
#define GPIO3_GDIR_OFFSET 0x04
#define GPIO5_BASE_ADDR 0x30240000
#define GPIO5_DR_OFFSET 0x00
#define GPIO5_GDIR_OFFSET 0x04

#include "gpio_pins.h"
#include "imx7dea-com.h"

static int majorNumber;
static struct class* pbusClass = NULL;
static struct device* pbusDevice = NULL;
static void __iomem *gpio5_base;


static int dev_open(struct inode*, struct file*);
static int dev_release(struct inode*, struct file*);
static ssize_t dev_read(struct file*, char*, size_t, loff_t*);
static ssize_t dev_write(struct file*, const char*, size_t, loff_t*);
static void timer_callback(struct timer_list *t);

static struct file_operations fops =
{
    .open = dev_open,
    .release = dev_release,
    .read = dev_read,
    .write = dev_write,
};

static struct timer_list my_timer;

static void init_gpio(void)
{
    if (!request_mem_region(GPIO5_BASE_ADDR, sizeof(u32), "gpio5")) {
        pr_err("Failed to request GPIO5 memory region\n");
        return;
    }

    gpio5_base = ioremap(GPIO5_BASE_ADDR, sizeof(u32));
    if (!gpio5_base) {
        pr_err("Failed to map GPIO5 base address\n");
        release_mem_region(GPIO5_BASE_ADDR, sizeof(u32));
        return;
    }

    // Инициализация gpioLed_U2
    iowrite32(0, gpio5_base + GPIO5_DR_OFFSET); // Выключаем LED_U2
    iowrite32((1 << 0), gpio5_base + GPIO5_GDIR_OFFSET); // Устанавливаем LED_U2 как выход

    // Инициализация таймера
    timer_setup(&my_timer, timer_callback, 0);
    mod_timer(&my_timer, jiffies + msecs_to_jiffies(1000));
}

static void cleanup_gpio(void)
{
    if (gpio5_base) {
        iounmap(gpio5_base);
        gpio5_base = NULL;
    }

    release_mem_region(GPIO5_BASE_ADDR, sizeof(u32));
}

static int __init pbus_init(void)
{
    printk(KERN_INFO "pbus: Initializing the pbus module\n");

    majorNumber = register_chrdev(0, DEVICE_NAME, &fops);
    if (majorNumber < 0)
    {
        printk(KERN_ALERT "pbus failed to register a major number\n");
        return majorNumber;
    }

    pbusClass = class_create(THIS_MODULE, CLASS_NAME);
    if (IS_ERR(pbusClass))
    {
        unregister_chrdev(majorNumber, DEVICE_NAME);
        printk(KERN_ALERT "Failed to register device class\n");
        return PTR_ERR(pbusClass);
    }

    pbusDevice = device_create(pbusClass, NULL, MKDEV(majorNumber, 0), NULL, DEVICE_NAME);
    if (IS_ERR(pbusDevice))
    {
        class_destroy(pbusClass);
        unregister_chrdev(majorNumber, DEVICE_NAME);
        printk(KERN_ALERT "Failed to create the device\n");
        return PTR_ERR(pbusDevice);
    }

    init_gpio();  // Инициализация GPIO

    printk(KERN_INFO "pbus: Device created correctly\n");

    return 0;
}

static void __exit pbus_exit(void)
{
    del_timer_sync(&my_timer); // Удаляем таймер перед выходом

    device_destroy(pbusClass, MKDEV(majorNumber, 0));
    class_unregister(pbusClass);
    class_destroy(pbusClass);
    unregister_chrdev(majorNumber, DEVICE_NAME);
    printk(KERN_INFO "pbus: Goodbye from the pbus module\n");

    cleanup_gpio();  // Очистка GPIO
}

static int dev_open(struct inode* inodep, struct file* filep)
{
    printk(KERN_INFO "pbus: Device has been opened\n");
    return 0;
}

static int dev_release(struct inode* inodep, struct file* filep)
{
    printk(KERN_INFO "pbus: Device successfully closed\n");
    return 0;
}

static ssize_t dev_read(struct file* filep, char* buffer, size_t len, loff_t* offset)
{
    u32 gpio5_value;

    if (!gpio5_base) {
        printk(KERN_ALERT "GPIO not initialized\n");
        return -EFAULT;
    }

    gpio5_value = ioread32(gpio5_base);

    if (copy_to_user(buffer, &gpio5_value, sizeof(u32))) {
        printk(KERN_ALERT "Failed to copy GPIO5 value to user\n");
        return -EFAULT;
    }

    printk(KERN_INFO "pbus: Read GPIO5 register value: 0x%08x\n", gpio5_value);
    return sizeof(u32);
}

static ssize_t dev_write(struct file* filep, const char* buffer, size_t len, loff_t* offset)
{
    u32 gpio5_value;

    if (!gpio5_base) {
        printk(KERN_ALERT "GPIO not initialized\n");
        return -EFAULT;
    }

    if (copy_from_user(&gpio5_value, buffer, sizeof(u32))) {
        printk(KERN_ALERT "Failed to copy GPIO5 value from user\n");
        return -EFAULT;
    }

    // Write to GPIO5 register
    iowrite32(gpio5_value, gpio5_base);

    printk(KERN_INFO "pbus: Wrote to GPIO5 register value: 0x%08x\n", gpio5_value);
    return sizeof(u32);
}

static void timer_callback(struct timer_list *t)
{
    static int led_state = 0;

    if (gpio5_base) {
        if (led_state == 0) {
            iowrite32((1 << 0), gpio5_base + GPIO5_DR_OFFSET); // Зажигаем LED_U2
        } else {
            iowrite32(0, gpio5_base + GPIO5_DR_OFFSET); // Тушим LED_U2
        }

        led_state = 1 - led_state; // Переключаем состояние
        mod_timer(&my_timer, jiffies + msecs_to_jiffies(1000)); // Перезапускаем таймер
    }
}

module_init(pbus_init);
module_exit(pbus_exit);

MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("Simple Linux driver for /dev/pbus");
MODULE_VERSION("0.1");
MODULE_LICENSE("GPL");

Постсткриптум i.MX7d

Врядл-ли он у Вас заработает прямо так сразу. Много но. И одно из этих «Но» это то, что шпаргалка теперь под рукой. Чем еще интересен этот малюсенький код, так это тем, что его сгенерировал обычный бесплатный искусственный интеллект после долгого и упорного объяснения что он должен сделать ,и некоторых других аспектов взаимодействия человека и железяки с алгоритмом.