1、模块初始化,注册一个平台设备,调用platform_driver_register 时系统会使用s3c2440_i2c_driver 中的driver.name 字段去遍历平台设备链表,以找到相应平台设备。
- static int __init i2c_adap_s3c_init(void)
- {
- int ret;
- ret = platform_driver_register(&s3c2410_i2c_driver);
- if (ret == 0) {
- ret = platform_driver_register(&s3c2440_i2c_driver);
- if (ret)
- platform_driver_unregister(&s3c2410_i2c_driver);
- }
- return ret;
- }
2、找到对应的平台设备,则该平台设备结构体指针作为参数,回调probe 字段填入的函数-s3c24xx_i2c_probe.
- static struct platform_driver s3c2440_i2c_driver = {
- .probe = s3c24xx_i2c_probe,
- .remove = s3c24xx_i2c_remove,
- .resume = s3c24xx_i2c_resume,
- .driver = {
- .owner = THIS_MODULE,
- .name = "s3c2440-i2c",
- },
- };
3、probe函数对平台设备进行初始化等操作,其中会使用内核API platform_get_resource 与platform_get_irq 获得所需的内存和中断号等。
- static int s3c24xx_i2c_probe(struct platform_device *pdev)
- {
- struct s3c24xx_i2c *i2c;
- struct s3c2410_platform_i2c *pdata;
- struct resource *res;
- int ret;
- pdata = pdev->dev.platform_data;
- if (!pdata) {
- dev_err(&pdev->dev, "no platform data\n");
- return -EINVAL;
- }
- i2c = kzalloc(sizeof(struct s3c24xx_i2c), GFP_KERNEL);
- if (!i2c) {
- dev_err(&pdev->dev, "no memory for state\n");
- return -ENOMEM;
- }
- strlcpy(i2c->adap.name, "s3c2410-i2c", sizeof(i2c->adap.name));
- i2c->adap.owner = THIS_MODULE;
- i2c->adap.algo = &s3c24xx_i2c_algorithm;
- i2c->adap.retries = 2;
- i2c->adap.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
- i2c->tx_setup = 50;
- spin_lock_init(&i2c->lock);
- init_waitqueue_head(&i2c->wait);
- /* find the clock and enable it */
- i2c->dev = &pdev->dev;
- i2c->clk = clk_get(&pdev->dev, "i2c");
- if (IS_ERR(i2c->clk)) {
- dev_err(&pdev->dev, "cannot get clock\n");
- ret = -ENOENT;
- goto err_noclk;
- }
- dev_dbg(&pdev->dev, "clock source %p\n", i2c->clk);
- clk_enable(i2c->clk);
- /* map the registers */
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (res == NULL) {
- dev_err(&pdev->dev, "cannot find IO resource\n");
- ret = -ENOENT;
- goto err_clk;
- }
- i2c->ioarea = request_mem_region(res->start, resource_size(res),
- pdev->name);
- if (i2c->ioarea == NULL) {
- dev_err(&pdev->dev, "cannot request IO\n");
- ret = -ENXIO;
- goto err_clk;
- }
- i2c->regs = ioremap(res->start, resource_size(res));
- if (i2c->regs == NULL) {
- dev_err(&pdev->dev, "cannot map IO\n");
- ret = -ENXIO;
- goto err_ioarea;
- }
- dev_dbg(&pdev->dev, "registers %p (%p, %p)\n",
- i2c->regs, i2c->ioarea, res);
- /* setup info block for the i2c core */
- i2c->adap.algo_data = i2c;
- i2c->adap.dev.parent = &pdev->dev;
- /* initialise the i2c controller */
- ret = s3c24xx_i2c_init(i2c);
- if (ret != 0)
- goto err_iomap;
- /* find the IRQ for this unit (note, this relies on the init call to
- * ensure no current IRQs pending
- */
- i2c->irq = ret = platform_get_irq(pdev, 0);
- if (ret <= 0) {
- dev_err(&pdev->dev, "cannot find IRQ\n");
- goto err_iomap;
- }
- ret = request_irq(i2c->irq, s3c24xx_i2c_irq, IRQF_DISABLED,
- dev_name(&pdev->dev), i2c);
- if (ret != 0) {
- dev_err(&pdev->dev, "cannot claim IRQ %d\n", i2c->irq);
- goto err_iomap;
- }
- ret = s3c24xx_i2c_register_cpufreq(i2c);
- if (ret < 0) {
- dev_err(&pdev->dev, "failed to register cpufreq notifier\n");
- goto err_irq;
- }
- /* Note, previous versions of the driver used i2c_add_adapter()
- * to add the bus at any number. We now pass the bus number via
- * the platform data, so if unset it will now default to always
- * being bus 0.
- */
- i2c->adap.nr = pdata->bus_num;
- ret = i2c_add_numbered_adapter(&i2c->adap);
- if (ret < 0) {
- dev_err(&pdev->dev, "failed to add bus to i2c core\n");
- goto err_cpufreq;
- }
- platform_set_drvdata(pdev, i2c);
- dev_info(&pdev->dev, "%s: S3C I2C adapter\n", dev_name(&i2c->adap.dev));
- return 0;
- err_cpufreq:
- s3c24xx_i2c_deregister_cpufreq(i2c);
- err_irq:
- free_irq(i2c->irq, i2c);
- err_iomap:
- iounmap(i2c->regs);
- err_ioarea:
- release_resource(i2c->ioarea);
- kfree(i2c->ioarea);
- err_clk:
- clk_disable(i2c->clk);
- clk_put(i2c->clk);
- err_noclk:
- kfree(i2c);
- return ret;
- }
- /* s3c24xx_i2c_remove
- *
- * called when device is removed from the bus
- */
- static int s3c24xx_i2c_remove(struct platform_device *pdev)
- {
- struct s3c24xx_i2c *i2c = platform_get_drvdata(pdev);
- s3c24xx_i2c_deregister_cpufreq(i2c);
- i2c_del_adapter(&i2c->adap);
- free_irq(i2c->irq, i2c);
- clk_disable(i2c->clk);
- clk_put(i2c->clk);
- iounmap(i2c->regs);
- release_resource(i2c->ioarea);
- kfree(i2c->ioarea);
- kfree(i2c);
- return 0;
- }
- static struct s3c24xx_i2c s3c24xx_i2c = {
- .lock = __SPIN_LOCK_UNLOCKED(s3c24xx_i2c.lock),
- .wait = __WAIT_QUEUE_HEAD_INITIALIZER(s3c24xx_i2c.wait),
- .tx_setup = 50,
- .adap = {
- .name = "s3c2410-i2c",
- .owner = THIS_MODULE,
- .algo = &s3c24xx_i2c_algorithm, //i2c适配器能力
- .retries = 2,
- .class = I2C_CLASS_HWMON,
- },
- };
- static const struct i2c_algorithm s3c24xx_i2c_algorithm = {
- .master_xfer = s3c24xx_i2c_xfer,
- .functionality = s3c24xx_i2c_func,
- };