Beispiel #1
0
static int i2c_qmsi_transfer(struct device *dev, struct i2c_msg *msgs,
			     u8_t num_msgs, u16_t addr)
{
	struct i2c_qmsi_driver_data *driver_data = GET_DRIVER_DATA(dev);
	qm_i2c_t instance = GET_CONTROLLER_INSTANCE(dev);
	int rc;

	__ASSERT_NO_MSG(msgs);
	if (!num_msgs) {
		return 0;
	}

	device_busy_set(dev);

	for (int i = 0; i < num_msgs; i++) {
		u8_t op =  msgs[i].flags & I2C_MSG_RW_MASK;
		bool stop = (msgs[i].flags & I2C_MSG_STOP) == I2C_MSG_STOP;
		qm_i2c_transfer_t xfer = { 0 };
		if (op == I2C_MSG_WRITE) {
			xfer.tx = msgs[i].buf;
			xfer.tx_len = msgs[i].len;
		} else {
			xfer.rx = msgs[i].buf;
			xfer.rx_len = msgs[i].len;
		}

		xfer.callback = transfer_complete;
		xfer.callback_data = dev;
		xfer.stop = stop;

		k_sem_take(&driver_data->sem, K_FOREVER);
		rc = qm_i2c_master_irq_transfer(instance, &xfer, addr);
		k_sem_give(&driver_data->sem);

		if (rc != 0) {
			device_busy_clear(dev);
			return -EIO;
		}

		/* Block current thread until the I2C transfer completes. */
		k_sem_take(&driver_data->device_sync_sem, K_FOREVER);

		if (driver_data->transfer_status != 0) {
			device_busy_clear(dev);
			return -EIO;
		}
	}

	device_busy_clear(dev);

	return 0;
}
Beispiel #2
0
static int i2c_dw_transfer(struct device *dev,
			   struct i2c_msg *msgs, uint8_t num_msgs,
			   uint16_t slave_address)
{
	struct i2c_dw_rom_config const * const rom = dev->config->config_info;
	struct i2c_dw_dev_config * const dw = dev->driver_data;
	struct i2c_msg *cur_msg = msgs;
	uint8_t msg_left = num_msgs;
	uint8_t pflags;
	int ret;

	volatile struct i2c_dw_registers * const regs =
		(struct i2c_dw_registers *)rom->base_address;

	/* Why bother processing no messages */
	if (!msgs || !num_msgs) {
		return -ENOTSUP;
	}

	/* First step, check if there is current activity */
	if ((regs->ic_status.bits.activity) || (dw->state & I2C_DW_BUSY)) {
		return -EIO;
	}

	dw->state |= I2C_DW_BUSY;

	ret = _i2c_dw_setup(dev, slave_address);
	if (ret) {
		dw->state = I2C_DW_STATE_READY;
		return ret;
	}

	/* Enable controller */
	regs->ic_enable.bits.enable = 1;

	/*
	 * While waiting at device_sync_call_wait(), kernel can switch to idle
	 * task which in turn can call _sys_soc_suspend() hook of Power
	 * Management App (PMA).
	 * device_busy_set() call here, would indicate to PMA that it should not
	 * execute PM policies that would turn off this ip block, causing an
	 * ongoing hw transaction to be left in an inconsistent state.
	 * Note : This is just a sample to show a possible use of the API, it is
	 * upto the driver expert to see, if he actually needs it here, or
	 * somewhere else, or not needed as the driver's suspend()/resume()
	 * can handle everything
	 */
	device_busy_set(dev);

		/* Process all the messages */
	while (msg_left > 0) {
		pflags = dw->xfr_flags;

		dw->xfr_buf = cur_msg->buf;
		dw->xfr_len = cur_msg->len;
		dw->xfr_flags = cur_msg->flags;
		dw->rx_pending = 0;

		/* Need to RESTART if changing transfer direction */
		if ((pflags & I2C_MSG_RW_MASK)
		    != (dw->xfr_flags & I2C_MSG_RW_MASK)) {
			dw->xfr_flags |= I2C_MSG_RESTART;
		}

		/* Send STOP if this is the last message */
		if (msg_left == 1) {
			dw->xfr_flags |= I2C_MSG_STOP;
		}

		dw->state &= ~(I2C_DW_CMD_SEND | I2C_DW_CMD_RECV);

		if ((dw->xfr_flags & I2C_MSG_RW_MASK) == I2C_MSG_WRITE) {
			dw->state |= I2C_DW_CMD_SEND;
			dw->request_bytes = 0;
		} else {
			dw->state |= I2C_DW_CMD_RECV;
			dw->request_bytes = dw->xfr_len;
		}

		/* Enable interrupts to trigger ISR */
		if (regs->ic_con.bits.master_mode) {
			/* Enable necessary interrupts */
			regs->ic_intr_mask.raw = (DW_ENABLE_TX_INT_I2C_MASTER |
						  DW_ENABLE_RX_INT_I2C_MASTER);
		} else {
			/* Enable necessary interrupts */
			regs->ic_intr_mask.raw = DW_ENABLE_TX_INT_I2C_SLAVE;
		}

		/* Wait for transfer to be done */
		device_sync_call_wait(&dw->sync);

		if (dw->state & I2C_DW_CMD_ERROR) {
			ret = -EIO;
			break;
		}

		/* Something wrong if there is something left to do */
		if (dw->xfr_len > 0) {
			ret = -EIO;
			break;
		}

		cur_msg++;
		msg_left--;
	}

	device_busy_clear(dev);

	dw->state = I2C_DW_STATE_READY;

	return ret;
}
Beispiel #3
0
static int spi_qmsi_transceive(struct device *dev,
			       const void *tx_buf, u32_t tx_buf_len,
			       void *rx_buf, u32_t rx_buf_len)
{
	const struct spi_qmsi_config *spi_config = dev->config->config_info;
	qm_spi_t spi = spi_config->spi;
	struct spi_qmsi_runtime *context = dev->driver_data;
	qm_spi_config_t *cfg = &context->cfg;
	u8_t dfs = frame_size_to_dfs(cfg->frame_size);
	qm_spi_async_transfer_t *xfer;
	int rc;

	k_sem_take(&context->sem, K_FOREVER);
	if (pending_transfers[spi].dev) {
		k_sem_give(&context->sem);
		return -EBUSY;
	}
	pending_transfers[spi].dev = dev;
	k_sem_give(&context->sem);

	device_busy_set(dev);

	xfer = &pending_transfers[spi].xfer;

	xfer->rx = rx_buf;
	xfer->rx_len = rx_buf_len / dfs;
	/* This cast is necessary to drop the "const" modifier, since QMSI xfer
	 * does not take a const pointer.
	 */
	xfer->tx = (u8_t *)tx_buf;
	xfer->tx_len = tx_buf_len / dfs;
	xfer->callback_data = dev;
	xfer->callback = transfer_complete;

	if (tx_buf_len == 0) {
		cfg->transfer_mode = QM_SPI_TMOD_RX;
	} else if (rx_buf_len == 0) {
		cfg->transfer_mode = QM_SPI_TMOD_TX;
	} else {
		/* FIXME: QMSI expects rx_buf_len and tx_buf_len to
		 * have the same size.
		 */
		cfg->transfer_mode = QM_SPI_TMOD_TX_RX;
	}

	if (context->loopback) {
		QM_SPI[spi]->ctrlr0 |= BIT(11);
	}

	rc = qm_spi_set_config(spi, cfg);
	if (rc != 0) {
		device_busy_clear(dev);
		return -EINVAL;
	}

	spi_control_cs(dev, true);

	rc = qm_spi_irq_transfer(spi, xfer);
	if (rc != 0) {
		spi_control_cs(dev, false);
		device_busy_clear(dev);
		return -EIO;
	}
	k_sem_take(&context->device_sync_sem, K_FOREVER);

	device_busy_clear(dev);

	return context->rc ? -EIO : 0;
}