王明列头像
关注

蚂蚁S9矿板PS 自定义PS串口驱动

参考

蚂蚁S9矿板引脚定义.csdn
蚂蚁S9矿板PS 自带的at24c02驱动测试.csdn
axi_uartlite测试.csdn
AXI UART_LITE linux测试
AXI UART16550测试

PL

参考
蚂蚁S9矿板PS 自带的at24c02驱动测试.csdn

设备树

#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/input/input.h>
#include <dt-bindings/media/xilinx-vip.h>
#include <dt-bindings/phy/phy.h>
#include <dt-bindings/interrupt-controller/irq.h>

/ {
	model = "z7 Board ant 789";
	compatible = "xlnx,zynq-zc702", "xlnx,zynq-7000";
	chosen {
		bootargs = "console=ttyPS0,115200 earlycon=cdns,mmio,0xe0000000,115200n8 keep_bootcon earlyprintk root=/dev/mmcblk0p2 rw rootwait";
		stdout-path = "serial0:115200n8";
	};

 	led{
		compatible="ming,led";
		status="okay";
		default-state="on";
      		  led-gpio = <&gpio0 37 GPIO_ACTIVE_HIGH>;
	};

	beeper{
		compatible = "ming,beeper";
		status = "okay";
		default-state = "off";
		beeper-gpio = <&gpio0 38 GPIO_ACTIVE_HIGH>;
	};

	keys {
		compatible = "gpio-keys";
		autorepeat;
                   	gpio-key1 {
			label = "ps_key1";
			gpios = <&gpio0 47 GPIO_ACTIVE_LOW>;
			linux,code = <KEY_UP>;
			gpio-key,wakeup;
			autorepeat;
		};

		gpio-key2 {
			label = "ps_key2";
			gpios = <&gpio0 51 GPIO_ACTIVE_LOW>;
			linux,code = <KEY_DOWN>;
			gpio-key,wakeup;
			autorepeat;
		};
	};
 

};


&uart0 {

    compatible = "ming,myuart";

    status = "okay";

};

myuart.c

/*
 * myuart.c
 *
 * Zynq-7000 PS UART0 自定义 Linux 字符设备驱动
 *
 * 功能:
 * 1. 通过 device tree 匹配 UART0
 * 2. 打开 UART clock
 * 3. 初始化 PS UART
 * 4. 创建 /dev/myuart
 * 5. 支持阻塞式收发
 *
 * 测试:
 *
 * 发送:
 *     echo hello > /dev/myuart
 *
 * 接收:
 *     cat /dev/myuart
 *
 */

#include <linux/sched/signal.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/io.h>
#include <linux/clk.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <linux/delay.h>

#define MYUART_NAME "myuart"

/*
 ============================================================
                     Zynq PS UART寄存器
 ============================================================
*/
/*
 * Control Register
 *
 * 地址偏移 0x00
 */
#define UART_CR        0x00
/*
 * Mode Register
 *
 * 地址偏移 0x04
 */
#define UART_MR        0x04
/*
 * Baud rate generator
 *
 * 地址偏移 0x18
 */
#define UART_BAUDGEN   0x18
/*
 * RX timeout
 *
 * 地址偏移 0x1c
 */
#define UART_RXTOUT    0x1c
/*
 * RX FIFO trigger level
 *
 * 地址偏移 0x20
 */
#define UART_RXWM      0x20
/*
 * Status Register
 *
 * 地址偏移 0x2c
 */
#define UART_SR        0x2c
/*
 * FIFO Register
 *
 * 地址偏移 0x30
 *
 * 写入:
 *      发送数据
 *
 * 读取:
 *      接收数据
 */
#define UART_FIFO      0x30
/*
 * Baud divider
 *
 * 地址偏移 0x34
 */
#define UART_BAUDDIV   0x34

/*
 ============================================================
                     Control Register bit
 ============================================================
*/
/*
 * FIFO复位
 */
#define CR_RXRST       (1<<0)
#define CR_TXRST       (1<<1)
/*
 * RX使能
 */
#define CR_RXEN        (1<<2)
/*
 * RX关闭
 */
#define CR_RXDIS       (1<<3)
/*
 * TX使能
 */
#define CR_TXEN        (1<<4)
/*
 * TX关闭
 */
#define CR_TXDIS       (1<<5)
/*
 * RX timeout复位
 */
#define CR_RSTTO       (1<<6)

/*
 ============================================================
                     Status Register bit
 ============================================================
*/
/*
 * RX FIFO为空
 *
 * 1:
 *      没有数据
 *
 * 0:
 *      有数据
 */
#define SR_RXEMPTY     (1<<1)
/*
 * TX FIFO为空
 */
#define SR_TXEMPTY     (1<<3)
/*
 * TX FIFO满
 */
#define SR_TXFULL      (1<<4)

/*
 ============================================================
                     驱动私有结构体
 ============================================================
*/
struct myuart_dev
{
    /*
     * UART寄存器虚拟地址
     *
     * 物理地址:
     *
     * UART0:
     *      0xe0000000
     *
     */
    void __iomem *base;
    /*
     * PS UART 时钟
     *
     * device tree:
     *
     * clocks =
     *      <&clkc 23>,
     *      <&clkc 40>;
     *
     */
    struct clk *uart_clk;
    /*
     * APB 外设时钟
     */
    struct clk *pclk;
    /*
     * 字符设备号
     */
    dev_t devid;
    /*
     * cdev对象
     */
    struct cdev cdev;
    /*
     * 自动创建 /sys/class/myuart
     */
    struct class *class;
};

static struct myuart_dev uart;

/*
 ============================================================
                     UART寄存器打印
 ============================================================
*/
static void uart_dump(void)
{
    printk("\n----- UART REGISTER -----\n");
    printk("CR       =0x%08x\n", readl(uart.base+UART_CR));
    printk("MR       =0x%08x\n", readl(uart.base+UART_MR));
    printk("BAUDGEN  =0x%08x\n", readl(uart.base+UART_BAUDGEN));
    printk("SR       =0x%08x\n", readl(uart.base+UART_SR));
    printk("BAUDDIV  =0x%08x\n", readl(uart.base+UART_BAUDDIV));
    printk("------------------------\n");
}

/*
 ============================================================
                     UART硬件初始化
 ============================================================
*/
static void uart_hw_init(void)
{
    printk("uart hardware init\n");
    /*
     * 关闭TX/RX
     */
    writel(CR_TXDIS | CR_RXDIS, uart.base+UART_CR);
    /*
     * 复位发送和接收FIFO
     */
    writel(CR_TXRST | CR_RXRST, uart.base+UART_CR);
    /*
     * Mode Register
     *
     * 0x20:
     *
     * 8bit
     * no parity
     * normal mode
     */
    writel(0x20, uart.base+UART_MR);
    /*
     * 设置波特率
     *
     * UART CLK =100MHz
     *
     * baud =
     *
     * 100000000/(62*(13+1))
     *
     * ≈115200
     *
     */
    writel(62, uart.base+UART_BAUDGEN);
    writel(13, uart.base+UART_BAUDDIV);
    /*
     * RX timeout
     *
     * 防止RX一直等待
     */
    writel(0x20, uart.base+UART_RXTOUT);
    /*
     * RX FIFO触发等级
     *
     * 1字节即可读取
     */
    writel(1, uart.base+UART_RXWM);
    /*
     * 开启UART
     */
    writel(CR_TXEN | CR_RXEN | CR_RSTTO, uart.base+UART_CR);
    uart_dump();
}

/*
 ============================================================
                     write函数
 ============================================================
*/
/*
 * 用户:
 *
 * echo hello > /dev/myuart
 *
 * 会进入这里
 *
 */
static ssize_t myuart_write(
        struct file *file,
        const char __user *buf,
        size_t count,
        loff_t *ppos)
{
    size_t i;
    char ch;
    printk("myuart write count=%zu\n", count);
    /*
     * 一个字节一个字节发送
     */
    for(i=0;i<count;i++)
    {
        /*
         * 从用户空间复制数据
         */
        if(copy_from_user(&ch, buf+i, 1))
        {
            return -EFAULT;
        }
        /*
         * 等待TX FIFO有空间
         *
         * SR bit4:
         *
         * 1:
         *      FIFO满
         *
         */
        while(readl(uart.base+UART_SR) & SR_TXFULL)
        {
            cpu_relax();
        }
        /*
         * 写FIFO发送
         */
        writel(ch, uart.base+UART_FIFO);
        printk("tx:%02x\n", ch);
    }
    return count;
}

/*
 ============================================================
                     read函数
 ============================================================
*/
/*
 *
 * cat /dev/myuart
 *
 * 如果没有数据:
 *
 *     阻塞等待
 *
 * 收到数据:
 *
 *     返回
 *
 */
static ssize_t myuart_read(
        struct file *file,
        char __user *buf,
        size_t count,
        loff_t *ppos)
{
    char ch;
    printk("myuart read wait\n");
    while(readl(uart.base+UART_SR) & SR_RXEMPTY)
    {
        /*
         * 如果用户 Ctrl+C
         */
        if(signal_pending(current))
        {
            printk("read interrupted\n");
            return -ERESTARTSYS;
        }
        /*
         * 释放CPU
         */
        msleep(10);
    }
    ch = readl(uart.base+UART_FIFO) & 0xff;
    printk("rx=%02x\n", ch);
    if(copy_to_user(buf, &ch, 1))
    {
        return -EFAULT;
    }
    return 1;
}

/*
 ============================================================
                  file_operations
 ============================================================
*/
static const struct file_operations myuart_fops =
{
    .owner = THIS_MODULE,
    /*
     * 写
     */
    .write = myuart_write,
    /*
     * 读
     */
    .read = myuart_read,
};

/*
 ============================================================
                  probe
 ============================================================
*/
static int myuart_probe(
        struct platform_device *pdev)
{
    struct resource *res;
    int ret;
    printk("myuart probe\n");
    /*
     * 从设备树获取reg
     *
     * uart0:
     *
     * reg=<0xe0000000 0x1000>
     *
     */
    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    if(!res)
    {
        return -EINVAL;
    }
    /*
     * 映射寄存器
     */
    uart.base = devm_ioremap_resource(&pdev->dev, res);
    if(IS_ERR(uart.base))
    {
        return PTR_ERR(uart.base);
    }
    printk("uart virt=%p\n", uart.base);
    /*
     * 获取UART clock
     *
     * 对应:
     *
     * clocks =
     *      <&clkc 23>,
     *      <&clkc 40>;
     *
     */
    uart.uart_clk = devm_clk_get(&pdev->dev, "uart_clk");
    if(IS_ERR(uart.uart_clk))
    {
        printk("get uart_clk failed\n");
        return PTR_ERR(uart.uart_clk);
    }
    uart.pclk = devm_clk_get(&pdev->dev, "pclk");
    if(IS_ERR(uart.pclk))
    {
        printk("get pclk failed\n");
        return PTR_ERR(uart.pclk);
    }
    /*
     * 打开时钟
     */
    clk_prepare_enable(uart.uart_clk);
    clk_prepare_enable(uart.pclk);
    /*
     * 初始化UART硬件
     */
    uart_hw_init();
    /*
     * 注册字符设备
     */
    ret = alloc_chrdev_region(&uart.devid, 0, 1, MYUART_NAME);
    if(ret)
        return ret;
    cdev_init(&uart.cdev, &myuart_fops);
    uart.cdev.owner = THIS_MODULE;
    cdev_add(&uart.cdev, uart.devid, 1);
    /*
     * 创建class
     */
    uart.class = class_create(THIS_MODULE, MYUART_NAME);
    /*
     * 创建设备节点
     *
     * /dev/myuart
     */
    device_create(uart.class, NULL, uart.devid, NULL, MYUART_NAME);
    printk("create /dev/myuart\n");
    return 0;
}

/*
 ============================================================
                  remove
 ============================================================
*/
static int myuart_remove(
        struct platform_device *pdev)
{
    device_destroy(uart.class, uart.devid);
    class_destroy(uart.class);
    cdev_del(&uart.cdev);
    unregister_chrdev_region(uart.devid, 1);
    clk_disable_unprepare(uart.pclk);
    clk_disable_unprepare(uart.uart_clk);
    return 0;
}

/*
 ============================================================
                 device tree匹配
 ============================================================
*/
static const struct of_device_id myuart_of_match[] =
{
    {
        .compatible = "ming,myuart",
    },
    {},
};

MODULE_DEVICE_TABLE(of, myuart_of_match);

/*
 ============================================================
                 platform driver
 ============================================================
*/
static struct platform_driver myuart_driver =
{
    .probe = myuart_probe,
    .remove = myuart_remove,
    .driver =
    {
        .name = "myuart",
        .of_match_table = myuart_of_match,
    },
};

/*
 ============================================================
                 模块入口
 ============================================================
*/
module_platform_driver(myuart_driver);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("ming");
MODULE_DESCRIPTION("Zynq PS UART0 EMIO custom driver");

测试

root@ant:~# insmod myuart.ko
myuart probe
uart virt=03393dc7
uart hardware init

----- UART REGISTER -----
CR       =0x00000114
MR       =0x00000020
BAUDGEN  =0x0000003e
SR       =0x0000000a
BAUDDIV  =0x0000000d
------------------------
create /dev/myuart
root@ant:~# cat /dev/myuart
myuart read wait
rx=aa
▒myuart read wait
rx=22
"myuart read wait
rx=aa
▒myuart read wait
rx=22
"myuart read wait
^Cread interrupted

root@ant:~# echo "aa" >/dev/myuart
myuart write count=3
tx:61
tx:61
tx:0a

转载自 CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/qq_26074053/article/details/163345731

文章来源crawl

评论

赞0

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--