平台: 三星2440
内核版本:4.20
分析将会按照驱动中函数的执行顺序。
一、装载和卸载函数
static const struct platform_device_id s3c24xx_driver_ids[] = {
{
.name = "s3c2410-i2c",
.driver_data = 0,
}, {
.name = "s3c2440-i2c",
.driver_data = QUIRK_S3C2440,
},{ },
};
//dt匹配表
static const struct of_device_id s3c24xx_i2c_match[] = {
{ .compatible = "samsung,s3c2410-i2c", .data = (void *)0 },
{ .compatible = "samsung,s3c2440-i2c", .data = (void *)QUIRK_S3C2440 },
{},
};
MODULE_DEVICE_TABLE(of, s3c24xx_i2c_match);
//I2C控制器属于plaform总线(控制器都是)
static struct platform_driver s3c24xx_i2c_driver = {
.probe = s3c24xx_i2c_probe,
.remove = s3c24xx_i2c_remove,
.id_table = s3c24xx_driver_ids,
.driver = {
.name = "s3c-i2c",
.pm = S3C24XX_DEV_PM_OPS,
.of_match_table = of_match_ptr(s3c24xx_i2c_match),
},
};
static int __init i2c_adap_s3c_init(void)
{
//注册platform_driver
return platform_driver_register(&s3c24xx_i2c_driver);
}
subsys_initcall(i2c_adap_s3c_init);
static void __exit i2c_adap_s3c_exit(void)
{
//注销platform_driver
platform_driver_unregister(&s3c24xx_i2c_driver);
}
module_exit(i2c_adap_s3c_exit);
二、probe()函数
static int s3c24xx_i2c_probe(struct platform_device *pdev)
{
struct s3c24xx_i2c *i2c;
struct s3c2410_platform_i2c *pdata = NULL;
struct resource *res;
int ret;
if (!pdev->dev.of_node) {
//获取platform_data, 这些数据一般是跟板级有关的
pdata = dev_get_platdata(&pdev->dev);
......
}
//为自定义的i2c结构体申请内存空间,一般驱动都会封装一个结构体,将需要的数据
//放在一起
i2c = devm_kzalloc(&pdev->dev, sizeof(struct s3c24xx_i2c), GFP_KERNEL);
//为这些platform_data申请内存空间
i2c->pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
//初始化s3c24xx_i2c,自定义的结构体
i2c->quirks = s3c24xx_get_device_quirks(pdev);
i2c->sysreg = ERR_PTR(-ENOENT);
if (pdata)
memcpy(i2c->pdata, pdata, sizeof(*pdata));
else
s3c24xx_i2c_parse_dt(pdev->dev.of_node, i2c);
//初始化adapter
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_DEPRECATED;
i2c->tx_setup = 50;
//初始化等待队列头
init_waitqueue_head(&i2c->wait);
//获取时钟并使能
i2c->dev = &pdev->dev;
i2c->clk = devm_clk_get(&pdev->dev, "i2c");
/* map the registers */
//获取IO资源,就是寄存器地址
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
//寄存器地址映射为内核空间的虚拟地址
/*
devm_ioremap_resource包含两个动作:
申请内存资源:devm_request_mem_region
映射为虚拟地址:devm_ioremap
*/
i2c->regs = devm_ioremap_resource(&pdev->dev, res);
/* setup info block for the i2c core */
i2c->adap.algo_data = i2c; //algorithm数据
i2c->adap.dev.parent = &pdev->dev;
i2c->pctrl = devm_pinctrl_get_select_default(i2c->dev);
//初始化i2c引脚
if (i2c->pdata->cfg_gpio)
i2c->pdata->cfg_gpio(to_platform_device(i2c->dev));
else if (IS_ERR(i2c->pctrl) && s3c24xx_i2c_parse_dt_gpio(i2c))
return -EINVAL;
//初始化I2C控制器
ret = clk_prepare_enable(i2c->clk);
ret = s3c24xx_i2c_init(i2c);
clk_disable(i2c->clk);
//申请中断
if (!(i2c->quirks & QUIRK_POLL)) {
i2c->irq = ret = platform_get_irq(pdev, 0);
ret = devm_request_irq(&pdev->dev, i2c->irq, s3c24xx_i2c_irq,
0, dev_name(&pdev->dev), i2c);
}
//设置CPU频率
ret = s3c24xx_i2c_register_cpufreq(i2c);
// 设置总线数,之前的版本不需要设置,通过i2c_add_adapter()即可
// 新版本要设置,否则默认为0
i2c->adap.nr = i2c->pdata->bus_num;
i2c->adap.dev.of_node = pdev->dev.of_node;
platform_set_drvdata(pdev, i2c);
pm_runtime_enable(&pdev->dev);
// 注册adapter ,使用该函数要设置i2c->adap.nr
// 如果使用i2c_add_adapter()就不需要设置
ret = i2c_add_numbered_adapter(&i2c->adap);
return 0;
}
上面将一些错误判断及Log信息去掉了,只留下关键的部分。
三、I2C引脚初始化
static int s3c24xx_i2c_parse_dt_gpio(struct s3c24xx_i2c *i2c)
{
int idx, gpio, ret;
if (i2c->quirks & QUIRK_NO_GPIO)
return 0;
for (idx = 0; idx < 2; idx++) {
//从设备树中获取引脚
gpio = of_get_gpio(i2c->dev->of_node, idx);
i2c->gpios[idx] = gpio;
//申请引脚功能
ret = gpio_request(gpio, "i2c-bus");
}
return 0;
free_gpio:
while (--idx >= 0)
gpio_free(i2c->gpios[idx]);
return -EINVAL;
}
目前大部分的芯片这部分都是直接在dts中配置就行。pinctrl驱动会进行初始化,所以很多控制器驱动中不会看到对引脚的初始化。
四、I2C控制器初始化
static int s3c24xx_i2c_init(struct s3c24xx_i2c *i2c)
{
struct s3c2410_platform_i2c *pdata;
unsigned int freq;
//获取platform_data
pdata = i2c->pdata;
//设置从机地址
writeb(pdata->slave_addr, i2c->regs + S3C2410_IICADD);
//清空控制器寄存器和状态寄存器
writel(0, i2c->regs + S3C2410_IICCON);
writel(0, i2c->regs + S3C2410_IICSTAT);
/* we need to work out the divisors for the clock... */
//设置I2C控制器时钟频率
if (s3c24xx_i2c_clockrate(i2c, &freq) != 0) {
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%02x\n",
readl(i2c->regs + S3C2410_IICCON));
return 0;
}
五、I2C通信方式 — i2c_algorithm
static int s3c24xx_i2c_xfer(struct i2c_adapter *adap,
struct i2c_msg *msgs, int num)
{
struct s3c24xx_i2c *i2c = (struct s3c24xx_i2c *)adap->algo_data;
int retry;
int ret;
//使能I2C时钟
ret = clk_enable(i2c->clk);
if (ret)
return ret;
//传输失败,可重新传输
for (retry = 0; retry < adap->retries; retry++) {
//传输数据
ret = s3c24xx_i2c_doxfer(i2c, msgs, num);
//传输成功后关闭时钟
if (ret != -EAGAIN) {
clk_disable(i2c->clk);
return ret;
}
dev_dbg(i2c->dev, "Retrying transmission (%d)\n", retry);
udelay(100);
}
clk_disable(i2c->clk);
return -EREMOTEIO;
}
static u32 s3c24xx_i2c_func(struct i2c_adapter *adap)
{
//具备I2C, SMBUS, NOSTART等功能
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_NOSTART |
I2C_FUNC_PROTOCOL_MANGLING;
}
static const struct i2c_algorithm s3c24xx_i2c_algorithm = {
.master_xfer = s3c24xx_i2c_xfer, //传输
.functionality = s3c24xx_i2c_func, //具备的功能
};