欢迎光临
我们一直在努力

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

赞(0)
未经允许不得转载:171主机测评 » 蚂蚁S9矿板PS 自定义PS串口驱动
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址