我们公司用的产品主要是freescale公司的powerpc系列,期中MPC8548,MPC8572用的比较多。由于要给老外做整个板卡上硬件的测试工作,所以对于板上资源是必须要了解的。本篇文章的侧重点在于板上各设备是如何注册的(只讲设备注册,不讲驱动注册),而对于板上有哪些资源,datasheet上都写得明明白白,在此不再赘述。
好了,我们先查看一下在linux-2.6.23内核上的MPC85XX系列的板级资源文件
arch/ppc/syslib/mpc85xx_devices.c
由于文件太大,我把主要部分列出来
static struct fs_platform_info mpc85xx_fcc1_pdata = {
.fs_no = fsid_fcc1,
.cp_page = CPM_CR_FCC1_PAGE,
.cp_block = CPM_CR_FCC1_SBLOCK,
.rx_ring = 32,
.tx_ring = 32,
.rx_copybreak = 240,
.use_napi = 0,
.napi_weight = 17,
.clk_mask = CMX1_CLK_MASK,
.clk_route = CMX1_CLK_ROUTE,
.clk_trx = (PC_F1RXCLK | PC_F1TXCLK),
.mem_offset = FCC1_MEM_OFFSET,
};
static struct fs_platform_info mpc85xx_fcc2_pdata = {
.fs_no = fsid_fcc2,
.cp_page = CPM_CR_FCC2_PAGE,
.cp_block = CPM_CR_FCC2_SBLOCK,
.rx_ring = 32,
.tx_ring = 32,
.rx_copybreak = 240,
.use_napi = 0,
.napi_weight = 17,
.clk_mask = CMX2_CLK_MASK,
.clk_route = CMX2_CLK_ROUTE,
.clk_trx = (PC_F2RXCLK | PC_F2TXCLK),
.mem_offset = FCC2_MEM_OFFSET,
};
static struct fs_platform_info mpc85xx_fcc3_pdata = {
.fs_no = fsid_fcc3,
.cp_page = CPM_CR_FCC3_PAGE,
.cp_block = CPM_CR_FCC3_SBLOCK,
.rx_ring = 32,
.tx_ring = 32,
.rx_copybreak = 240,
.use_napi = 0,
.napi_weight = 17,
.clk_mask = CMX3_CLK_MASK,
.clk_route = CMX3_CLK_ROUTE,
.clk_trx = (PC_F3RXCLK | PC_F3TXCLK),
.mem_offset = FCC3_MEM_OFFSET,
};
static struct plat_serial8250_port serial_platform_data[] = {
[0] = {
.mapbase = 0x4500,
.irq = MPC85xx_IRQ_DUART,
.iotype = UPIO_MEM,
.flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_SHARE_IRQ,
},
[1] = {
.mapbase = 0x4600,
.irq = MPC85xx_IRQ_DUART,
.iotype = UPIO_MEM,
.flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_SHARE_IRQ,
},
{ },
};
struct platform_device ppc_sys_platform_devices[] = {
[MPC85xx_TSEC1] = {
.name = "fsl-gianfar",
.id = 1,
.dev.platform_data = &mpc85xx_tsec1_pdata,
.num_resources = 4,
.resource = (struct resource[]) {
{
.start = MPC85xx_ENET1_OFFSET,
.end = MPC85xx_ENET1_OFFSET +
MPC85xx_ENET1_SIZE - 1,
.flags = IORESOURCE_MEM,
},
{
.name = "tx",
.start = MPC85xx_IRQ_TSEC1_TX,
.end = MPC85xx_IRQ_TSEC1_TX,
.flags = IORESOURCE_IRQ,
},
{
.name = "rx",
.start = MPC85xx_IRQ_TSEC1_RX,
.end = MPC85xx_IRQ_TSEC1_RX,
.flags = IORESOURCE_IRQ,
},
{
.name = "error",
.start = MPC85xx_IRQ_TSEC1_ERROR,
.end = MPC85xx_IRQ_TSEC1_ERROR,
.flags = IORESOURCE_IRQ,
},
},
},
[MPC85xx_TSEC2] = {
.name = "fsl-gianfar",
.id = 2,
.dev.platform_data = &mpc85xx_tsec2_pdata,
.num_resources = 4,
.resource = (struct resource[]) {
{
.start = MPC85xx_ENET2_OFFSET,
.end = MPC85xx_ENET2_OFFSET +
MPC85xx_ENET2_SIZE - 1,
.flags = IORESOURCE_MEM,
},
{
.name = "tx",
.start = MPC85xx_IRQ_TSEC2_TX,
.end = MPC85xx_IRQ_TSEC2_TX,
.flags = IORESOURCE_IRQ,
},
{
.name = "rx",
.start = MPC85xx_IRQ_TSEC2_RX,
.end = MPC85xx_IRQ_TSEC2_RX,
.flags = IORESOURCE_IRQ,
},
{
.name = "error",
.start = MPC85xx_IRQ_TSEC2_ERROR,
.end = MPC85xx_IRQ_TSEC2_ERROR,
.flags = IORESOURCE_IRQ,
},
},
},
[MPC85xx_FEC] = {
.name = "fsl-gianfar",
.id = 3,
.dev.platform_data = &mpc85xx_fec_pdata,
.num_resources = 2,
.resource = (struct resource[]) {
{
.start = MPC85xx_ENET3_OFFSET,
.end = MPC85xx_ENET3_OFFSET +
MPC85xx_ENET3_SIZE - 1,
.flags = IORESOURCE_MEM,
},
{
.start = MPC85xx_IRQ_FEC,
.end = MPC85xx_IRQ_FEC,
.flags = IORESOURCE_IRQ,
},
},
},
[MPC85xx_IIC1] = {
.name = "fsl-i2c",
.id = 1,
.dev.platform_data = &mpc85xx_fsl_i2c_pdata,
.num_resources = 2,
.resource = (struct resource[]) {
{
.start = MPC85xx_IIC1_OFFSET,
.end = MPC85xx_IIC1_OFFSET +
MPC85xx_IIC1_SIZE - 1,
.flags = IORESOURCE_MEM,
},
{
.start = MPC85xx_IRQ_IIC1,
.end = MPC85xx_IRQ_IIC1,
.flags = IORESOURCE_IRQ,
},
},
},
[MPC85xx_DMA0] = {
.name = "fsl-dma",
.id = 0,
.num_resources = 2,
.resource = (struct resource[]) {
{
.start = MPC85xx_DMA0_OFFSET,
.end = MPC85xx_DMA0_OFFSET +
MPC85xx_DMA0_SIZE - 1,
.flags = IORESOURCE_MEM,
},
{
.start = MPC85xx_IRQ_DMA0,
.end = MPC85xx_IRQ_DMA0,
.flags = IORESOURCE_IRQ,
},
},
},
[MPC85xx_DMA1] = {
.name = "fsl-dma",
.id = 1,
.num_resources = 2,
.resource = (struct resource[]) {
{
.start = MPC85xx_DMA1_OFFSET,
.end = MPC85xx_DMA1_OFFSET +
MPC85xx_DMA1_SIZE - 1,
.flags = IORESOURCE_MEM,
},
{
.start = MPC85xx_IRQ_DMA1,
.end = MPC85xx_IRQ_DMA1,
.flags = IORESOURCE_IRQ,
},
},
},
[MPC85xx_DMA2] = {
.name = "fsl-dma",
.id = 2,
.num_resources = 2,
.resource = (struct resource[]) {
{
.start = MPC85xx_DMA2_OFFSET,
.end = MPC85xx_DMA2_OFFSET +
MPC85xx_DMA2_SIZE - 1,
.flags = IORESOURCE_MEM,
},
{
.start = MPC85xx_IRQ_DMA2,
.end = MPC85xx_IRQ_DMA2,
.flags = IORESOURCE_IRQ,
},
},
},
[MPC85xx_DMA3] = {
.name = "fsl-dma",
.id = 3,
.num_resources = 2,
.resource = (struct resource[]) {
{
.start = MPC85xx_DMA3_OFFSET,
.end = MPC85xx_DMA3_OFFSET +
MPC85xx_DMA3_SIZE - 1,
.flags = IORESOURCE_MEM,
},
{
.start = MPC85xx_IRQ_DMA3,
.end = MPC85xx_IRQ_DMA3,
.flags = IORESOURCE_IRQ,
},
},
},
[MPC85xx_DUART] = {
.name = "serial8250",
.id = PLAT8250_DEV_PLATFORM,
.dev.platform_data = serial_platform_data,
},
[MPC85xx_PERFMON] = {
.name = "fsl-perfmon",
.id = 1,
.num_resources = 2,
.resource = (struct resource[]) {
{
.start = MPC85xx_PERFMON_OFFSET,
.end = MPC85xx_PERFMON_OFFSET +
MPC85xx_PERFMON_SIZE - 1,
.flags = IORESOURCE_MEM,
},
{
.start = MPC85xx_IRQ_PERFMON,
.end = MPC85xx_IRQ_PERFMON,
.flags = IORESOURCE_IRQ,
},
},
},
[MPC85xx_SEC2] = {
.name = "fsl-sec2",
.id = 1,
.num_resources = 2,
.resource = (struct resource[]) {
{
.start = MPC85xx_SEC2_OFFSET,
.end = MPC85xx_SEC2_OFFSET +
MPC85xx_SEC2_SIZE - 1,
.flags = IORESOURCE_MEM,
},
{
.start = MPC85xx_IRQ_SEC2,
.end = MPC85xx_IRQ_SEC2,
.flags = IORESOURCE_IRQ,
},
},
},
[MPC85xx_CPM_FCC1] = {
.name = "fsl-cpm-fcc",
.id = 1,
.num_resources = 4,
.dev.platform_data = &mpc85xx_fcc1_pdata,
.resource = (struct resource[]) {
{
.name = "fcc_regs",
.start = 0x91300,
.end = 0x9131F,
.flags = IORESOURCE_MEM,
},
{
.name = "fcc_regs_c",
.start = 0x91380,
.end = 0x9139F,
.flags = IORESOURCE_MEM,
},
{
.name = "fcc_pram",
.start = 0x88400,
.end = 0x884ff,
.flags = IORESOURCE_MEM,
},
{
.start = SIU_INT_FCC1,
.end = SIU_INT_FCC1,
.flags = IORESOURCE_IRQ,
},
},
},
[MPC85xx_CPM_FCC2] = {
.name = "fsl-cpm-fcc",
.id = 2,
.num_resources = 4,
.dev.platform_data = &mpc85xx_fcc2_pdata,
.resource = (struct resource[]) {
{
.name = "fcc_regs",
.start = 0x91320,
.end = 0x9133F,
.flags = IORESOURCE_MEM,
},
{
.name = "fcc_regs_c",
.start = 0x913A0,
.end = 0x913CF,
.flags = IORESOURCE_MEM,
},
{
.name = "fcc_pram",
.start = 0x88500,
.end = 0x885ff,
.flags = IORESOURCE_MEM,
},
{
.start = SIU_INT_FCC2,
.end = SIU_INT_FCC2,
.flags = IORESOURCE_IRQ,
},
},
},
[MPC85xx_CPM_FCC3] = {
.name = "fsl-cpm-fcc",
.id = 3,
.num_resources = 4,
.dev.platform_data = &mpc85xx_fcc3_pdata,
.resource = (struct resource[]) {
{
.name = "fcc_regs",
.start = 0x91340,
.end = 0x9135F,
.flags = IORESOURCE_MEM,
},
{
.name = "fcc_regs_c",
.start = 0x913D0,
.end = 0x913FF,
.flags = IORESOURCE_MEM,
},
{
.name = "fcc_pram",
.start = 0x88600,
.end = 0x886ff,
.flags = IORESOURCE_MEM,
},
{
.start = SIU_INT_FCC3,
.end = SIU_INT_FCC3,
.flags = IORESOURCE_IRQ,
},
},
},
[MPC85xx_CPM_I2C] = {
.name = "fsl-cpm-i2c",
.id = 1,
.num_resources = 2,
.resource = (struct resource[]) {
{
.start = 0x91860,
.end = 0x918BF,
.flags = IORESOURCE_MEM,
},
{
.start = SIU_INT_I2C,
.end = SIU_INT_I2C,
.flags = IORESOURCE_IRQ,
},
},
},
[MPC85xx_CPM_SCC1] = {
.name = "fsl-cpm-scc",
.id = 1,
.num_resources = 2,
.resource = (struct resource[]) {
{
.start = 0x91A00,
.end = 0x91A1F,
.flags = IORESOURCE_MEM,
},
{
.start = SIU_INT_SCC1,
.end = SIU_INT_SCC1,
.flags = IORESOURCE_IRQ,
},
},
},
[MPC85xx_CPM_SCC2] = {
.name = "fsl-cpm-scc",
.id = 2,
.num_resources = 2,
.resource = (struct resource[]) {
{
.start = 0x91A20,
.end = 0x91A3F,
.flags = IORESOURCE_MEM,
},
{
.start = SIU_INT_SCC2,
.end = SIU_INT_SCC2,
.flags = IORESOURCE_IRQ,
},
},
},
[MPC85xx_CPM_SCC3] = {
.name = "fsl-cpm-scc",
.id = 3,
.num_resources = 2,
.resource = (struct resource[]) {
{
.start = 0x91A40,
.end = 0x91A5F,
.flags = IORESOURCE_MEM,
},
{
.start = SIU_INT_SCC3,
.end = SIU_INT_SCC3,
.flags = IORESOURCE_IRQ,
},
},
},
[MPC85xx_CPM_SCC4] = {
.name = "fsl-cpm-scc",
.id = 4,
.num_resources = 2,
.resource = (struct resource[]) {
{
.start = 0x91A60,
.end = 0x91A7F,
.flags = IORESOURCE_MEM,
},
{
.start = SIU_INT_SCC4,
.end = SIU_INT_SCC4,
.flags = IORESOURCE_IRQ,
},
},
},
[MPC85xx_CPM_SPI] = {
.name = "fsl-cpm-spi",
.id = 1,
.num_resources = 2,
.resource = (struct resource[]) {
{
.start = 0x91AA0,
.end = 0x91AFF,
.flags = IORESOURCE_MEM,
},
{
.start = SIU_INT_SPI,
.end = SIU_INT_SPI,
.flags = IORESOURCE_IRQ,
},
},
},
[MPC85xx_CPM_MCC1] = {
.name = "fsl-cpm-mcc",
.id = 1,
.num_resources = 2,
.resource = (struct resource[]) {
{
.start = 0x91B30,
.end = 0x91B3F,
.flags = IORESOURCE_MEM,
},
{
.start = SIU_INT_MCC1,
.end = SIU_INT_MCC1,
.flags = IORESOURCE_IRQ,
},
},
},
[MPC85xx_CPM_MCC2] = {
.name = "fsl-cpm-mcc",
.id = 2,
.num_resources = 2,
.resource = (struct resource[]) {
{
.start = 0x91B50,
.end = 0x91B5F,
.flags = IORESOURCE_MEM,
},
{
.start = SIU_INT_MCC2,
.end = SIU_INT_MCC2,
.flags = IORESOURCE_IRQ,
},
},
},
[MPC85xx_CPM_SMC1] = {
.name = "fsl-cpm-smc",
.id = 1,
.num_resources = 2,
.resource = (struct resource[]) {
{
.start = 0x91A80,
.end = 0x91A8F,
.flags = IORESOURCE_MEM,
},
{
.start = SIU_INT_SMC1,
.end = SIU_INT_SMC1,
.flags = IORESOURCE_IRQ,
},
},
},
[MPC85xx_CPM_SMC2] = {
.name = "fsl-cpm-smc",
.id = 2,
.num_resources = 2,
.resource = (struct resource[]) {
{
.start = 0x91A90,
.end = 0x91A9F,
.flags = IORESOURCE_MEM,
},
{
.start = SIU_INT_SMC2,
.end = SIU_INT_SMC2,
.flags = IORESOURCE_IRQ,
},
},
},
[MPC85xx_CPM_USB] = {
.name = "fsl-cpm-usb",
.id = 2,
.num_resources = 2,
.resource = (struct resource[]) {
{
.start = 0x91B60,
.end = 0x91B7F,
.flags = IORESOURCE_MEM,
},
{
.start = SIU_INT_USB,
.end = SIU_INT_USB,
.flags = IORESOURCE_IRQ,
},
},
},
[MPC85xx_eTSEC1] = {
.name = "fsl-gianfar",
.id = 1,
.dev.platform_data = &mpc85xx_etsec1_pdata,
.num_resources = 4,
.resource = (struct resource[]) {
{
.start = MPC85xx_ENET1_OFFSET,
.end = MPC85xx_ENET1_OFFSET +
MPC85xx_ENET1_SIZE - 1,
.flags = IORESOURCE_MEM,
},
{
.name = "tx",
.start = MPC85xx_IRQ_TSEC1_TX,
.end = MPC85xx_IRQ_TSEC1_TX,
.flags = IORESOURCE_IRQ,
},
{
.name = "rx",
.start = MPC85xx_IRQ_TSEC1_RX,
.end = MPC85xx_IRQ_TSEC1_RX,
.flags = IORESOURCE_IRQ,
},
{
.name = "error",
.start = MPC85xx_IRQ_TSEC1_ERROR,
.end = MPC85xx_IRQ_TSEC1_ERROR,
.flags = IORESOURCE_IRQ,
},
},
},
[MPC85xx_eTSEC2] = {
.name = "fsl-gianfar",
.id = 2,
.dev.platform_data = &mpc85xx_etsec2_pdata,
.num_resources = 4,
.resource = (struct resource[]) {
{
.start = MPC85xx_ENET2_OFFSET,
.end = MPC85xx_ENET2_OFFSET +
MPC85xx_ENET2_SIZE - 1,
.flags = IORESOURCE_MEM,
},
{
.name = "tx",
.start = MPC85xx_IRQ_TSEC2_TX,
.end = MPC85xx_IRQ_TSEC2_TX,
.flags = IORESOURCE_IRQ,
},
{
.name = "rx",
.start = MPC85xx_IRQ_TSEC2_RX,
.end = MPC85xx_IRQ_TSEC2_RX,
.flags = IORESOURCE_IRQ,
},
{
.name = "error",
.start = MPC85xx_IRQ_TSEC2_ERROR,
.end = MPC85xx_IRQ_TSEC2_ERROR,
.flags = IORESOURCE_IRQ,
},
},
},
[MPC85xx_eTSEC3] = {
.name = "fsl-gianfar",
.id = 3,
.dev.platform_data = &mpc85xx_etsec3_pdata,
.num_resources = 4,
.resource = (struct resource[]) {
{
.start = MPC85xx_ENET3_OFFSET,
.end = MPC85xx_ENET3_OFFSET +
MPC85xx_ENET3_SIZE - 1,
.flags = IORESOURCE_MEM,
},
{
.name = "tx",
.start = MPC85xx_IRQ_TSEC3_TX,
.end = MPC85xx_IRQ_TSEC3_TX,
.flags = IORESOURCE_IRQ,
},
{
.name = "rx",
.start = MPC85xx_IRQ_TSEC3_RX,
.end = MPC85xx_IRQ_TSEC3_RX,
.flags = IORESOURCE_IRQ,
},
{
.name = "error",
.start = MPC85xx_IRQ_TSEC3_ERROR,
.end = MPC85xx_IRQ_TSEC3_ERROR,
.flags = IORESOURCE_IRQ,
},
},
},
[MPC85xx_eTSEC4] = {
.name = "fsl-gianfar",
.id = 4,
.dev.platform_data = &mpc85xx_etsec4_pdata,
.num_resources = 4,
.resource = (struct resource[]) {
{
.start = 0x27000,
.end = 0x27fff,
.flags = IORESOURCE_MEM,
},
{
.name = "tx",
.start = MPC85xx_IRQ_TSEC4_TX,
.end = MPC85xx_IRQ_TSEC4_TX,
.flags = IORESOURCE_IRQ,
},
{
.name = "rx",
.start = MPC85xx_IRQ_TSEC4_RX,
.end = MPC85xx_IRQ_TSEC4_RX,
.flags = IORESOURCE_IRQ,
},
{
.name = "error",
.start = MPC85xx_IRQ_TSEC4_ERROR,
.end = MPC85xx_IRQ_TSEC4_ERROR,
.flags = IORESOURCE_IRQ,
},
},
},
[MPC85xx_IIC2] = {
.name = "fsl-i2c",
.id = 2,
.dev.platform_data = &mpc85xx_fsl_i2c2_pdata,
.num_resources = 2,
.resource = (struct resource[]) {
{
.start = 0x03100,
.end = 0x031ff,
.flags = IORESOURCE_MEM,
},
{
.start = MPC85xx_IRQ_IIC1,
.end = MPC85xx_IRQ_IIC1,
.flags = IORESOURCE_IRQ,
},
},
},
[MPC85xx_MDIO] = {
.name = "fsl-gianfar_mdio",
.id = 0,
.dev.platform_data = &mpc85xx_mdio_pdata,
.num_resources = 1,
.resource = (struct resource[]) {
{
.start = 0x24520,
.end = 0x2453f,
.flags = IORESOURCE_MEM,
},
},
},
};
static int __init mach_mpc85xx_fixup(struct platform_device *pdev)
{
ppc_sys_fixup_mem_resource(pdev, CCSRBAR);
return 0;
}
static int __init mach_mpc85xx_init(void)
{
ppc_sys_device_fixup = mach_mpc85xx_fixup;
return 0;
}
postcore_initcall(mach_mpc85xx_init);
比较多啊,其实还没列完,前面还省略了一部分内容,比如串口部分。
虽然很长,其实内容都一样哈,这些板上资源被定义为平台设备,注意最后
ppc_sys_device_fixup = mach_mpc85xx_fixup;
这里ppc_sys_device_fixup 在内核中是一个全局的函数指针,大家或许已经注意到了,在ppc系列可能有很多处理器类型,这里指向了
MPC85XX系列是因为我们选择了该类型处理器。如果是在其他型号板子资源文件中肯定指向其他了。
好,接着看这么多的设备是怎么注册的呢,要看另一个文件
arch/ppc/syslib/ppc_sys.c
我只需要看该文件的最后部分
static int __init ppc_sys_init(void)
303 {
304 unsigned int i, dev_id, ret = 0;
305
306 BUG_ON(cur_ppc_sys_spec == NULL);
307
308 for (i = 0; i < cur_ppc_sys_spec->num_devices; i++) {
309 dev_id = cur_ppc_sys_spec->device_list[i];
310 if ((dev_id != -1) &&
311 !(cur_ppc_sys_spec->config[dev_id] & PPC_SYS_CONFIG_DISABLED)) {
312 if (ppc_sys_device_fixup != NULL)
313 ppc_sys_device_fixup(&ppc_sys_platform_devices
314 [dev_id]);
315 if (platform_device_register
316 (&ppc_sys_platform_devices[dev_id])) {//这里就是我们的板子上的device了
317 ret = 1;
318 printk(KERN_ERR
319 "unable to register device %d/n",
320 dev_id);
321 }
322 }
323 }
324
325 ppc_sys_inited = 1;
326 return ret;
327 }
328
329 subsys_initcall(ppc_sys_init);
然后在内核起来的时候进行注册各设备。
device向内核注册了,那么与device相关的driver什么时候注册的,driver代码在哪呢?
这些driver代码分散在drivers目录下各目录中,比如dma controller相关的driver在 drivers/dma下,
i2c controller相关的driver在drivers/i2c下。
这里以i2c controller作为例子。
各i2c controller驱动在drivers/i2c/busses/目录下,而各i2c device驱动在drivers/i2c/chips/目录下
我们要查看的i2c controller驱动文件为
drivers/i2c/busses/i2c-mpc.c
驱动内容很简单,就不贴出来了。主要就是一个平台设备的驱动,实现了i2c的读写,i2c device的探测,remove等功能。
然后向内核去注册这个驱动。
驱动怎么匹配设备的?
这个我就不关心了,由总线的方法去匹配。这里platform总线匹配方法很简单的。根据struct platform_device 和
struct platform_driver里面的.name域,看名字是否相同进行匹配。
写这篇文章主要是为了理清板级设备如何注册。这跟我们平常做设备驱动方法有点不同,我们平常做一个设备驱动,通常把注册设备和注册驱动放在一起。内核则不同,其实都一样的啦,有设备驱动模型在,总能匹配的。