参考
蚂蚁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



