2012.3.26
PRU-suart的分析:
1.PRU operating frequency *
2.PRU mode (Both PRU cores or Single PRU core)
3.McASP serializer to Soft-UART mapping
4.Soft-UART mode (Full Duplex or Half Duplex)
5.Maximum baud rate
6.Receiver over-sampling
7.McASP instance number
8.FIFO time-out
PRU采用DMA传输,结构如下:
64 struct suart_dma {
65 void *dma_vaddr_buff_tx; //DMA传输的虚拟地址
66 void *dma_vaddr_buff_rx; //DMA接收的虚拟地址
67 dma_addr_t dma_phys_addr_tx;//DMA传输的物理地址
68 dma_addr_t dma_phys_addr_rx;//DMA接收的物理地址
69 };
SUART结构:
74 struct omapl_pru_suart {
75 struct uart_port port[NR_SUART]; //串口端口号
76 arm_pru_iomap pru_arm_iomap; //pru的内存映射
77 struct semaphore port_sem[NR_SUART];
78 struct clk *clk_pru; //PRU的时钟
79 struct clk *clk_mcasp; //audio时钟
80 const struct firmware *fw; //固件指针,包含固件大小和内存地址
81 suart_struct_handle suart_hdl[NR_SUART];//每个端口对应一个
82 struct suart_dma suart_dma_addr[NR_SUART];
83 u32 clk_freq_pru;
84 u32 clk_freq_mcasp;
85 u32 tx_loadsz;
86 };
设备的注册,依旧是platform设备模型
板级初始化文件中添加:
178 static __init int omapl138_hawk_config_pru_suart(void)
179 {
180 int ret;
181
182 ret = da8xx_pinmux_setup(da850_pru_suart_pins);//设置引脚
183 if (ret)
184 pr_warning("hawk_init: hawk_pru_suart_pins mux set fa iled:%d\n", ret);
185 ret = da8xx_register_pru_suart();
186 if (ret)
187 pr_warning("hawk_init: pru suart registration failed: %d\n", ret);
188 return ret;
189
190
191 }
192 device_initcall(omapl138_hawk_config_pru_suart);
platform 设备属性如下
189 struct platform_device omapl_pru_suart_device = {
190 .name = "ti_omapl_pru_suart",
191 .id = 1,
192 .num_resources = ARRAY_SIZE(omapl138_pru_suart_resources),
193 .resource = omapl138_pru_suart_resources,
194 };
195
196 #define OMAPL138_PRU_SUART_VERSION 1
197
198 static struct ti_pru_suart_platform_data ti_pru_suart_pdata = {
199 .version = OMAPL138_PRU_SUART_VERSION,
200 };
201
202 int __init da8xx_register_pru_suart(void)
203 {
204 omapl_pru_suart_device.dev.platform_data = &ti_pru_suart_pdata;
205 return platform_device_register(&omapl_pru_suart_device);
206 }
platform device已经注册进系统了,再实现platform driver即可!
1039 static int __init pru_suart_init(void)
1040 {
1041 int ret;
1042
1043 __suart_debug("SUART serial driver loaded\n");
1044 pru_suart_reg.nr = NR_SUART;
1045 ret = uart_register_driver(&pru_suart_reg);
1046 if (ret)
1047 return ret;
1048 ret = platform_driver_register(&serial_omapl_pru_driver);
1049 if (ret)
1050 goto out;
1051
1052 return ret;
1053 out:
1054 uart_unregister_driver(&pru_suart_reg);
1055 return ret;
1056 }
1057
1058 module_init(pru_suart_init);
1059
1060 static void __exit omapl_pru_suart_exit(void)
1061 {
1062 platform_driver_unregister(&serial_omapl_pru_driver);
1063 uart_unregister_driver(&pru_suart_reg);
1064 iounmap(dma_vaddr_buff);
1065 __suart_debug("SUART serial driver unloaded\n");
1066 }
1067
1068 module_exit(omapl_pru_suart_exit);
这里注册了PRU,同时注册了UART,很好理解就像USB一样,首先需要注册USB,然后在注册具体的功能设备。
注册串口框架后,只需往里面添加具体的端口即可,端口数不得超过.nr
748 static struct uart_driver pru_suart_reg = {
749 .owner = THIS_MODULE,
750 .driver_name = DRV_NAME, //驱动名
751 .dev_name = "ttySU", //对应设备名
752 .major = 0, //主设备号为0 ,自动分配
753 .minor = 16, //次设备从16开始
754 .nr = NR_SUART, //max soft-uart count
755 };
重点在Pru的注册和探测:
1028 static struct platform_driver serial_omapl_pru_driver = {
1029 .probe = omapl_pru_suart_probe,
1030 .remove = __devexit_p(omapl_pru_suart_remove),
1031 .suspend = omapl_pru_suart_suspend,//NULL
1032 .resume = omapl_pru__suart_resume,//NULL
1033 .driver = {
1034 .name = DRV_NAME,//与platform device name一致
1035 .owner = THIS_MODULE,
1036 },
1037 };
逐段分析:
慢慢的会扯出一大堆数据结构
static int __devinit omapl_pru_suart_probe(struct platform_device *pdev)
758 {
759 struct omapl_pru_suart *soft_uart;//自己定义的结构体
760 struct ti_pru_suart_platform_data *pdata;/* 私有数据这里只有 版本号 */
761 struct resource *res_mem[PLATFORM_SUART_RES_SZ];//一组资源数组
762 int err, i;
763 unsigned char *fw_data = NULL;
764
765 pdata = pdev->dev.platform_data; //获得版本号
766 if (!pdata) {
767 dev_err(&pdev->dev, "no platform data provided for pru!\n");
768 return -ENODEV;
769 }
770
771 soft_uart = kzalloc(sizeof(struct omapl_pru_suart), GFP_KERNEL);//分配结构体空间,并初始为0
772 if (!soft_uart)
773 return -ENOMEM;
774 /* PLATFORM_SUART_RES_SZ 为5,因为在device resource中,前五个与内存和寄存器分配有关,其余的资源信息标识中断信息等,具体划分
number function addr size
0 PRU_MEM 0x01c30000 0xffff
1 MCASP0_REG 12k
2 PSC0_REG 3k //pru由psc0控制
3 PSC1_REG 3K //mcasp由psc1控制
4 SHARED_RAM 8K //0x8000 0000
5 INT_SUART_1 IRQ_EVTOUT0 3 34
6 2 1 4 35
... ... ... ...
12 8 7 10 41
775 for (i = 0; i < PLATFORM_SUART_RES_SZ; i++) {
776 res_mem[i] = platform_get_resource(pdev, IORESOURCE_MEM, i);
777 if (!res_mem[i]) {
778 dev_err(&pdev->dev,
779 "unable to get pru memory resources!\n");
780 err = -ENODEV;
781 goto probe_exit;
782 }
783 }
784
785 if (!request_mem_region(res_mem[0]->start, resource_size(res_mem[0]),
786 dev_name(&pdev->dev))) {
787 dev_err(&pdev->dev, "pru memory region already claimed!\n");
788 err = -EBUSY;
789 goto probe_exit;
790 }
791 if (!request_mem_region(res_mem[1]->start, resource_size(res_mem[1]),
792 dev_name(&pdev->dev))) {
793 dev_err(&pdev->dev, "mcasp memory region already claimed!\n");
794 err = -EBUSY;
795 goto probe_exit_1;
796 }
......
810 soft_uart->pru_arm_iomap.pru_io_addr = ioremap(res_mem[0]->start, resource_size(res_mem[0]));
/* IO映射,这里映射的res_mem[0],即是PRU的单元地址 ,下面还会分别映射
MCASP,PSC0 PSC1 SYSCFG的寄存器空间*/
850 soft_uart->clk_pru = clk_get(&pdev->dev, "pru_ck");
//获取pru时钟,
851 if (IS_ERR(soft_uart->clk_pru)) {
852 dev_err(&pdev->dev, "no clock available: pru_ck\n");
853 err = PTR_ERR(soft_uart->clk_pru);
854 soft_uart->clk_pru = NULL;
855 goto probe_exit_iounmap_2;
856 }
857 soft_uart->clk_freq_pru = clk_get_rate(soft_uart->clk_pru);//根据时钟获取串波特率范围
858
859 soft_uart->clk_mcasp = clk_get(NULL, "mcasp_pru"); //150MHz
//获取MCASP 时钟
860 if (IS_ERR(soft_uart->clk_mcasp)) {
861 dev_err(&pdev->dev, "no clock available: mcasp\n");
862 err = PTR_ERR(soft_uart->clk_mcasp);
863 soft_uart->clk_mcasp = NULL;
864 goto probe_exit_clk_pru;
865 }
866 soft_uart->clk_freq_mcasp = clk_get_rate(soft_uart->clk_mcasp);
867 clk_enable(soft_uart->clk_mcasp);
868 clk_enable(soft_uart->clk_pru);
/* PRU,MCASP 的时钟系统默认是没有定义的,需要自己去完善,采用PLL0-SYSCLK2作为时钟源 ,接下来必须使能时钟 */
869 err = request_firmware(&soft_uart->fw, "PRU_SUART_Emulation.bin",
870 &pdev->dev);
871 if (err) {
872 dev_err(&pdev->dev, "can't load firmware\n");
873 err = -ENODEV;
874 goto probe_exit_clk;
875 }
request_firmware这个函数貌似在内核中没有见过,查找。。。迷茫了。。。继续往下面看看。。。
876 dev_info(&pdev->dev, "fw size %td. downloading...\n",
877 soft_uart->fw->size);
878
879 /* download firmware into pru & init */
880 fw_data = kmalloc(soft_uart->fw->size, GFP_KERNEL);
881 memcpy((void *)fw_data, (const void *)soft_uart->fw->data,
882 soft_uart->fw->size);
883
884 dma_phys_addr = res_mem[4]->start;
885 dma_vaddr_buff = ioremap(res_mem[4]->start, resource_size(res_mem[4]));
886 if (!dma_vaddr_buff) {
887 __suart_err("Failed to allocate shared ram.\n");
888 err = -EFAULT;
889 goto probe_exit_clk;
890 }
891
892 soft_uart->pru_arm_iomap.pFifoBufferPhysBase = (void *)dma_phys_addr; //共享内存,即FIFO的物理地址
893 soft_uart->pru_arm_iomap.pFifoBufferVirtBase = (void *)dma_vaddr_buff;//映射后的虚拟地址
894
895 err = pru_softuart_init(SUART_DEFAULT_BAUD, SUART_DEFAULT_BAUD,
896 SUART_DEFAULT_OVRSMPL, fw_data,
897 soft_uart->fw->size, &soft_uart->pru_arm_iomap);
if (err) {
899 dev_err(&pdev->dev, "pru init error\n");
900 err = -ENODEV;
901 kfree((const void *)fw_data);
902 goto probe_release_fw;
903 }
904 kfree((const void *)fw_data);
查找资料后,再根据PRU的特定,清楚了。
PRU有专门的指令,不支持C语言编程,使用pasm进行汇编和链接,故SUART的设置应该是专门的代码完成的,即固件程序,这里为PRU_SUART_Emulation.bin
在探测阶段,在文件系统/lib/firmware/目录下找到该文件时,会加载到firmware *指向的空间等待复制到PRU的IRAM中,当然内核也要支持firmware,
在设备通用配置里添加对用户空间加载固件的支持。
现在返回看看那个PRU的结构体成员。
const struct firmware *fw;
err= request_firmware(&soft_uart->fw, "PRU_SUART_Emulation.bin",
,&pdev->dev);
这个函数干了什么,只有看代码才知道了
465 static int
466 _request_firmware(const struct firmware **firmware_p, const char *name,
467 struct device *device, int uevent)
468 {
469 struct device *f_dev;
470 struct firmware_priv *fw_priv;
471 struct firmware *firmware;
472 struct builtin_fw *builtin;
473 int retval;
474
475 if (!firmware_p)
476 return -EINVAL;
477
478 *firmware_p = firmware = kzalloc(sizeof(*firmware), GFP_KERNEL); //分配内存空间
479 if (!firmware) {
480 dev_err(device, "%s: kmalloc(struct firmware) failed\n",
481 __func__);
482 retval = -ENOMEM;
483 goto out;
484 }
...
}
。。。继续往下读,功能就清楚了,根据传入的*name,builtin比较,若有匹配的将该文件的地址及大小传给对应的firmware.
那么原文的代码也清楚了,在文件系统/lib/firmware目录下找到“PRU_SUART_Emulation.bin”,将其地址和大小传给firmware.然后拷贝到fw_data指向的地址空间。
pru_softuart_ini()这个就是对功能的具体初始化来,这里是软串口。
且看函数原型:
33 /*
34 * suart Initialization routine
35 */
36 short pru_softuart_init(unsigned int txBaudValue,//115200
37 unsigned int rxBaudValue,//115200
38 unsigned int oversampling,//1
39 unsigned char *pru_suart_emu_code,
40 unsigned int fw_size, arm_pru_iomap * arm_iomap_pru)
传入了串口的传输/接收波特率,模式8X/16X,初始化固件,大小,固件下载地址(PRU).
到这里又需要了解串口驱动的架构了。
369 struct uart_driver {
370 struct module *owner;
371 const char *driver_name;
372 const char *dev_name;
373 int major;
374 int minor;
375 int nr;
376 struct console *cons;
377
378 /*
379 * these are private; the low level driver should not
380 * touch these; they should be initialised to NULL
381 */
382 struct uart_state *state;
383 struct tty_driver *tty_driver;
384 };
在前面系统进行串口驱动注册时需要该结构体。
大概了解了串口驱动框架,还是回到pru_softuart_init();
arm_pru_iomap 结构体包含了PRU,PSC0/1,McASP,SYSCFG,FIFO的虚拟地址集合
...
55 omapl_addr = (unsigned int)arm_iomap_pru->syscfg_io_addr;
56
57 omapl_addr = (unsigned int)arm_iomap_pru->psc1_io_addr;
58 suart_mcasp_psc_enable(omapl_addr);
59
60 omapl_addr = (unsigned int)arm_iomap_pru->mcasp_io_addr;
61 // Configure McASP0
62 suart_mcasp_config(omapl_addr, txBaudValue, rxBaudValue, ove rsampling,
63 arm_iomap_pru);
64 retval = arm_to_pru_intr_init();
...
依次分析上面的suart_mcasp_psc_enable(),suart_mcasp_config(),arm_to_pru_intr_init().
1.suart_mcasp_psc_enable();
这没什么分析,首先判断PSC设置是否完成,然后设置McASP使能,等待完成返回,这里是阻塞等待。
无数据传输,可能还没初始化,电源时能,等待
2.suart_mcasp_config();
由于PRU外扩UART是占用McASPy引脚,对于hawkboard 9-12空闲
/*
131 *====================
132 * API implementations
133 *====================
134 */
135 /*
136 * McASP configuration routine
137 */
138
139 void suart_mcasp_config(unsigned int mcasp_addr,
140 unsigned int txBaudValue,
141 unsigned int rxBaudValue,
142 unsigned int oversampling,
143 arm_pru_iomap * pru_arm_iomap)
到这,该去了解McASP的配置相关的了。
McASP --多通道音频串行端口
其速率波特率设置如下,以发送为例:
234 short suart_mcasp_tx_baud_set(unsigned int txBaudValue,
235 arm_pru_iomap * pru_arm_iomap)
236 {
237 unsigned int clkDivVal;
238 unsigned int loopCnt;
239 short status = SUART_SUCCESS;
240 short foundVal = SUART_FALSE;
241
242 CSL_McaspRegsOvly mcasp0Regs =
243 (CSL_McaspRegsOvly) pru_arm_iomap->mcasp_io_addr;
244
245 /* Search the supported baud rate in the table ?13? */
246 for (loopCnt = 0; loopCnt < SUART_NUM_OF_BAUDS_SUPPORTED; loopCnt++ ) {
247 if (txBaudValue == lt_tx_baud_rate[loopCnt][0]) {
248 foundVal = SUART_TRUE;
249 break;
250 }
251 }
//上面是查找输入的波特率是否是McASP产生的,一共是13组,若没有返回无效。
252 if (foundVal == SUART_TRUE) {
253 clkDivVal = lt_tx_baud_rate[loopCnt][2];
254
255 mcasp0Regs->ACLKXCTL |=
256 clkDivVal << CSL_MCASP_ACLKXCTL_CLKXDIV_SHIFT;
257 clkDivVal = lt_tx_baud_rate[loopCnt][3]; /* starts f rom 0 */
258 mcasp0Regs->AHCLKXCTL |=
259 clkDivVal << CSL_MCASP_AHCLKXCTL_HCLKXDIV_SHIFT;
260 } else {
261 return SUART_INVALID_TX_BAUD;
262 }
263 return (status);
264 }
给出由波特率计算分频的公式,
传输:
ACLKX = (AUXCLK)/(CLKXDIV * HCLKXDIV)=
1-32 1-4096
AUXCLK = 24MHZ
BAUD = 115200bps
Divisor = (CLKXDIV*HCLKDIV) = 208
接收:
ACLKX = (AUXCLK)/(CLKXDIV * HCLKXDIV)*Oversampling
BAUD =115200
Davisor = 208/8 = 26
将以上的值写入到对应的寄存器即略过。。。
3.arm_to_pru_intr_init();
这才进入到真正与PRU相关的地方
函数名也体现了CPU到PRU的中断初始化,即中断映射,接着打开PRU,将固件下载至PRU的IRAM。
70 pru_enable(0, arm_iomap_pru);
71 pru_enable(1, arm_iomap_pru);
72
73 pru_load(PRU_NUM0, (unsigned int *)pru_suart_emu_code,
74 (fw_size / sizeof(unsigned int)), arm_iomap_pru);
75 pru_load(PRU_NUM1, (unsigned int *)pru_suart_emu_code,
76 (fw_size / sizeof(unsigned int)), arm_iomap_pru);
至于载入过程即是简单的COPY代码进IRAM:
// Load the specified PRU with code
34 Uint32 pru_load(Uint8 pruNum, Uint32 * pruCode, Uint32 codeSizeI nWords,
35 arm_pru_iomap * pru_arm_iomap){{
{
...
50 // Copy dMAX code to its instruction RAM
51 for (i = 0; i < codeSizeInWords; i++) {
52 pruIram[i] = pruCode[i];
53 }
..
}
程序到这里固件的加载已经完成,可以执行PRU单元的内容了。
78 suart_set_pru_id(0);
79 suart_set_pru_id(1);
80
81 pru_run(PRU_NUM0, arm_iomap_pru);
//只运行了PRU0因为只使用了三个串口
82 // pru_run(PRU_NUM1, arm_iomap_pru);
83
84 /* Initialize gUartStatuTable */
85 for (idx = 0; idx < 8; idx++) {
86 gUartStatuTable[idx] = ePRU_SUART_UART_FREE;
87 }
//gUartStatuTable 作为全局UART状态标识,防止重复打开
88
89 return status;
90 }
到这里PRU部分的功能已经完成了,完成串口对应的操作集,驱动探测阶段就算成功了。