Linux IICフレームワーク(上)
IICのフレームワーク構造はSPIと類似しており、バス駆動層(IICメインコントローラ駆動層)、コア層、スレーブデバイス駆動層を有している.このセクションでは、主にIICマスターコントローラの登録およびスレーブデバイスの登録手順について説明します.まず、IICメインコントローラの構造struct i 2 c_について説明するadapterと説明IICスレーブデバイスの構造struct i 2 c_client
struct i2c_adapterの定義は次のとおりです.
Algoでは、プライマリコントローラのデータ伝送方式が定義されており、clientはチェーンヘッダであり、複数のスレーブデバイスがバスに接続されている可能性があるため、clientはコントローラの下のスレーブデバイスをリンクするために使用される
SPIコントローラと同様にIICコントローラもプラットフォームリソースなのでplatformでカーネルに登録
s3c2410_i2c_driverとs 3 c 2440_i2c_driverの定義はnameフィールドが異なる以外は同じです
とplatform_デバイスマッチングに成功すると、s 3 c 24 xx_が呼び出されます.i2c_probe()関数
i2c_add_numbered_adapter()はi 2 c_を呼び出すregister_adapter()は実際の登録作業を完了する
struct i2c_adapterの定義は次のとおりです.
struct i2c_adapter {
struct module *owner; /* */
unsigned int id; /*algorithm */
unsigned int class; /* classes to allow probing for */
const struct i2c_algorithm *algo; /* */
void *algo_data; /*algorithm */
/* --- administration stuff. */
/* */
int (*client_register)(struct i2c_client *) __deprecated;
/* */
int (*client_unregister)(struct i2c_client *) __deprecated;
/* data fields that are valid for all devices */
u8 level; /* nesting level for lockdep */
struct mutex bus_lock;
struct mutex clist_lock;
int timeout; /* in jiffies */
int retries; /* */
struct device dev;
int nr; /* */
struct list_head clients; /* */
char name[48]; /* */
struct completion dev_released;/* */
};
Algoでは、プライマリコントローラのデータ伝送方式が定義されており、clientはチェーンヘッダであり、複数のスレーブデバイスがバスに接続されている可能性があるため、clientはコントローラの下のスレーブデバイスをリンクするために使用される
SPIコントローラと同様にIICコントローラもプラットフォームリソースなのでplatformでカーネルに登録
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;
}
s3c2410_i2c_driverとs 3 c 2440_i2c_driverの定義はnameフィールドが異なる以外は同じです
static struct platform_driver s3c2410_i2c_driver = {
.probe = s3c24xx_i2c_probe,
.remove = s3c24xx_i2c_remove,
.suspend_late = s3c24xx_i2c_suspend_late,
.resume = s3c24xx_i2c_resume,
.driver = {
.owner = THIS_MODULE,
.name = "s3c2410-i2c",
},
};
static struct platform_driver s3c2440_i2c_driver = {
.probe = s3c24xx_i2c_probe,
.remove = s3c24xx_i2c_remove,
.suspend_late = s3c24xx_i2c_suspend_late,
.resume = s3c24xx_i2c_resume,
.driver = {
.owner = THIS_MODULE,
.name = "s3c2440-i2c",
},
};
とplatform_デバイスマッチングに成功すると、s 3 c 24 xx_が呼び出されます.i2c_probe()関数
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;// IIC
if (!pdata) {
dev_err(&pdev->dev, "no platform data
");
return -EINVAL;
}
/* struct s3c24xx_i2c*/
i2c = kzalloc(sizeof(struct s3c24xx_i2c), GFP_KERNEL);
if (!i2c) {
dev_err(&pdev->dev, "no memory for state
");
return -ENOMEM;
}
/* IIC */
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
");
ret = -ENOENT;
goto err_noclk;
}
dev_dbg(&pdev->dev, "clock source %p
", i2c->clk);
clk_enable(i2c->clk);
/* map the registers */
/* IIC */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res == NULL) {
dev_err(&pdev->dev, "cannot find IO resource
");
ret = -ENOENT;
goto err_clk;
}
/* IIC */
i2c->ioarea = request_mem_region(res->start, (res->end-res->start)+1,
pdev->name);
if (i2c->ioarea == NULL) {
dev_err(&pdev->dev, "cannot request IO
");
ret = -ENXIO;
goto err_clk;
}
/* IIC */
i2c->regs = ioremap(res->start, (res->end-res->start)+1);
if (i2c->regs == NULL) {
dev_err(&pdev->dev, "cannot map IO
");
ret = -ENXIO;
goto err_ioarea;
}
dev_dbg(&pdev->dev, "registers %p (%p, %p)
",
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 */
/* s3c24xx IIC */
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
");
goto err_iomap;
}
/* IIC */
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
", i2c->irq);
goto err_iomap;
}
ret = s3c24xx_i2c_register_cpufreq(i2c);
if (ret < 0) {
dev_err(&pdev->dev, "failed to register cpufreq notifier
");
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;
/* IIC IIC */
ret = i2c_add_numbered_adapter(&i2c->adap);
if (ret < 0) {
dev_err(&pdev->dev, "failed to add bus to i2c core
");
goto err_cpufreq;
}
platform_set_drvdata(pdev, i2c);
dev_info(&pdev->dev, "%s: S3C I2C adapter
", 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;
}
i2c_add_numbered_adapter()はi 2 c_を呼び出すregister_adapter()は実際の登録作業を完了する
static int i2c_register_adapter(struct i2c_adapter *adap)
{
int res = 0, dummy;
/* Can't register until after driver model init */
if (unlikely(WARN_ON(!i2c_bus_type.p)))
return -EAGAIN;
mutex_init(&adap->bus_lock);
mutex_init(&adap->clist_lock);
INIT_LIST_HEAD(&adap->clients);//
mutex_lock(&core_lock);
/* Add the adapter to the driver core.
* If the parent pointer is not set up,
* we add this adapter to the host bus.
*/
if (adap->dev.parent == NULL) {
adap->dev.parent = &platform_bus;
pr_debug("I2C adapter driver [%s] forgot to specify "
"physical device
", adap->name);
}
/* Set default timeout to 1 second if not already set */
if (adap->timeout == 0)
adap->timeout = HZ;
dev_set_name(&adap->dev, "i2c-%d", adap->nr);
adap->dev.release = &i2c_adapter_dev_release;
adap->dev.class = &i2c_adapter_class;// i2c_adaoter_class
res = device_register(&adap->dev);//
if (res)
goto out_list;
dev_dbg(&adap->dev, "adapter [%s] registered
", adap->name);
/* create pre-declared device nodes for new-style drivers */
/* */
if (adap->nr < __i2c_first_dynamic_bus_num)
i2c_scan_static_board_info(adap);
/* Notify drivers */
dummy = bus_for_each_drv(&i2c_bus_type, NULL, adap,
i2c_do_add_adapter);
out_unlock:
mutex_unlock(&core_lock);
return res;
out_list:
idr_remove(&i2c_adapter_idr, adap->nr);
goto out_unlock;
}
static void i2c_scan_static_board_info(struct i2c_adapter *adapter)
{
struct i2c_devinfo *devinfo;
mutex_lock(&__i2c_board_lock);
/* IIC */
list_for_each_entry(devinfo, &__i2c_board_list, list) {
/* IIC */
if (devinfo->busnum == adapter->nr
&& !i2c_new_device(adapter,
&devinfo->board_info))
dev_err(&adapter->dev,
"Can't create device at 0x%02x
",
devinfo->board_info.addr);
}
mutex_unlock(&__i2c_board_lock);
}
struct i2c_client *
i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
{
struct i2c_client *client;
int status;
client = kzalloc(sizeof *client, GFP_KERNEL);
if (!client)
return NULL;
client->adapter = adap;// IIC
client->dev.platform_data = info->platform_data;
if (info->archdata)
client->dev.archdata = *info->archdata;
client->flags = info->flags;
client->addr = info->addr;//
client->irq = info->irq;
strlcpy(client->name, info->type, sizeof(client->name));
/* a new style driver may be bound to this device when we
* return from this function, or any later moment (e.g. maybe
* hotplugging will load the driver module). and the device
* refcount model is the standard driver model one.
*/
status = i2c_attach_client(client);
if (status < 0) {
kfree(client);
client = NULL;
}
return client;
}
int i2c_attach_client(struct i2c_client *client)
{
struct i2c_adapter *adapter = client->adapter;
int res;
/* Check for address business */
/* */
res = i2c_check_addr(adapter, client->addr);
if (res)
return res;
client->dev.parent = &client->adapter->dev;
client->dev.bus = &i2c_bus_type;//
if (client->driver)
client->dev.driver = &client->driver->driver;
if (client->driver && !is_newstyle_driver(client->driver)) {
client->dev.release = i2c_client_release;
dev_set_uevent_suppress(&client->dev, 1);
} else
client->dev.release = i2c_client_dev_release;
dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adapter),
client->addr);// +
res = device_register(&client->dev);//
if (res)
goto out_err;
mutex_lock(&adapter->clist_lock);
list_add_tail(&client->list, &adapter->clients);// IIC
mutex_unlock(&adapter->clist_lock);
dev_dbg(&adapter->dev, "client [%s] registered with bus id %s
",
client->name, dev_name(&client->dev));
/* adapter , ,s3c24xx */
if (adapter->client_register) {
if (adapter->client_register(client)) {
dev_dbg(&adapter->dev, "client_register "
"failed for client [%s] at 0x%02x
",
client->name, client->addr);
}
}
return 0;
out_err:
dev_err(&adapter->dev, "Failed to attach i2c client %s at 0x%02x "
"(%d)
", client->name, client->addr, res);
return res;
}