上一篇文章讲述了i2c子系统体系结构,总线驱动、设备驱动的知识点,下面就s3c2440 i2c总线驱动的实现详细讲解,它的源码位于drivers/i2c/busses/i2c-s3c2410.c
一、i2c平台设备资源
iic驱动中使用的平台设备与前面看门狗、rtc等方式原理相同,但定义路径有所不同,并且设置了额外一些参数。mach_smdk2440.c文件中smdk2440_machine_init函数初始化了平台设备,还对s3c_device_i2c0平台设备进行额外的设置(s3c_i2c0_set_platdata),s3c_device_i2c0平台设备定义在arch/arm/plat-s3c/dev-i2c0.c。
static void __init smdk2440_machine_init(void)
{
s3c24xx_fb_set_platdata(&smdk2440_fb_info);
s3c_i2c0_set_platdata(null);
platform_add_devices(smdk2440_devices, array_size(smdk2440_devices));
smdk_machine_init();
}
下面是s3c_device_i2c0平台设备相关部分,s3c_i2c0_set_platdata初始化s3c_device_i2c0.dev.platform_data为default_i2c_data0
static struct resource s3c_i2c_resource[] = {
[0] = {
.start = s3c_pa_iic,
.end = s3c_pa_iic sz_4k - 1,
.flags = ioresource_mem,
},
[1] = {
.start = irq_iic,
.end = irq_iic,
.flags = ioresource_irq,
},
};
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_i2c_resource),
.resource = s3c_i2c_resource,
};
static struct s3c2410_platform_i2c default_i2c_data0 __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_data0;
npd = kmemdup(pd, sizeof(struct s3c2410_platform_i2c), gfp_kernel);
if (!npd)
printk(kern_err "%s: no memory for platform data\n", __func__);
else if (!npd->cfg_gpio)
npd->cfg_gpio = s3c_i2c0_cfg_gpio;
s3c_device_i2c0.dev.platform_data = npd;
}
void s3c_i2c0_cfg_gpio(struct platform_device *dev)
{ // 位于arch/arm/plat-s3c24xx/setup-i2c.c
s3c2410_gpio_cfgpin(s3c2410_gpe(15), s3c2410_gpe15_iicsda);
s3c2410_gpio_cfgpin(s3c2410_gpe(14), s3c2410_gpe14_iicscl);
}
二、总线驱动实现
1.i2c适配器驱动加载卸载
-
staticint s3c24xx_i2c_calcdivisor(unsignedlong clkin, unsignedint wanted,
-
unsignedint *div1, unsignedint *divs)
-
{
-
unsignedint calc_divs = clkin / wanted;
-
unsignedint calc_div1;
-
-
if (calc_divs > (16*16))
-
calc_div1 = 512;
-
else
-
calc_div1 = 16;
-
-
calc_divs = calc_div1-1;
-
calc_divs /= calc_div1;
-
-
if (calc_divs == 0)
-
calc_divs = 1;
-
if (calc_divs > 17)
-
calc_divs = 17;
-
-
*divs = calc_divs;
-
*div1 = calc_div1;
-
-
return clkin / (calc_divs * calc_div1);
-
}
-
-
-
-
-
-
-
-
-
staticint s3c24xx_i2c_clockrate(struct s3c24xx_i2c *i2c, unsigned int *got)
-
{
-
struct s3c2410_platform_i2c *pdata = i2c->dev->platform_data;
-
unsignedlong clkin = clk_get_rate(i2c->clk);
-
unsignedint divs, div1;
-
unsignedlong target_frequency;
-
u32 iiccon;
-
int freq;
-
-
i2c->clkrate = clkin;
-
clkin /= 1000;
-
-
dev_dbg(i2c->dev,"pdata desired frequency %lu\n", pdata->frequency);
-
-
target_frequency = pdata->frequency ? pdata->frequency : 100000;
-
-
target_frequency /= 1000;
-
-
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);
-
-
if (s3c24xx_i2c_is2440(i2c)) {
-
unsignedlong 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=lx\n", sda_delay);
-
writel(sda_delay, i2c->regs s3c2440_iiclc);
-
}
-
-
return 0;
-
}
-
-
#ifdef config_cpu_freq
-
-
#define freq_to_i2c(_n) container_of(_n, struct s3c24xx_i2c, freq_transition)
-
-
staticint s3c24xx_i2c_cpufreq_transition(struct notifier_block *nb,
-
unsignedlong val,void *data)
-
{
-
struct s3c24xx_i2c *i2c = freq_to_i2c(nb);
-
unsignedlong flags;
-
unsignedint got;
-
int delta_f;
-
int ret;
-
-
delta_f = clk_get_rate(i2c->clk) - i2c->clkrate;
-
-
-
-
-
-
-
if ((val == cpufreq_postchange && delta_f < 0) ||
-
(val == cpufreq_prechange && delta_f > 0)) {
-
spin_lock_irqsave(&i2c->lock, flags);
-
ret = s3c24xx_i2c_clockrate(i2c, &got);
-
spin_unlock_irqrestore(&i2c->lock, flags);
-
-
if (ret < 0)
-
dev_err(i2c->dev,"cannot find frequency\n");
-
else
-
dev_info(i2c->dev,"setting freq %d\n", got);
-
}
-
-
return 0;
-
}
-
-
staticinlineint s3c24xx_i2c_register_cpufreq(struct s3c24xx_i2c *i2c)
-
{
-
i2c->freq_transition.notifier_call = s3c24xx_i2c_cpufreq_transition;
-
-
return cpufreq_register_notifier(&i2c->freq_transition,
-
cpufreq_transition_notifier);
-
}
-
-
staticinlinevoid s3c24xx_i2c_deregister_cpufreq(struct s3c24xx_i2c *i2c)
-
{
-
cpufreq_unregister_notifier(&i2c->freq_transition,
-
cpufreq_transition_notifier);
-
}
-
-
#else
-
staticinlineint s3c24xx_i2c_register_cpufreq(struct s3c24xx_i2c *i2c)
-
{
-
return 0;
-
}
-
-
staticinlinevoid s3c24xx_i2c_deregister_cpufreq(struct s3c24xx_i2c *i2c)
-
{
-
}
-
#endif
-
-
-
-
-
-
-
staticint s3c24xx_i2c_init(struct s3c24xx_i2c *i2c)
-
{
-
unsignedlong iicon = s3c2410_iiccon_irqen | s3c2410_iiccon_acken;
-
struct s3c2410_platform_i2c *pdata;
-
unsignedint freq;
-
-
-
-
pdata = i2c->dev->platform_data;
-
-
-
-
if (pdata->cfg_gpio)
-
pdata->cfg_gpio(to_platform_device(i2c->dev));
-
-
-
-
writeb(pdata->slave_addr, i2c->regs s3c2410_iicadd);
-
-
dev_info(i2c->dev,"slave address 0xx\n", pdata->slave_addr);
-
-
writel(iicon, i2c->regs s3c2410_iiccon);
-
-
-
-
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;
-
}
-
-
-
-
dev_info(i2c->dev,"bus frequency set to %d khz\n", freq);
-
dev_dbg(i2c->dev,"s3c2410_iiccon=0xlx\n", iicon);
-
-
return 0;
-
}
-
-
-
-
-
-
-
staticint 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;
-
if (!pdata) {
-
dev_err(&pdev->dev,"no platform data\n");
-
return -einval;
-
}
-
-
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->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);
-
-
-
-
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);
-
-
-
-
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);
-
-
-
-
i2c->adap.algo_data = i2c;
-
i2c->adap.dev.parent = &pdev->dev;
-
-
-
-
ret = s3c24xx_i2c_init(i2c);
-
if (ret != 0)
-
goto err_iomap;
-
-
-
-
-
-
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;
-
}
-
-
-
-
-
-
-
-
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;
-
}
-
-
-
-
-
-
-
staticint 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);
-
kfree(i2c->ioarea);
-
kfree(i2c);
-
-
return 0;
-
}
-
-
#ifdef config_pm
-
staticint s3c24xx_i2c_suspend_noirq(struct device *dev)
-
{
-
struct platform_device *pdev = to_platform_device(dev);
-
struct s3c24xx_i2c *i2c = platform_get_drvdata(pdev);
-
-
i2c->suspended = 1;
-
-
return 0;
-
}
-
-
staticint s3c24xx_i2c_resume(struct device *dev)
-
{
-
struct platform_device *pdev = to_platform_device(dev);
-
struct s3c24xx_i2c *i2c = platform_get_drvdata(pdev);
-
-
i2c->suspended = 0;
-
s3c24xx_i2c_init(i2c);
-
-
return 0;
-
}
-
-
staticstruct dev_pm_ops s3c24xx_i2c_dev_pm_ops = {
-
.suspend_noirq = s3c24xx_i2c_suspend_noirq,
-
.resume = s3c24xx_i2c_resume,
-
};
-
-
#define s3c24xx_dev_pm_ops (&s3c24xx_i2c_dev_pm_ops)
-
#else
-
#define s3c24xx_dev_pm_ops null
-
#endif
-
-
-
-
staticstruct platform_device_id s3c24xx_driver_ids[] = {
-
{
-
.name ="s3c2410-i2c",
-
.driver_data = type_s3c2410,
-
}, {
-
.name ="s3c2440-i2c",
-
.driver_data = type_s3c2440,
-
}, { },
-
};
-
module_device_table(platform, s3c24xx_driver_ids);
-
-
staticstruct 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,
-
},
-
};
-
-
staticint __init i2c_adap_s3c_init(void)
-
{
-
return platform_driver_register(&s3c24xx_i2c_driver);
-
}
-
subsys_initcall(i2c_adap_s3c_init);
-
-
staticvoid __exit i2c_adap_s3c_exit(void)
-
{
-
platform_driver_unregister(&s3c24xx_i2c_driver);
-
}
-
module_exit(i2c_adap_s3c_exit);
-
-
module_description("s3c24xx i2c bus driver");
-
module_author("ben dooks, ");
-
module_license("gpl");
i2c_adap_s3c_init注册i2c平台驱动s3c24xx_i2c_driver,它是platform_driver结构体,不要误解为i2c_driver。同时还实现了probe、remove、id_table、driver,其中suspend、resume在driver中实现。i2c_adap_s3c_exit注销s3c24xx_i2c_driver。
关于平台驱动s3c24xx_i2c_driver中名字为s3c_i2c与平台设备中名字s3c2410-i2c不一样,怎么匹配?这里主要在于id_table,s3c24xx_driver_ids包含了驱动所支持的设备id表,判断这个表中的名字与平台设备中名字一致,则匹配成功。
2.i2c通信方法
-
#include
-
#include
-
-
#include
-
#include
-
#include
-
#include
-
#include
-
#include
-
#include
-
#include
-
#include
-
#include
-
#include
-
-
#include
-
#include
-
-
#include
-
#include
-
-
-
-
enum s3c24xx_i2c_state {
-
state_idle,
-
state_start,
-
state_read,
-
state_write,
-
state_stop
-
};
-
-
enum s3c24xx_i2c_type {
-
type_s3c2410,
-
type_s3c2440,
-
};
-
-
struct s3c24xx_i2c {
-
spinlock_t lock;
-
wait_queue_head_t wait;
-
unsignedint suspended:1;
-
-
struct i2c_msg *msg;
-
unsignedint msg_num;
-
unsignedint msg_idx;
-
unsignedint msg_ptr;
-
-
unsignedint tx_setup;
-
unsignedint irq;
-
-
enum s3c24xx_i2c_state state;
-
unsignedlong clkrate;
-
-
void __iomem *regs;
-
struct clk *clk;
-
struct device *dev;
-
struct resource *ioarea;
-
struct i2c_adapter adap;
-
-
#ifdef config_cpu_freq
-
struct notifier_block freq_transition;
-
#endif
-
};
-
-
-
-
-
-
-
-
-
staticinlineint s3c24xx_i2c_is2440(struct s3c24xx_i2c *i2c)
-
{
-
struct platform_device *pdev = to_platform_device(i2c->dev);
-
enum s3c24xx_i2c_type type;
-
-
type = platform_get_device_id(pdev)->driver_data;
-
return type == type_s3c2440;
-
}
-
-
-
-
-
-
-
-
staticinlinevoid s3c24xx_i2c_master_complete(struct s3c24xx_i2c *i2c, int ret)
-
{
-
dev_dbg(i2c->dev,"master_complete %d\n", ret);
-
-
i2c->msg_ptr = 0;
-
i2c->msg = null;
-
i2c->msg_idx ;
-
i2c->msg_num = 0;
-
if (ret)
-
i2c->msg_idx = ret;
-
-
wake_up(&i2c->wait);
-
}
-
-
staticinlinevoid s3c24xx_i2c_disable_ack(struct s3c24xx_i2c *i2c)
-
{
-
unsignedlong tmp;
-
-
tmp = readl(i2c->regs s3c2410_iiccon);
-
writel(tmp & ~s3c2410_iiccon_acken, i2c->regs s3c2410_iiccon);
-
}
-
-
staticinlinevoid s3c24xx_i2c_enable_ack(struct s3c24xx_i2c *i2c)
-
{
-
unsignedlong tmp;
-
-
tmp = readl(i2c->regs s3c2410_iiccon);
-
writel(tmp | s3c2410_iiccon_acken, i2c->regs s3c2410_iiccon);
-
}
-
-
-
-
staticinlinevoid s3c24xx_i2c_disable_irq(struct s3c24xx_i2c *i2c)
-
{
-
unsignedlong tmp;
-
-
tmp = readl(i2c->regs s3c2410_iiccon);
-
writel(tmp & ~s3c2410_iiccon_irqen, i2c->regs s3c2410_iiccon);
-
}
-
-
staticinlinevoid s3c24xx_i2c_enable_irq(struct s3c24xx_i2c *i2c)
-
{
-
unsignedlong tmp;
-
-
tmp = readl(i2c->regs s3c2410_iiccon);
-
writel(tmp | s3c2410_iiccon_irqen, i2c->regs s3c2410_iiccon);
-
}
-
-
-
-
-
-
-
-
staticvoid s3c24xx_i2c_message_start(struct s3c24xx_i2c *i2c,
-
struct i2c_msg *msg)
-
{
-
unsignedint addr = (msg->addr & 0x7f) << 1;
-
unsignedlong stat;
-
unsignedlong iiccon;
-
-
stat = 0;
-
stat |= s3c2410_iicstat_txrxen;
-
-
if (msg->flags & i2c_m_rd) {
-
stat |= s3c2410_iicstat_master_rx;
-
addr |= 1;
-
}else
-
stat |= s3c2410_iicstat_master_tx;
-
-
if (msg->flags & i2c_m_rev_dir_addr)
-
addr ^= 1;
-
-
-
s3c24xx_i2c_enable_ack(i2c);
-
-
iiccon = readl(i2c->regs s3c2410_iiccon);
-
writel(stat, i2c->regs s3c2410_iicstat);
-
-
dev_dbg(i2c->dev,"start: lx to iicstat, x to ds\n", stat, addr);
-
writeb(addr, i2c->regs s3c2410_iicds);
-
-
-
-
-
ndelay(i2c->tx_setup);
-
-
dev_dbg(i2c->dev,"iiccon, lx\n", iiccon);
-
writel(iiccon, i2c->regs s3c2410_iiccon);
-
-
stat |= s3c2410_iicstat_start;
-
writel(stat, i2c->regs s3c2410_iicstat);
-
}
-
-
staticinlinevoid s3c24xx_i2c_stop(struct s3c24xx_i2c *i2c, int ret)
-
{
-
unsignedlong iicstat = readl(i2c->regs s3c2410_iicstat);
-
-
dev_dbg(i2c->dev,"stop\n");
-
-
-
iicstat &= ~s3c2410_iicstat_start;
-
writel(iicstat, i2c->regs s3c2410_iicstat);
-
-
i2c->state = state_stop;
-
-
s3c24xx_i2c_master_complete(i2c, ret);
-
s3c24xx_i2c_disable_irq(i2c);
-
}
-
-
-
-
-
-
-
-
-
-
-
staticinlineint is_lastmsg(struct s3c24xx_i2c *i2c)
-
{
-
return i2c->msg_idx >= (i2c->msg_num - 1);
-
}
-
-
-
-
-
-
-
staticinlineint is_msglast(struct s3c24xx_i2c *i2c)
-
{
-
return i2c->msg_ptr == i2c->msg->len-1;
-
}
-
-
-
-
-
-
-
staticinlineint is_msgend(struct s3c24xx_i2c *i2c)
-
{
-
return i2c->msg_ptr >= i2c->msg->len;
-
}
-
-
-
-
-
-
-
staticint i2s_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat)
-
{
-
-
unsignedlong tmp;
-
unsignedchar byte;
-
int ret = 0;
-
-
switch (i2c->state) {
-
-
case state_idle:
-
dev_err(i2c->dev,"%s: called in state_idle\n", __func__);
-
goto out;
-
break;
-
-
case state_stop:
-
dev_err(i2c->dev,"%s: called in state_stop\n", __func__);
-
s3c24xx_i2c_disable_irq(i2c);
-
goto out_ack;
-
-
case state_start:
-
-
-
-
-
if (iicstat & s3c2410_iicstat_lastbit &&
-
!(i2c->msg->flags & i2c_m_ignore_nak)) {
-
-
-
dev_dbg(i2c->dev,"ack was not received\n");
-
s3c24xx_i2c_stop(i2c, -enxio);
-
goto out_ack;
-
}
-
-
if (i2c->msg->flags & i2c_m_rd)
-
i2c->state = state_read;
-
else
-
i2c->state = state_write;
-
-
-
-
-
if (is_lastmsg(i2c) && i2c->msg->len == 0) {
-
s3c24xx_i2c_stop(i2c, 0);
-
goto out_ack;
-
}
-
-
if (i2c->state == state_read)
-
goto prepare_read;
-
-
-
-
-
case state_write:
-
-
-
-
-
if (!(i2c->msg->flags & i2c_m_ignore_nak)) {
-
if (iicstat & s3c2410_iicstat_lastbit) {
-
dev_dbg(i2c->dev,"write: no ack\n");
-
-
s3c24xx_i2c_stop(i2c, -econnrefused);
-
goto out_ack;
-
}
-
}
-
-
retry_write:
-
-
if (!is_msgend(i2c)) {
-
byte = i2c->msg->buf[i2c->msg_ptr ];
-
writeb(byte, i2c->regs s3c2410_iicds);
-
-
-
-
-
-
-
-
ndelay(i2c->tx_setup);
-
-
}elseif (!is_lastmsg(i2c)) {
-
-
-
dev_dbg(i2c->dev,"write: next message\n");
-
-
i2c->msg_ptr = 0;
-
i2c->msg_idx ;
-
i2c->msg ;
-
-
-
if (i2c->msg->flags & i2c_m_nostart) {
-
-
if (i2c->msg->flags & i2c_m_rd) {
-
-
-
-
-
s3c24xx_i2c_stop(i2c, -einval);
-
}
-
-
goto retry_write;
-
}else {
-
-
s3c24xx_i2c_message_start(i2c, i2c->msg);
-
i2c->state = state_start;
-
}
-
-
}else {
-
-
-
s3c24xx_i2c_stop(i2c, 0);
-
}
-
break;
-
-
case state_read:
-
-
-
-
-
-
byte = readb(i2c->regs s3c2410_iicds);
-
i2c->msg->buf[i2c->msg_ptr ] = byte;
-
-
prepare_read:
-
if (is_msglast(i2c)) {
-
-
-
if (is_lastmsg(i2c))
-
s3c24xx_i2c_disable_ack(i2c);
-
-
}elseif (is_msgend(i2c)) {
-
-
-
-
if (is_lastmsg(i2c)) {
-
-
dev_dbg(i2c->dev,"read: send stop\n");
-
-
s3c24xx_i2c_stop(i2c, 0);
-
}else {
-
-
dev_dbg(i2c->dev,"read: next transfer\n");
-
-
i2c->msg_ptr = 0;
-
i2c->msg_idx ;
-
i2c->msg ;
-
}
-
}
-
-
break;
-
}
-
-
-
-
out_ack:
-
tmp = readl(i2c->regs s3c2410_iiccon);
-
tmp &= ~s3c2410_iiccon_irqpend;
-
writel(tmp, i2c->regs s3c2410_iiccon);
-
out:
-
return ret;
-
}
-
-
-
-
-
-
-
static irqreturn_t s3c24xx_i2c_irq(int irqno,void *dev_id)
-
{
-
-
struct s3c24xx_i2c *i2c = dev_id;
-
unsignedlong status;
-
unsignedlong tmp;
-
-
status = readl(i2c->regs s3c2410_iicstat);
-
-
if (status & s3c2410_iicstat_arbitr) {
-
-
dev_err(i2c->dev,"deal with arbitration loss\n");
-
}
-
-
if (i2c->state == state_idle) {
-
dev_dbg(i2c->dev,"irq: error i2c->state == idle\n");
-
-
tmp = readl(i2c->regs s3c2410_iiccon);
-
tmp &= ~s3c2410_iiccon_irqpend;
-
writel(tmp, i2c->regs s3c2410_iiccon);
-
goto out;
-
}
-
-
-
-
-
i2s_s3c_irq_nextbyte(i2c, status);
-
-
out:
-
return irq_handled;
-
}
-
-
-
-
-
-
-
-
staticint s3c24xx_i2c_set_master(struct s3c24xx_i2c *i2c)
-
{
-
unsignedlong iicstat;
-
int timeout = 400;
-
-
while (timeout-- > 0) {
-
iicstat = readl(i2c->regs s3c2410_iicstat);
-
-
if (!(iicstat & s3c2410_iicstat_busbusy))
-
return 0;
-
-
msleep(1);
-
}
-
-
return -etimedout;
-
}
-
-
-
-
-
-
-
staticint s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c,
-
struct i2c_msg *msgs,int num)
-
{
-
unsignedlong timeout;
-
int ret;
-
-
if (i2c->suspended)
-
return -eio;
-
-
ret = s3c24xx_i2c_set_master(i2c);
-
if (ret != 0) {
-
dev_err(i2c->dev,"cannot get bus (error %d)\n", ret);
-
ret = -eagain;
-
goto out;
-
}
-
-
spin_lock_irq(&i2c->lock);
-
-
i2c->msg = msgs;
-
i2c->msg_num = num;
-
i2c->msg_ptr = 0;
-
i2c->msg_idx = 0;
-
i2c->state = state_start;
-
-
s3c24xx_i2c_enable_irq(i2c);
-
s3c24xx_i2c_message_start(i2c, msgs);
-
spin_unlock_irq(&i2c->lock);
-
-
timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, hz * 5);
-
-
ret = i2c->msg_idx;
-
-
-
-
-
if (timeout == 0)
-
dev_dbg(i2c->dev,"timeout\n");
-
elseif (ret != num)
-
dev_dbg(i2c->dev,"incomplete xfer (%d)\n", ret);
-
-
-
-
msleep(1);
-
-
out:
-
return ret;
-
}
-
-
-
-
-
-
-
-
staticint 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;
-
-
for (retry = 0; retry < adap->retries; retry ) {
-
-
ret = s3c24xx_i2c_doxfer(i2c, msgs, num);
-
-
if (ret != -eagain)
-
return ret;
-
-
dev_dbg(i2c->dev,"retrying transmission (%d)\n", retry);
-
-
udelay(100);
-
}
-
-
return -eremoteio;
-
}
-
-
-
static u32 s3c24xx_i2c_func(struct i2c_adapter *adap)
-
{
-
return i2c_func_i2c | i2c_func_smbus_emul | i2c_func_protocol_mangling;
-
}
-
-
-
-
staticconststruct i2c_algorithm s3c24xx_i2c_algorithm = {
-
.master_xfer = s3c24xx_i2c_xfer,
-
.functionality = s3c24xx_i2c_func,
-
};
i2c适配器的通信方法整个驱动的重点,主要实现i2c_algorithm的master_xfer()和functionality()。s3c24xx_i2c_xfer中调用了s3c24xx_i2c_doxfer,然后启动i2c,并且通过中断s3c24xx_i2c_irq和i2s_s3c_irq_nextbyte来一步步推进传输工作。
通信方法传输是以消息为单位的,所有先了解消息结构体。消息i2c_msg包括地址、标志、一条消息包含的数据及长度。
struct i2c_msg {
__u16 addr; /* slave address */
__u16 flags;
#define i2c_m_ten 0x0010 /* this is a ten bit chip address */
#define i2c_m_rd 0x0001 /* read data, from slave to master */
#define i2c_m_nostart 0x4000 /* if i2c_func_protocol_mangling */
#define i2c_m_rev_dir_addr 0x2000 /* if i2c_func_protocol_mangling */
#define i2c_m_ignore_nak 0x1000 /* if i2c_func_protocol_mangling */
#define i2c_m_no_rd_ack 0x0800 /* if i2c_func_protocol_mangling */
#define i2c_m_recv_len 0x0400 /* length will be first received byte */
__u16 len; /* msg length */
__u8 *buf; /* pointer to msg data */
};
代码中提到的is_lastmsg、is_msglast、is_msgend到底判断是什么?在s3c24xx_i2c中定义的struct i2c_msg *msg看出可以包含多条消息,而一条消息有可能包含多个数据,比如对于at24c02页写就包含多个数据。is_lastmsg判断是否是消息中最后一条,使用了变量msg_idx;is_msglast判断是否一条消息中最后一个数据,is_msgend判断是否是一条消息全部完成,所以这两个函数使用变量时msg_ptr。
下面就根据at24c02具体的讲解s3c24xx_i2c_irq和i2s_s3c_irq_nextbyte如何实现传输。
任意地址字节写时序如上所示,只需要一条消息即可。其中flag位为写,写0即可,消息包括两个数据目标地址、数据,消息如下所示。
struct i2c_msg *msg;
msg=malloc(sizeof(struct i2c_msg));
msg.len=2; // 1个目标地址和1个数据
msg.addr=0x50; // 设备地址
msg.flags=0; // write
msg.buf=(unsigned char*)malloc(2);
msg.buf[0]=0x10;// 目标地址
msg.buf[1]=0x58;// the data to write
s3c24xx_i2c_xfer中调用了s3c24xx_i2c_doxfer,在s3c24xx_i2c_doxfer中把消息传入i2c->msg,使能中断,置i2c->state 为state_start,调用s3c24xx_i2c_message_start启动i2c发送设备地址,就等待中断来做后续工作。当设备地址发送后就会进入中断,继续进入i2s_s3c_irq_nextbyte的state_start,判断消息为写,置i2c->state 为state_write,跳入state_write,在retry_write这一段中,if先判断是否一条消息所有数据发送完,没发送完,则每次发送一条等待下次中断进入,每发送一个数据都要清pend位;发送完else if判断是否最后一条消息,如果不是则要指针指向下一条消息继续if的步骤;最后else为发送完成,停止i2c。针对任意字节写只有一条消息,if中发送两次就完成本条消息传输。
任意地址字节读时序如上所示,需两条消息。第一条,写目标地址,flag位为写;第二条,读取数据,flag位为读,第一条与第二条消息之间要发送start。
struct i2c_msg *msgs;
msgs=malloc(2*sizeof(struct i2c_msg));
msgs[0].len=1; // 目标地址
msgs[0].addr=0x50; // 设备地址
msgs[0].flags=0; // write
msgs[0].buf=(unsigned char*)malloc(1);
msgs[0].buf[0]=0x10; // 目标地址
msgs[1].len=1; // 读出的数据
msgs[1].addr=0x50; // 设备地址
msgs[1].flags=i2c_m_rd; // read
msgs[1].buf=(unsigned char*)malloc(1);
msgs[1].buf[0]=0;// 初始化读缓冲
直接从中断开始讲,发送设备地址后,进入state_start,判断第一条消息为写,置i2c->state 为state_write,跳入state_write,第一条消息有一个数据,发送完成后,在else if中判断不是此条消息不是最后一条,就会执行else if中指向下一条消息,s3c24xx_i2c_message_start重新发送start,置i2c->state 为state_start。下次进入中跳到state_start,判断第二条消息为读,置i2c->state 为state_read跳入state_read,第二条消息只有一个数据,关闭ack,接收一个字节,停止i2c。看完这两个例子,再看i2s_s3c_irq_nextbyte就容易理解。