in busses/i2c-stm32f7.c [946:1112]
static int stm32f7_i2c_smbus_xfer_msg(struct stm32f7_i2c_dev *i2c_dev,
unsigned short flags, u8 command,
union i2c_smbus_data *data)
{
struct stm32f7_i2c_msg *f7_msg = &i2c_dev->f7_msg;
struct device *dev = i2c_dev->dev;
void __iomem *base = i2c_dev->base;
u32 cr1, cr2;
int i, ret;
f7_msg->result = 0;
reinit_completion(&i2c_dev->complete);
cr2 = readl_relaxed(base + STM32F7_I2C_CR2);
cr1 = readl_relaxed(base + STM32F7_I2C_CR1);
/* Set transfer direction */
cr2 &= ~STM32F7_I2C_CR2_RD_WRN;
if (f7_msg->read_write)
cr2 |= STM32F7_I2C_CR2_RD_WRN;
/* Set slave address */
cr2 &= ~(STM32F7_I2C_CR2_ADD10 | STM32F7_I2C_CR2_SADD7_MASK);
cr2 |= STM32F7_I2C_CR2_SADD7(f7_msg->addr);
f7_msg->smbus_buf[0] = command;
switch (f7_msg->size) {
case I2C_SMBUS_QUICK:
f7_msg->stop = true;
f7_msg->count = 0;
break;
case I2C_SMBUS_BYTE:
f7_msg->stop = true;
f7_msg->count = 1;
break;
case I2C_SMBUS_BYTE_DATA:
if (f7_msg->read_write) {
f7_msg->stop = false;
f7_msg->count = 1;
cr2 &= ~STM32F7_I2C_CR2_RD_WRN;
} else {
f7_msg->stop = true;
f7_msg->count = 2;
f7_msg->smbus_buf[1] = data->byte;
}
break;
case I2C_SMBUS_WORD_DATA:
if (f7_msg->read_write) {
f7_msg->stop = false;
f7_msg->count = 1;
cr2 &= ~STM32F7_I2C_CR2_RD_WRN;
} else {
f7_msg->stop = true;
f7_msg->count = 3;
f7_msg->smbus_buf[1] = data->word & 0xff;
f7_msg->smbus_buf[2] = data->word >> 8;
}
break;
case I2C_SMBUS_BLOCK_DATA:
if (f7_msg->read_write) {
f7_msg->stop = false;
f7_msg->count = 1;
cr2 &= ~STM32F7_I2C_CR2_RD_WRN;
} else {
f7_msg->stop = true;
if (data->block[0] > I2C_SMBUS_BLOCK_MAX ||
!data->block[0]) {
dev_err(dev, "Invalid block write size %d\n",
data->block[0]);
return -EINVAL;
}
f7_msg->count = data->block[0] + 2;
for (i = 1; i < f7_msg->count; i++)
f7_msg->smbus_buf[i] = data->block[i - 1];
}
break;
case I2C_SMBUS_PROC_CALL:
f7_msg->stop = false;
f7_msg->count = 3;
f7_msg->smbus_buf[1] = data->word & 0xff;
f7_msg->smbus_buf[2] = data->word >> 8;
cr2 &= ~STM32F7_I2C_CR2_RD_WRN;
f7_msg->read_write = I2C_SMBUS_READ;
break;
case I2C_SMBUS_BLOCK_PROC_CALL:
f7_msg->stop = false;
if (data->block[0] > I2C_SMBUS_BLOCK_MAX - 1) {
dev_err(dev, "Invalid block write size %d\n",
data->block[0]);
return -EINVAL;
}
f7_msg->count = data->block[0] + 2;
for (i = 1; i < f7_msg->count; i++)
f7_msg->smbus_buf[i] = data->block[i - 1];
cr2 &= ~STM32F7_I2C_CR2_RD_WRN;
f7_msg->read_write = I2C_SMBUS_READ;
break;
case I2C_SMBUS_I2C_BLOCK_DATA:
/* Rely on emulated i2c transfer (through master_xfer) */
return -EOPNOTSUPP;
default:
dev_err(dev, "Unsupported smbus protocol %d\n", f7_msg->size);
return -EOPNOTSUPP;
}
f7_msg->buf = f7_msg->smbus_buf;
/* Configure PEC */
if ((flags & I2C_CLIENT_PEC) && f7_msg->size != I2C_SMBUS_QUICK) {
cr1 |= STM32F7_I2C_CR1_PECEN;
cr2 |= STM32F7_I2C_CR2_PECBYTE;
if (!f7_msg->read_write)
f7_msg->count++;
} else {
cr1 &= ~STM32F7_I2C_CR1_PECEN;
cr2 &= ~STM32F7_I2C_CR2_PECBYTE;
}
/* Set number of bytes to be transferred */
cr2 &= ~(STM32F7_I2C_CR2_NBYTES_MASK | STM32F7_I2C_CR2_RELOAD);
cr2 |= STM32F7_I2C_CR2_NBYTES(f7_msg->count);
/* Enable NACK, STOP, error and transfer complete interrupts */
cr1 |= STM32F7_I2C_CR1_ERRIE | STM32F7_I2C_CR1_TCIE |
STM32F7_I2C_CR1_STOPIE | STM32F7_I2C_CR1_NACKIE;
/* Clear DMA req and TX/RX interrupt */
cr1 &= ~(STM32F7_I2C_CR1_RXIE | STM32F7_I2C_CR1_TXIE |
STM32F7_I2C_CR1_RXDMAEN | STM32F7_I2C_CR1_TXDMAEN);
/* Configure DMA or enable RX/TX interrupt */
i2c_dev->use_dma = false;
if (i2c_dev->dma && f7_msg->count >= STM32F7_I2C_DMA_LEN_MIN) {
ret = stm32_i2c_prep_dma_xfer(i2c_dev->dev, i2c_dev->dma,
cr2 & STM32F7_I2C_CR2_RD_WRN,
f7_msg->count, f7_msg->buf,
stm32f7_i2c_dma_callback,
i2c_dev);
if (!ret)
i2c_dev->use_dma = true;
else
dev_warn(i2c_dev->dev, "can't use DMA\n");
}
if (!i2c_dev->use_dma) {
if (cr2 & STM32F7_I2C_CR2_RD_WRN)
cr1 |= STM32F7_I2C_CR1_RXIE;
else
cr1 |= STM32F7_I2C_CR1_TXIE;
} else {
if (cr2 & STM32F7_I2C_CR2_RD_WRN)
cr1 |= STM32F7_I2C_CR1_RXDMAEN;
else
cr1 |= STM32F7_I2C_CR1_TXDMAEN;
}
/* Set Start bit */
cr2 |= STM32F7_I2C_CR2_START;
i2c_dev->master_mode = true;
/* Write configurations registers */
writel_relaxed(cr1, base + STM32F7_I2C_CR1);
writel_relaxed(cr2, base + STM32F7_I2C_CR2);
return 0;
}