Chinaunix首页 | 论坛 | 博客
  • 博客访问: 30995149
  • 博文数量: 230
  • 博客积分: 2868
  • 博客等级: 少校
  • 技术积分: 2223
  • 用 户 组: 普通用户
  • 注册时间: 2009-10-08 21:48
个人简介

Live & Learn

文章分类

全部博文(230)

文章存档

2022年(2)

2019年(5)

2018年(15)

2017年(42)

2016年(24)

2015年(13)

2014年(1)

2012年(5)

2011年(58)

2010年(56)

2009年(9)

我的朋友

分类: 嵌入式

2011-03-31 16:37:30

一、Linux 驱动的基本理论

理解linux驱动,最重要的是要区分devicedriver这两个概念,要搞清devicedriver之间的联系。 device 描述了某个设备所占用的硬件资源(地址、中断),可以理解为硬件方面描述。而driver则是描述了使用和操作该设备的方法、流程、逻辑,可以理解为软件方面的描述。这二者之间的对应联系是一个设备名。

我们来看一下两个结构体的定义:

struct platform_device {

const char* name;

int id;

struct device dev;

u32 num_resources;

struct resource * resource;

struct platform_device_id * id_entry;

struct pdev_archdata archdata;

};

arch/arm/mach-s 3c2440/mach-mini2440.c中初始化

struct device {

struct device *parent;

struct device_private *p;

struct kobject kobj;

const char *init_name;

struct device_type *type;

struct semaphore sem;

struct bus_type *bus;

struct device_driver *driver;

void *platform_data;

struct dev_pm_info power;

#ifdef CONFIG_NUMA

int numa_node;

#endif

u64 *dma_mask;

u64 coherent_dma_mask;

struct device_dma_parameters *dma_parms;

struct list_head dma_pools;

struct dma_coherent_mem *dma_mem;

struct dev_archdata archdata;

dev_t devt;

spinlock_t devres_lock;

struct list_head devres_head;

struct klist_node knode_class;

struct class *class;

const struct attribute_group **groups;

void (*release)(struct device *dev);

};

struct platform_driver {

int (*probe)(struct platform_device *);

int (*remove)(struct platform_device *);

void (*shutdown)(struct platform_device *);

int (*suspend)(struct platform_device *, pm_message_t state);

int (*resume)(struct platform_device *);

struct device_driver driver;

struct platform_device_id *id_table;

};

drivers/serial/8250.c中初始化

struct device_driver {

const char *name;

struct bus_type *bus;

struct module *owner;

const char *mod_name;

bool suppress_bind_attrs;

int (*probe) (struct device *dev);

int (*remove) (struct device *dev);

void (*shutdown) (struct device *dev);

int (*suspend) (struct device *dev, pm_message_t state);

int (*resume) (struct device *dev);

const struct attribute_group **groups;

const struct dev_pm_ops *pm;

struct driver_private *p;

};

二、ST16C554

st16c554是一款集成了4路标准异步串行收发器的串口扩展芯片,也就是通常所说的16c550(也同8250)适应串口。可以将其看成是简单封装了416c550的芯片。它的操作方法和寄存器用法与8250完全相同,因此我们可以用linux内经典的8250驱动来驱动st16c554

三、移植过程

硬件平台:mini2440 系统:linux-2.6.32.2,片选CS3

红色部分为添加的语句。

1. 修改arch/arm/mach-s3c2440/mach-mini2440.c

添加头文件

#ifdef CONFIG_SERIAL_8250_MINI2440_ST16C554

#include

#endif

初始化st16c554platform_device数据结构

static struct plat_serial8250_port mini2440_st16c554_8250_data[] = {

        [0] =
                   {
           .mapbase          = 0x18000000, 
           .irq              = IRQ_EINT4,                
           .uartclk          = 1843200,        
           .iotype           = UPIO_MEM,//UPIO_MEM32,           
           .flags            = (UPF_BOOT_AUTOCONF | UPF_IOREMAP),  
           .regshift         = 0,
    },
        [1] =
                   {
           .mapbase          = 0x18000008, 
           .irq              = IRQ_EINT5,                
           .uartclk          = 1843200,        
           .iotype           = UPIO_MEM,//UPIO_MEM32,           
           .flags            = (UPF_BOOT_AUTOCONF | UPF_IOREMAP),  
           .regshift         = 0,
    },
        [2] =
                   {
           .mapbase          = 0x18000010, 
           .irq              = IRQ_EINT6,                
           .uartclk          = 1843200,        
           .iotype           = UPIO_MEM,//UPIO_MEM32,           
           .flags            = (UPF_BOOT_AUTOCONF | UPF_IOREMAP),  
           .regshift         = 0,
    },
        [3] =
                   {
           .mapbase          = 0x18000018, 
           .irq              = IRQ_EINT8,                
           .uartclk          = 1843200,        
           .iotype           = UPIO_MEM,//UPIO_MEM32,           
           .flags            = (UPF_BOOT_AUTOCONF | UPF_IOREMAP),  
           .regshift         = 0,
    },
   {}
};

static struct platform_device mini2440_device_st16c554 = {
     .name            = "serial8250",
     .id              = PLAT8250_DEV_EXAR_ST16C554,
     .dev             = {
     .platform_data      = &mini2440_st16c554_8250_data,
     },
};

st16c554对应的platform_device数据结构体添加到mini2440对应的platform_device数据结构体中。

static struct platform_device *mini2440_devices[] __initdata = {

&s3c_device_usb,

&s3c_device_rtc,

&s3c_device_lcd,

&s3c_device_wdt,

&s3c_device_i2c0,

&s3c_device_iis,

&mini2440_device_eth,

//#ifdef CONFIG_SERIAL_8250_MINI2440_ST16C554

&mini2440_device_st16c554,

//#endif

&s3c24xx_uda134x,

&s3c_device_nand,

&s3c_device_sdi,

&s3c_device_usbgadget,

};

2.修改 drivers/serial/8250.c

添加头文件

#ifdef CONFIG_SERIAL_8250_MINI2440_ST16C554

#include

#endif

修改S3C2440 四个Bank使用的的 bus width8位,以及设定这四个Bank的总线 timing

static int __init serial8250_init(void)

{

int ret;

if (nr_uarts > UART_NR)

nr_uarts = UART_NR;

printk(KERN_INFO "Serial: 8250/16550 driver, "

"%d ports, IRQ sharing %sabled\n", nr_uarts,

share_irqs ? "en" : "dis");

#ifdef CONFIG_SERIAL_8250_MINI2440_ST16C554

     *((volatile unsigned int *)S3C2410_BWSCON) = ((*((volatile unsigned int *)S3C2410_BWSCON)) & ~(3<<12))| S3C2410_BWSCON_DW3_8;
       //     *((volatile unsigned int *)S3C2410_BANKCON1) = 0x1f7c;
       //     *((volatile unsigned int *)S3C2410_BANKCON2) = 0x1f7c;  
        *((volatile unsigned int *)S3C2410_BANKCON3) = 0x1f7c; //0x5650;//0x1f7c;  //Clock.
       //     *((volatile unsigned int *)S3C2410_BANKCON5) = 0x1f7c;

#endif

#ifdef CONFIG_SPARC

ret = sunserial_register_minors(&serial8250_reg, UART_NR);

#else

serial8250_reg.nr = UART_NR;

ret = uart_register_driver(&serial8250_reg);

#endif

if (ret)

goto out;

#ifdef CONFIG_8250_MINI2440_ST16C554

serial8250_isa_devs =

platform_device_alloc("serial8250", PLAT8250_DEV_EXAR_ST16C554);

#else

serial8250_isa_devs = platform_device_alloc("serial8250",

PLAT8250_DEV_LEGACY);

#endif

修改中断信号的类型为下降沿触发

if (i->head) {

list_add(&up->list, i->head);

spin_unlock_irq(&i->lock);

ret = 0;

} else {

INIT_LIST_HEAD(&up->list);

i->head = &up->list;

spin_unlock_irq(&i->lock);

#ifdef CONFIG_SERIAL_8250_MINI2440_ST16C554

irq_flags |=IRQF_TRIGGER_FALLING;  //硬件取反

#else

irq_flags |= up->port.irqflags;

#endif

ret = request_irq(up->port.irq, serial8250_interrupt,

irq_flags, "serial", i);

if (ret < 0)

serial_do_unlink(i, up);

}

3.修改 drivers/serial/Kconfig

添加一个编译配置选项

config SERIAL_8250_MINI2440_ST16C554

bool "Support MINI2440 Extend ST16C554/554D Quad UART"

depends on SERIAL_8250

help

A chip of ST16C554 is uesed to extend Quad UART on the MINI2440 Board. If you are tinkering with ST16C554, or have a machine with this UART, say Y here.

To compile this driver as a module, choose M here: the module will be called 8250_mini2440_st16c554.

4.重新编译内核

> make menucofig

Device Drivers à

Character Devices à

Serial Drivers à

<*> 8250/16c550 and compatible serial support

[*] Support MINI2440 Extend ST16C554/554D Quad UART

保存.config文件

> make zImage

这样驱动就添加好了,如果你的根文件系统使用了mdev,那么不用做任何修改,mdev会自动地将四个新串口添加在 /dev/serial/tty目录下面,分别为 ttyS0, ttyS1, ttyS2, ttyS3

查看更详细的信息

> cat /proc/tty/driver/serial 将显示四个串口的物理地址和虚拟地址

阅读(1712) | 评论(0) | 转发(0) |
0

上一篇:2416相比2440的十大优势

下一篇:SDL

给主人留下些什么吧!~~