#include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/init.h> #include <linux/device.h> #include <linux/cdev.h> #include <linux/io.h> #include <linux/slab.h> #include <asm/uaccess.h>

static int spi_major = 0; static int spi_minor = 0; static int spi_count = 1; static dev_t spi_dev; static struct cdev spi_cdev; static struct class *spi_class = NULL; static struct device *spi_device = NULL;

static volatile unsigned long *io_reg = NULL;

// SPI 时序参数 #define SPI_CLK_PERIOD_NS 1000 // 时钟周期 1us

static int spi_open(struct inode *inode, struct file *filp) { return 0; }

static int spi_release(struct inode *inode, struct file *filp) { return 0; }

static ssize_t spi_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) { return 0; }

static ssize_t spi_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) { unsigned char *data; unsigned int i;

data = kmalloc(count, GFP_KERNEL);
if (!data)
    return -ENOMEM;

if (copy_from_user(data, buf, count)) {
    kfree(data);
    return -EFAULT;
}

// 模拟 SPI 传输
for (i = 0; i < count; i++) {
    unsigned char byte = data[i];
    int j;

    // 发送数据
    for (j = 7; j >= 0; j--) {
        int bit = (byte >> j) & 0x01;
        *io_reg &= ~(1 << 0);   // 设置 SDI 引脚为低电平
        *io_reg &= ~(1 << 1);   // 设置 SCK 引脚为低电平
        *io_reg |= (bit << 0);  // 设置 SDI 引脚
        *io_reg |= (1 << 1);    // 设置 SCK 引脚为高电平
        *io_reg &= ~(1 << 1);   // 设置 SCK 引脚为低电平
    }

    // 接收数据
    byte = 0;
    for (j = 7; j >= 0; j--) {
        *io_reg &= ~(1 << 1);   // 设置 SCK 引脚为低电平
        byte |= ((*io_reg >> 2) & 0x01) << j;   // 读取 SDO 引脚数据
        *io_reg |= (1 << 1);    // 设置 SCK 引脚为高电平
    }
    printk(KERN_INFO "spi_write: %02x\n", byte);
}

kfree(data);
return count;

}

static struct file_operations spi_fops = { .owner = THIS_MODULE, .read = spi_read, .write = spi_write, .open = spi_open, .release = spi_release, };

static int __init spi_init(void) { int ret;

// 分配设备号
if (spi_major) {
    spi_dev = MKDEV(spi_major, spi_minor);
    ret = register_chrdev_region(spi_dev, spi_count, "spi");
} else {
    ret = alloc_chrdev_region(&spi_dev, spi_minor, spi_count, "spi");
    spi_major = MAJOR(spi_dev);
}
if (ret < 0) {
    printk(KERN_ERR "spi: unable to allocate major number\n");
    return ret;
}

// 创建设备类
spi_class = class_create(THIS_MODULE, "spi");
if (IS_ERR(spi_class)) {
    printk(KERN_ERR "spi: failed to create device class\n");
    unregister_chrdev_region(spi_dev, spi_count);
    return PTR_ERR(spi_class);
}

// 创建设备节点
spi_device = device_create(spi_class, NULL, spi_dev, NULL, "spi");
if (IS_ERR(spi_device)) {
    printk(KERN_ERR "spi: failed to create device node\n");
    class_destroy(spi_class);
    unregister_chrdev_region(spi_dev, spi_count);
    return PTR_ERR(spi_device);
}

// 初始化字符设备
cdev_init(&spi_cdev, &spi_fops);
spi_cdev.owner = THIS_MODULE;

// 注册字符设备
ret = cdev_add(&spi_cdev, spi_dev, spi_count);
if (ret < 0) {
    printk(KERN_ERR "spi: failed to add char device\n");
    device_destroy(spi_class, spi_dev);
    class_destroy(spi_class);
    unregister_chrdev_region(spi_dev, spi_count);
    return ret;
}

// 映射 GPIO 控制寄存器
io_reg = ioremap(0x80000000, 0x1000);

return 0;

}

static void __exit spi_exit(void) { // 解除映射 iounmap(io_reg);

// 注销字符设备
cdev_del(&spi_cdev);

// 销毁设备节点和设备类
device_destroy(spi_class, spi_dev);
class_destroy(spi_class);

// 释放设备号
unregister_chrdev_region(spi_dev, spi_count);

}

module_init(spi_init); module_exit(spi_exit);

MODULE_LICENSE("GPL"); MODULE_AUTHOR("Your Name"); MODULE_DESCRIPTION("SPI driver for MIPS");

MIPS 嵌入式 Linux 下使用 IO 口模拟 SPI 驱动代码 (1MHz)

原文地址: https://www.cveoy.top/t/topic/obD0 著作权归作者所有。请勿转载和采集!

免费AI点我,无需注册和登录