下面开始分析 linux/drivers/i2c/busses/i2c-s3c2410.c ,在设备与驱动匹配成功后,会执行 s3c24xx_i2c_probe() 函数,其源码如下:
/* 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;
struct resource *res;
int ret;
/* 这里 pdev->dev.platform_data 在 s3c_i2c0_set_platdata() 函数中设置,指向了系统初始化时的设置过的 s3c2410_platform_i2c 结构体 */
pdata = pdev->dev.platform_data;
if (!pdata) {
dev_err(&pdev->dev, "no platform data/n");
return -EINVAL;
}
/* 申请一段 sizeof(struct s3c24xx_i2c) 的内存,并清 0 */
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 总线的通信函数 */
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 设备的时钟,并使能 I2C 控制器时钟,后面会具体分析 */
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 总线 ( 总线号 ) 注册 adapter */
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;
}
系统在初始化时会将系统硬件中的时钟注册进系统,用双向循环连接起来,在 linux/arch/arm/plat-s3c24xx/s3c244x.c 中, s3c244x_init_clocks () 函数完成这个操作这个操作。
void __init s3c244x_init_clocks(int xtal)
{
/* initialise the clocks here, to allow other things like the
* console to use them, and to add new ones after the initialisation
*/
s3c24xx_register_baseclocks(xtal);
s3c244x_setup_clocks();
s3c2410_baseclk_add();
}
其中 s3c2410_baseclk_add() 的源码如下:
/* s3c2410_baseclk_add()
* Add all the clocks used by the s3c2410 or compatible CPUs
* such as the S3C2440 and S3C2442.
* We cannot use a system device as we are needed before any
* of the init-calls that initialise the devices are actually
* done.*/
int __init s3c2410_baseclk_add(void)
{
unsigned long clkslow = __raw_readl(S3C2410_CLKSLOW);
unsigned long clkcon = __raw_readl(S3C2410_CLKCON);
struct clk *clkp;
struct clk *xtal;
int ret;
int ptr;
clk_upll.enable = s3c2410_upll_enable;
if (s3c24xx_register_clock(&clk_usb_bus) < 0)
printk(KERN_ERR "failed to register usb bus clock/n");
/* register clocks from clock array */
clkp = init_clocks;
for (ptr = 0; ptr < ARRAY_SIZE(init_clocks); ptr++, clkp++) {
/* ensure that we note the clock state */
clkp->usage = clkcon & clkp->ctrlbit ? 1 : 0;
ret = s3c24xx_register_clock(clkp);
if (ret < 0) {
printk(KERN_ERR "Failed to register clock %s (%d)/n",
clkp->name, ret);
}
}
/* We must be careful disabling the clocks we are not intending to
* be using at boot time, as subsystems such as the LCD which do
* their own DMA requests to the bus can cause the system to lockup
* if they where in the middle of requesting bus access.
*
* Disabling the LCD clock if the LCD is active is very dangerous,
* and therefore the bootloader should be careful to not enable
* the LCD clock if it is not needed.
*/
/* install (and disable) the clocks we do not need immediately */
clkp = init_clocks_disable;
for (ptr = 0; ptr < ARRAY_SIZE(init_clocks_disable); ptr++, clkp++) {
ret = s3c24xx_register_clock(clkp);
if (ret < 0) {
printk(KERN_ERR "Failed to register clock %s (%d)/n",
clkp->name, ret);
}
s3c2410_clkcon_enable(clkp, 0);
}
/* show the clock-slow value */
xtal = clk_get(NULL, "xtal");
printk("CLOCK: Slow mode (%ld.%ld MHz), %s, MPLL %s, UPLL %s/n",
print_mhz(clk_get_rate(xtal) /
( 2 * S3C2410_CLKSLOW_GET_SLOWVAL(clkslow))),
(clkslow & S3C2410_CLKSLOW_SLOW) ? "slow" : "fast",
(clkslow & S3C2410_CLKSLOW_MPLL_OFF) ? "off" : "on",
(clkslow & S3C2410_CLKSLOW_UCLK_OFF) ? "off" : "on");
s3c_pwmclk_init();
return 0;
}
根据注释中给的提示,时钟被分成了两部分, init_clocks 和 init_clocks_disable ,其中 init_clocks 中的时钟是系统启动时会开启的,而 init_clocks_disable 中的时钟则在系统启动时会关闭。其中函数 s3c24xx_register_clock() 就是实现讲系统中的时钟插入到双向循环链表中。比如我们这里 I2C 的时钟的是定义在 init_clocks_disable 数组中,定义如下:
{
.name = "i2c ",
.id = -1,
.parent = &clk_p,
.enable = s3c2410_clkcon_enable,
.ctrlbit = S3C2410_CLKCON_IIC,
}
结构中保存了 I2C 控制器中时钟时能位的位置偏移,时钟名字已经时钟时能的函数等信息。
s3c24xx_i2c_probe 函数中有一段程序就是用来获取时钟信息,并使能 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);
clk_get(&pdev->dev, "i2c") 函数用于获取时钟信息,函数内部会将传入的“ i2c ”字符串和系统中各时钟的名字进行比较,看是否匹配,看上面的分析可知, I2C 控制器时钟注册时的时钟名也是“ i2c ”,这个过程实际上和 device , driver 的匹配过程是类似的。 clk_get 源码如下:
struct clk *clk_get(struct device *dev, const char *id)
{
struct clk *p;
struct clk *clk = ERR_PTR(-ENOENT);
int idno;
if (dev == NULL || dev->bus != &platform_bus_type)
idno = -1;
else
idno = to_platform_device(dev)->id;
spin_lock(&clocks_lock);
list_for_each_entry(p, &clocks, list) {
if (p->id == idno &&
strcmp(id, p->name) == 0 &&
try_module_get(p->owner)) {
clk = p;
break;
}
}
s3c24xx_i2c_probe 函数还调用了 s3c24xx_i2c_init(i2c) 函数进行了 S3C2440 上 I2C 控制器硬件上的初始化,源码如下:
/* 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->dev->platform_data;
/* inititalise the gpio */
if (pdata->cfg_gpio)
pdata->cfg_gpio(to_platform_device(i2c->dev)); /*I2C 控制器 IO 的初始化
/* write slave address */
/* 写入从设备的地址 */
writeb(pdata->slave_addr, i2c->regs + S3C2410_IICADD);
dev_info(i2c->dev, "slave address 0x%02x/n", pdata->slave_addr);
/* 使能接收发送中断和 I2C 总线应答信号 */
writel(iicon, i2c->regs + S3C2410_IICCON);
/* we need to work out the divisors for the clock... */
/* 这里 freq 用来获取实际的 I2C 时钟频率,具体指为 97KHZ ,后面会分析 */
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;
}
/* s3c24xx_i2c_clockrate
*
* work out a divisor for the user requested frequency setting,
* either by the requested frequency, or scanning the acceptable
* range of frequencies until something is found
*/
static int s3c24xx_i2c_clockrate(struct s3c24xx_i2c *i2c, unsigned int *got)
{
struct s3c2410_platform_i2c *pdata = i2c->dev->platform_data;
/* 从系统平台时钟队列中获取 pclk 的时钟频率,大小为 50MHZ */
unsigned long clkin = clk_get_rate(i2c->clk);
unsigned int divs, div1;
unsigned long target_frequency;
u32 iiccon;
int freq;
i2c->clkrate = clkin;
clkin /= 1000; /* clkin now in KHz */
dev_dbg(i2c->dev, "pdata desired frequency %lu/n", pdata->frequency);
target_frequency = pdata->frequency ? pdata->frequency : 100000;
target_frequency /= 1000; /* Target frequency now in KHz */
/* 目标频率在前面 default_i2c_data0 中 frequency 为 100KHZ ,根据 PCLK 和目标频率计算分频系数 ,计算后实际频率为 97KHZ ,即 freq 为 97K*/
freq = s3c24xx_i2c_calcdivisor(clkin, target_frequency, &div1, &divs);
if (freq > target_frequency) {
dev_err(i2c->dev,
"Unable to achieve desired frequency %luKHz." /
" Lowest achievable %dKHz/n", target_frequency, freq);
return -EINVAL;
}
*got = freq; /* 通过传入的指针返回实际频率 */
/* 根据时钟选择和分频系数配置对应硬件寄存器 */
iiccon = readl(i2c->regs + S3C2410_IICCON);
iiccon &= ~(S3C2410_IICCON_SCALEMASK | S3C2410_IICCON_TXDIV_512);
iiccon |= (divs-1);
if (div1 == 512)
iiccon |= S3C2410_IICCON_TXDIV_512;
writel(iiccon, i2c->regs + S3C2410_IICCON);
/* 判断是否为 S3C2440 */
if (s3c24xx_i2c_is2440(i2c)) {
unsigned long sda_delay;
if (pdata->sda_delay) {
sda_delay = (freq / 1000) * pdata->sda_delay;
sda_delay /= 1000000;
sda_delay = DIV_ROUND_UP(sda_delay, 5);
if (sda_delay > 3)
sda_delay = 3;
sda_delay |= S3C2410_IICLC_FILTER_ON;
} else
sda_delay = 0;
dev_dbg(i2c->dev, "IICLC=%08lx/n", sda_delay);
writel(sda_delay, i2c->regs + S3C2440_IICLC);
}
return 0;
}
到这里, I2C 控制器的硬件初始化操作基本上分析完了,接下来该分析 Linux 内核 I2C 总线的通信机制了 ~~