linux移植记录(一) - 串口驱动

    第一次移植Linux系统,新手上路,花了将近一个月的时间,夹杂着失望与喜悦,现在终于可以悠闲的喝点咖啡,写些博客了。

 

设备:交换机

开发板:BCM95836robo

 

由于开发环境比较特殊,u-boot无法使用,使用broadcom公司的CFE。

遇到的第一个问题是串口输出的问题,错误输出信息如下:

                       Closing network. eth1: 9277 sent, 105052 received, 0 interrupts Starting program at 0x80005690 Linux version 2.6.34 ([email protected]) (gcc version 4.0.0 (DENX ELDK 4.1 4.0.0)) #16 Tue Jun 29 09:29:27 CST 2010 bootconsole [early0] enabled CPU revision is: 00029006 (Broadcom BCM3302) ssb: Sonics Silicon Backplane found at address 0x18000000 Determined physical RAM map: memory: 02000000 @ 00000000 (usable) Initrd not found or empty - disabling initrd Zone PFN ranges: Normal 0x00000000 -> 0x00002000 Movable zone start PFN for each node early_node_map[1] active PFN ranges 0: 0x00000000 -> 0x00002000 Built 1 zonelists in Zone order, mobility grouping on. Total pages: 8128 Kernel command line: root=/dev/mtdblock2 rootfstype=squashfs,jffs2 init=/etc/pre init noinitrd console=ttyS0,9600 PID hash table entries: 128 (order: -3, 512 bytes) Dentry cache hash table entries: 4096 (order: 2, 16384 bytes) Inode-cache hash table entries: 2048 (order: 1, 8192 bytes) Primary instruction cache 16kB, VIPT, 2-way, linesize 16 bytes. Primary data cache 16kB, 2-way, VIPT, cache aliases, linesize 16 bytes Memory: 27540k/32768k available (3476k kernel code, 5228k reserved, 864k data, 1 92k init, 0k highmem) Hierarchical RCU implementation. RCU-based detection of stalled CPUs is enabled. NR_IRQS:128 Console: colour dummy device 80x25 xfc 8250 debug sentence! Calibrating delay loop... 198.65 BogoMIPS (lpj=397312) Security Framework initialized SELinux: Initializing. Mount-cache hash table entries: 512 NET: Registered protocol family 16 bio: create slab <bio-0> at 0 vgaarb: loaded NetLabel: Initializing NetLabel: domain hash size = 128 NetLabel: protocols = UNLABELED CIPSOv4 NetLabel: unlabeled traffic allowed by default Switching to clocksource MIPS PCI: Fixing up bridge 0000:00:00.0 PCI: Fixing up device 0000:00:00.0 PCI: Fixing latency timer of device 0000:00:00.0 to 168 NET: Registered protocol family 2 IP route cache hash table entries: 1024 (order: 0, 4096 bytes) TCP established hash table entries: 1024 (order: 1, 8192 bytes) TCP bind hash table entries: 1024 (order: 2, 20480 bytes) TCP: Hash tables configured (established 1024 bind 1024) TCP reno registered UDP hash table entries: 128 (order: 0, 6144 bytes) UDP-Lite hash table entries: 128 (order: 0, 6144 bytes) NET: Registered protocol family 1 init_vdso successfull audit: initializing netlink socket (disabled) type=2000 audit(0.934:1): initialized VFS: Disk quotas dquot_6.5.2 Dquot-cache hash table entries: 1024 (order 0, 4096 bytes) JFS: nTxBlock = 215, nTxLock = 1721 msgmni has been set to 53 cryptomgr_test used greatest stack depth: 6848 bytes left cryptomgr_test used greatest stack depth: 6752 bytes left alg: No test for stdrng (krng) Block layer SCSI generic (bsg) driver version 0.4 loaded (major 254) io scheduler noop registered io scheduler deadline registered io scheduler cfq registered (default) pci_hotplug: PCI Hot Plug PCI Core version: 0.5 shpchp: Standard Hot Plug PCI Controller Driver version: 0.4 pci-stub: invalid id string "" Serial: 8250/16550 driver, 4 ports, IRQ sharing enabled brd: module loaded CAN device driver interface sja1000 CAN netdevice driver b44: b44.c:v2.0 b44: Invalid MAC address found in EEPROM b44 ssb0:0: Problem fetching invariants of chip, aborting b44: probe of ssb0:0 failed with error -22 b44: Invalid MAC address found in EEPROM b44 ssb0:1: Problem fetching invariants of chip, aborting b44: probe of ssb0:1 failed with error -22 TCP bic registered Initializing XFRM netlink socket NET: Registered protocol family 17 can: controller area network core (rev 20090105 abi 8) NET: Registered protocol family 29 turn off boot console early0  

        在输出turn off boot console early0后,不在有信息打印,此时实际是启用linux自己的串口驱动ttyS,显然失败了。

 

简要流程:初始调用[注册串口失败] -- > 加载模块[注册设备] -- > 加载驱动[串口注册成功]

 

首分析下串口注册函数register_console()

注意这个结构体struct console console_drivers,在内核中被EXPORT_SYMBOL(即声明为内核中可见)console_drivers为链表结构,记录了注册成功的控制台驱动

struct console {

         char name[16];

         void  (*write)(struct console *, const char *, unsigned);

         int    (*read)(struct console *, char *, unsigned);

         struct tty_driver *(*device)(struct console *, int *);

         void  (*unblank)(void);

         int    (*setup)(struct console *, char *);

         int    (*early_setup)(void);

         short         flags;

         short         index;

         int    cflag;

         void  *data;

         struct       console *next;

};

其中flags参数需要理解,early0串口flags = CON_PRINTBUFFER | CON_BOOT,这里的CON_BOOT使它作为启动时的临时串口输出;ttyS0串口flags = CON_PRINTBUFFER

register_console()首先检查要注册的串口的合法性,然后将其加入console_drivers链表,链表中可能存在多个合法串口设备,此时查看flags标志,flags & CON_ENABLED == 1的即可现在使用的串口,显然只能有一个设备设置此标志,该标志在register_console()函数中被设置。

 

然后按照串口注册流程

1.       linux-2.6.36启动时注册early0串口设备,由于该串口设置了CON_BOOT标志,在register_console()函数中被置位CON_ENABLED,所有输出通过early0输出。

 

2.       然后注册ttyttyS,下面只讲ttyS

注册动作:serial8250_console_init() -- > register_console()

并在接下来进行了如下声明:console_initcall(serial8250_console_init);

上面宏定义在内核做do_initcall()时会被调用,可以理解为控制台注册;完整的serial8250_console_init()函数如下:

static int __init serial8250_console_init(void)

{

         if (nr_uarts > UART_NR)

                   nr_uarts = UART_NR;

 

         serial8250_isa_init_ports();

        

         register_console(&serial8250_console);

         return 0;

}

可见,在register_console前,会调用serial8250_isa_init_ports()函数对serial8250_ports进行初始化,而serial8250_ports的赋值来源于old_serial_port,这个结构体与具体板是没有关系的,通过读取SRIAL_PORT_DFNS的值[定义在相应架构的include/asm/serial.h]

static const struct old_serial_port old_serial_port[] = {

                  SERIAL_PORT_DFNS /* defined in asm/serial.h */

};

这里需要理解下serial8250_portsserial8250_console的联系

                  serial8250_console.data.con = serial8250_ports

 

由于mips架构不是统一的,在serial.h中是空的,所以这次初始的serial8250_portsiobase/membase[important]没有设置,而在稍后register_console()-> serial8250_console_setup()中会检查iobasemembase是否为空,

if (!port->iobase && !port->membase)

                   return -ENODEV;

导致注册失败;tty也一样会注册失败,此时console_drivers仍只有early0一个控制台驱动。

 

3.       然后加载模块,bcm47xxbcm47xx/serial.c定义的uart8250模块被加载,调用初始化函数uart8250_init()

static int __init uart8250_init(void)

{

         int i;

         struct ssb_mipscore *mcore = &(ssb_bcm47xx.mipscore);

 

         memset(&uart8250_data, 0,  sizeof(uart8250_data));

 

         for (i = 0; i < mcore->nr_serial_ports; i++)

         {

                   struct plat_serial8250_port *p = &(uart8250_data[i]);

                   struct ssb_serial_port *ssb_port = &(mcore->serial_ports[i]);

 

                   p->mapbase = (unsigned int) ssb_port->regs;

                   p->membase = (void *) ssb_port->regs;

                   p->irq = ssb_port->irq + 2;

                   p->uartclk = ssb_port->baud_base;

                   p->regshift = ssb_port->reg_shift;

                   p->iotype = UPIO_MEM;

                   p->flags = UPF_BOOT_AUTOCONF | UPF_SHARE_IRQ;

         }

 

         return platform_device_register(&uart8250_device);

}

 

首先根据bcm47xx板的参数ssb_bcm47xx对设备进行初始化,这里比较重要的是mapbase, iotype, flags, type, uartclk

mapbase代表串口的地址,赋值为0xb800 0300 / 0xb800 0400

iotype代表io的类型,赋值为UPIO_MMIO

flags代表标志,赋值为UPF_BOOT_AUTOCONF | UPF_SHARE_IRQ

type代表串口类型,未进行赋值,由于flags设置了UPF_BOOT_AUTOCONF位,会在注册时进行自动的串口类型检测

uartclk代表uart clock

然后通过platform_device_register()注册了两个设备(分别对应于uart0/uart1),具体动作为:platform_device_register() -- > platform_device_add() -- > device_add()将设备添加

 

4.       加载驱动程序,加载8250串口驱动时调用函数serial8250_init()进行初始化。

serial8250_init() -- > serial8250_register_ports() -- > uart_add_one_port() -- >

uart_configure_port(),这个函数会对串口进行配置,在这里会用到先前使用的flags,其中一段代码如下:

if (port->flags & UPF_BOOT_AUTOCONF) {

                   if (!(port->flags & UPF_FIXED_TYPE)) {

                            port->type = PORT_UNKNOWN;

                            flags |= UART_CONFIG_TYPE;

                   }

                   port->ops->config_port(port, flags);

         }

由于设置了UPF_BOOT_AUTOCONF,配置端口,这里主要检测串的类型(即设置iotype字段)。在检测类型前,会做两个端口是否存在的测试。

         代码在运行至第一个测试时,失败[错误所在]

scratch = serial_inp(up, UART_IER);

serial_outp(up, UART_IER, 0);

/*

* Mask out IER[7:4] bits for test as some UARTs (e.g. TL

* 16C754B) allow only to modify them if an EFR bit is set.

*/

scratch2 = serial_inp(up, UART_IER) & 0x0f;

serial_outp(up, UART_IER, 0x0F);

 

scratch3 = serial_inp(up, UART_IER) & 0x0f;

serial_outp(up, UART_IER, scratch);

if (scratch2 != 0 || scratch3 != 0x0F) {

// -- add by yoyo

printk(KERN_INFO "IER test failed (%02x, %02x), scratch = %02x/n",

                       scratch2, scratch3, scratch);

goto out;

}

实际上只是检测写入UART_IER寄存器的值能否被正确读出,但内核启动时的输出  如下:

IER test failed (01, 01), scratch = 01

始终读入的都是0x01,显然是不正确的。

        

如果两个测试通过,然后探测串口的类型,这里用的是IIR寄存器的高2位,对type进行赋值,否则type = PORT_UNKNOWN

         然后进行串口第二次的注册:

if (port->cons && !(port->cons->flags & CON_ENABLED))

                   register_console(port->cons);

         这一次将ttyS0加入的console_drivers链表,设置CON_ENABLED位,并且删除了early0串口,至此,串口加载完成

        

 

第一次控制台注册时失败是正常的,因为串口的地址随板的不同而不同,而此时具体的模块还没加载进来,无法获知正确的串口地址信息。第二次控制台注册成功,因为bcm47xx板模块已被加载,设备的基本参数被赋予了正确的数值,所以此时控制台注册成功。可以确定地址没有错误,那可能是输入输出函数的问题。

 

    回到原点,查看CFE中串口驱动代码,找到输入输出函数,这里是最终的调用:

        #define READREG(sc,r) phys_read8((sc)->uart_base+((r)^0x3))

       #define WRITEREG(sc,r,v) phys_write8((sc)->uart_base+((r)^0x3),(v))

 

    而linux2.6.34内核中串口驱动的输入输出函数:

readb(p->membase + offset );

writeb(value, p->membase + offset);

   对比下,可以发现少了异或0x3,于是作如下修改:

readb(p->membase + (offset ^ 0x3));     

writeb(value, p->membase + (offset ^ 0x3));

   修改后重新编译,运行Linux,串口ttyS启动:

                           Closing network. eth1: 11250 sent, 11290 received, 0 interrupts Starting program at 0x800052f0 Linux version 2.6.34 ([email protected]) (gcc version 4.3.3 (Buildroot 2010.02-git) ) #166 Fri Jul 16 20:20:17 CST 2010 bootconsole [early0] enabled CPU revision is: 00029006 (Broadcom BCM3302) ssb: Sonics Silicon Backplane found at address 0x18000000 Determined physical RAM map: memory: 02000000 @ 00000000 (usable) User-defined physical RAM map: memory: 02000000 @ 00000000 (usable) Initial ramdisk at: 0x80500000 (1376256 bytes) Zone PFN ranges: Normal 0x00000000 -> 0x00002000 Movable zone start PFN for each node early_node_map[1] active PFN ranges 0: 0x00000000 -> 0x00002000 Built 1 zonelists in Zone order, mobility grouping on. Total pages: 8128 Kernel command line: console=ttyS0,9600 root=/dev/ram0 rw rootfstype=cramfs mem=32M rd_start=0x80500000 rd_size=0x150000 init=/linuxrc PID hash table entries: 128 (order: -3, 512 bytes) Dentry cache hash table entries: 4096 (order: 2, 16384 bytes) Inode-cache hash table entries: 2048 (order: 1, 8192 bytes) Primary instruction cache 16kB, VIPT, 2-way, linesize 16 bytes. Primary data cache 16kB, 2-way, VIPT, cache aliases, linesize 16 bytes Memory: 26016k/32768k available (3379k kernel code, 6752k reserved, 921k data, 204k init, 0k highmem) Hierarchical RCU implementation. RCU-based detection of stalled CPUs is enabled. NR_IRQS:128 Console: colour dummy device 80x25 To register serial 8250 console! Calibrating delay loop... 198.65 BogoMIPS (lpj=397312) Security Framework initialized SELinux: Initializing. Mount-cache hash table entries: 512 NET: Registered protocol family 16 bio: create slab <bio-0> at 0 usbcore: registered new interface driver usbfs usbcore: registered new interface driver hub usbcore: registered new device driver usb NetLabel: Initializing NetLabel: domain hash size = 128 NetLabel: protocols = UNLABELED CIPSOv4 NetLabel: unlabeled traffic allowed by default Switching to clocksource MIPS NET: Registered protocol family 2 IP route cache hash table entries: 1024 (order: 0, 4096 bytes) TCP established hash table entries: 1024 (order: 1, 8192 bytes) TCP bind hash table entries: 1024 (order: 2, 20480 bytes) TCP: Hash tables configured (established 1024 bind 1024) TCP reno registered UDP hash table entries: 128 (order: 0, 6144 bytes) UDP-Lite hash table entries: 128 (order: 0, 6144 bytes) NET: Registered protocol family 1 Trying to unpack rootfs image as initramfs... rootfs image is not initramfs (junk in compressed archive); looks like an initrd Freeing initrd memory: 1344k freed platform_device_register(serial8250_device) in serial.c init_vdso successfull audit: initializing netlink socket (disabled) type=2000 audit(1.205:1): initialized VFS: Disk quotas dquot_6.5.2 Dquot-cache hash table entries: 1024 (order 0, 4096 bytes) squashfs: version 4.0 (2009/01/31) Phillip Lougher msgmni has been set to 53 cryptomgr_test used greatest stack depth: 6856 bytes left cryptomgr_test used greatest stack depth: 6592 bytes left alg: No test for stdrng (krng) Block layer SCSI generic (bsg) driver version 0.4 loaded (major 254) io scheduler noop registered io scheduler deadline registered io scheduler cfq registered (default) Serial: 8250/16550 driver, 4 ports, IRQ sharing enabled serial8250.0: ttyS0 at MMIO 0xb8000300 (irq = 3) is a 16550A console [ttyS0] enabled, bootconsole disabled console [ttyS0] enabled, bootconsole disabled brd: module loaded usbcore: registered new interface driver libusual mice: PS/2 mouse device common for all mice usbcore: registered new interface driver hiddev usbcore: registered new interface driver usbhid usbhid: USB HID core driver TCP bic registered Initializing XFRM netlink socket NET: Registered protocol family 17 registered taskstats version 1 md: Waiting for all devices to be available before autodetect md: If you don't use raid, use raid=noautodetect md: Autodetecting RAID arrays. md: Scanned 0 and added 0 devices. md: autorun ... md: ... autorun DONE. RAMDISK: cramfs filesystem found at block 0 RAMDISK: Loading 1108KiB [1 disk] into ram disk... done. Algorithmics/MIPS FPU Emulator v1.5 ****************************************** XFC Starting System Init! ****************************************** #

你可能感兴趣的:(linux,struct,cache,table,interface,protocols)