linux2.6.28 24CXX芯片I2C设备驱动

设备与驱动匹配

1.match过程

i2c_add_driver–>i2c_register_driver–>i2c_bus_type–>.match->i2c_device_match–>of_driver_match_device/i2c_match_id(比 较i2c_driver->id_table->name和client->name,如果相同,则匹配上,匹配上之后,运行 driver_register调用driver_probe_device进行设备与驱动绑定。),

图片

图片

static int __init at24_init(void)
{
io_limit = rounddown_pow_of_two(io_limit);

//执行完i2c_add_numbered_adapter函数后,内核的i2c总线上已有adapter device和client device
// .id_table = at24_ids中的名字和i2c_client中的名字 进行匹配
return i2c_add_driver(&at24_driver);
}
module_init(at24_init);

static struct i2c_driver at24_driver = {
.driver = {
.name = “at24”,//这个名字用于创建文件,不用于匹配
.owner = THIS_MODULE,
},
///当i2c_client和i2c_driver(at24_driver)匹配时调用
.probe = at24_probe,
.remove = __devexit_p(at24_remove),
//ID表是用来和i2c_client匹配用的,
// static struct i2c_board_info i2c_devs0[]用来建立i2c_client(相当于device)
//
//int i2c_attach_client(struct i2c_client *client)
//i2c_new_device函数
.id_table = at24_ids,
};

//
//struct i2c_client *i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
// ------> strlcpy(client->name, info->type, sizeof(client->name));// i2c_board_info中的名字给了client
//static struct i2c_board_info i2c_devs0[]
"24c08"这个名字就是用来创建i2c_client来和里static struct i2c_driver at24_driver进行匹配的。

static const struct i2c_device_id at24_ids[] = {
/* needs 8 addresses as A0-A2 are ignored /
/
old variants can’t be handled with this generic entry! /
{ “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) },
{ “at24”, 0 },
{ /
END OF LIST */ }
};

/-------------------------------------------------------------------------/

static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
struct at24_platform_data chip;
bool writable;
bool use_smbus = false;
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;
}
    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;
}

if (!is_power_of_2(chip.byte_len))
    dev_warn(&client->dev,
        "byte_len looks suspicious (no power of 2)!\n");
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)) {
        err = -EPFNOSUPPORT;
        goto err_out;
    }
    use_smbus = true;
}

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)
 */
at24->bin.attr.name = "eeprom";
at24->bin.attr.mode = chip.flags & AT24_FLAG_IRUGO ? S_IRUGO : S_IRUSR;

//除了这种操作i2c设备的方法外,
//
//static int __init i2c_dev_init(void)
//res = register_chrdev(I2C_MAJOR, “i2c”, &i2cdev_fops); 也是一种操作i2c设备的方法
at24->bin.read = at24_bin_read;
at24->bin.size = chip.byte_len;

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->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);
    }
}

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\n",
    at24->bin.size, client->name,
    writable ? "(writable)" : "(read-only)");
dev_dbg(&client->dev,
    "page_size %d, num_addresses %d, write_max %d%s\n",
    chip.page_size, num_addresses,
    at24->write_max,
    use_smbus ? ", use_smbus" : "");

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 ssize_t at24_bin_read(struct kobject *kobj, struct bin_attribute *attr,
char *buf, loff_t off, size_t count)
{
struct at24_data *at24;
ssize_t retval = 0;

at24 = dev_get_drvdata(container_of(kobj, struct device, kobj));
/*
 * Read data from chip, protecting against concurrent updates
 * from this host, but not from other I2C masters.
 */
mutex_lock(&at24->lock);

while (count) {
    ssize_t    status;

    status = at24_eeprom_read(at24, buf, off, count);
    if (status <= 0) {
        if (retval == 0)
            retval = status;
        break;
    }
    buf += status;
    off += status;
    count -= status;
    retval += status;
}

mutex_unlock(&at24->lock);

return retval;

}

static ssize_t at24_eeprom_read(struct at24_data *at24, char *buf,
unsigned offset, size_t count)
{
struct i2c_msg msg[2];
u8 msgbuf[2];
struct i2c_client *client;
int status, i;

memset(msg, 0, sizeof(msg));

/*
 * REVISIT some multi-address chips don't rollover page reads to
 * the next slave address, so we may need to truncate the count.
 * Those chips might need another quirk flag.
 *
 * If the real hardware used four adjacent 24c02 chips and that
 * were misconfigured as one 24c08, that would be a similar effect:
 * one "eeprom" file not four, but larger reads would fail when
 * they crossed certain pages.
 */

/*
 * Slave address and byte offset derive from the offset. Always
 * set the byte address; on a multi-master board, another master
 * may have changed the chip's "current" address pointer.
 */
client = at24_translate_offset(at24, &offset);

if (count > io_limit)
    count = io_limit;

/* Smaller eeproms can work given some SMBus extension calls */
if (at24->use_smbus) {
    if (count > I2C_SMBUS_BLOCK_MAX)
        count = I2C_SMBUS_BLOCK_MAX;
    status = i2c_smbus_read_i2c_block_data(client, offset,
            count, buf);
    dev_dbg(&client->dev, "smbus read %zu@%d --> %d\n",
            count, offset, status);
    return (status < 0) ? -EIO : status;
}

/*
 * When we have a better choice than SMBus calls, use a combined
 * I2C message. Write address; then read up to io_limit data bytes.
 * Note that read page rollover helps us here (unlike writes).
 * msgbuf is u8 and will cast to our needs.
 */
i = 0;
if (at24->chip.flags & AT24_FLAG_ADDR16)
    msgbuf[i++] = offset >> 8;
msgbuf[i++] = offset;

msg[0].addr = client->addr;
msg[0].buf = msgbuf;
msg[0].len = i;

msg[1].addr = client->addr;
msg[1].flags = I2C_M_RD;
msg[1].buf = buf;
msg[1].len = count;

中有i2c_transfer
status = i2c_transfer(client->adapter, msg, 2);
dev_dbg(&client->dev, “i2c read %zu@%d --> %d\n”,
count, offset, status);

if (status == 2)
    return count;
else if (status >= 0)
    return -EIO;
else
    return status;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xx-xx-xxx-xxx

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值