I2C驱动框架分析(3):DW_I2C驱动分析
I2C驅(qū)動(dòng)框架分析(1):I2C重要概念與數(shù)據(jù)結(jié)構(gòu)
I2C驅(qū)動(dòng)框架分析(2):I2C框架源碼分析
I2C驅(qū)動(dòng)框架分析(3):DW_I2C驅(qū)動(dòng)分析
第三章:DW_I2C驅(qū)動(dòng)
其驅(qū)動(dòng)文件在drivers/i2c/busses/i2c-designware-platdrv.c。
3.1 I2C_platform驅(qū)動(dòng)
dw_i2c的platform驅(qū)動(dòng)定義為:
static struct platform_driver dw_i2c_driver = {.probe = dw_i2c_plat_probe,.remove = dw_i2c_plat_remove,.driver = {.name = "i2c_designware",.of_match_table = of_match_ptr(dw_i2c_of_match),.acpi_match_table = ACPI_PTR(dw_i2c_acpi_match),.pm = DW_I2C_DEV_PMOPS,},
};
調(diào)用platform_driver_register(&dw_i2c_driver)函數(shù)注冊(cè)到platform總線上,platform_driver_register函數(shù)流程( 矢量圖可放大查看):
以上流程主要工作是,將I2C_platform驅(qū)動(dòng)進(jìn)行注冊(cè),并進(jìn)行device與driver匹配,最后執(zhí)行probe函數(shù)。上述流程適用與所有的paltform驅(qū)動(dòng)。
3.2 DW_I2C probe函數(shù)
當(dāng)device與driver匹配完成后,執(zhí)行probe函數(shù),DW_I2C probe函數(shù)為dw_i2c_plat_probe,函數(shù)主要流程為:
通過(guò)調(diào)用i2c_detect_slave_mode函數(shù)來(lái)判斷控制器主從模式,其內(nèi)容主要為:
//讀取i2c設(shè)備樹子節(jié)點(diǎn)reg屬性
of_property_read_u32(child, "reg", ®)//地址高bit30為1,即為從
reg & I2C_OWN_SLAVE_ADDRESS
【關(guān)于dw_i2c速率】
dw_i2c速率賦值有兩種方法:
第一種是通過(guò)
struct dw_i2c_platform_data *pdata = dev_get_platdata(&pdev->dev)來(lái)賦值第二種是調(diào)用
i2c_parse_fw_timings函數(shù),從設(shè)備樹解析clock-frequency屬性來(lái)賦值
3.3 DW_I2C主從模式設(shè)置
從之前的內(nèi)容得知,通過(guò)從i2c設(shè)備樹子節(jié)點(diǎn)地址來(lái)判斷i2c控制器的主從設(shè)置。
3.3.1DW_I2C主機(jī)模式設(shè)置
調(diào)用i2c_dw_configure_master函數(shù)完成,其函數(shù)內(nèi)容為:
static void i2c_dw_configure_master(struct dw_i2c_dev *dev)
{struct i2c_timings *t = &dev->timings;dev->functionality = I2C_FUNC_10BIT_ADDR | DW_IC_DEFAULT_FUNCTIONALITY;dev->master_cfg = DW_IC_CON_MASTER | DW_IC_CON_SLAVE_DISABLE |DW_IC_CON_RESTART_EN;dev->mode = DW_IC_MASTER;switch (t->bus_freq_hz) {case 100000:dev->master_cfg |= DW_IC_CON_SPEED_STD;break;case 3400000:dev->master_cfg |= DW_IC_CON_SPEED_HIGH;break;default:dev->master_cfg |= DW_IC_CON_SPEED_FAST;}
}
上述主要工作為:
- 設(shè)置
functionality屬性; - 設(shè)置主機(jī)標(biāo)志位;
- 設(shè)置傳輸速率。
3.3.2 DW_I2C從機(jī)模式設(shè)置
調(diào)用i2c_dw_configure_slave函數(shù)實(shí)現(xiàn)從機(jī)模式設(shè)置,函數(shù)為:
static void i2c_dw_configure_slave(struct dw_i2c_dev *dev)
{dev->functionality = I2C_FUNC_SLAVE | DW_IC_DEFAULT_FUNCTIONALITY;dev->slave_cfg = DW_IC_CON_RX_FIFO_FULL_HLD_CTRL |DW_IC_CON_RESTART_EN | DW_IC_CON_STOP_DET_IFADDRESSED;dev->mode = DW_IC_SLAVE;
}
上述主要工作為:
- 設(shè)置
functionality屬性; - 設(shè)置從機(jī)機(jī)標(biāo)志位;
- 設(shè)置從機(jī)模式。
3.4 DW_I2C主機(jī)probe函數(shù)
主機(jī)probe函數(shù)由i2c_dw_probe,完成主機(jī)模式功能配置,函數(shù)的流程如下圖所示:
上述流程中主要工作為:
- 主機(jī)模式相關(guān)硬件初始化;
- 例化適配器成員變量,提供i2c_dw_algo接口;
- 提供中斷服務(wù)函數(shù);
- 向bus添加adapter的id,并注冊(cè)adapter設(shè)備.
在上面的工作中,最重要的是第2點(diǎn)與第3點(diǎn),下面將詳細(xì)介紹。
3.4.1 傳輸變量i2c_dw_algo
i2c_dw_algo是 struct i2c_algorithm結(jié)構(gòu)體類型變量,其內(nèi)容為:
static const struct i2c_algorithm i2c_dw_algo = {.master_xfer = i2c_dw_xfer,.functionality = i2c_dw_func,
};
i2c_dw_xfer鉤子函數(shù)的形參為:
- adap: 當(dāng)前傳遞的適配器;
- msgs: 要傳遞的消息(數(shù)據(jù))內(nèi)容;
- num: 消息的長(zhǎng)度。
函數(shù)的主要內(nèi)容如下:
static int i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
{struct dw_i2c_dev *dev = i2c_get_adapdata(adap);int ret;...//例化dw_i2c_dev設(shè)備成員變量dev->msgs = msgs;dev->msgs_num = num;dev->cmd_err = 0;dev->msg_write_idx = 0;dev->msg_read_idx = 0;dev->msg_err = 0;dev->status = STATUS_IDLE;dev->abort_source = 0;dev->rx_outstanding = 0;ret = i2c_dw_acquire_lock(dev);if (ret)goto done_nolock;//判斷當(dāng)前設(shè)備是否busyret = i2c_dw_wait_bus_not_busy(dev);if (ret < 0)goto done;//開(kāi)始傳輸消息i2c_dw_xfer_init(dev);/* Wait for tx to complete */if (!wait_for_completion_timeout(&dev->cmd_complete, adap->timeout)) {dev_err(dev->dev, "controller timed out\n");/* i2c_dw_init implicitly disables the adapter */i2c_recover_bus(&dev->adapter);i2c_dw_init_master(dev);ret = -ETIMEDOUT;goto done;}...
}
上述函數(shù)中主要工作為:
- 例化dw_i2c_dev設(shè)備成員變量,特別是dev->msgs;
- 判斷當(dāng)前設(shè)備是否busy;
- 設(shè)置傳輸時(shí)的參數(shù),比如中斷和從機(jī)地址。
其中第3點(diǎn)調(diào)用i2c_dw_xfer_init函數(shù)實(shí)現(xiàn),其把內(nèi)容為:
static void i2c_dw_xfer_init(struct dw_i2c_dev *dev)
{struct i2c_msg *msgs = dev->msgs;u32 ic_con, ic_tar = 0;//關(guān)閉i2c__i2c_dw_disable(dev);//讀取i2c硬件配置信息,并設(shè)置從機(jī)地址ic_con = dw_readl(dev, DW_IC_CON);if (msgs[dev->msg_write_idx].flags & I2C_M_TEN) {//10bit從機(jī)地址ic_con |= DW_IC_CON_10BITADDR_MASTER;ic_tar = DW_IC_TAR_10BITADDR_MASTER;} else {ic_con &= ~DW_IC_CON_10BITADDR_MASTER;}dw_writel(dev, ic_con, DW_IC_CON);dw_writel(dev, msgs[dev->msg_write_idx].addr | ic_tar, DW_IC_TAR);//關(guān)閉中斷i2c_dw_disable_int(dev);//使能i2c__i2c_dw_enable(dev);/* Dummy read to avoid the register getting stuck on Bay Trail */dw_readl(dev, DW_IC_ENABLE_STATUS);//使能中斷dw_readl(dev, DW_IC_CLR_INTR);dw_writel(dev, DW_IC_INTR_MASTER_MASK, DW_IC_INTR_MASK);
}
由于本驅(qū)動(dòng)在中斷中處理數(shù)據(jù)接受與發(fā)送,在最后的使能中斷,其使能的中斷包括如下:
DW_IC_INTR_TX_EMPTY
DW_IC_INTR_RX_FULL
DW_IC_INTR_TX_ABRT
DW_IC_INTR_STOP_DET
【注釋】
在很多i2c的ip驅(qū)動(dòng)中,
master_xfer函數(shù)指針都用于實(shí)現(xiàn)數(shù)據(jù)傳輸,在dw_i2c驅(qū)動(dòng)中,master_xfer用于設(shè)置傳輸時(shí)的參數(shù),真正數(shù)據(jù)傳輸依靠中斷服務(wù)函數(shù)實(shí)現(xiàn)。
3.4.2 中斷服務(wù)函數(shù)i2c_dw_isr
在中斷服務(wù)函數(shù)i2c_dw_isr中,實(shí)際調(diào)用函數(shù)i2c_dw_irq_handler_master來(lái)實(shí)現(xiàn),其函數(shù)主要內(nèi)容為:
static int i2c_dw_irq_handler_master(struct dw_i2c_dev *dev)
{u32 stat;//獲取中斷類型stat = i2c_dw_read_clear_intrbits(dev);if (stat & DW_IC_INTR_TX_ABRT) {dev->cmd_err |= DW_IC_ERR_TX_ABRT;dev->status = STATUS_IDLE;dw_writel(dev, 0, DW_IC_INTR_MASK);goto tx_aborted;}//讀中斷if (stat & DW_IC_INTR_RX_FULL)i2c_dw_read(dev);//發(fā)中斷if (stat & DW_IC_INTR_TX_EMPTY)i2c_dw_xfer_msg(dev);...return 0;
}
-
讀中斷處理,當(dāng)接受FIFO滿時(shí),觸發(fā)接受中斷由
i2c_dw_read函數(shù)實(shí)現(xiàn)讀,其內(nèi)容為:static void i2c_dw_read(struct dw_i2c_dev *dev) {struct i2c_msg *msgs = dev->msgs;int rx_valid;for (; dev->msg_read_idx < dev->msgs_num; dev->msg_read_idx++) {u32 len;u8 *buf;if (!(msgs[dev->msg_read_idx].flags & I2C_M_RD))continue;if (!(dev->status & STATUS_READ_IN_PROGRESS)) {len = msgs[dev->msg_read_idx].len;buf = msgs[dev->msg_read_idx].buf;} else {len = dev->rx_buf_len;buf = dev->rx_buf;}//讀有效數(shù)據(jù)個(gè)數(shù)rx_valid = dw_readl(dev, DW_IC_RXFLR);for (; len > 0 && rx_valid > 0; len--, rx_valid--) {u32 flags = msgs[dev->msg_read_idx].flags;//讀數(shù)據(jù)*buf = dw_readl(dev, DW_IC_DATA_CMD);/* Ensure length byte is a valid value */if (flags & I2C_M_RECV_LEN &&*buf <= I2C_SMBUS_BLOCK_MAX && *buf > 0) {len = i2c_dw_recv_len(dev, *buf);}buf++;dev->rx_outstanding--;}if (len > 0) {dev->status |= STATUS_READ_IN_PROGRESS;dev->rx_buf_len = len;dev->rx_buf = buf;return;} elsedev->status &= ~STATUS_READ_IN_PROGRESS;} } -
發(fā)中斷處理。由函數(shù)
i2c_dw_xfer_msg實(shí)現(xiàn),其內(nèi)容為:static void i2c_dw_xfer_msg(struct dw_i2c_dev *dev) {struct i2c_msg *msgs = dev->msgs;u32 intr_mask;int tx_limit, rx_limit;u32 addr = msgs[dev->msg_write_idx].addr;u32 buf_len = dev->tx_buf_len;u8 *buf = dev->tx_buf;bool need_restart = false;intr_mask = DW_IC_INTR_MASTER_MASK;for (; dev->msg_write_idx < dev->msgs_num; dev->msg_write_idx++) {u32 flags = msgs[dev->msg_write_idx].flags;if (msgs[dev->msg_write_idx].addr != addr) {dev_err(dev->dev,"%s: invalid target address\n", __func__);dev->msg_err = -EINVAL;break;}if (!(dev->status & STATUS_WRITE_IN_PROGRESS)) {/* new i2c_msg */buf = msgs[dev->msg_write_idx].buf;buf_len = msgs[dev->msg_write_idx].len;if ((dev->master_cfg & DW_IC_CON_RESTART_EN) &&(dev->msg_write_idx > 0))need_restart = true;}tx_limit = dev->tx_fifo_depth - dw_readl(dev, DW_IC_TXFLR);rx_limit = dev->rx_fifo_depth - dw_readl(dev, DW_IC_RXFLR);while (buf_len > 0 && tx_limit > 0 && rx_limit > 0) {u32 cmd = 0;if (dev->msg_write_idx == dev->msgs_num - 1 &&buf_len == 1 && !(flags & I2C_M_RECV_LEN))cmd |= BIT(9);if (need_restart) {cmd |= BIT(10);need_restart = false;}if (msgs[dev->msg_write_idx].flags & I2C_M_RD) {/* Avoid rx buffer overrun */if (dev->rx_outstanding >= dev->rx_fifo_depth)break;dw_writel(dev, cmd | 0x100, DW_IC_DATA_CMD);rx_limit--;dev->rx_outstanding++;} else{//發(fā)送數(shù)據(jù)dw_writel(dev, cmd | *buf++, DW_IC_DATA_CMD);}tx_limit--; buf_len--;}dev->tx_buf = buf;dev->tx_buf_len = buf_len;if (buf_len > 0 || flags & I2C_M_RECV_LEN) {/* more bytes to be written */dev->status |= STATUS_WRITE_IN_PROGRESS;break;} elsedev->status &= ~STATUS_WRITE_IN_PROGRESS;}if (dev->msg_write_idx == dev->msgs_num)intr_mask &= ~DW_IC_INTR_TX_EMPTY;if (dev->msg_err)intr_mask = 0;//清除發(fā)送中斷dw_writel(dev, intr_mask, DW_IC_INTR_MASK); }【注釋】:
dw_i2c驅(qū)動(dòng)使用中斷的方式來(lái)傳輸數(shù)據(jù),其發(fā)送中斷的大致機(jī)制為:當(dāng)TX_FIFO里的數(shù)據(jù)量低于FIFO的閾值,則會(huì)觸發(fā)中斷。
在驅(qū)動(dòng)中實(shí)現(xiàn)機(jī)制是:系統(tǒng)上電時(shí),沒(méi)有使能發(fā)送中斷;當(dāng)有數(shù)據(jù)發(fā)送時(shí),調(diào)用到
master_xfer函數(shù)指針時(shí),會(huì)進(jìn)行發(fā)送中斷使能,此時(shí)TX_FIFO為空,發(fā)送中斷觸發(fā),進(jìn)入中斷服務(wù)函數(shù)中進(jìn)行數(shù)據(jù)發(fā)送處理。
3.5 DW_I2C從機(jī)probe函數(shù)
從機(jī)probe函數(shù)由i2c_dw_probe_slave實(shí)現(xiàn),完成從機(jī)配置,其函數(shù)流程為:
3.5.1 傳輸變量i2c_dw_algo
在例化adapter成員變量時(shí),i2c_dw_algo如下:
static const struct i2c_algorithm i2c_dw_algo = {.functionality = i2c_dw_func,.reg_slave = i2c_dw_reg_slave,.unreg_slave = i2c_dw_unreg_slave,
};
由于作為從機(jī),i2c_dw_algo變量沒(méi)有提供傳輸函數(shù),但是在i2c_dw_reg_slave中,實(shí)現(xiàn)作為從機(jī)時(shí)其從機(jī)地址的設(shè)置。
【注釋】
在
i2c_dw_reg_slave函數(shù)中,設(shè)置從機(jī)地址時(shí),實(shí)際是被i2c_slave_register調(diào)用。
i2c_slave_register函數(shù)在添加i2c設(shè)備時(shí),設(shè)備驅(qū)動(dòng)會(huì)使用到,比如eeprom驅(qū)動(dòng)。因此在控制器作為從機(jī)設(shè)置從機(jī)地址時(shí),設(shè)置方法如下所示:
i2c0: i2c@1cc30000 {#address-cells = <1>;#size-cells = <0>;compatible = "snps,designware-i2c";reg = <0x1cc30000 0x1000>;interrupt-parent = <&gic>;interrupts = <0 117 IRQ_TYPE_LEVEL_HIGH>;clock-names = "ic_clk", "pclk";clocks = <&sysclk1>, <&sysclk1>;clock-frequency = <100000>;status = "okay";//test for i2c slaveeeprom@64{ compatible = "slave-24c02";reg = <0x40000064>; //控制器從機(jī)地址為0x64};};
3.5.2 中斷服務(wù)函數(shù)i2c_dw_isr_slave
在i2c_dw_isr_slave中斷服務(wù)函數(shù)中,實(shí)際調(diào)用的是i2c_dw_irq_handler_slave,函數(shù)內(nèi)容為:
static int i2c_dw_irq_handler_slave(struct dw_i2c_dev *dev)
{u32 raw_stat, stat, enabled;u8 val, slave_activity;stat = dw_readl(dev, DW_IC_INTR_STAT);enabled = dw_readl(dev, DW_IC_ENABLE);raw_stat = dw_readl(dev, DW_IC_RAW_INTR_STAT);slave_activity = ((dw_readl(dev, DW_IC_STATUS) &DW_IC_STATUS_SLAVE_ACTIVITY) >> 6);...if (stat & DW_IC_INTR_RD_REQ) { //接受到讀請(qǐng)求if (slave_activity) {if (stat & DW_IC_INTR_RX_FULL) {val = dw_readl(dev, DW_IC_DATA_CMD); //將FIFO數(shù)據(jù)讀取出來(lái)if (!i2c_slave_event(dev->slave,I2C_SLAVE_WRITE_RECEIVED,&val)) {dev_vdbg(dev->dev, "Byte %X acked!",val);}dw_readl(dev, DW_IC_CLR_RD_REQ);stat = i2c_dw_read_clear_intrbits_slave(dev);} else {dw_readl(dev, DW_IC_CLR_RD_REQ);dw_readl(dev, DW_IC_CLR_RX_UNDER);stat = i2c_dw_read_clear_intrbits_slave(dev);}if (!i2c_slave_event(dev->slave,I2C_SLAVE_READ_REQUESTED,&val))dw_writel(dev, val, DW_IC_DATA_CMD);}}if (stat & DW_IC_INTR_RX_DONE) {if (!i2c_slave_event(dev->slave, I2C_SLAVE_READ_PROCESSED,&val))dw_readl(dev, DW_IC_CLR_RX_DONE);i2c_slave_event(dev->slave, I2C_SLAVE_STOP, &val);stat = i2c_dw_read_clear_intrbits_slave(dev);return 1;}if (stat & DW_IC_INTR_RX_FULL) {val = dw_readl(dev, DW_IC_DATA_CMD);if (!i2c_slave_event(dev->slave, I2C_SLAVE_WRITE_RECEIVED,&val))dev_vdbg(dev->dev, "Byte %X acked!", val);} else {i2c_slave_event(dev->slave, I2C_SLAVE_STOP, &val);stat = i2c_dw_read_clear_intrbits_slave(dev);}return 1;
}
在處理讀寫數(shù)據(jù)時(shí),使用i2c_slave_event函數(shù)進(jìn)行處理,下一小節(jié)將介紹。
3.5.3 I2C slave events
在上一小節(jié)中,多次調(diào)用i2c_slave_event函數(shù),這是i2c控制器作為從機(jī)時(shí)的事件機(jī)制,函數(shù)原型為:
static inline int i2c_slave_event(struct i2c_client *client,enum i2c_slave_event event, u8 *val)
client 描述I2C slave設(shè)備。 event 是下面描述的特殊事件類型之一。 val 為要讀/寫的數(shù)據(jù)字節(jié)保存一個(gè)u8值,因此是雙向的。即使val不用于事件,也必須始終提供指向val的指針,即不要在這里使用NULL。
event事件類型有:
-
I2C_SLAVE_WRITE_REQUESTED:
val未使用,ret總是 0。另一個(gè)I2C master想要向我們寫入數(shù)據(jù)。一旦檢測(cè)到我們自己的地址和寫位,就應(yīng)該發(fā)送這個(gè)事件。數(shù)據(jù)還沒(méi)有到達(dá),所以沒(méi)有需要處理或返回的內(nèi)容。
-
I2C_SLAVE_READ_REQUESTED:
val返回要發(fā)送的第一個(gè)字節(jié),ret總是 0。另一個(gè)I2C master想從我們這里讀取數(shù)據(jù)。一旦檢測(cè)到我們自己的地址和讀位,就應(yīng)該發(fā)送此事件。返回后,總線驅(qū)動(dòng)程序應(yīng)該發(fā)送第一個(gè)字節(jié)。
-
I2C_SLAVE_WRITE_RECEIVED:
val總線驅(qū)動(dòng)發(fā)送接收的字節(jié),ret0表示該字節(jié)被ACK,errno表示該字節(jié)被NACK。另一個(gè)I2C master發(fā)送了一個(gè)字節(jié)給我們,需要在
val中設(shè)置。如果ret為零,總線驅(qū)動(dòng)程序應(yīng)該ack這個(gè)字節(jié)。如果ret是errno,則該字節(jié)應(yīng)該被刪除。 -
I2C_SLAVE_READ_PROCESSED:
val返回要發(fā)送的下一個(gè)字節(jié),ret總是 0。總線驅(qū)動(dòng)請(qǐng)求將下一個(gè)字節(jié)以
val的形式發(fā)送給另一個(gè)I2C master。 -
I2C_SLAVE_STOP:
val未使用,ret總是 0。接收到停止條件。
【注釋】
具體的事件行為由i2c設(shè)備實(shí)現(xiàn)。
?
總結(jié)
以上是生活随笔為你收集整理的I2C驱动框架分析(3):DW_I2C驱动分析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Codeforces Round #58
- 下一篇: P6378 [PA2010] Riddl