一、基于s3c6410的I2C总线控制器驱动程序分析及其在BSP中的添加
1. 适配器驱动分析1.1 平台总线驱动结构体及其注册与注销- //1. s3c2410 I2C总线的平台驱动结构体
- static struct platform_driver s3c24xx_i2c_driver = {
- .probe = s3c24xx_i2c_probe,
- .remove = s3c24xx_i2c_remove,
- .id_table = s3c24xx_driver_ids,
- .driver = {
- .owner = THIS_MODULE,
- .name = "s3c-i2c",
- .pm = S3C24XX_DEV_PM_OPS,
- .of_match_table = s3c24xx_i2c_match,
- },
- };
- //2. 注册I2C平台总线驱动
- static int __init i2c_adap_s3c_init(void)
- {
- return platform_driver_register(&s3c24xx_i2c_driver);
- }
- subsys_initcall(i2c_adap_s3c_init); //子系统初始化,在系统初始化时在do_initcall中调用
- //3. 注销I2C平台总线驱动
- static void __exit i2c_adap_s3c_exit(void)
- {
- platform_driver_unregister(&s3c24xx_i2c_driver);
- }
- module_exit(i2c_adap_s3c_exit);
1.2 s3c24xx_i2c_probe函数分析
s3c24xx_i2c_probe函数总的来说是:给s3c24xx_i2c结构体及其私有数据分配内存,并且设置该结构体内的需要设置的成员。
- /* s3c24xx_i2c_probe
- *
- * called by the bus driver when a suitable device is found
- */
- static int s3c24xx_i2c_probe(struct platform_device *pdev)
- {
- struct s3c24xx_i2c *i2c;
- struct s3c2410_platform_i2c *pdata = NULL;
- struct resource *res;
- int ret;
- //1. 判断是否已创建设备节点
- if (!pdev->dev.of_node) {
- pdata = pdev->dev.platform_data; //如果没有,把平台数据赋值给内部指针
- if (!pdata) {
- dev_err(&pdev->dev, "no platform data\n");
- return -EINVAL;
- }
- }
- //2. 分配s3c24xx_i2c结构体内存
- i2c = kzalloc(sizeof(struct s3c24xx_i2c), GFP_KERNEL);
- if (!i2c) {
- dev_err(&pdev->dev, "no memory for state\n");
- return -ENOMEM;
- }
- //3. 分配s3c24xx_i2c结构体私有数据的内存
- i2c->pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
- if (!i2c->pdata) {
- ret = -ENOMEM;
- goto err_noclk;
- }
- //4. 如果私有数据指针不为NULL,复制设备数据
- if (pdata)
- memcpy(i2c->pdata, pdata, sizeof(*pdata));
- else
- s3c24xx_i2c_parse_dt(pdev->dev.of_node, i2c);
- //5. 设置i2c->adap
- 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;
- //6. 初始化I2C的自旋锁和等待队列
- spin_lock_init(&i2c->lock);
- init_waitqueue_head(&i2c->wait);
- //7. 获取并使能I2C时钟
- /* 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);
- //8. 寄存器映射
- /* 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);
- //9. 为i2c核心设置部分信息
- /* setup info block for the i2c core */
- i2c->adap.algo_data = i2c;
- i2c->adap.dev.parent = &pdev->dev;
- //10. 初始化i2c控制器
- /* initialise the i2c controller */
- ret = s3c24xx_i2c_init(i2c);
- if (ret != 0)
- goto err_iomap;
- //11. 获取并注册I2C IRQ
- /* 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, 0,
- dev_name(&pdev->dev), i2c);
- if (ret != 0) {
- dev_err(&pdev->dev, "cannot claim IRQ %d\n", i2c->irq);
- goto err_iomap;
- }
- //12. 以CPU的频率注册I2C结构体
- 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 = i2c->pdata->bus_num; //当处理器包含多个I2C控制器时,用bus_num区分
- i2c->adap.dev.of_node = pdev->dev.of_node;
- //13. 添加适配器
- 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;
- }
- of_i2c_register_devices(&i2c->adap);
- platform_set_drvdata(pdev, i2c);
- dev_info(&pdev->dev, "%s: S3C I2C adapter\n", dev_name(&i2c->adap.dev));
- clk_disable(i2c->clk);
- 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);
- s3c24xx_i2c_dt_gpio_free(i2c);
- kfree(i2c->ioarea);
- kfree(i2c);
- return 0;
- }
1.3 s3c24xx_i2c_init函数分析- /* s3c24xx_i2c_init
- *
- * initialise the controller, set the IO lines and frequency
- */
- static int s3c24xx_i2c_init(struct s3c24xx_i2c *i2c)
- {
- unsigned long iicon = S3C2410_IICCON_IRQEN | S3C2410_IICCON_ACKEN;
- struct s3c2410_platform_i2c *pdata;
- unsigned int freq;
- /* get the plafrom data */
- pdata = i2c->pdata;
- /* inititalise the gpio */
- if (pdata->cfg_gpio)
- pdata->cfg_gpio(to_platform_device(i2c->dev));
- else
- if (s3c24xx_i2c_parse_dt_gpio(i2c))
- return -EINVAL;
- /* write slave address */
- writeb(pdata->slave_addr, i2c->regs + S3C2410_IICADD);
- dev_info(i2c->dev, "slave address 0x%02x\n", pdata->slave_addr);
- writel(iicon, i2c->regs + S3C2410_IICCON);
- /* we need to work out the divisors for the clock... */
- if (s3c24xx_i2c_clockrate(i2c, &freq) != 0) {
- writel(0, i2c->regs + S3C2410_IICCON);
- dev_err(i2c->dev, "cannot meet bus frequency required\n");
- return -EINVAL;
- }
- /* todo - check that the i2c lines aren't being dragged anywhere */
- dev_info(i2c->dev, "bus frequency set to %d KHz\n", freq);
- dev_dbg(i2c->dev, "S3C2410_IICCON=0x%02lx\n", iicon);
- return 0;
- }
2.适配器设备驱动- static struct resource s3c_i2c0_resource[] = {
- [0] = DEFINE_RES_MEM(S3C_PA_IIC, SZ_4K),
- [1] = DEFINE_RES_IRQ(IRQ_IIC),
- };
- struct platform_device s3c_device_i2c0 = {
- .name = "s3c2410-i2c",
- #ifdef CONFIG_S3C_DEV_I2C1
- .id = 0,
- #else
- .id = -1,
- #endif
- .num_resources = ARRAY_SIZE(s3c_i2c0_resource),
- .resource = s3c_i2c0_resource,
- };
- struct s3c2410_platform_i2c default_i2c_data __initdata = {
- .flags = 0,
- .slave_addr = 0x10,
- .frequency = 100*1000,
- .sda_delay = 100,
- };
- void __init s3c_i2c0_set_platdata(struct s3c2410_platform_i2c *pd)
- {
- struct s3c2410_platform_i2c *npd;
- if (!pd)
- pd = &default_i2c_data;
- npd = s3c_set_platdata(pd, sizeof(struct s3c2410_platform_i2c),
- &s3c_device_i2c0);
- if (!npd->cfg_gpio)
- npd->cfg_gpio = s3c_i2c0_cfg_gpio;
- }
3.BSP中添加I2C适配器 - /* linux/arch/arm/mach-s3c64xx/mach-jason6410.c
- *
- * Copyright 2012 Jason Lu <gfvvz@yahoo.com.cn>
- * http://jason2012.blog.chinaunix.net
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- */
- #include <linux/init.h>
- #include <linux/interrupt.h>
- #include <linux/fb.h>
- #include <linux/gpio.h>
- #include <linux/kernel.h>
- #include <linux/list.h>
- #include <linux/dm9000.h>
- #include <linux/mtd/mtd.h>
- #include <linux/mtd/partitions.h>
- #include <linux/serial_core.h>
- #include <linux/types.h>
- #include <asm/mach-types.h>
- #include <asm/mach/arch.h>
- #include <asm/mach/map.h>
- #include <mach/map.h>
- #include <mach/regs-gpio.h>
- #include <mach/regs-modem.h>
- #include <mach/regs-srom.h>
- #include <plat/s3c6410.h>
- #include <plat/adc.h>
- #include <plat/cpu.h>
- #include <plat/devs.h>
- #include <plat/fb.h>
- #include <plat/nand.h>
- #include <plat/regs-serial.h>
- #include <plat/ts.h>
- #include <plat/regs-fb-v4.h>
- #include
- #include <video/platform_lcd.h>
- #define UCON S3C2410_UCON_DEFAULT
- #define ULCON (S3C2410_LCON_CS8 | S3C2410_LCON_PNONE | S3C2410_LCON_STOPB)
- #define UFCON (S3C2410_UFCON_RXTRIG8 | S3C2410_UFCON_FIFOMODE)
- static struct s3c2410_uartcfg jason6410_uartcfgs[] __initdata = {
- [0] = {
- .hwport = 0,
- .flags = 0,
- .ucon = UCON,
- .ulcon = ULCON,
- .ufcon = UFCON,
- },
- [1] = {
- .hwport = 1,
- .flags = 0,
- .ucon = UCON,
- .ulcon = ULCON,
- .ufcon = UFCON,
- },
- [2] = {
- .hwport = 2,
- .flags = 0,
- .ucon = UCON,
- .ulcon = ULCON,
- .ufcon = UFCON,
- },
- [3] = {
- .hwport = 3,
- .flags = 0,
- .ucon = UCON,
- .ulcon = ULCON,
- .ufcon = UFCON,
- },
- };
- /* Nand flash */
- static struct mtd_partition jason6410_nand_part[] = {
- {
- .name = "u-boot-2011.06",
- .offset = 0,
- .size = (4 * 128 *SZ_1K),
- .mask_flags = MTD_CAP_NANDFLASH,
- },
- {
- .name = "Linux Kernel 3.2.8",
- .offset = MTDPART_OFS_APPEND,
- .size = (5*SZ_1M) ,
- .mask_flags = MTD_CAP_NANDFLASH,
- },
- {
- .name = "UBI File System",
- .offset = MTDPART_OFS_APPEND,
- .size = MTDPART_SIZ_FULL,
- }
- };
- static struct s3c2410_nand_set jason6410_nand_sets[] = {
- [0] = {
- .name = "nand",
- .nr_chips = 1,
- .nr_partitions = ARRAY_SIZE(jason6410_nand_part),
- .partitions = jason6410_nand_part,
- },
- };
- static struct s3c2410_platform_nand jason6410_nand_info = {
- .tacls = 25,
- .twrph0 = 55,
- .twrph1 = 40,
- .nr_sets = ARRAY_SIZE(jason6410_nand_sets),
- .sets = jason6410_nand_sets,
- };
- static struct platform_device *jason6410_devices[] __initdata = {
- &s3c_device_nand,
- &s3c_device_i2c0,
- };
- static void __init jason6410_map_io(void)
- {
- s3c64xx_init_io(NULL, 0);
- s3c24xx_init_clocks(12000000);
- s3c24xx_init_uarts(jason6410_uartcfgs, ARRAY_SIZE(jason6410_uartcfgs));
- }
- static void __init jason6410_machine_init(void)
- {
- s3c_device_nand.name = "s3c6410-nand";
- s3c_nand_set_platdata(&jason6410_nand_info);
- s3c_i2c0_set_platdata(NULL);
- platform_add_devices(jason6410_devices, ARRAY_SIZE(jason6410_devices));
- }
- MACHINE_START(JASON6410, "JASON6410")
- /* Maintainer: Darius Augulis <augulis.darius@gmail.com> */
- .atag_offset = 0x100,
- .init_irq = s3c6410_init_irq,
- .map_io = jason6410_map_io,
- .init_machine = jason6410_machine_init,
- .timer = &s3c24xx_timer,
- MACHINE_END
二、AT24 EEPROM I2C设备驱动程序分析及其在BSP中的添加- static struct i2c_driver at24_driver = { //该结构体较新内核才有的,具体变化详见附录
- .driver = {
- .name = "at24",
- .owner = THIS_MODULE,
- },
- .probe = at24_probe,
- .remove = __devexit_p(at24_remove),
- .id_table = at24_ids,
- };
- static int __init at24_init(void)
- {
- if (!io_limit) {
- pr_err("at24: io_limit must not be 0!\n");
- return -EINVAL;
- }
- io_limit = rounddown_pow_of_two(io_limit);
- return i2c_add_driver(&at24_driver);
- }
- module_init(at24_init);
- static void __exit at24_exit(void)
- {
- i2c_del_driver(&at24_driver);
- }
- module_exit(at24_exit);
1.2 i2c_device_id结构体
- /* create non-zero magic value for given eeprom parameters */
- #define AT24_DEVICE_MAGIC(_len, _flags) \
- ((1 << AT24_SIZE_FLAGS | (_flags)) \
- << AT24_SIZE_BYTELEN | ilog2(_len))
- static const struct i2c_device_id at24_ids[] = {
- /* needs 8 addresses as A0-A2 are ignored */
- { "24c00", AT24_DEVICE_MAGIC(128 / 8, AT24_FLAG_TAKE8ADDR) },
- /* old variants can't be handled with this generic */
- { "24c01", AT24_DEVICE_MAGIC(1024 / 8, 0) },
- { "24c02", AT24_DEVICE_MAGIC(2048 / 8, 0) },
- /* spd is a 24c02 in memory DIMMs */
- { "spd", AT24_DEVICE_MAGIC(2048 / 8,
- AT24_FLAG_READONLY | AT24_FLAG_IRUGO) },
- { "24c04", AT24_DEVICE_MAGIC(4096 / 8, 0) },
- /* 24rf08 quirk is handled at i2c-core */
- { "24c08", AT24_DEVICE_MAGIC(8192 / 8, 0) },
- { "24c16", AT24_DEVICE_MAGIC(16384 / 8, 0) },
- { "24c32", AT24_DEVICE_MAGIC(32768 / 8, AT24_FLAG_ADDR16) },
- { "24c64", AT24_DEVICE_MAGIC(65536 / 8, AT24_FLAG_ADDR16) },
- { "24c128", AT24_DEVICE_MAGIC(131072 / 8, AT24_FLAG_ADDR16) },
- { "24c256", AT24_DEVICE_MAGIC(262144 / 8, AT24_FLAG_ADDR16) },
- { "24c512", AT24_DEVICE_MAGIC(524288 / 8, AT24_FLAG_ADDR16) },
- { "24c1024", AT24_DEVICE_MAGIC(1048576 / 8, AT24_FLAG_ADDR16) },
- { "at24", 0 },
- { /* END OF LIST */ }
- };
- MODULE_DEVICE_TABLE(i2c, at24_ids);
1.3 at24_probe函数分析- //该函数的参数及实现是较新内核才有的,具体变化详见附录
- static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
- {
- struct at24_platform_data chip;
- bool writable;
- int use_smbus = 0;
- struct at24_data *at24;
- int err;
- unsigned i, num_addresses;
- kernel_ulong_t magic;
- if (client->dev.platform_data) {
- chip = *(struct at24_platform_data *)client->dev.platform_data;
- } else {
- if (!id->driver_data) {
- err = -ENODEV;
- goto err_out;
- }
- magic = id->driver_data;
- chip.byte_len = BIT(magic & AT24_BITMASK(AT24_SIZE_BYTELEN));
- magic >>= AT24_SIZE_BYTELEN;
- chip.flags = magic & AT24_BITMASK(AT24_SIZE_FLAGS);
- /*
- * This is slow, but we can't know all eeproms, so we better
- * play safe. Specifying custom eeprom-types via platform_data
- * is recommended anyhow.
- */
- chip.page_size = 1;
- /* update chipdata if OF is present */
- at24_get_ofdata(client, &chip);
- chip.setup = NULL;
- chip.context = NULL;
- }
- if (!is_power_of_2(chip.byte_len))
- dev_warn(&client->dev,
- "byte_len looks suspicious (no power of 2)!\n");
- if (!chip.page_size) {
- dev_err(&client->dev, "page_size must not be 0!\n");
- err = -EINVAL;
- goto err_out;
- }
- if (!is_power_of_2(chip.page_size))
- dev_warn(&client->dev,
- "page_size looks suspicious (no power of 2)!\n");
- /* Use I2C operations unless we're stuck with SMBus extensions. */
- if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
- if (chip.flags & AT24_FLAG_ADDR16) {
- err = -EPFNOSUPPORT;
- goto err_out;
- }
- if (i2c_check_functionality(client->adapter,
- I2C_FUNC_SMBUS_READ_I2C_BLOCK)) {
- use_smbus = I2C_SMBUS_I2C_BLOCK_DATA;
- } else if (i2c_check_functionality(client->adapter,
- I2C_FUNC_SMBUS_READ_WORD_DATA)) {
- use_smbus = I2C_SMBUS_WORD_DATA;
- } else if (i2c_check_functionality(client->adapter,
- I2C_FUNC_SMBUS_READ_BYTE_DATA)) {
- use_smbus = I2C_SMBUS_BYTE_DATA;
- } else {
- err = -EPFNOSUPPORT;
- goto err_out;
- }
- }
- if (chip.flags & AT24_FLAG_TAKE8ADDR)
- num_addresses = 8;
- else
- num_addresses = DIV_ROUND_UP(chip.byte_len,
- (chip.flags & AT24_FLAG_ADDR16) ? 65536 : 256);
- at24 = kzalloc(sizeof(struct at24_data) +
- num_addresses * sizeof(struct i2c_client *), GFP_KERNEL);
- if (!at24) {
- err = -ENOMEM;
- goto err_out;
- }
- mutex_init(&at24->lock);
- at24->use_smbus = use_smbus;
- at24->chip = chip;
- at24->num_addresses = num_addresses;
- /*
- * Export the EEPROM bytes through sysfs, since that's convenient.
- * By default, only root should see the data (maybe passwords etc)
- */
- sysfs_bin_attr_init(&at24->bin);
- at24->bin.attr.name = "eeprom";
- at24->bin.attr.mode = chip.flags & AT24_FLAG_IRUGO ? S_IRUGO : S_IRUSR;
- at24->bin.read = at24_bin_read;
- at24->bin.size = chip.byte_len;
- at24->macc.read = at24_macc_read;
- writable = !(chip.flags & AT24_FLAG_READONLY);
- if (writable) {
- if (!use_smbus || i2c_check_functionality(client->adapter,
- I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)) {
- unsigned write_max = chip.page_size;
- at24->macc.write = at24_macc_write;
- at24->bin.write = at24_bin_write;
- at24->bin.attr.mode |= S_IWUSR;
- if (write_max > io_limit)
- write_max = io_limit;
- if (use_smbus && write_max > I2C_SMBUS_BLOCK_MAX)
- write_max = I2C_SMBUS_BLOCK_MAX;
- at24->write_max = write_max;
- /* buffer (data + address at the beginning) */
- at24->writebuf = kmalloc(write_max + 2, GFP_KERNEL);
- if (!at24->writebuf) {
- err = -ENOMEM;
- goto err_struct;
- }
- } else {
- dev_warn(&client->dev,
- "cannot write due to controller restrictions.");
- }
- }
- at24->client[0] = client;
- /* use dummy devices for multiple-address chips */
- for (i = 1; i < num_addresses; i++) {
- at24->client[i] = i2c_new_dummy(client->adapter,
- client->addr + i);
- if (!at24->client[i]) {
- dev_err(&client->dev, "address 0x%02x unavailable\n",
- client->addr + i);
- err = -EADDRINUSE;
- goto err_clients;
- }
- }
- err = sysfs_create_bin_file(&client->dev.kobj, &at24->bin);
- if (err)
- goto err_clients;
- i2c_set_clientdata(client, at24);
- dev_info(&client->dev, "%zu byte %s EEPROM, %s, %u bytes/write\n",
- at24->bin.size, client->name,
- writable ? "writable" : "read-only", at24->write_max);
- if (use_smbus == I2C_SMBUS_WORD_DATA ||
- use_smbus == I2C_SMBUS_BYTE_DATA) {
- dev_notice(&client->dev, "Falling back to %s reads, "
- "performance will suffer\n", use_smbus ==
- I2C_SMBUS_WORD_DATA ? "word" : "byte");
- }
- /* export data to kernel code */
- if (chip.setup)
- chip.setup(&at24->macc, chip.context);
- return 0;
- err_clients:
- for (i = 1; i < num_addresses; i++)
- if (at24->client[i])
- i2c_unregister_device(at24->client[i]);
- kfree(at24->writebuf);
- err_struct:
- kfree(at24);
- err_out:
- dev_dbg(&client->dev, "probe error %d\n", err);
- return err;
- }
- static int __devexit at24_remove(struct i2c_client *client)
- {
- struct at24_data *at24;
- int i;
- at24 = i2c_get_clientdata(client);
- sysfs_remove_bin_file(&client->dev.kobj, &at24->bin);
- for (i = 1; i < at24->num_addresses; i++)
- i2c_unregister_device(at24->client[i]);
- kfree(at24->writebuf);
- kfree(at24);
- return 0;
- }
2. BSP中添加at24c08 I2C 信息
- /* linux/arch/arm/mach-s3c64xx/mach-jason6410.c
- *
- * Copyright 2012 Jason Lu <gfvvz@yahoo.com.cn>
- * http://jason2012.blog.chinaunix.net
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- */
- #include <linux/init.h>
- #include <linux/interrupt.h>
- #include <linux/fb.h>
- #include <linux/gpio.h>
- #include <linux/kernel.h>
- #include <linux/list.h>
- #include <linux/dm9000.h>
- #include <linux/mtd/mtd.h>
- #include <linux/mtd/partitions.h>
- #include <linux/serial_core.h>
- #include <linux/types.h>
- #include <asm/mach-types.h>
- #include <asm/mach/arch.h>
- #include <asm/mach/map.h>
- #include <mach/map.h>
- #include <mach/regs-gpio.h>
- #include <mach/regs-modem.h>
- #include <mach/regs-srom.h>
- #include <plat/s3c6410.h>
- #include <plat/adc.h>
- #include <plat/cpu.h>
- #include <plat/devs.h>
- #include <plat/fb.h>
- #include <plat/nand.h>
- #include <plat/regs-serial.h>
- #include <plat/ts.h>
- #include <plat/regs-fb-v4.h>
- #include <plat/iic.h>
- #include <video/platform_lcd.h>
- #define UCON S3C2410_UCON_DEFAULT
- #define ULCON (S3C2410_LCON_CS8 | S3C2410_LCON_PNONE | S3C2410_LCON_STOPB)
- #define UFCON (S3C2410_UFCON_RXTRIG8 | S3C2410_UFCON_FIFOMODE)
- static struct s3c2410_uartcfg jason6410_uartcfgs[] __initdata = {
- [0] = {
- .hwport = 0,
- .flags = 0,
- .ucon = UCON,
- .ulcon = ULCON,
- .ufcon = UFCON,
- },
- [1] = {
- .hwport = 1,
- .flags = 0,
- .ucon = UCON,
- .ulcon = ULCON,
- .ufcon = UFCON,
- },
- [2] = {
- .hwport = 2,
- .flags = 0,
- .ucon = UCON,
- .ulcon = ULCON,
- .ufcon = UFCON,
- },
- [3] = {
- .hwport = 3,
- .flags = 0,
- .ucon = UCON,
- .ulcon = ULCON,
- .ufcon = UFCON,
- },
- };
- /* Nand flash */
- static struct mtd_partition jason6410_nand_part[] = {
- {
- .name = "u-boot-2011.06",
- .offset = 0,
- .size = (4 * 128 *SZ_1K),
- .mask_flags = MTD_CAP_NANDFLASH,
- },
- {
- .name = "Linux Kernel 3.2.8",
- .offset = MTDPART_OFS_APPEND,
- .size = (5*SZ_1M) ,
- .mask_flags = MTD_CAP_NANDFLASH,
- },
- {
- .name = "UBI File System",
- .offset = MTDPART_OFS_APPEND,
- .size = MTDPART_SIZ_FULL,
- }
- };
- static struct s3c2410_nand_set jason6410_nand_sets[] = {
- [0] = {
- .name = "nand",
- .nr_chips = 1,
- .nr_partitions = ARRAY_SIZE(jason6410_nand_part),
- .partitions = jason6410_nand_part,
- },
- };
- static struct s3c2410_platform_nand jason6410_nand_info = {
- .tacls = 25,
- .twrph0 = 55,
- .twrph1 = 40,
- .nr_sets = ARRAY_SIZE(jason6410_nand_sets),
- .sets = jason6410_nand_sets,
- };
- static struct platform_device *jason6410_devices[] __initdata = {
- &s3c_device_nand,
- &s3c_device_i2c0,
- };
- static struct i2c_board_info i2c_devs0[] __initdata = { //i2c: add @2012.4.1
- { I2C_BOARD_INFO("24c08", 0x50), },
- };
- static struct i2c_board_info i2c_devs1[] __initdata = { //i2c: add @2012.4.1
- /* Add your i2c device here */
- };
- static void __init jason6410_map_io(void)
- {
- s3c64xx_init_io(NULL, 0);
- s3c24xx_init_clocks(12000000);
- s3c24xx_init_uarts(jason6410_uartcfgs, ARRAY_SIZE(jason6410_uartcfgs));
- }
- static void __init jason6410_machine_init(void)
- {
- s3c_device_nand.name = "s3c6410-nand";
- s3c_nand_set_platdata(&jason6410_nand_info);
- s3c_i2c0_set_platdata(NULL);
-
- /*
- * 在添加如下加粗代码后,内核报错,如下图,必须用:
- * make KALLSYMS_EXTRA_PASS=1 uImage -j4
- * 添加红色部分才能顺利通过编译,具体原因尚未清楚
- */
- if (ARRAY_SIZE(i2c_devs0)) { //i2c: add @2012.4.1
- i2c_register_board_info(0, i2c_devs0, ARRAY_SIZE(i2c_devs0));
- }
- if (ARRAY_SIZE(i2c_devs1)) { //i2c: add @2012.4.1
- i2c_register_board_info(1, i2c_devs1, ARRAY_SIZE(i2c_devs1));
- }
- platform_add_devices(jason6410_devices, ARRAY_SIZE(jason6410_devices));
- }
- MACHINE_START(JASON6410, "JASON6410")
- /* Maintainer: Darius Augulis <augulis.darius@gmail.com> */
- .atag_offset = 0x100,
- .init_irq = s3c6410_init_irq,
- .map_io = jason6410_map_io,
- .init_machine = jason6410_machine_init,
- .timer = &s3c24xx_timer,
- MACHINE_END
三、内核配置Device Drivers --->
<*> I2C support --->
I2C Hardware Bus support --->
<*> S3C2410 I2C Driver //默认配置里是已选的
Device Drivers --->
[*] Misc devices --->
EEPROM support --->
<*> I2C EEPROMs from most vendors //输入"Y",选择编译进内核
最后,在启动信息里,多出如下红色方框里几行。上面一个红色方框里的是s3c6410适配器的device和driver匹配成功后,在调用probe函数时,probe函数及probe函数里s3c24xx_i2c_init函数的的打印信息。
下面一个方框里的打印信息是BSP中i2c_board_info结构体中的I2C_BOARD_INFO成员里的.name成员24c08与at24.c里的at24_ids结构体里的某一成员名匹配成功后,调用probe函数,该函数打印出如此信息。
四、简单测试
附录:upgrading-clients: Upgrading I2C Drivers to the new 2.6 Driver Model
- Upgrading I2C Drivers to the new 2.6 Driver Model
- =================================================
- Ben Dooks <ben-linux@fluff.org>
- Introduction
- ------------
- This guide outlines how to alter existing Linux 2.6 client drivers from
- the old to the new new binding methods.
- Example old-style driver
- ------------------------
- struct example_state {
- struct i2c_client client;
- ....
- };
- static struct i2c_driver example_driver;
- static unsigned short ignore[] = { I2C_CLIENT_END };
- static unsigned short normal_addr[] = { OUR_ADDR, I2C_CLIENT_END };
- I2C_CLIENT_INSMOD;
- static int example_attach(struct i2c_adapter *adap, int addr, int kind)
- {
- struct example_state *state;
- struct device *dev = &adap->dev; /* to use for dev_ reports */
- int ret;
- state = kzalloc(sizeof(struct example_state), GFP_KERNEL);
- if (state == NULL) {
- dev_err(dev, "failed to create our state\n");
- return -ENOMEM;
- }
- example->client.addr = addr;
- example->client.flags = 0;
- example->client.adapter = adap;
- i2c_set_clientdata(&state->i2c_client, state);
- strlcpy(client->i2c_client.name, "example", I2C_NAME_SIZE);
- ret = i2c_attach_client(&state->i2c_client);
- if (ret < 0) {
- dev_err(dev, "failed to attach client\n");
- kfree(state);
- return ret;
- }
- dev = &state->i2c_client.dev;
- /* rest of the initialisation goes here. */
- dev_info(dev, "example client created\n");
- return 0;
- }
- static int example_detach(struct i2c_client *client)
- {
- struct example_state *state = i2c_get_clientdata(client);
- i2c_detach_client(client);
- kfree(state);
- return 0;
- }
- static int example_attach_adapter(struct i2c_adapter *adap)
- {
- return i2c_probe(adap, &addr_data, example_attach);
- }
- static struct i2c_driver example_driver = {
- .driver = {
- .owner = THIS_MODULE,
- .name = "example",
- },
- .attach_adapter = example_attach_adapter,
- .detach_client = example_detach,
- .suspend = example_suspend,
- .resume = example_resume,
- };
- Updating the client
- -------------------
- The new style binding model will check against a list of supported
- devices and their associated address supplied by the code registering
- the busses. This means that the driver .attach_adapter and
- .detach_client methods can be removed, along with the addr_data,
- as follows:
- - static struct i2c_driver example_driver;
- - static unsigned short ignore[] = { I2C_CLIENT_END };
- - static unsigned short normal_addr[] = { OUR_ADDR, I2C_CLIENT_END };
- - I2C_CLIENT_INSMOD;
- - static int example_attach_adapter(struct i2c_adapter *adap)
- - {
- - return i2c_probe(adap, &addr_data, example_attach);
- - }
- static struct i2c_driver example_driver = {
- - .attach_adapter = example_attach_adapter,
- - .detach_client = example_detach,
- }
- Add the probe and remove methods to the i2c_driver, as so:
- static struct i2c_driver example_driver = {
- + .probe = example_probe,
- + .remove = example_remove,
- }
- Change the example_attach method to accept the new parameters
- which include the i2c_client that it will be working with:
- - static int example_attach(struct i2c_adapter *adap, int addr, int kind)
- + static int example_probe(struct i2c_client *client,
- + const struct i2c_device_id *id)
- Change the name of example_attach to example_probe to align it with the
- i2c_driver entry names. The rest of the probe routine will now need to be
- changed as the i2c_client has already been setup for use.
- The necessary client fields have already been setup before
- the probe function is called, so the following client setup
- can be removed:
- - example->client.addr = addr;
- - example->client.flags = 0;
- - example->client.adapter = adap;
- -
- - strlcpy(client->i2c_client.name, "example", I2C_NAME_SIZE);
- The i2c_set_clientdata is now:
- - i2c_set_clientdata(&state->client, state);
- + i2c_set_clientdata(client, state);
- The call to i2c_attach_client is no longer needed, if the probe
- routine exits successfully, then the driver will be automatically
- attached by the core. Change the probe routine as so:
- - ret = i2c_attach_client(&state->i2c_client);
- - if (ret < 0) {
- - dev_err(dev, "failed to attach client\n");
- - kfree(state);
- - return ret;
- - }
- Remove the storage of 'struct i2c_client' from the 'struct example_state'
- as we are provided with the i2c_client in our example_probe. Instead we
- store a pointer to it for when it is needed.
- struct example_state {
- - struct i2c_client client;
- + struct i2c_client *client;
- the new i2c client as so:
- - struct device *dev = &adap->dev; /* to use for dev_ reports */
- + struct device *dev = &i2c_client->dev; /* to use for dev_ reports */
- And remove the change after our client is attached, as the driver no
- longer needs to register a new client structure with the core:
- - dev = &state->i2c_client.dev;
- In the probe routine, ensure that the new state has the client stored
- in it:
- static int example_probe(struct i2c_client *i2c_client,
- const struct i2c_device_id *id)
- {
- struct example_state *state;
- struct device *dev = &i2c_client->dev;
- int ret;
- state = kzalloc(sizeof(struct example_state), GFP_KERNEL);
- if (state == NULL) {
- dev_err(dev, "failed to create our state\n");
- return -ENOMEM;
- }
- + state->client = i2c_client;
- Update the detach method, by changing the name to _remove and
- to delete the i2c_detach_client call. It is possible that you
- can also remove the ret variable as it is not not needed for
- any of the core functions.
- - static int example_detach(struct i2c_client *client)
- + static int example_remove(struct i2c_client *client)
- {
- struct example_state *state = i2c_get_clientdata(client);
- - i2c_detach_client(client);
- And finally ensure that we have the correct ID table for the i2c-core
- and other utilities:
- + struct i2c_device_id example_idtable[] = {
- + { "example", 0 },
- + { }
- +};
- +
- +MODULE_DEVICE_TABLE(i2c, example_idtable);
- static struct i2c_driver example_driver = {
- .driver = {
- .owner = THIS_MODULE,
- .name = "example",
- },
- + .id_table = example_ids,
- Our driver should now look like this:
- struct example_state {
- struct i2c_client *client;
- ....
- };
- static int example_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
- {
- struct example_state *state;
- struct device *dev = &client->dev;
- state = kzalloc(sizeof(struct example_state), GFP_KERNEL);
- if (state == NULL) {
- dev_err(dev, "failed to create our state\n");
- return -ENOMEM;
- }
- state->client = client;
- i2c_set_clientdata(client, state);
- /* rest of the initialisation goes here. */
- dev_info(dev, "example client created\n");
- return 0;
- }
- static int example_remove(struct i2c_client *client)
- {
- struct example_state *state = i2c_get_clientdata(client);
- kfree(state);
- return 0;
- }
- static struct i2c_device_id example_idtable[] = {
- { "example", 0 },
- { }
- };
- MODULE_DEVICE_TABLE(i2c, example_idtable);
- static struct i2c_driver example_driver = {
- .driver = {
- .owner = THIS_MODULE,
- .name = "example",
- },
- .id_table = example_idtable,
- .probe = example_probe,
- .remove = example_remove,
- .suspend = example_suspend,
- .resume = example_resume,
- };
阅读(1683) | 评论(0) | 转发(0) |