I2C控制器的设备对象内核已经实现并关联到platform总线。
I2C控制器的驱动对象内核已经实现。
看mach-tiny4412.h
/plat-samsung/目录下
/drivers/i2c/ 看 *.o 文件
看i2c-s3c2410.c 从下往上看。
.id_table
匹配成功后看 probe函数:
一个I2C控制器对应一个struct s3c24xx_i2c结构体对象:
struct s3c24xx_i2c *i2c;
struct s3c24xx_i2c { wait_queue_head_t wait; unsigned int quirks; unsigned int suspended:1; struct i2c_msg *msg; //IIC要传输的数据, unsigned int msg_num; //数组元素格式 unsigned int msg_idx; unsigned int msg_ptr; unsigned int tx_setup; unsigned int irq; //中断号 enum s3c24xx_i2c_state state; unsigned long clkrate; void __iomem *regs; //通过platform_get_resource拿到物理基地址,映射完后赋值 struct clk *clk; struct device *dev; struct resource *ioarea; struct i2c_adapter adap; //读写数据的算法 struct s3c2410_platform_i2c *pdata; int gpios[2]; #ifdef CONFIG_CPU_FREQ struct notifier_block freq_transition; #endif };
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_NOSTART */ #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 */ };
/* * i2c_adapter is the structure used to identify a physical i2c bus along * with the access algorithms necessary to access it. */ struct i2c_adapter { struct module *owner; unsigned int class; /* classes to allow probing for */ const struct i2c_algorithm *algo; /* the algorithm to access the bus */ void *algo_data; /* data fields that are valid for all devices */ struct rt_mutex bus_lock; int timeout; /* in jiffies */ int retries; struct device dev; /* the adapter device */ int nr; char name[48]; struct completion dev_released; struct mutex userspace_clients_lock; struct list_head userspace_clients; };
/* * The following structs are for those who like to implement new bus drivers: * i2c_algorithm is the interface to a class of hardware solutions which can * be addressed using the same bus algorithms - i.e. bit-banging or the PCF8584 * to name two of the most common. */ struct i2c_algorithm { /* If an adapter algorithm can't do I2C-level access, set master_xfer to NULL. If an adapter algorithm can do SMBus access, set smbus_xfer. If set to NULL, the SMBus protocol is simulated using common I2C messages */ /* master_xfer should return the number of messages successfully processed, or a negative value on error */ int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, int num); int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr, unsigned short flags, char read_write, u8 command, int size, union i2c_smbus_data *data); /* To determine what the adapter supports */ u32 (*functionality) (struct i2c_adapter *); };
tiny4412一共是9个IIC控制器接口,如果都加入的话,probe函数最多可以被调用9次。
来自exynos4412数据手册:
29.2特性12C总线接口的特点是:9频道多主机。
从12C总线接口(通用频道8个,高清多媒体接口专用频道1个)
7位寻址模式串行、8位定向和双向数据传输
支持高达100千位在标准模式支持高达400千位在快速模式。
支持主发送、主接收、从发送和从接收操作
支持中断或轮询事件
probe.c 部分代码: I2C控制器的初始化,访问总线的读写算法的实现。
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;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); //获取资源
i2c->ioarea = request_mem_region(res->start, resource_size(res),
pdev->name);
i2c->regs = ioremap(res->start, resource_size(res));
ret = s3c24xx_i2c_init(i2c);
/* 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;
}
ret = request_irq(i2c->irq, s3c24xx_i2c_irq, 0,
dev_name(&pdev->dev), i2c);
ret = i2c_add_numbered_adapter(&i2c->adap); //非常重要,下面有分析
if (ret < 0) {
dev_err(&pdev->dev, "failed to add bus to i2c core
");
goto err_cpufreq;
}
s3c24xx_i2c_algorithm中的 .master_xfer
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; pm_runtime_get_sync(&adap->dev); clk_enable(i2c->clk); for (retry = 0; retry < adap->retries; retry++) { ret = s3c24xx_i2c_doxfer(i2c, msgs, num); //真正的从总线上收发数据 if (ret != -EAGAIN) { clk_disable(i2c->clk); pm_runtime_put_sync(&adap->dev); return ret; } dev_dbg(i2c->dev, "Retrying transmission (%d) ", retry); udelay(100); } clk_disable(i2c->clk); pm_runtime_put_sync(&adap->dev); return -EREMOTEIO; }
/* s3c24xx_i2c_doxfer * * this starts an i2c transfer */ static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c, struct i2c_msg *msgs, int num) { unsigned long 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) ", ret); ret = -EAGAIN; goto out; } i2c->msg = msgs; i2c->msg_num = num; i2c->msg_ptr = 0; i2c->msg_idx = 0; i2c->state = STATE_START; s3c24xx_i2c_enable_irq(i2c); //使能I2C中断 s3c24xx_i2c_message_start(i2c, msgs); timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5); ret = i2c->msg_idx; /* having these next two as dev_err() makes life very * noisy when doing an i2cdetect */ if (timeout == 0) dev_dbg(i2c->dev, "timeout "); else if (ret != num) dev_dbg(i2c->dev, "incomplete xfer (%d) ", ret); /* For QUIRK_HDMIPHY, bus is already disabled */ if (i2c->quirks & QUIRK_HDMIPHY) goto out; s3c24xx_i2c_wait_idle(i2c); out: return ret; }
/* s3c24xx_i2c_set_master * * get the i2c bus for a master transaction */ static int s3c24xx_i2c_set_master(struct s3c24xx_i2c *i2c) { unsigned long iicstat; int timeout = 400; //检查400次 while (timeout-- > 0) { iicstat = readl(i2c->regs + S3C2410_IICSTAT); if (!(iicstat & S3C2410_IICSTAT_BUSBUSY)) return 0; msleep(1); } return -ETIMEDOUT; }
/* s3c24xx_i2c_message_start * * put the start of a message onto the bus */ static void s3c24xx_i2c_message_start(struct s3c24xx_i2c *i2c, struct i2c_msg *msg) { unsigned int addr = (msg->addr & 0x7f) << 1; //7位地址,左移一位。 unsigned long stat; unsigned long iiccon; stat = 0; stat |= S3C2410_IICSTAT_TXRXEN; if (msg->flags & I2C_M_RD) { stat |= S3C2410_IICSTAT_MASTER_RX; // 2<<6,对应配置。
addr |= 1; } else stat |= S3C2410_IICSTAT_MASTER_TX; //3<<6 对应上图数据手册截图 if (msg->flags & I2C_M_REV_DIR_ADDR) addr ^= 1; /* todo - check for whether ack wanted or not */ s3c24xx_i2c_enable_ack(i2c); //使能ACK iiccon = readl(i2c->regs + S3C2410_IICCON); writel(stat, i2c->regs + S3C2410_IICSTAT); dev_dbg(i2c->dev, "START: %08lx to IICSTAT, %02x to DS ", stat, addr); writeb(addr, i2c->regs + S3C2410_IICDS); /* delay here to ensure the data byte has gotten onto the bus * before the transaction is started */ ndelay(i2c->tx_setup); dev_dbg(i2c->dev, "iiccon, %08lx ", iiccon); writel(iiccon, i2c->regs + S3C2410_IICCON); stat |= S3C2410_IICSTAT_START; //1<<5
writel(stat, i2c->regs + S3C2410_IICSTAT); }
1:struct i2c_board_info xx = {};
i2c_register_board_info();
2:匹配busnum ,
3: 生成:struct i2c_client{
.name = xxx
}/**
* i2c_add_numbered_adapter - declare i2c adapter, use static bus number * @adap: the adapter to register (with adap->nr initialized) * Context: can sleep * * This routine is used to declare an I2C adapter when its bus number * matters. For example, use it for I2C adapters from system-on-chip CPUs, * or otherwise built in to the system's mainboard, and where i2c_board_info * is used to properly configure I2C devices. * * If the requested bus number is set to -1, then this function will behave * identically to i2c_add_adapter, and will dynamically assign a bus number. * * If no devices have pre-been declared for this bus, then be sure to * register the adapter before any dynamically allocated ones. Otherwise * the required bus ID may not be available. * * When this returns zero, the specified adapter became available for * clients using the bus number provided in adap->nr. Also, the table * of I2C devices pre-declared using i2c_register_board_info() is scanned, * and the appropriate driver model device nodes are created. Otherwise, a * negative errno value is returned. */ int i2c_add_numbered_adapter(struct i2c_adapter *adap) { int id; int status; if (adap->nr == -1) /* -1 means dynamically assign bus id */ return i2c_add_adapter(adap); if (adap->nr & ~MAX_IDR_MASK) return -EINVAL; retry: if (idr_pre_get(&i2c_adapter_idr, GFP_KERNEL) == 0) return -ENOMEM; mutex_lock(&core_lock); /* "above" here means "above or equal to", sigh; * we need the "equal to" result to force the result */ status = idr_get_new_above(&i2c_adapter_idr, adap, adap->nr, &id); if (status == 0 && id != adap->nr) { status = -EBUSY; idr_remove(&i2c_adapter_idr, id); } mutex_unlock(&core_lock); if (status == -EAGAIN) goto retry; if (status == 0) status = i2c_register_adapter(adap); //下面看这个
return status; }
static int i2c_register_adapter(struct i2c_adapter *adap) { int res = 0; /* Can't register until after driver model init */ if (unlikely(WARN_ON(!i2c_bus_type.p))) { res = -EAGAIN; goto out_list; } /* Sanity checks */ if (unlikely(adap->name[0] == '