static int stm32f7_i2c_smbus_xfer_msg()

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;
}