Android学习之 Platform总线 2

来源:互联网 发布:matlab 矩阵怎么转置 编辑:程序博客网 时间:2024/06/16 18:52

1       基于Platform总线的驱动开发流程

·定义初始化platform bus

·定义各种platform devices

·注册各种platform devices

·定义相关platform driver

·注册相关platform driver

·操作相关设备

 

基于Mx53QSB为例,实现流程如下:

1.1 初始化platform_bus

初始化代码在kernel_imx/drivers/base/platform.c#L28

struct device platform_bus = {

       .init_name  = "platform",

};

EXPORT_SYMBOL_GPL(platform_bus);

 

 

int __init platform_bus_init(void)   #L1020

{

       int error;

 

       early_platform_cleanup();

 

       error = device_register(&platform_bus);

       if (error)

              return error;

       error =  bus_register(&platform_bus_type);

       if (error)

              device_unregister(&platform_bus);

       return error;

}

 

该函数创建了一个名为“platform”的设备,后续platform的设备都会以此为parent,在sysfs中表示为:所有platform类型的设备都会添加在platform_bus所代表的目录下,即/sys/devices/platform

 

Platform_bus必须在系统注册任何platform driverplatform device之前初始化,实现如下:

Kernel_imx/drivers/base/Init.c  #L20

/**

 * driver_init - initialize driver model.

 *

 * Call the driver model init functions to initialize their

 * subsystems. Called early from init/main.c.

 */

void __init driver_init(void)

{

       /* These are the core pieces */

       devtmpfs_init();

       devices_init();

       buses_init();

       classes_init();

       firmware_init();

       hypervisor_init();

 

       /* These are also core pieces, but must come after the

        * core core pieces.

        */

       platform_bus_init();

       system_bus_init();

       cpu_dev_init();

       memory_dev_init();

}

start_kernel(init/main.c#L539) >>rest_init(init/main.c#L429)>>kenel_init(init/main.c#L866)>>do_basic_setup(init/Main.c#L797)>>driver_init>>platform_bus_init

 

 

platform driver platform devices 的初始化是在do_initcallsdo_basic_setup(init/Main.c#L806处调用))中进行的的。

1.2 定义platform_device

Kernel_imx/arch/arm/mach-mx5/Devices.c #L649中定义了系统的资源,大部分板级资源都在这里集中定义。

static struct resource mxci2c1_resources[] = {

       {

              .start = I2C1_BASE_ADDR,

              .end = I2C1_BASE_ADDR + SZ_4K - 1,

              .flags = IORESOURCE_MEM,

       },

       {

              .start = MXC_INT_I2C1,

              .end = MXC_INT_I2C1,

              .flags = IORESOURCE_IRQ,

       },

};

 

static struct resource mxci2c2_resources[] = {

       {

              .start = I2C2_BASE_ADDR,

              .end = I2C2_BASE_ADDR + SZ_4K - 1,

              .flags = IORESOURCE_MEM,

       },

       {

              .start = MXC_INT_I2C2,

              .end = MXC_INT_I2C2,

              .flags = IORESOURCE_IRQ,

       },

};

 

static struct resource mxci2c3_resources[] = {

       {

              .start = I2C3_BASE_ADDR,

              .end = I2C3_BASE_ADDR + SZ_4K - 1,

              .flags = IORESOURCE_MEM,

              },

       {

              .start = MXC_INT_I2C3,

              .end = MXC_INT_I2C3,

              .flags = IORESOURCE_IRQ,

              },

};

 

struct platform_device mxci2c_devices[] = {

       {

              .name = "imx-i2c",

              .id = 0,

              .num_resources = ARRAY_SIZE(mxci2c1_resources),

              .resource = mxci2c1_resources,

       },

       {

              .name = "imx-i2c",

              .id = 1,

              .num_resources = ARRAY_SIZE(mxci2c2_resources),

              .resource = mxci2c2_resources,

       },

       {

              .name = "imx-i2c",

              .id = 2,

              .num_resources = ARRAY_SIZE(mxci2c3_resources),

              .resource = mxci2c3_resources,

       },

};
设备名称为imx-i2c.id说明设备的序号,共有三个, mxci2c1_resourcesmxci2c2_resourcesmxci2c3_resources分别对应设备的资源,资源包括I2c控制器的寄存器空间和中断信息。

 

1.3 注册platform_device

定义了platflat_device后,需要添加到系统中,就可以调用函数mxc_register_device

Kernel_imx/arch /arm/plat-mxc.c#L108

int __init mxc_register_device(struct platform_device *pdev, void *data)

{

       int ret;

 

       pdev->dev.platform_data = data;

 

       ret = platform_device_register(pdev);

       if (ret)

              pr_debug("Unable to register platform device '%s': %d/n",

                      pdev->name, ret);

 

       return ret;

}

mxc_board_init()函数中,调用mxc_register_device对一些外设进行注册。

static void __init mxc_board_init(void)

{

       mxc_ipu_data.di_clk[0] = clk_get(NULL, "ipu_di0_clk");

       mxc_ipu_data.di_clk[1] = clk_get(NULL, "ipu_di1_clk");

       mxc_ipu_data.csi_clk[0] = clk_get(NULL, "ssi_ext1_clk");

       mxc_spdif_data.spdif_core_clk = clk_get(NULL, "spdif_xtal_clk");

       clk_put(mxc_spdif_data.spdif_core_clk);

 

       mxcsdhc3_device.resource[2].start = IOMUX_TO_IRQ_V3(SD3_CD);

       mxcsdhc3_device.resource[2].end = IOMUX_TO_IRQ_V3(SD3_CD);

 

       mxc_cpu_common_init();

       mx53_loco_io_init();

 

       mxc_register_device(&mxc_dma_device, NULL);

       mxc_register_device(&mxc_wdt_device, NULL);

       mxc_register_device(&mxci2c_devices[0], &mxci2c_data);

       mxc_register_device(&mxci2c_devices[1], &mxci2c_data);

 

       mx53_loco_init_da9052();

 

       mxc_register_device(&mxc_rtc_device, NULL);

       mxc_register_device(&mxc_ipu_device, &mxc_ipu_data);

       mxc_register_device(&mxc_ldb_device, &ldb_data);

       mxc_register_device(&mxc_tve_device, &tve_data);

       mxc_register_device(&mxcvpu_device, &mxc_vpu_data);

       mxc_register_device(&gpu_device, &z160_revision);

       mxc_register_device(&mxcscc_device, NULL);

       mxc_register_device(&mxc_dvfs_core_device, &dvfs_core_data);

       mxc_register_device(&busfreq_device, &bus_freq_data);

       mxc_register_device(&mxc_iim_device, &iim_data);

       mxc_register_device(&mxc_pwm2_device, NULL);

       mxc_register_device(&mxc_pwm1_backlight_device, &mxc_pwm_backlight_data);

       mxc_register_device(&mxcsdhc1_device, &mmc1_data);

       mxc_register_device(&mxcsdhc3_device, &mmc3_data);

       mxc_register_device(&mxc_ssi1_device, NULL);

       mxc_register_device(&mxc_ssi2_device, NULL);

       mxc_register_device(&mxc_alsa_spdif_device, &mxc_spdif_data);

       mxc_register_device(&ahci_fsl_device, &sata_data);

       mxc_register_device(&mxc_fec_device, &fec_data);

       /* ASRC is only available for MX53 TO2.0 */

       if (cpu_is_mx53_rev(CHIP_REV_2_0) >= 1) {

              mxc_asrc_data.asrc_core_clk = clk_get(NULL, "asrc_clk");

              clk_put(mxc_asrc_data.asrc_core_clk);

              mxc_asrc_data.asrc_audio_clk = clk_get(NULL, "asrc_serial_clk");

              clk_set_rate(mxc_asrc_data.asrc_audio_clk, 1190000);

              clk_put(mxc_asrc_data.asrc_audio_clk);

              mxc_register_device(&mxc_asrc_device, &mxc_asrc_data);

       }

 

       i2c_register_board_info(0, mxc_i2c0_board_info,

                            ARRAY_SIZE(mxc_i2c0_board_info));

       i2c_register_board_info(1, mxc_i2c1_board_info,

                            ARRAY_SIZE(mxc_i2c1_board_info));

 

       mxc_register_device(&mxc_sgtl5000_device, &sgtl5000_data);

       mx5_usb_dr_init();

       mx5_set_host1_vbus_func(mx53_loco_usbh1_vbus);

       mx5_usbh1_init();

       mxc_register_device(&mxc_v4l2_device, NULL);

       mxc_register_device(&mxc_v4l2out_device, NULL);

       mxc_register_device(&mxc_android_pmem_device, &android_pmem_data);

       mxc_register_device(&mxc_android_pmem_gpu_device, &android_pmem_gpu_data);

       mxc_register_device(&usb_mass_storage_device, &mass_storage_data);

       mxc_register_device(&usb_rndis_device, &rndis_data);

       mxc_register_device(&android_usb_device, &android_usb_data);

       loco_add_device_buttons();

}

 

mxc_board_init定义在Mx53_LOCO板子的MACHINE_START中。

MACHINE_START(MX53_LOCO, "Freescale MX53 LOCO Board")

       /* Maintainer: Freescale Semiconductor, Inc. */

       .fixup = fixup_mxc_board,

       .map_io = mx5_map_io,

       .init_irq = mx5_init_irq,

       .init_machine = mxc_board_init,

       .timer = &mxc_timer,

MACHINE_END

利用mxc_register_device将系统资源注册进系统,在此之前platform bus 需要初始化成功,否则无法将platform devices挂接到platform bus上。为了保证platform drvier初始化时,相关platform资源已经注册进系统,mxc_board_init需要很早执行,而其作为init_machine时,将优先于系统所有驱动的初始化。

调用顺序如下:start_kernel(init/main.c#L539) >>setup_arch(arch/arm/kernel/setup.c#L670)>> init_machine(arch/arm/kernel/setup.c#L742)>> customize_machine(arch/arm/kernel/setup.c#L663)>>arch_initcall(arch/arm/kernel/setup.c#L670)

 

void __init setup_arch(char **cmdline_p)

{

        struct tag *tags = (struct tag *)&init_tags;

        struct machine_desc *mdesc;

        char *from = default_command_line;

 

        unwind_init();

 

        setup_processor();

        mdesc = setup_machine(machine_arch_type);

        machine_name = mdesc->name;

 

        if (mdesc->soft_reboot)

              reboot_setup("s");

 

        if (__atags_pointer)

              tags = phys_to_virt(__atags_pointer);

        else if (mdesc->boot_params)

              tags = phys_to_virt(mdesc->boot_params);

        else

              init_tags.mem.start = PHYS_OFFSET;

 

        /*

         * If we have the old style parameters, convert them to

         * a tag list.

         */

        if (tags->hdr.tag != ATAG_CORE)

              convert_to_tag_list(tags);

        if (tags->hdr.tag != ATAG_CORE)

              tags = (struct tag *)&init_tags;

 

        if (mdesc->fixup)

              mdesc->fixup(mdesc, tags, &from, &meminfo);

 

        if (tags->hdr.tag == ATAG_CORE) {

              if (meminfo.nr_banks != 0)

                     squash_mem_tags(tags);

              save_atags(tags);

              parse_tags(tags);

        }

 

        init_mm.start_code = (unsigned long) _text;

        init_mm.end_code   = (unsigned long) _etext;

        init_mm.end_data   = (unsigned long) _edata;

        init_mm.brk         = (unsigned long) _end;

 

        /* parse_early_param needs a boot_command_line */

        strlcpy(boot_command_line, from, COMMAND_LINE_SIZE);

 

        /* populate cmd_line too for later use, preserving boot_command_line */

        strlcpy(cmd_line, boot_command_line, COMMAND_LINE_SIZE);

        *cmdline_p = cmd_line;

 

        parse_early_param();

 

        paging_init(mdesc);

        request_standard_resources(&meminfo, mdesc);

 

#ifdef CONFIG_SMP

        smp_init_cpus();

#endif

 

        cpu_init();

        tcm_init();

 

        /*

         * Set up various architecture-specific pointers

         */

        init_arch_irq = mdesc->init_irq;

        system_timer = mdesc->timer;

        init_machine = mdesc->init_machine;

 

#ifdef CONFIG_VT

#if defined(CONFIG_VGA_CONSOLE)

        conswitchp = &vga_con;

#elif defined(CONFIG_DUMMY_CONSOLE)

        conswitchp = &dummy_con;

#endif

#endif

        early_trap_init();

}

 

 

static void (*init_machine)(void) __initdata;

 

static int __init customize_machine(void)

{

        /* customizes platform devices, or adds new ones */

        if (init_machine)

              init_machine();

        return 0;

}

arch_initcall(customize_machine);

对于arch-initcallcustomize_machine放在特定的段中,系统将在某个地方运行所有的arch_initcall所修饰的函数。

Kernel-imx/include/linux/Init.h#L156

 

 

#ifndef MODULE

 

#ifndef __ASSEMBLY__

 

/* initcalls are now grouped by functionality into separate

 * subsections. Ordering inside the subsections is determined

 * by link order.

 * For backwards compatibility, initcall() puts the call in

 * the device init subsection.

 *

 * The `id' arg to __define_initcall() is needed so that multiple initcalls

 * can point at the same handler without causing duplicate-symbol build errors.

 */

 

#define __define_initcall(level,fn,id) /

        static initcall_t __initcall_##fn##id __used /

        __attribute__((__section__(".initcall" level ".init"))) = fn

 

/*

 * Early initcalls run before initializing SMP.

 *

 * Only for built-in code, not modules.

 */

#define early_initcall(fn)            __define_initcall("early",fn,early)

 

/*

 * A "pure" initcall has no dependencies on anything else, and purely

 * initializes variables that couldn't be statically initialized.

 *

 * This only exists for built-in code, not for modules.

 */

#define pure_initcall(fn)             __define_initcall("0",fn,0)

 

#define core_initcall(fn)             __define_initcall("1",fn,1)

#define core_initcall_sync(fn)          __define_initcall("1s",fn,1s)

#define postcore_initcall(fn)             __define_initcall("2",fn,2)

#define postcore_initcall_sync(fn)  __define_initcall("2s",fn,2s)

#define arch_initcall(fn)             __define_initcall("3",fn,3)

 

#define arch_initcall_sync(fn)         __define_initcall("3s",fn,3s)

#define subsys_initcall(fn)        __define_initcall("4",fn,4)

#define subsys_initcall_sync(fn)     __define_initcall("4s",fn,4s)

#define fs_initcall(fn)                  __define_initcall("5",fn,5)

#define fs_initcall_sync(fn)              __define_initcall("5s",fn,5s)

#define rootfs_initcall(fn)           __define_initcall("rootfs",fn,rootfs)

#define device_initcall(fn)         __define_initcall("6",fn,6)

#define device_initcall_sync(fn)      __define_initcall("6s",fn,6s)

#define late_initcall(fn)        __define_initcall("7",fn,7)

#define late_initcall_sync(fn)           __define_initcall("7s",fn,7s)

 

#define __initcall(fn) device_initcall(fn)

 

#define __exitcall(fn) /

        static exitcall_t __exitcall_##fn __exit_call = fn

 

#define console_initcall(fn) /

        static initcall_t __initcall_##fn /

        __used __section(.con_initcall.init) = fn

 

#define security_initcall(fn) /

        static initcall_t __initcall_##fn /

        __used __section(.security_initcall.init) = fn

 

struct obs_kernel_param {

        const char *str;

        int (*setup_func)(char *);

        int early;

};

 

/*

 * Only for really core code.  See moduleparam.h for the normal way.

 *

 * Force the alignment so the compiler doesn't space elements of the

 * obs_kernel_param "array" too far apart in .init.setup.

 */

#define __setup_param(str, unique_id, fn, early)                   /

        static const char __setup_str_##unique_id[] __initconst     /

              __aligned(1) = str; /

        static struct obs_kernel_param __setup_##unique_id  /

              __used __section(.init.setup)                  /

              __attribute__((aligned((sizeof(long)))))  /

              = { __setup_str_##unique_id, fn, early }

 

#define __setup(str, fn)                             /

        __setup_param(str, fn, fn, 0)

 

/* NOTE: fn is as per module_param, not __setup!  Emits warning if fn

 * returns non-zero. */

#define early_param(str, fn)                                   /

        __setup_param(str, fn, fn, 1)

 

/* Relies on boot_command_line being set */

void __init parse_early_param(void);

void __init parse_early_options(char *cmdline);

#endif /* __ASSEMBLY__ */

 

/**

 * module_init() - driver initialization entry point

 * @x: function to be run at kernel boot time or module insertion

 *

 * module_init() will either be called during do_initcalls() (if

 * builtin) or at module insertion time (if a module).  There can only

 * be one per module.

 */

#define module_init(x) __initcall(x);

 

/**

 * module_exit() - driver exit entry point

 * @x: function to be run when driver is removed

 *

 * module_exit() will wrap the driver clean-up code

 * with cleanup_module() when used with rmmod when

 * the driver is a module.  If the driver is statically

 * compiled into the kernel, module_exit() has no effect.

 * There can only be one per module.

 */

#define module_exit(x) __exitcall(x);

 

#else /* MODULE */

 

/* Don't use these in modules, but some people do... */

#define early_initcall(fn)            module_init(fn)

#define core_initcall(fn)             module_init(fn)

#define postcore_initcall(fn)             module_init(fn)

#define arch_initcall(fn)             module_init(fn)

#define subsys_initcall(fn)        module_init(fn)

#define fs_initcall(fn)                  module_init(fn)

#define device_initcall(fn)         module_init(fn)

#define late_initcall(fn)        module_init(fn)

 

#define security_initcall(fn)              module_init(fn)

 

/* Each module must use one module_init(). */

#define module_init(initfn)                             /

        static inline initcall_t __inittest(void)            /

        { return initfn; }                             /

        int init_module(void) __attribute__((alias(#initfn)));

 

/* This is only required if you want to be unloadable. */

#define module_exit(exitfn)                                   /

        static inline exitcall_t __exittest(void)           /

        { return exitfn; }                                   /

        void cleanup_module(void) __attribute__((alias(#exitfn)));

 

#define __setup_param(str, unique_id, fn) /* nothing */

#define __setup(str, func)                /* nothing */

#endif

 

高亮显示部分arch_initcall =_define_initcall("3",fn,3)可知,其被链接器放在section .initcall3.init中;

 

Kernel_imx/include/asm_generic/Vmlinux.lds.h#L595

#define INITCALLS                                                 /

       *(.initcallearly.init)                                      /

       VMLINUX_SYMBOL(__early_initcall_end) = .;                /

     *(.initcall0.init)                                      /

     *(.initcall0s.init)                                    /

     *(.initcall1.init)                                      /

     *(.initcall1s.init)                                    /

     *(.initcall2.init)                                      /

     *(.initcall2s.init)                                    /

     *(.initcall3.init)                                      /

     *(.initcall3s.init)                                    /

     *(.initcall4.init)                                      /

     *(.initcall4s.init)                                    /

     *(.initcall5.init)                                      /

     *(.initcall5s.init)                                    /

       *(.initcallrootfs.init)                                     /

     *(.initcall6.init)                                      /

     *(.initcall6s.init)                                    /

     *(.initcall7.init)                                      /

     *(.initcall7s.init)

 

#define INIT_CALLS                                               /

              VMLINUX_SYMBOL(__initcall_start) = .;                   /

              INITCALLS                                    /

              VMLINUX_SYMBOL(__initcall_end) = .;

 

 

 

 

 

Kernel_imx/init/Main.c#L779定义

 

static void __init do_initcalls(void)

{

       initcall_t *fn;

 

       for (fn = __early_initcall_end; fn < __initcall_end; fn++)

              do_one_initcall(*fn);

 

       /* Make sure there is no pending stuff from the initcall sequence */

       flush_scheduled_work();

}

 

因此_initcall_fnx,数字越小,越先被调用,所以arch_initcall优先于module_initplatform_driver,后续讲到)所修饰的函数。

arch_initcall修饰的函数调用顺序如下:

start_kernel(init/main.c#L539) >>rest_init(init/main.c#L429在函数的最后一个调用)>>kenel_init(init/main.c#L866)>>do_basic_setup(init/Main.c#L797)>> do_initcalls()(在函数的最后),因为此时platform_bus_init在此前已经初始化完毕,因此便可将设备挂接到总线上了。

 

1.4 定义platform_driver

Platform bus和设备都定义好之后,需要定义一个platform driver用来驱动设备。

对于设备来说:

struct platform_device mxci2c_devices[] = {

       {

              .name = "imx-i2c",

              .id = 0,

              .num_resources = ARRAY_SIZE(mxci2c1_resources),

              .resource = mxci2c1_resources,

       },

       {

              .name = "imx-i2c",

              .id = 1,

              .num_resources = ARRAY_SIZE(mxci2c2_resources),

              .resource = mxci2c2_resources,

       },

       {

              .name = "imx-i2c",

              .id = 2,

              .num_resources = ARRAY_SIZE(mxci2c3_resources),

              .resource = mxci2c3_resources,

       },

};

platform总线上devicedriver的匹配规则可知,I2Cplatform driver的名字是imx-i2c

Kernel_imx/drivers/i2c/busses/Mxc_i2c.c#L774

/*!

 * This structure contains pointers to the power management callback functions.

 */

static struct platform_driver mxci2c_driver = {

       .driver = {

                 .name = "mxc_i2c",

                 .owner = THIS_MODULE,

                 .pm = &mxci2c_dev_pm_ops,

                 },

       .probe = mxci2c_probe,

       .remove = mxci2c_remove,

};

 

1.5 注册platform_driver

Kernel_imx/drivers/i2c/busses/Mxc_i2c.c#L789

/*!

 * Function requests the interrupts and registers the i2c adapter structures.

 *

 * @return The function returns 0 on success and a non-zero value on failure.

 */

static int __init mxc_i2c_init(void)

{

       /* Register the device driver structure. */

       return platform_driver_register(&mxci2c_driver);

}

subsys_initcall(mxc_i2c_init);

module_exit(mxc_i2c_exit);

 

mxc_i2c_init中注册mxci2c_driver,其执行是在subsys_initcall定义的代码段中(有些driver是在module_initcall定义的代码段中,具体见Kernel-imx/include/linux/Init.h#L156),

#define subsys_initcall(fn)        __define_initcall("4",fn,4)

因此在代码段3中,把设备注册到内核中,在4把驱动和内核绑定,并最终调用mxci2c_probe

static int mxci2c_probe(struct platform_device *pdev)

{

       mxc_i2c_device *mxc_i2c;

       struct mxc_i2c_platform_data *i2c_plat_data = pdev->dev.platform_data;

       struct resource *res;

       int id = pdev->id;

       u32 clk_freq;

       int ret = 0;

       int i;

 

       mxc_i2c = kzalloc(sizeof(mxc_i2c_device), GFP_KERNEL);

       if (!mxc_i2c) {

              return -ENOMEM;

       }

 

       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);

       if (res == NULL) {

              ret = -ENODEV;

              goto err1;

       }

       mxc_i2c->membase = ioremap(res->start, res->end - res->start + 1);

 

       /*

        * Request the I2C interrupt

        */

       mxc_i2c->irq = platform_get_irq(pdev, 0);

       if (mxc_i2c->irq < 0) {

              ret = mxc_i2c->irq;

              goto err2;

       }

 

       ret = request_irq(mxc_i2c->irq, mxc_i2c_handler,

                       0, pdev->name, mxc_i2c);

       if (ret < 0) {

              goto err2;

       }

 

       init_waitqueue_head(&mxc_i2c->wq);

 

       mxc_i2c->low_power = false;

 

       gpio_i2c_active(id);

 

       mxc_i2c->clk = clk_get(&pdev->dev, "i2c_clk");

       clk_freq = clk_get_rate(mxc_i2c->clk);

       mxc_i2c->clkdiv = -1;

       if (i2c_plat_data->i2c_clk) {

              /* Calculate divider and round up any fractional part */

              int div = (clk_freq + i2c_plat_data->i2c_clk - 1) /

                  i2c_plat_data->i2c_clk;

              for (i = 0; i2c_clk_table[i].div != 0; i++) {

                     if (i2c_clk_table[i].div >= div) {

                            mxc_i2c->clkdiv = i2c_clk_table[i].reg_value;

                            break;

                     }

              }

       }

       if (mxc_i2c->clkdiv == -1) {

              i--;

              mxc_i2c->clkdiv = 0x1F;      /* Use max divider */

       }

       dev_dbg(&pdev->dev, "i2c speed is %d/%d = %d bps, reg val = 0x%02X/n",

              clk_freq, i2c_clk_table[i].div,

              clk_freq / i2c_clk_table[i].div, mxc_i2c->clkdiv);

 

       /*

        * Set the adapter information

        */

       strlcpy(mxc_i2c->adap.name, pdev->name, 48);

       mxc_i2c->adap.id = mxc_i2c->adap.nr = id;

       mxc_i2c->adap.algo = &mxc_i2c_algorithm;

       mxc_i2c->adap.timeout = 1;

       platform_set_drvdata(pdev, mxc_i2c);

       i2c_set_adapdata(&mxc_i2c->adap, mxc_i2c);

       ret = i2c_add_numbered_adapter(&mxc_i2c->adap);

       if (ret < 0)

              goto err3;

 

       printk(KERN_INFO "MXC I2C driver/n");

       return 0;

 

      err3:

       free_irq(mxc_i2c->irq, mxc_i2c);

       gpio_i2c_inactive(id);

      err2:

       iounmap(mxc_i2c->membase);

      err1:

       dev_err(&pdev->dev, "failed to probe i2c adapter/n");

       kfree(mxc_i2c);

       return ret;

}

 

当进入probe函数后,需要获取设备的资源信息,其函数为platform_get_resource,根据参数类型(egtype=IORESOURCE_MEN)来获取指定的资源。

利用platform_get_irq来获取资源中的中断号。

 

Probe函数获取物理IO空间,通过request_mem_regionioremap等操作物理地址转换成内核中的虚拟地址,初始化I2c控制器。获得中断号后,通过request_irq来向系统注册中断,并将此I2c控制器添加到系统中。

 

 

1.6 操作设备

进行了platform_device_registerplatform_driver_registe后,驱动的相应信息就出现在sys目录的相应文件夹下,然后就是考虑如何调用设备和怎么对设备进行打开读写等操作

 

Platform总线只是为了方便管理接在CPU总线上的设备,与用户空间的交互,如读写还是需要利用file_operations

 

对于I2C总线来说,其file_operations如下:

static const struct file_operations i2cdev_fops = {

       .owner         = THIS_MODULE,

       .llseek          = no_llseek,

       .read            = i2cdev_read,

       .write            = i2cdev_write,

       .unlocked_ioctl = i2cdev_ioctl,

       .open           = i2cdev_open,

       .release       = i2cdev_release,

};

对于file_operations的具体实现,后续进行

 

原创粉丝点击