/* * (C) Copyright 2009-2010 * Nokia Siemens Networks, michael.lawnick.ext@nsn.com * * Portions Copyright (C) 2010 - 2016 Cavium, Inc. * * This file contains the shared part of the driver for the i2c adapter in * Cavium Networks' OCTEON processors and ThunderX SOCs. * * This file is licensed under the terms of the GNU General Public * License version 2. This program is licensed "as is" without any * warranty of any kind, whether express or implied.
*/
/** * octeon_i2c_wait - wait for the IFLG to be set * @i2c: The struct octeon_i2c * * Returns: 0 on success, otherwise a negative errno.
*/ staticint octeon_i2c_wait(struct octeon_i2c *i2c)
{ long time_left;
/* * Some chip revisions don't assert the irq in the interrupt * controller. So we must poll for the IFLG change.
*/ if (i2c->broken_irq_mode) {
u64 end = get_jiffies_64() + i2c->adap.timeout;
while (!octeon_i2c_test_iflg(i2c) &&
time_before64(get_jiffies_64(), end))
usleep_range(I2C_OCTEON_EVENT_WAIT / 2, I2C_OCTEON_EVENT_WAIT);
/** * octeon_i2c_hlc_wait - wait for an HLC operation to complete * @i2c: The struct octeon_i2c * * Returns: 0 on success, otherwise -ETIMEDOUT.
*/ staticint octeon_i2c_hlc_wait(struct octeon_i2c *i2c)
{ int time_left;
/* * Some cn38xx boards don't assert the irq in the interrupt * controller. So we must poll for the valid bit change.
*/ if (i2c->broken_irq_mode) {
u64 end = get_jiffies_64() + i2c->adap.timeout;
while (!octeon_i2c_hlc_test_valid(i2c) &&
time_before64(get_jiffies_64(), end))
usleep_range(I2C_OCTEON_EVENT_WAIT / 2, I2C_OCTEON_EVENT_WAIT);
/* * This is ugly... in HLC mode the status is not in the status register * but in the lower 8 bits of OCTEON_REG_SW_TWSI.
*/ if (i2c->hlc_enabled)
stat = __raw_readq(i2c->twsi_base + OCTEON_REG_SW_TWSI(i2c)); else
stat = octeon_i2c_stat_read(i2c);
switch (stat) { /* Everything is fine */ case STAT_IDLE: case STAT_AD2W_ACK: case STAT_RXADDR_ACK: case STAT_TXADDR_ACK: case STAT_TXDATA_ACK: return 0;
/* ACK allowed on pre-terminal bytes only */ case STAT_RXDATA_ACK: if (!final_read) return 0; return -EIO;
/* NAK allowed on terminal byte only */ case STAT_RXDATA_NAK: if (final_read) return 0; return -EIO;
/* Arbitration lost */ case STAT_LOST_ARB_38: case STAT_LOST_ARB_68: case STAT_LOST_ARB_78: case STAT_LOST_ARB_B0: return -EAGAIN;
/* Being addressed as local target, should back off & listen */ case STAT_SLAVE_60: case STAT_SLAVE_70: case STAT_GENDATA_ACK: case STAT_GENDATA_NAK: return -EOPNOTSUPP;
/* Core busy as local target */ case STAT_SLAVE_80: case STAT_SLAVE_88: case STAT_SLAVE_A0: case STAT_SLAVE_A8: case STAT_SLAVE_LOST: case STAT_SLAVE_NAK: case STAT_SLAVE_ACK: return -EOPNOTSUPP;
case STAT_TXDATA_NAK: case STAT_BUS_ERROR: return -EIO; case STAT_TXADDR_NAK: case STAT_RXADDR_NAK: case STAT_AD2W_NAK: return -ENXIO;
case STAT_WDOG_TOUT:
mode = __raw_readq(i2c->twsi_base + OCTEON_REG_MODE(i2c)); /* Set BUS_MON_RST to reset bus monitor */
mode |= BUS_MON_RST_MASK;
octeon_i2c_writeq_flush(mode, i2c->twsi_base + OCTEON_REG_MODE(i2c)); return -EIO; default:
dev_err(i2c->dev, "unhandled state: %d\n", stat); return -EIO;
}
}
staticint octeon_i2c_recovery(struct octeon_i2c *i2c)
{ int ret;
ret = i2c_recover_bus(&i2c->adap); if (ret) /* recover failed, try hardware re-init */
ret = octeon_i2c_init_lowlevel(i2c); return ret;
}
/** * octeon_i2c_start - send START to the bus * @i2c: The struct octeon_i2c * * Returns: 0 on success, otherwise a negative errno.
*/ staticint octeon_i2c_start(struct octeon_i2c *i2c)
{ int ret;
u8 stat;
octeon_i2c_ctl_write(i2c, TWSI_CTL_ENAB | TWSI_CTL_STA);
ret = octeon_i2c_wait(i2c); if (ret) goto error;
stat = octeon_i2c_stat_read(i2c); if (stat == STAT_START || stat == STAT_REP_START) /* START successful, bail out */ return 0;
error: /* START failed, try to recover */
ret = octeon_i2c_recovery(i2c); return (ret) ? ret : -EAGAIN;
}
/* send STOP to the bus */ staticvoid octeon_i2c_stop(struct octeon_i2c *i2c)
{
octeon_i2c_ctl_write(i2c, TWSI_CTL_ENAB | TWSI_CTL_STP);
}
/** * octeon_i2c_read - receive data from the bus via low-level controller * @i2c: The struct octeon_i2c * @target: Target address * @data: Pointer to the location to store the data * @rlength: Length of the data * @recv_len: flag for length byte * * The address is sent over the bus, then the data is read. * * Returns: 0 on success, otherwise a negative errno.
*/ staticint octeon_i2c_read(struct octeon_i2c *i2c, int target,
u8 *data, u16 *rlength, bool recv_len)
{ int i, result, length = *rlength; bool final_read = false;
result = octeon_i2c_wait(i2c); if (result) return result;
/* address OK ? */
result = octeon_i2c_check_status(i2c, false); if (result) return result;
for (i = 0; i < length; i++) { /* * For the last byte to receive TWSI_CTL_AAK must not be set. * * A special case is I2C_M_RECV_LEN where we don't know the * additional length yet. If recv_len is set we assume we're * not reading the final byte and therefore need to set * TWSI_CTL_AAK.
*/ if ((i + 1 == length) && !(recv_len && i == 0))
final_read = true;
/* clear iflg to allow next event */ if (final_read)
octeon_i2c_ctl_write(i2c, TWSI_CTL_ENAB); else
octeon_i2c_ctl_write(i2c, TWSI_CTL_ENAB | TWSI_CTL_AAK);
result = octeon_i2c_wait(i2c); if (result) return result;
data[i] = octeon_i2c_data_read(i2c, &result); if (result) return result; if (recv_len && i == 0) { if (data[i] > I2C_SMBUS_BLOCK_MAX) return -EPROTO;
length += data[i];
}
result = octeon_i2c_check_status(i2c, final_read); if (result) return result;
}
*rlength = length; return 0;
}
/** * octeon_i2c_write - send data to the bus via low-level controller * @i2c: The struct octeon_i2c * @target: Target address * @data: Pointer to the data to be sent * @length: Length of the data * * The address is sent over the bus, then the data. * * Returns: 0 on success, otherwise a negative errno.
*/ staticint octeon_i2c_write(struct octeon_i2c *i2c, int target, const u8 *data, int length)
{ int i, result;
result = octeon_i2c_wait(i2c); if (result) return result;
}
return 0;
}
/* high-level-controller pure read of up to 8 bytes */ staticint octeon_i2c_hlc_read(struct octeon_i2c *i2c, struct i2c_msg *msgs)
{ int i, j, ret = 0;
u64 cmd;
for (i = 0, j = msgs[0].len - 1; i < msgs[0].len && i < 4; i++, j--)
msgs[0].buf[j] = (cmd >> (8 * i)) & 0xff;
if (msgs[0].len > 4) {
cmd = __raw_readq(i2c->twsi_base + OCTEON_REG_SW_TWSI_EXT(i2c)); for (i = 0; i < msgs[0].len - 4 && i < 4; i++, j--)
msgs[0].buf[j] = (cmd >> (8 * i)) & 0xff;
}
err: return ret;
}
/* high-level-controller pure write of up to 8 bytes */ staticint octeon_i2c_hlc_write(struct octeon_i2c *i2c, struct i2c_msg *msgs)
{ int i, j, ret = 0;
u64 cmd;
/** * octeon_i2c_hlc_block_comp_read - high-level-controller composite block read * @i2c: The struct octeon_i2c * @msgs: msg[0] contains address, place read data into msg[1] * * i2c core command is constructed and written into the SW_TWSI register. * The execution of the command will result in requested data being * placed into a FIFO buffer, ready to be read. * Used in the case where the i2c xfer is for greater than 8 bytes of read data. * * Returns: 0 on success, otherwise a negative errno.
*/ staticint octeon_i2c_hlc_block_comp_read(struct octeon_i2c *i2c, struct i2c_msg *msgs)
{ int ret;
u16 len, i;
u64 cmd;
/* read data in FIFO */
octeon_i2c_writeq_flush(TWSX_BLOCK_STS_RESET_PTR,
i2c->twsi_base + OCTEON_REG_BLOCK_STS(i2c)); for (i = 0; i <= len; i += 8) { /* Byte-swap FIFO data and copy into msg buffer */
__be64 rd = cpu_to_be64(__raw_readq(i2c->twsi_base + OCTEON_REG_BLOCK_FIFO(i2c)));
/** * octeon_i2c_hlc_block_comp_write - high-level-controller composite block write * @i2c: The struct octeon_i2c * @msgs: msg[0] contains address, msg[1] contains data to be written * * i2c core command is constructed and write data is written into the FIFO buffer. * The execution of the command will result in HW write, using the data in FIFO. * Used in the case where the i2c xfer is for greater than 8 bytes of write data. * * Returns: 0 on success, otherwise a negative errno.
*/ staticint octeon_i2c_hlc_block_comp_write(struct octeon_i2c *i2c, struct i2c_msg *msgs)
{ bool set_ext; int ret;
u16 len, i;
u64 cmd, ext = 0;
/** * octeon_i2c_xfer - The driver's xfer function * @adap: Pointer to the i2c_adapter structure * @msgs: Pointer to the messages to be processed * @num: Length of the MSGS array * * Returns: the number of messages processed, or a negative errno on failure.
*/ int octeon_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{ struct octeon_i2c *i2c = i2c_get_adapdata(adap); int i, ret = 0;
if (IS_LS_FREQ(i2c->twsi_freq)) { if (num == 1) { if (msgs[0].len > 0 && msgs[0].len <= 8) { if (msgs[0].flags & I2C_M_RD)
ret = octeon_i2c_hlc_read(i2c, msgs); else
ret = octeon_i2c_hlc_write(i2c, msgs); goto out;
}
} elseif (num == 2) { if ((msgs[0].flags & I2C_M_RD) == 0 &&
(msgs[1].flags & I2C_M_RECV_LEN) == 0 &&
msgs[0].len > 0 && msgs[0].len <= 2 &&
msgs[1].len > 0 &&
msgs[0].addr == msgs[1].addr) { if (msgs[1].len <= 8) { if (msgs[1].flags & I2C_M_RD)
ret = octeon_i2c_hlc_comp_read(i2c, msgs); else
ret = octeon_i2c_hlc_comp_write(i2c, msgs); goto out;
} elseif (msgs[1].len <= 1024 && OCTEON_REG_BLOCK_CTL(i2c)) { if (msgs[1].flags & I2C_M_RD)
ret = octeon_i2c_hlc_block_comp_read(i2c, msgs); else
ret = octeon_i2c_hlc_block_comp_write(i2c, msgs); goto out;
}
}
}
}
for (i = 0; ret == 0 && i < num; i++) { struct i2c_msg *pmsg = &msgs[i];
/* zero-length messages are not supported */ if (!pmsg->len) {
ret = -EOPNOTSUPP; break;
}
ret = octeon_i2c_start(i2c); if (ret) return ret;
if (pmsg->flags & I2C_M_RD)
ret = octeon_i2c_read(i2c, pmsg->addr, pmsg->buf,
&pmsg->len, pmsg->flags & I2C_M_RECV_LEN); else
ret = octeon_i2c_write(i2c, pmsg->addr, pmsg->buf,
pmsg->len);
}
octeon_i2c_stop(i2c);
out: return (ret != 0) ? ret : num;
}
/* calculate and set clock divisors */ void octeon_i2c_set_clock(struct octeon_i2c *i2c)
{ int tclk, thp_base, inc, thp_idx, mdiv_idx, ndiv_idx, foscl, diff; bool is_plat_otx2; /* * Find divisors to produce target frequency, start with large delta * to cover wider range of divisors, note thp = TCLK half period and * ds is OSCL output frequency divisor.
*/ unsignedint thp, mdiv_min, mdiv = 2, ndiv = 0, ds = 10; unsignedint delta_hz = INITIAL_DELTA_HZ;
for (ndiv_idx = 0; ndiv_idx < 8 && delta_hz != 0; ndiv_idx++) { /* * An mdiv value of less than 2 seems to not work well * with ds1337 RTCs, so we constrain it to larger values.
*/ for (mdiv_idx = 15; mdiv_idx >= mdiv_min && delta_hz != 0; mdiv_idx--) { /* * For given ndiv and mdiv values check the * two closest thp values.
*/
tclk = i2c->twsi_freq * (mdiv_idx + 1) * ds;
tclk *= (1 << ndiv_idx); if (is_plat_otx2)
thp_base = (i2c->sys_freq / tclk) - 2; else
thp_base = (i2c->sys_freq / (tclk * 2)) - 1;
/* * Generate STOP to finish the unfinished transaction. * Can't generate STOP via the TWSI CTL register * since it could bring the TWSI controller into an inoperable state.
*/
octeon_i2c_write_int(i2c, TWSI_INT_SDA_OVR | TWSI_INT_SCL_OVR);
udelay(5);
octeon_i2c_write_int(i2c, TWSI_INT_SDA_OVR);
udelay(5);
octeon_i2c_write_int(i2c, 0);
}
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.