staticconstchar *const abort_sources[] = {
[ABRT_7B_ADDR_NOACK] = "slave address not acknowledged (7bit mode)",
[ABRT_10ADDR1_NOACK] = "first address byte not acknowledged (10bit mode)",
[ABRT_10ADDR2_NOACK] = "second address byte not acknowledged (10bit mode)",
[ABRT_TXDATA_NOACK] = "data not acknowledged",
[ABRT_GCALL_NOACK] = "no acknowledgement for a general call",
[ABRT_GCALL_READ] = "read after general call",
[ABRT_SBYTE_ACKDET] = "start byte acknowledged",
[ABRT_SBYTE_NORSTRT] = "trying to send start byte when restart is disabled",
[ABRT_10B_RD_NORSTRT] = "trying to read when restart is disabled (10bit mode)",
[ABRT_MASTER_DIS] = "trying to use disabled adapter",
[ARB_LOST] = "lost arbitration",
[ABRT_SLAVE_FLUSH_TXFIFO] = "read command so flush old data in the TX FIFO",
[ABRT_SLAVE_ARBLOST] = "slave lost the bus while transmitting data to a remote master",
[ABRT_SLAVE_RD_INTX] = "incorrect slave-transmitter mode configuration",
};
/* * Note we'll check the return value of the regmap IO accessors only * at the probe stage. The rest of the code won't do this because * basically we have MMIO-based regmap, so none of the read/write methods * can fail.
*/
dev->map = devm_regmap_init(dev->dev, NULL, dev, &map_cfg); if (IS_ERR(dev->map)) {
dev_err(dev->dev, "Failed to init the registers map\n"); return PTR_ERR(dev->map);
}
/* * Only standard mode at 100kHz, fast mode at 400kHz, * fast mode plus at 1MHz and high speed mode at 3.4MHz are supported.
*/ for (i = 0; i < ARRAY_SIZE(supported_speeds); i++) { if (t->bus_freq_hz == supported_speeds[i]) return 0;
}
dev_err(dev->dev, "%d Hz is unsupported, only 100kHz, 400kHz, 1MHz and 3.4MHz are supported\n",
t->bus_freq_hz);
/* * The HCNT/LCNT information coming from ACPI should be the most accurate * for given platform. However, some systems get it wrong. On such systems * we get better results by calculating those based on the input clock.
*/ staticconststruct dmi_system_id i2c_dw_no_acpi_params[] = {
{
.ident = "Dell Inspiron 7348",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 7348"),
},
},
{}
};
/* * Try to get SDA hold time and *CNT values from an ACPI method for * selected speed modes.
*/
i2c_dw_acpi_params(device, "SSCN", &dev->ss_hcnt, &dev->ss_lcnt, &ss_ht);
i2c_dw_acpi_params(device, "FMCN", &dev->fs_hcnt, &dev->fs_lcnt, &fs_ht);
i2c_dw_acpi_params(device, "FPCN", &dev->fp_hcnt, &dev->fp_lcnt, &fp_ht);
i2c_dw_acpi_params(device, "HSCN", &dev->hs_hcnt, &dev->hs_lcnt, &hs_ht);
switch (t->bus_freq_hz) { case I2C_MAX_STANDARD_MODE_FREQ:
dev->sda_hold_time = ss_ht; break; case I2C_MAX_FAST_MODE_PLUS_FREQ:
dev->sda_hold_time = fp_ht; break; case I2C_MAX_HIGH_SPEED_MODE_FREQ:
dev->sda_hold_time = hs_ht; break; case I2C_MAX_FAST_MODE_FREQ: default:
dev->sda_hold_time = fs_ht; break;
}
}
static u32 i2c_dw_acpi_round_bus_speed(struct device *device)
{
u32 acpi_speed; int i;
acpi_speed = i2c_acpi_find_bus_speed(device); /* * Some DSDTs use a non standard speed, round down to the lowest * standard speed.
*/ for (i = 0; i < ARRAY_SIZE(supported_speeds); i++) { if (acpi_speed >= supported_speeds[i]) return supported_speeds[i];
}
/* * Find bus speed from the "clock-frequency" device property, ACPI * or by using fast mode if neither is set.
*/ if (acpi_speed && t->bus_freq_hz)
t->bus_freq_hz = min(t->bus_freq_hz, acpi_speed); elseif (acpi_speed || t->bus_freq_hz)
t->bus_freq_hz = max(t->bus_freq_hz, acpi_speed); else
t->bus_freq_hz = I2C_MAX_FAST_MODE_FREQ;
}
ret = i2c_dw_acquire_lock(dev); if (ret) return 0;
ret = regmap_read(dev->map, reg, &val);
i2c_dw_release_lock(dev);
return ret ? 0 : val;
}
u32 i2c_dw_scl_hcnt(struct dw_i2c_dev *dev, unsignedint reg, u32 ic_clk,
u32 tSYMBOL, u32 tf, int offset)
{ if (!ic_clk) return i2c_dw_read_scl_reg(dev, reg);
/* * Conditional expression: * * IC_[FS]S_SCL_HCNT + 3 >= IC_CLK * (tHD;STA + tf) * * This is just experimental rule; the tHD;STA period turned * out to be proportinal to (_HCNT + 3). With this setting, * we could meet both tHIGH and tHD;STA timing specs. * * If unsure, you'd better to take this alternative. * * The reason why we need to take into account "tf" here, * is the same as described in i2c_dw_scl_lcnt().
*/ return DIV_ROUND_CLOSEST_ULL((u64)ic_clk * (tSYMBOL + tf), MICRO) - 3 + offset;
}
u32 i2c_dw_scl_lcnt(struct dw_i2c_dev *dev, unsignedint reg, u32 ic_clk,
u32 tLOW, u32 tf, int offset)
{ if (!ic_clk) return i2c_dw_read_scl_reg(dev, reg);
/* * Conditional expression: * * IC_[FS]S_SCL_LCNT + 1 >= IC_CLK * (tLOW + tf) * * DW I2C core starts counting the SCL CNTs for the LOW period * of the SCL clock (tLOW) as soon as it pulls the SCL line. * In order to meet the tLOW timing spec, we need to take into * account the fall time of SCL signal (tf). Default tf value * should be 0.3 us, for safety.
*/ return DIV_ROUND_CLOSEST_ULL((u64)ic_clk * (tLOW + tf), MICRO) - 1 + offset;
}
int i2c_dw_set_sda_hold(struct dw_i2c_dev *dev)
{ unsignedint reg; int ret;
ret = i2c_dw_acquire_lock(dev); if (ret) return ret;
/* Configure SDA Hold Time if required */
ret = regmap_read(dev->map, DW_IC_COMP_VERSION, ®); if (ret) goto err_release_lock;
if (reg >= DW_IC_SDA_HOLD_MIN_VERS) { if (!dev->sda_hold_time) { /* Keep previous hold time setting if no one set it */
ret = regmap_read(dev->map, DW_IC_SDA_HOLD,
&dev->sda_hold_time); if (ret) goto err_release_lock;
}
/* * Workaround for avoiding TX arbitration lost in case I2C * slave pulls SDA down "too quickly" after falling edge of * SCL by enabling non-zero SDA RX hold. Specification says it * extends incoming SDA low to high transition while SCL is * high but it appears to help also above issue.
*/ if (!(dev->sda_hold_time & DW_IC_SDA_HOLD_RX_MASK))
dev->sda_hold_time |= 1 << DW_IC_SDA_HOLD_RX_SHIFT;
dev_dbg(dev->dev, "SDA Hold Time TX:RX = %d:%d\n",
dev->sda_hold_time & ~(u32)DW_IC_SDA_HOLD_RX_MASK,
dev->sda_hold_time >> DW_IC_SDA_HOLD_RX_SHIFT);
} elseif (dev->set_sda_hold_time) {
dev->set_sda_hold_time(dev);
} elseif (dev->sda_hold_time) {
dev_warn(dev->dev, "Hardware too old to adjust SDA hold time.\n");
dev->sda_hold_time = 0;
}
abort_needed = (raw_intr_stats & DW_IC_INTR_MST_ON_HOLD) ||
(ic_stats & DW_IC_STATUS_MASTER_HOLD_TX_FIFO_EMPTY); if (abort_needed) { if (!(enable & DW_IC_ENABLE_ENABLE)) {
regmap_write(dev->map, DW_IC_ENABLE, DW_IC_ENABLE_ENABLE); /* * Wait 10 times the signaling period of the highest I2C * transfer supported by the driver (for 400KHz this is * 25us) to ensure the I2C ENABLE bit is already set * as described in the DesignWare I2C databook.
*/
fsleep(DIV_ROUND_CLOSEST_ULL(10 * MICRO, t->bus_freq_hz)); /* Set ENABLE bit before setting ABORT */
enable |= DW_IC_ENABLE_ENABLE;
}
regmap_write(dev->map, DW_IC_ENABLE, enable | DW_IC_ENABLE_ABORT);
ret = regmap_read_poll_timeout(dev->map, DW_IC_ENABLE, enable,
!(enable & DW_IC_ENABLE_ABORT), 10,
100); if (ret)
dev_err(dev->dev, "timeout while trying to abort current transfer\n");
}
do {
__i2c_dw_disable_nowait(dev); /* * The enable status register may be unimplemented, but * in that case this test reads zero and exits the loop.
*/
regmap_read(dev->map, DW_IC_ENABLE_STATUS, &status); if ((status & 1) == 0) return;
/* * Wait 10 times the signaling period of the highest I2C * transfer supported by the driver (for 400kHz this is * 25us) as described in the DesignWare I2C databook.
*/
usleep_range(25, 250);
} while (timeout--);
dev_warn(dev->dev, "timeout in disabling adapter\n");
}
u32 i2c_dw_clk_rate(struct dw_i2c_dev *dev)
{ /* * Clock is not necessary if we got LCNT/HCNT values directly from * the platform code.
*/ if (!dev->get_clk_rate_khz) {
dev_dbg_once(dev->dev, "Callback get_clk_rate_khz() is not defined\n"); return 0;
} return dev->get_clk_rate_khz(dev);
}
int i2c_dw_prepare_clk(struct dw_i2c_dev *dev, bool prepare)
{ int ret;
if (prepare) { /* Optional interface clock */
ret = clk_prepare_enable(dev->pclk); if (ret) return ret;
ret = clk_prepare_enable(dev->clk); if (ret)
clk_disable_unprepare(dev->pclk);
int i2c_dw_acquire_lock(struct dw_i2c_dev *dev)
{ int ret;
if (!dev->acquire_lock) return 0;
ret = dev->acquire_lock(); if (!ret) return 0;
dev_err(dev->dev, "couldn't acquire bus ownership\n");
return ret;
}
void i2c_dw_release_lock(struct dw_i2c_dev *dev)
{ if (dev->release_lock)
dev->release_lock();
}
/* * Waiting for bus not busy
*/ int i2c_dw_wait_bus_not_busy(struct dw_i2c_dev *dev)
{ unsignedint status; int ret;
ret = regmap_read_poll_timeout(dev->map, DW_IC_STATUS, status,
!(status & DW_IC_STATUS_ACTIVITY),
1100, 20000); if (ret) {
dev_warn(dev->dev, "timeout waiting for bus ready\n");
i2c_recover_bus(&dev->adapter);
regmap_read(dev->map, DW_IC_STATUS, &status); if (!(status & DW_IC_STATUS_ACTIVITY))
ret = 0;
}
return ret;
}
int i2c_dw_handle_tx_abort(struct dw_i2c_dev *dev)
{ unsignedlong abort_source = dev->abort_source; int i;
if (abort_source & DW_IC_TX_ARB_LOST) return -EAGAIN; if (abort_source & DW_IC_TX_ABRT_GCALL_READ) return -EINVAL; /* wrong msgs[] data */
return -EIO;
}
int i2c_dw_set_fifo_size(struct dw_i2c_dev *dev)
{
u32 tx_fifo_depth, rx_fifo_depth; unsignedint param; int ret;
/* DW_IC_COMP_PARAM_1 not implement for IP issue */ if ((dev->flags & MODEL_MASK) == MODEL_WANGXUN_SP) {
dev->tx_fifo_depth = TXGBE_TX_FIFO_DEPTH;
dev->rx_fifo_depth = TXGBE_RX_FIFO_DEPTH;
return 0;
}
/* * Try to detect the FIFO depth if not set by interface driver, * the depth could be from 2 to 256 from HW spec.
*/
ret = i2c_dw_acquire_lock(dev); if (ret) return ret;
ret = regmap_read(dev->map, DW_IC_COMP_PARAM_1, ¶m);
i2c_dw_release_lock(dev); if (ret) return ret;
staticint i2c_dw_prepare(struct device *device)
{ /* * If the ACPI companion device object is present for this device, * it may be accessed during suspend and resume of other devices via * I2C operation regions, so tell the PM core and middle layers to * avoid skipping system suspend/resume callbacks for it in that case.
*/ return !has_acpi_companion(device);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.