OMAPL138 PRU SOFT-UART实现(一)

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部分的功能已经完成了,完成串口对应的操作集,驱动探测阶段就算成功了。

你可能感兴趣的:(Linux设备驱动)