/*
i2c.c分析
1. mt_i2c_init() // module_init
|
platform_driver_register(&mt_i2c_driver)
2. static struct platform_driver mt_i2c_driver = {
.probe = mt_i2c_probe,
.remove = mt_i2c_remove,
.suspend = mt_i2c_suspend,
.resume = mt_i2c_resume,
.driver = {
.of_match_table = mt_i2c_of_match, // 用于与dts中的 platform device 匹配 {.compatible = "mediatek,mt6735m-i2c",},
},
};
3. mt_i2c_probe(struct platform_device *pdev)
|
platform_get_resource(pdev, IORESOURCE_MEM, 0); // 1. 获取平台内存资源 - dts中写的
request_mem_region(res->start, resource_size(res), pdev->name) // 向系统申请占用内存资源,其他驱动不可用,类似锁
i2c = kzalloc(sizeof(struct mt_i2c_t), GFP_KERNEL);
i2c->base = of_iomap(pdev->dev.of_node, 0); // 2. ioremap()
irq = irq_of_parse_and_map(pdev->dev.of_node, 0); // 获取中断号irq,从dts获取平台设备资源,并映射出中断号
request_irq(irq, mt_i2c_irq, IRQF_TRIGGER_LOW, I2C_DRV_NAME, i2c); // 3. 获取平台中断资源(在上面),并申请中断request_irq()
mt_i2c_init_hw(i2c); // i2c硬件初始化 - 通过直接写寄存器
|
i2c_writel(i2c, OFFSET_SOFTRESET, 0x0001);
i2c_writel(i2c, OFFSET_DCM_EN, 0x0);
i2c->adap.nr = i2c->id;
i2c->adap.owner = THIS_MODULE;
i2c->adap.algo = &mt_i2c_algorithm;
|
.master_xfer = standard_i2c_transfer,
gt1x_tpd.c中
gt1x_i2c_read()
i2c_read_mtk(addr, buffer, len);
_do_i2c_read(msgs, addr, buffer, len);
i2c_transfer(gt1x_i2c_client->adapter, msgs, 2);
__i2c_transfer(adap, msgs, num);
adap->algo->master_xfer(adap, msgs, num);
i2c->adap.algo_data = NULL;
i2c->adap.timeout = 2 * HZ;
i2c->adap.retries = 1;
i2c_add_numbered_adapter(&i2c->adap); //注册i2c adapter,构建、初始化在前面
|
i2c_add_adapter(adap); // 注册i2c adapter
4. i2c_add_adapter(struct i2c_adapter *adapter)
|
adapter->nr = id;
i2c_register_adapter(adapter);
|
if (unlikely(WARN_ON(!i2c_bus_type.p))) { // unlikely() 编译器会根据上下文判断,这段代码是否成立,如果不成立,直接在编译的时候去掉
dev_set_name(&adap->dev, "i2c-%d", adap->nr); // 设置device的名字: /sys/bus/i2c/devices/i2c-%d
adap->dev.bus = &i2c_bus_type; // 指定总线为: i2c总线
adap->dev.type = &i2c_adapter_type;
device_register(&adap->dev); // 将i2c_client注册到i2c总线中 client->dev.bus = &i2c_bus_type;
i2c_scan_static_board_info(adap); // 遍历__i2c_board_list链表,逐个创建i2c_client(只创建总线编号与adapter相同的)
|
list_for_each_entry(devinfo, &__i2c_board_list, list) { // 遍历__i2c_board_list链表
// 调用i2c_new_device(),将当前的adapter和board_info传入
if (devinfo->busnum == adapter->nr && !i2c_new_device(adapter, &devinfo->board_info))
总结:
1. i2c_client是在adapter注册的时候创建的,并注册到i2c总线
2. adapter也会注册到i2c总线
5. i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
|
client = kzalloc(sizeof *client, GFP_KERNEL); // 分配一下i2c_client
// 初始化i2c_client
client->adapter = adap; // client 关联adapter
client->addr = info->addr; // 【info来自dts】将dts中的i2c addr赋值给client
client->irq = info->irq;
strlcpy(client->name, info->type, sizeof(client->name)); // 名字也是来自于dts
client->dev.parent = &client->adapter->dev;
client->dev.bus = &i2c_bus_type; // 指定总线为: i2c总线
client->dev.type = &i2c_client_type;
client->dev.of_node = info->of_node;
i2c_dev_set_name(adap, client); // 设置device的名字: /sys/bus/i2c/devices/0-0050
|
dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adap), client->addr | ((client->flags & I2C_CLIENT_TEN) ? 0xa000 : 0));
device_register(&client->dev); // 将i2c_client注册到i2c总线中 client->dev.bus = &i2c_bus_type;
总结:
1. 构建了一个i2c_client
2. 通过dts中的信息和当前的adapter 初始化i2c_client
3. 将i2c_client注册到i2c总线, /sys/bus/i2c/devices/(0-0050)"%d-%04x"
*/
/*
1. mt_i2c_init() // module_init
|
platform_driver_register(&mt_i2c_driver)
2. static struct platform_driver mt_i2c_driver = {
.probe = mt_i2c_probe,
3. mt_i2c_probe(struct platform_device *pdev)
|
i2c->adap.algo = &mt_i2c_algorithm;
|
.master_xfer = standard_i2c_transfer,
gt1x_tpd.c中
gt1x_i2c_read()
i2c_read_mtk(addr, buffer, len);
_do_i2c_read(msgs, addr, buffer, len);
i2c_transfer(gt1x_i2c_client->adapter, msgs, 2);
__i2c_transfer(adap, msgs, num);
adap->algo->master_xfer(adap, msgs, num);
4. static struct i2c_algorithm mt_i2c_algorithm = {
.master_xfer = standard_i2c_transfer,
.functionality = mt_i2c_functionality,
};
5. standard_i2c_do_transfer(i2c, msgs, num);
|
s32 left_num = num;
while (left_num--) {
standard_i2c_start_xfer(i2c, msgs++);
|
msg_ext.addr = msg->addr; // 初始化i2c结构体、和msg结构体
msg_ext.flags = msg->flags;
msg_ext.len = msg->len;
msg_ext.buf = msg->buf;
msg_ext.ext_flag = 0;
msg_ext.timing = i2c->defaul_speed;
i2c->read_flag = (msg_ext.flags & I2C_M_RD);
i2c->addr = msg_ext.addr;
_i2c_translate_msg(i2c, &msg_ext); // 继续初始化i2c结构体、和msg结构体
|
I2CINFO(I2C_T_TRANSFERFLOW, "Before i2c transfer .....\n");
if (msg->addr & 0xFF00) msg->ext_flag |= msg->addr & 0xFF00;
i2c->msg_buf = msg->buf;
i2c->msg_len = msg->len;
if (msg->ext_flag & I2C_DMA_FLAG) i2c->dma_en = true;
else i2c->dma_en = false;
if (msg->ext_flag & I2C_WR_FLAG) i2c->op = I2C_MASTER_WRRD;
else {
if (msg->flags & I2C_M_RD) i2c->op = I2C_MASTER_RD;
else i2c->op = I2C_MASTER_WR;
}
mt_i2c_clock_enable(i2c); // 设置i2c时钟,在clk.c中实现
|
if (i2c->dma_en)
enable_clock(MT_CG_PERI_APDMA, "i2c");
enable_clock(i2c->pdn, "i2c");
_i2c_transfer_interface(i2c);
|
i2c_set_speed(i2c); // 设置i2c速率
_i2c_write_reg(i2c); // 在i2c时序的起始位之前,初始化i2c adapter寄存器
i2c_writel(i2c, OFFSET_START, 0x0001); // i2c时序的起始位
_i2c_deal_result(i2c); // 处理结果: 根据应答位是否被拉低,做出处理
|
if (i2c->poll_en) { // log 显示不是轮询模式: poll_en==0
} else { // 如果是中断模式,此处睡眠,等待中断显式唤醒等待队列,才继续往下走
tmo = wait_event_timeout(i2c->wait, atomic_read(&i2c->trans_stop), tmo);
}
if (!(tmo == 0 || atomic_read(&i2c->trans_err))) { // Transfer success 传输成功 - 应答位正常
} else { // Transfer err 传输失败 - 根据应答位判断是超时or无应答
if (tmo == 0) I2CERR("id=%d,addr: %x, transfer timeout\n", i2c->id, i2c->addr);
else I2CERR("id=%d,addr: %x, transfer error\n", i2c->id, i2c->addr); // i2c addr 不对的时候走这里
if (i2c->irq_stat & I2C_HS_NACKERR) I2CERR("I2C_HS_NACKERR\n");
if (i2c->irq_stat & I2C_ACKERR) I2CERR("I2C_ACKERR\n"); // i2c addr 不对的时候走这里
_i2c_dump_info(i2c); // 此处倾倒i2c info
I2CINFO(I2C_T_TRANSFERFLOW, "After i2c transfer .....\n");
6. static irqreturn_t mt_i2c_irq(s32 irqno, void *dev_id) // mt_i2c_probe()中申请的中断
|
i2c->irq_stat = i2c_readl(i2c, OFFSET_INTR_STAT); // 读取i2c ack的状态,后面用以判断是timeout 还是ack err
wake_up(&i2c->wait); // 显式唤醒i2c的等待队列
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/err.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/wait.h>
#include <linux/mm.h>
#include <linux/dma-mapping.h>
#include <asm/scatterlist.h>
#include <linux/scatterlist.h>
#ifdef CONFIG_OF
#include <linux/of_irq.h>
#include <linux/of_address.h>
#endif
#include <mach/mt_clkmgr.h> /* mt_clkmgr.h will be removed after CCF porting is finished. */
#include <asm/io.h>
/* #include <mach/dma.h> */
/* #include <mach/mt_reg_base.h> */
#include <mt_i2c.h>
#include <mt-plat/sync_write.h>
#include "../../base/power/mt6735/mt_pm_init.h"
/* #include "mach/memory.h" */
/* #include <mach/i2c.h> */
/* #include <linux/aee.h> */
#define TAG "MT_I2C"
#define DMA_LOG_LEN 7
static struct i2c_dma_info g_dma_data[DMA_LOG_LEN];
static void __iomem *spm_i2c_base;
/* extern unsigned int mt_get_bus_freq(void); */
typedef int (*pmaster_xfer) (struct i2c_adapter *adap, struct i2c_msg *msgs, int num);
/* define ONLY_KERNEL */
/******************************internal API********************************************************/
void i2c_writel(struct mt_i2c_t *i2c, u8 offset, u16 value)
{
/* __raw_writew(value, (i2c->base) + (offset)); */
mt_reg_sync_writel(value, (i2c->base) + (offset));
}
u32 i2c_readl(struct mt_i2c_t *i2c, u8 offset)
{
return __raw_readl((void *)((i2c->base) + (offset)));
}
/***********************************declare API**************************/
static void mt_i2c_clock_enable(struct mt_i2c_t *i2c);
static void mt_i2c_clock_disable(struct mt_i2c_t *i2c);
/***********************************I2C common Param **************************/
u32 I2C_TIMING_REG_BACKUP[7] = { 0 };
u32 I2C_HIGHSP_REG_BACKUP[7] = { 0 };
#ifdef CONFIG_OF
static void __iomem *ap_dma_base;
#endif
/***********************************I2C Param only used in kernel*****************/
/*this field is only for 3d camera*/
static struct mt_i2c_msg g_msg[2];
static struct mt_i2c_t *g_i2c[2];
#define I2C_DRV_NAME "mt-i2c"
/***********************************i2c debug********************************************************/
/* #define I2C_DEBUG_FS */
#ifdef I2C_DEBUG_FS
#define PORT_COUNT 7
#define MESSAGE_COUNT 16
#define I2C_T_DMA 1
#define I2C_T_TRANSFERFLOW 2
#define I2C_T_SPEED 3
/*7 ports,16 types of message */
u8 i2c_port[PORT_COUNT][MESSAGE_COUNT];
#define I2CINFO(type, format, arg...) I2CLOG(format, ## arg)
static ssize_t show_config(struct device *dev, struct device_attribute *attr, char *buff)
{
s32 i = 0;
s32 j = 0;
char *buf = buff;
for (i = 0; i < PORT_COUNT; i++) {
for (j = 0; j < MESSAGE_COUNT; j++)
i2c_port[i][j] += '0';
strncpy(buf, (char *)i2c_port[i], MESSAGE_COUNT);
buf += MESSAGE_COUNT;
*buf = '\n';
buf++;
for (j = 0; j < MESSAGE_COUNT; j++)
i2c_port[i][j] -= '0';
}
return buf - buff;
}
static ssize_t set_config(struct device *dev, struct device_attribute *attr, const char *buf,
size_t count)
{
s32 port, type, status;
if (sscanf(buf, "%d %d %d", &port, &type, &status) != 0) {
if (port >= PORT_COUNT || port < 0 || type >= MESSAGE_COUNT || type < 0) {
/*Invalid param */
I2CERR("i2c debug system: Parameter overflowed!\n");
} else {
if (status != 0)
i2c_port[port][type] = 1;
else
i2c_port[port][type] = 0;
I2CLOG("port:%d type:%d status:%s\ni2c debug system: Parameter accepted!\n",
port, type, status ? "on" : "off");
}
} else {
/*parameter invalid */
I2CERR("i2c debug system: Parameter invalid!\n");
}
return count;
}
static DEVICE_ATTR(debug, S_IRUGO | S_IWUSR, show_config, set_config);
#else
#define I2CINFO(type, format, arg...)
#endif
/***********************************common API********************************************************/
/*
add this function for cast pointer from PA to VA
1 32bit, but open 4G RAM
2 64bit
-32-bit/64-bit is ok
-32-bit with open 4G DRAM config will lose high 32bit ([63:32])
Note: this function will cast 64 bit address to 32 bit, so, must need to confirm [63:32] of address
is 0. need flag GFP_DMA32 for dma_alloc_coherent()
*/
char *mt_i2c_bus_to_virt(unsigned long address)
{
return (char *)address;
}
/*Set i2c port speed*/
static s32 i2c_set_speed(struct mt_i2c_t *i2c)
{
s32 ret = 0;
s32 mode = 0;
u32 khz = 0;
/* u32 base = i2c->base; */
u16 step_cnt_div = 0;
u16 sample_cnt_div = 0;
u32 tmp, sclk, hclk = i2c->clk;
u16 max_step_cnt_div = 0;
u32 diff, min_diff = i2c->clk;
u16 sample_div = MAX_SAMPLE_CNT_DIV;
u16 step_div = 0;
/* I2CFUC(); */
/* I2CLOG("i2c_set_speed=================\n"); */
/* compare the current speed with the latest mode */
mode &#
i2c子系统分析:源码1. kernel-3.18\drivers\misc\mediatek\i2c\mt6735\i2c.c
最新推荐文章于 2024-05-14 21:30:00 发布
![](https://img-home.csdnimg.cn/images/20240711042549.png)