串口驱动另外两个重要结构体:
270 struct uart_port {
271 spinlock_t lock; /* port lock */
272 unsigned long iobase; /* in/out[bwl] */
273 unsigned char __iomem *membase; /* read/write[bwl] */
274 unsigned int (*serial_in)(struct uart_port *, int);
275 void (*serial_out)(struct uart_port *, int, int) ;
276 unsigned int irq; /* irq number */
277 unsigned long irqflags; /* irq flags */
278 unsigned int uartclk; /* base uart clock */
279 unsigned int fifosize; /* tx fifo size */
280 unsigned char x_char; /* xon/xoff char */
......
timeout */
335 unsigned int type; /* port type */
336 const struct uart_ops *ops;
};
UART驱动程序拥有的每个端口都存在uart_port结构的一个实例,而uart_ops是每个UART驱动所必须支持的硬件操作的入口函数集合。
具体要实现以下功能:
203 * This structure describes all the operations that can be
204 * done on the physical hardware.
205 */
206 struct uart_ops {
207 unsigned int (*tx_empty)(struct uart_port *);
208 void (*set_mctrl)(struct uart_port *, unsigned int mctrl );
209 unsigned int (*get_mctrl)(struct uart_port *);
210 void (*stop_tx)(struct uart_port *);
211 void (*start_tx)(struct uart_port *);
...
218 void (*flush_buffer)(struct uart_port *);
219 void (*set_termios)(struct uart_port *, struct ktermios *new,
220 struct ktermios *old);
...
242 void (*config_port)(struct uart_port *, int);
...
};
UART驱动注册:
1.通过uart_register_driver(struct uart_driver *);向串行核心注册。
2.调用uart_add_one_port(struct uart_driver *, struct uart_port *)注册其支持的每个端口。
而作为其他设备模拟UART,这里是PRU实现串口,也需要填充以上结构,PRU就可以看作一个CPLD,模拟配置
状态寄存器,读数据寄存器,写数据寄存器,及FIFO就可以实现一个基本的UART功能。
906 for (i = 0; i < NR_SUART; i++) {
907 soft_uart->port[i].ops = &pru_suart_ops;
...
921 soft_uart->port[i].line = i; /* need the id/line from pd ev */
922 soft_uart->suart_hdl[i].uartNum = i + 1;
...
938 soft_uart->port[i].serial_out = NULL;
939 uart_add_one_port(&pru_suart_reg, &soft_uart->port[i]);
940 init_MUTEX(&soft_uart->port_sem[i]);
}
以上对uart_port进行了填充,只列出了其中几项,每个端口在进行填充后都必须向串口核心添加。调用uart_add_one_port(),一旦提交后,又会回调该端口OPS的config_port()入口,完成配置,一次注册才算完成。
跟踪了一下 NR_SUART == 3,因为McASP引脚被用作其他外设占用,比如采用RMII的EMAC接口,空闲的McASP引脚只有 7,8,9,10,13,14只能模拟三个串口,
所以在上面只运行来PRU0(最多支持4个),这里也只初始化了三个端口.0,1,2.
在/dev目录下也只会有三个串口设备。若需要扩展,修改引脚即可。
下面列出几个入口:
模块插入:
pru_suart_init()
->uart_register_driver()
->omapl_pru_suart_probe()
->uart_add_one_port()
->pru_suart_config_port()
->pru_suart_request_port()
->pru_suart_request_port()
模块卸载:
omapl_pru_suart_exit()
->platform_driver_unregister()
->omapl_pru_suart_remove()
->uart_remove_one_port()
->pru_suart_release_port()
->pru_softuart_close()
打开端口:
pru_suart_startup()
->suart_intr_clrmask()
->request_irq()
串行发送:
驱动程序收集和UART端口相关的循环缓冲区中待发数据,在驱动程序的start_tx的入口函数omapl_pru_rx_chars,数据存放于struct circ_buf xmit->buf中。
串行接收:
中断处理例程中omapl_pru_tx_chars()中,使用 tty_insert_flip_string()和
tty_flip_buffer_push(tty),将收到的数据推出到tty驱动程序。
NOTE: 带‘+’为添加内容,‘-’为删去内容
1.添加mcasp/pru时钟
arch/arm/mach-davinci/da850.c
1).
#include <linux/cpufreq.h>
+#include <linux/delay.h>
2).
+static struct clk pru_clk = {
+ .name = "pru_ck",
+ .parent = &pll0_sysclk2,
+ .lpsc = DA8XX_LPSC0_DMAX,
+ .flags = ALWAYS_ENABLED,
+};
+
+static struct clk mcasp_pru_clk = {
+ .name = "mcasp_pru",
+ .parent = &pll0_sysclk2,
+ .lpsc = DA8XX_LPSC1_McASP0,
+ .gpsc = 1,
+ .flags = DA850_CLK_ASYNC3,
+};
3).
+ CLK(NULL, "pru_ck", &pru_clk),
CLK(NULL, "uart1", &uart1_clk),
CLK(NULL, "uart2", &uart2_clk),
CLK(NULL, "aintc", &aintc_clk),
CLK(NULL, "arm", &arm_clk),
CLK(NULL, "rmii", &rmii_clk),
CLK("davinci_emac.1", NULL, &emac_clk),
+ CLK(NULL, "mcasp_pru", &mcasp_pru_clk),
2.配置引脚
1).
static const struct mux_config da850_pins[] = {
#ifdef CONFIG_DAVINCI_MUX
- MUX_CFG(DA850, EMA_A_8, 11, 28, 15, 1, false)
+ MUX_CFG(DA850, EMA_A_8, 11, 28, 15, 8, false)
2).
+const short da850_pru_suart_pins[] __initdata = {
+ DA850_AHCLKX, DA850_ACLKX, DA850_AFSX,
+ DA850_AHCLKR, DA850_ACLKR, DA850_AFSR,
+ DA850_AXR_11 DA850_AXR_9,
+ DA850_AXR_12 DA850_AXR_10,
+ -1
+};
+
const short da850_uart1_pins[] __initdata = {
...
- DA850_EMA_A_10, DA850_EMA_A_11, DA850_EMA_A_12, DA850_EMA_A_13,
+ /*DA850_EMA_A_10,*/ DA850_EMA_A_11, DA850_EMA_A_12, DA850_EMA_A_13,
3.添加platform结构
arch/arm/mach-davinci/devices-da8xx.c
1).
#include <linux/serial_8250.h>
+#include <linux/ti_omapl_pru_suart.h>
+#include <linux/can/platform/ti_omapl_pru_can.h>
2).
+#define OMAPL138_PRU_MEM_BASE 0x01C30000
+
+#define OMAPL138_INT_PRU_SUART_1 IRQ_DA8XX_EVTOUT0
+#define OMAPL138_INT_PRU_SUART_2 IRQ_DA8XX_EVTOUT1
+#define OMAPL138_INT_PRU_SUART_3 IRQ_DA8XX_EVTOUT2
+#define OMAPL138_INT_PRU_SUART_4 IRQ_DA8XX_EVTOUT3
+#define OMAPL138_INT_PRU_SUART_5 IRQ_DA8XX_EVTOUT4
+#define OMAPL138_INT_PRU_SUART_6 IRQ_DA8XX_EVTOUT5
+#define OMAPL138_INT_PRU_SUART_7 IRQ_DA8XX_EVTOUT6
+#define OMAPL138_INT_PRU_SUART_8 IRQ_DA8XX_EVTOUT7
+#ifndef CONFIG_OMAPL_SUART_MCASP
+#define CONFIG_OMAPL_SUART_MCASP 0
+#endif
+static struct resource omapl138_pru_suart_resources[] = {
+ {
+ .name = "omapl_pru_suart",
+ .start = OMAPL138_PRU_MEM_BASE,
+ .end = OMAPL138_PRU_MEM_BASE + 0xFFFF,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+#if (CONFIG_OMAPL_SUART_MCASP == 0)
+ .start = DAVINCI_DA8XX_MCASP0_REG_BASE,
+ .end = DAVINCI_DA8XX_MCASP0_REG_BASE + (SZ_1K * 12) - 1,
+ .flags = IORESOURCE_MEM,
+#elif (CONFIG_OMAPL_SUART_MCASP == 1)
+ .start = DAVINCI_DA830_MCASP1_REG_BASE,
+ .end = DAVINCI_DA830_MCASP1_REG_BASE + (SZ_1K * 12) - 1,
+ .flags = IORESOURCE_MEM,
+#elif (CONFIG_OMAPL_SUART_MCASP == 2)
+ .start = DAVINCI_DA830_MCASP2_REG_BASE,
+ .end = DAVINCI_DA830_MCASP2_REG_BASE + (SZ_1K * 12) - 1,
+ .flags = IORESOURCE_MEM,
+#endif
+ },
+
+ {
+ .start = DA8XX_PSC0_BASE,
+ .end = DA8XX_PSC0_BASE + (SZ_1K * 3) - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = DA8XX_PSC1_BASE,
+ .end = DA8XX_PSC1_BASE + (SZ_1K * 3) - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = DA8XX_SHARED_RAM_BASE,
+ .end = DA8XX_SHARED_RAM_BASE + (SZ_1K * 8) - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = OMAPL138_INT_PRU_SUART_1,
+ .end = OMAPL138_INT_PRU_SUART_1,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .start = OMAPL138_INT_PRU_SUART_2,
+ .end = OMAPL138_INT_PRU_SUART_2,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .start = OMAPL138_INT_PRU_SUART_3,
+ .end = OMAPL138_INT_PRU_SUART_3,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .start = OMAPL138_INT_PRU_SUART_4,
+ .end = OMAPL138_INT_PRU_SUART_4,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .start = OMAPL138_INT_PRU_SUART_5,
+ .end = OMAPL138_INT_PRU_SUART_5,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .start = OMAPL138_INT_PRU_SUART_6,
+ .end = OMAPL138_INT_PRU_SUART_6,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .start = OMAPL138_INT_PRU_SUART_7,
+ .end = OMAPL138_INT_PRU_SUART_7,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .start = OMAPL138_INT_PRU_SUART_8,
+ .end = OMAPL138_INT_PRU_SUART_8,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+struct platform_device omapl_pru_suart_device = {
+ .name = "ti_omapl_pru_suart",
+ .id = 1,
+ .num_resources = ARRAY_SIZE(omapl138_pru_suart_resources),
+ .resource = omapl138_pru_suart_resources,
+};
+
+#define OMAPL138_PRU_SUART_VERSION 1
+
+static struct ti_pru_suart_platform_data ti_pru_suart_pdata = {
+ .version = OMAPL138_PRU_SUART_VERSION,
+};
+
+int __init da8xx_register_pru_suart(void)
+{
+ omapl_pru_suart_device.dev.platform_data = &ti_pru_suart_pdata;
+ return platform_device_register(&omapl_pru_suart_device);
+}
4.添加声明
arch/arm/mach-davinci/include/mach/da8xx.h
+int da8xx_register_pru_suart(void);
int da8xx_register_lcdc(struct da8xx_lcdc_platform_data *pdata);
+extern const short da850_pru_suart_pins[];
extern const short da850_mcasp_pins[];
5.
arch/arm/mach-davinci/include/mach/memory.h
#define DAVINCI_DDR_BASE 0x80000000
#define DA8XX_DDR_BASE 0xc0000000
+#define DA8XX_SHARED_RAM_BASE 0x80000000
6.平台注册
arch/arm/mach-davinci/board-omapl138-hawk.c
1).
+static int __init da850_evm_config_pru_suart(void)
+{
+ int ret;
+
+ if (!machine_is_davinci_da850_evm())
+ return 0;
+
+ ret = da8xx_pinmux_setup(da850_pru_suart_pins);
+ if (ret)
+ pr_warning("da850_evm_init: da850_pru_suart_pins mux setup failed: %d\n",
+ ret);
+
+ ret = da8xx_register_pru_suart();
+ if (ret)
+ pr_warning("da850_evm_init: pru suart registration failed: %d\n", ret);
+ return ret;
+}
2)omapl138_hawk_init(void)中调用注册函数,首先要屏蔽MCASP的注册函数,否则
资源会出现冲突。
531 /* if (HAS_MCASP) {
532 if ((HAS_MCBSP0 || HAS_MCBSP1))
533 pr_warning("WARNING: both McASP and McBSP are enable d, "
534 "but they share pins.\n"
535 "\tDisable one of them.\n");
536
537 ret = da8xx_pinmux_setup(da850_mcasp_pins);
538 if (ret)
539 pr_warning("hawk_init: mcasp mux setup failed:"
540 "%d\n", ret);
541
542 da8xx_register_mcasp(0, &omapl138_hawk_snd_data);
543 }
544 */
545 // pru_suart_init
546 omapl138_hawk_config_pru_suart();