这几天移植在linux内核下调试16C554,扩展四串口驱动,用的是linux内核自带的8250.c驱动。
(1)8250.c内自带serial8250_init(),它在内核初始化时完成驱动的注册,所以我们需要添加的是设备资源。
(2)在arch/arm/mach-s3c2410/mach-smdk2410.c中添加四串口设备:
staticstruct plat_serial8250_port my2410_st16c554_8250_data[] = {
{ //ARM体系结构中将IO和MEMORY统一编址,
//因此这里使用的是Memory地址
.mapbase = (unsigned long)S3C2410_CS1 +0x0FC0,
.irq =IRQ_EINT2,
.uartclk =1843200,
.iotype =UPIO_MEM32,
.flags = (UPF_BOOT_AUTOCONF |UPF_IOREMAP),
.regshift = 0,
},
//PORT(S3C2410_CS1 + 0x0, IRQ_EINT0),
{ //ARM体系结构中将IO和MEMORY统一编址,
//因此这里使用的是Memory地址
.mapbase = (unsigned long)S3C2410_CS1 +0x0FA0,
.irq =IRQ_EINT8,
.uartclk =1843200,
.iotype =UPIO_MEM32,
.flags = (UPF_BOOT_AUTOCONF |UPF_IOREMAP),
.regshift = 0,
},
//PORT(S3C2410_CS2 + 0x0, IRQ_EINT1),
{ //ARM体系结构中将IO和MEMORY统一编址,
//因此这里使用的是Memory地址
.mapbase = (unsigned long)S3C2410_CS1 +0x0F60,
.irq =IRQ_EINT11,
.uartclk =1843200,
.iotype =UPIO_MEM32,
.flags = (UPF_BOOT_AUTOCONF |UPF_IOREMAP),
.regshift = 0,
},
//PORT(S3C2410_CS3 + 0x0, IRQ_EINT2),
{ //ARM体系结构中将IO和MEMORY统一编址,
//因此这里使用的是Memory地址
.mapbase = (unsigned long)S3C2410_CS1 +0x0EE0,
.irq =IRQ_EINT19,
.uartclk =1843200,
.iotype =UPIO_MEM32,
.flags = (UPF_BOOT_AUTOCONF |UPF_IOREMAP),
.regshift = 0,
},
//PORT(S3C2410_CS5 + 0x0, IRQ_EINT3),
{ },
};
static struct platform_device my2410_device_st16c554 = {
.name = "serial8250",
.id = PLAT8250_DEV_PLATFORM,
.dev = {
.platform_data = &my2410_st16c554_8250_data,
},
};
然后在smdk2410_devices中添加上面定义的设备。
static struct platform_device *smdk2410_devices[] __initdata ={
&s3c_device_usb,
&s3c_device_lcd,
&s3c_device_wdt,
&s3c_device_i2c,
&s3c_device_iis,
&s3c_device_nand,
&my2410_device_st16c554,
};
(3)下面就要修改8250.c
首先在serial8250_init()中,serial8250_isa_devs =platform_device_register_simple("serial8250",PLAT8250_DEV_PLATFORM,NULL,0)这个函数有点诡异,我起先对它没有修改,但是内核启动之后设备总是注册不成功,找了找原因,实在不知道其中的PLAT8250_DEV_PLATFORM是什么意思,故把它改成-1,再次启动内核,貌似有了点进展。
但是在内核启动的过程中,总是停止在serial8250_config_port()中autoconfig()中的serial_in()中,找到源代码查看,发现serial_in()中的readl()出了问题。readl()是访问内存空间虚拟地址的一个函数,在调用它之前必须将I/O空间映射到虚拟内存空间,所以估计是没有映射。
找到:
static int serial8250_request_std_resource(struct uart_8250_port*up)
{
unsigned int size = 8<<up->port.regshift;
int ret = 0;
printk("the port's size is %d",size);
switch (up->port.iotype) {
case UPIO_MEM:
if(!up->port.mapbase)
break;
if(!request_mem_region(up->port.mapbase, size,"serial")) {
ret =-EBUSY;
break;
}
if(up->port.flags & UPF_IOREMAP){
up->port.membase= ioremap(up->port.mapbase, size);
if(!up->port.membase) {
release_mem_region(up->port.mapbase,size);
ret= -ENOMEM;
}
}
break;
case UPIO_HUB6:
case UPIO_PORT:
if(!request_region(up->port.iobase, size,"serial"))
ret =-EBUSY;
break;
}
return ret;
}
发现里面的iotype没有我们想要的UPIO_MEM32,所以没有进行request_mem_region和ioremap,故将UPIO_MEM改为UPIO_MEM32。
再次编译,启动,可以注册了,下一步就要进行测试
-----------------------------------------------------------------------------------------------------
1、参照网页《我在Linux-2.6.32.2下为ST16C554移植驱动的经历》编写
sc16c554四串口驱动;
原文:http://blog.chinaunix.net/u3/116495/showart.php?id=2272834
2、出现问题:
(1)首次没有移植成功;是因为sc16c554芯片硬件上复位信号电平给弄反了,以致于编译通过后,虽然用cat/proc/tty/driver/serial命令也出现了四个串口,但是串口信息不对,全为UNKOWN设备。后放弃。
(2)遂自己编写驱动查找原因,编写了字符型设备来驱动16c554,发现片选、数据、和地址信号都有,就是没有中断,就查硬件设备三要素(电源、时钟、复位),后来发现设备复位电平不对,把芯片管脚翘起,收发正常。
(3)芯片正常后,仿照8250.c编写tty驱动,编写成功,linux2.6.13和linux2.6.32.2上使用supervivi在norflash上引导正常,但是用友善之臂提供的vboot引导出现问题,不成功。老是报内核地址为NULL,非法引用,然后就是一片寄存器值。看vboot源码没有找到问题所在,然后按照(1)的方法又进行二次移植。
(4)二次移植时,居然正常了,欣喜中。但很快发现数据发送不对,每次只能发送16个字节。问题出在
#definePORT(_base,_irq) \
{
.mapbase = (unsignedlong)_base, \
.irq =_irq, \
.uartclk =16000000, \
.iotype =UPIO_MEM32, \
.flags = (UPF_BOOT_AUTOCONF | UPF_IOREMAP), \
.regshift = 0, \
}
iotype应为UPIO_MEM,即8位读写。
更改后即可。
(3)ttS3收发不对,使用cat/proc/tty/driver/serial命令查看时总是被识别为XScale设备和UNKOWN设备。后来发现是
*((volatile unsigned int *)S3C2410_BWSCON) =
((*((volatile unsigned int*)S3C2410_BWSCON)) &~(0x30333<<4))
| S3C2410_BWSCON_DW1_8
| S3C2410_BWSCON_DW2_8
| S3C2410_BWSCON_DW3_8
| S3C2410_BWSCON_DW4_8;
设置不对,应该为
*((volatile unsigned int *)S3C2410_BWSCON) =
((*((volatile unsigned int*)S3C2410_BWSCON)) &~(0x03333<<4))
| S3C2410_BWSCON_DW1_8
| S3C2410_BWSCON_DW2_8
| S3C2410_BWSCON_DW3_8
| S3C2410_BWSCON_DW4_8;
因为原作者用的CS1,CS2,CS3,CS5,我这里CS5变成了CS4,所以0x30333<<要变成0x03333。
(5)驱动正常后,参照linux-2.6.32.2\drivers\serial\8250_exar_st16c554.c,又看了mach-mini2440.c的源文件,编写了8250_exar_sc16c554.c驱动(恢复了mach-mini2440.c和8250.c中关于串口部分的原貌见附件),功能测试正常。
3、关于8250_exar_sc16c554.
硬件平台:s3c2440 自制印制板
操作系统:linux-2.6.32.2
编 译 器:gcc-linux-arm4.3.2
#include <linux/module.h>
#include <linux/init.h>
#include<linux/serial_8250.h>
#include<mach/regs-mem.h>
#include <mach/map.h>
#definePORT(_base,_irq) \
{ .iobase = (unsignedlong)_base, \
.mapbase = (resource_size_t)_base, \
.irq = (unsignedint)_irq, \
.irqflags =IRQF_TRIGGER_RISING, \
.uartclk =16000000, \
.iotype =UPIO_MEM, \
.flags =(UPF_BOOT_AUTOCONF | UPF_IOREMAP), \
.regshift = 0, \
}
static struct plat_serial8250_port exar_data[] = {
PORT(S3C2410_CS1 + 0x0, IRQ_EINT0),
PORT(S3C2410_CS2 + 0x0, IRQ_EINT1),
PORT(S3C2410_CS3 + 0x0, IRQ_EINT2),
PORT(S3C2410_CS4 + 0x0, IRQ_EINT3),
{ },
};
static struct platform_device exar_device = {
.name ="serial8250",
.id =PLAT8250_DEV_EXAR_ST16C554,
.dev ={
.platform_data =exar_data,
},
};
static int __init exar_init(void)
{
*((volatile unsigned int *)S3C2410_BWSCON) =
((*((volatile unsigned int *)S3C2410_BWSCON)) &~(0x03333<<4))
| S3C2410_BWSCON_DW1_8
| S3C2410_BWSCON_DW2_8
| S3C2410_BWSCON_DW3_8
| S3C2410_BWSCON_DW4_8;
returnplatform_device_register(&exar_device);
}
module_init(exar_init);
MODULE_AUTHOR("Kou Jinqiao");
MODULE_DESCRIPTION("8250 serial probe module for Exarcards-sc16c554");
MODULE_LICENSE("GPL");
把8250_exar_sc16c554.c放入linux/drivers/serial/下,修改Makefile文件,重新makemenuconfig 选中8250_exar_sc16c554,然后make zImage即可。
这几天移植在linux内核下调试16C554,扩展四串口驱动,用的是linux内核自带的8250.c驱动。
(1)8250.c内自带serial8250_init(),它在内核初始化时完成驱动的注册,所以我们需要添加的是设备资源。
(2)在arch/arm/mach-s3c2410/mach-smdk2410.c中添加四串口设备:
staticstruct plat_serial8250_port my2410_st16c554_8250_data[] = {
{ //ARM体系结构中将IO和MEMORY统一编址,
//因此这里使用的是Memory地址
.mapbase = (unsigned long)S3C2410_CS1 +0x0FC0,
.irq =IRQ_EINT2,
.uartclk =1843200,
.iotype =UPIO_MEM32,
.flags = (UPF_BOOT_AUTOCONF |UPF_IOREMAP),
.regshift = 0,
},
//PORT(S3C2410_CS1 + 0x0, IRQ_EINT0),
{ //ARM体系结构中将IO和MEMORY统一编址,
//因此这里使用的是Memory地址
.mapbase = (unsigned long)S3C2410_CS1 +0x0FA0,
.irq =IRQ_EINT8,
.uartclk =1843200,
.iotype =UPIO_MEM32,
.flags = (UPF_BOOT_AUTOCONF |UPF_IOREMAP),
.regshift = 0,
},
//PORT(S3C2410_CS2 + 0x0, IRQ_EINT1),
{ //ARM体系结构中将IO和MEMORY统一编址,
//因此这里使用的是Memory地址
.mapbase = (unsigned long)S3C2410_CS1 +0x0F60,
.irq =IRQ_EINT11,
.uartclk =1843200,
.iotype =UPIO_MEM32,
.flags = (UPF_BOOT_AUTOCONF |UPF_IOREMAP),
.regshift = 0,
},
//PORT(S3C2410_CS3 + 0x0, IRQ_EINT2),
{ //ARM体系结构中将IO和MEMORY统一编址,
//因此这里使用的是Memory地址
.mapbase = (unsigned long)S3C2410_CS1 +0x0EE0,
.irq =IRQ_EINT19,
.uartclk =1843200,
.iotype =UPIO_MEM32,
.flags = (UPF_BOOT_AUTOCONF |UPF_IOREMAP),
.regshift = 0,
},
//PORT(S3C2410_CS5 + 0x0, IRQ_EINT3),
{ },
};
static struct platform_device my2410_device_st16c554 = {
.name = "serial8250",
.id = PLAT8250_DEV_PLATFORM,
.dev = {
.platform_data = &my2410_st16c554_8250_data,
},
};
然后在smdk2410_devices中添加上面定义的设备。
static struct platform_device *smdk2410_devices[] __initdata ={
&s3c_device_usb,
&s3c_device_lcd,
&s3c_device_wdt,
&s3c_device_i2c,
&s3c_device_iis,
&s3c_device_nand,
&my2410_device_st16c554,
};
(3)下面就要修改8250.c
首先在serial8250_init()中,serial8250_isa_devs =platform_device_register_simple("serial8250",PLAT8250_DEV_PLATFORM,NULL,0)这个函数有点诡异,我起先对它没有修改,但是内核启动之后设备总是注册不成功,找了找原因,实在不知道其中的PLAT8250_DEV_PLATFORM是什么意思,故把它改成-1,再次启动内核,貌似有了点进展。
但是在内核启动的过程中,总是停止在serial8250_config_port()中autoconfig()中的serial_in()中,找到源代码查看,发现serial_in()中的readl()出了问题。readl()是访问内存空间虚拟地址的一个函数,在调用它之前必须将I/O空间映射到虚拟内存空间,所以估计是没有映射。
找到:
static int serial8250_request_std_resource(struct uart_8250_port*up)
{
unsigned int size = 8<<up->port.regshift;
int ret = 0;
printk("the port's size is %d",size);
switch (up->port.iotype) {
case UPIO_MEM:
if(!up->port.mapbase)
break;
if(!request_mem_region(up->port.mapbase, size,"serial")) {
ret =-EBUSY;
break;
}
if(up->port.flags & UPF_IOREMAP){
up->port.membase= ioremap(up->port.mapbase, size);
if(!up->port.membase) {
release_mem_region(up->port.mapbase,size);
ret= -ENOMEM;
}
}
break;
case UPIO_HUB6:
case UPIO_PORT:
if(!request_region(up->port.iobase, size,"serial"))
ret =-EBUSY;
break;
}
return ret;
}
发现里面的iotype没有我们想要的UPIO_MEM32,所以没有进行request_mem_region和ioremap,故将UPIO_MEM改为UPIO_MEM32。
再次编译,启动,可以注册了,下一步就要进行测试。