Example #1
0
/**
 * cros_ec_cmd_xfer_spi - Transfer a message over SPI and receive the reply
 *
 * @ec_dev: ChromeOS EC device
 * @ec_msg: Message to transfer
 */
static int cros_ec_cmd_xfer_spi(struct cros_ec_device *ec_dev,
				struct cros_ec_command *ec_msg)
{
	struct cros_ec_spi *ec_spi = ec_dev->priv;
	struct spi_transfer trans;
	struct spi_message msg;
	int i, len;
	u8 *ptr;
	int sum;
	int ret = 0, final_ret;

	/*
	 * We have the shared ec_dev buffer plus we do lots of separate spi_sync
	 * calls, so we need to make sure only one person is using this at a
	 * time.
	 */
	mutex_lock(&ec_spi->lock);

	len = cros_ec_prepare_tx(ec_dev, ec_msg);
	dev_dbg(ec_dev->dev, "prepared, len=%d\n", len);

	/* If it's too soon to do another transaction, wait */
	if (ec_spi->last_transfer_ns) {
		unsigned long delay;	/* The delay completed so far */

		delay = ktime_get_ns() - ec_spi->last_transfer_ns;
		if (delay < EC_SPI_RECOVERY_TIME_NS)
			ndelay(EC_SPI_RECOVERY_TIME_NS - delay);
	}

	/* Transmit phase - send our message */
	debug_packet(ec_dev->dev, "out", ec_dev->dout, len);
	memset(&trans, 0, sizeof(trans));
	trans.tx_buf = ec_dev->dout;
	trans.len = len;
	trans.cs_change = 1;
	spi_message_init(&msg);
	spi_message_add_tail(&trans, &msg);
	ret = spi_sync(ec_spi->spi, &msg);

	/* Get the response */
	if (!ret) {
		ret = cros_ec_spi_receive_response(ec_dev,
				ec_msg->insize + EC_MSG_TX_PROTO_BYTES);
	} else {
		dev_err(ec_dev->dev, "spi transfer failed: %d\n", ret);
	}

	/*
	 * Turn off CS, possibly adding a delay to ensure the rising edge
	 * doesn't come too soon after the end of the data.
	 */
	spi_message_init(&msg);
	memset(&trans, 0, sizeof(trans));
	trans.delay_usecs = ec_spi->end_of_msg_delay;
	spi_message_add_tail(&trans, &msg);

	final_ret = spi_sync(ec_spi->spi, &msg);
	ec_spi->last_transfer_ns = ktime_get_ns();
	if (!ret)
		ret = final_ret;
	if (ret < 0) {
		dev_err(ec_dev->dev, "spi transfer failed: %d\n", ret);
		goto exit;
	}

	ptr = ec_dev->din;

	/* check response error code */
	ec_msg->result = ptr[0];
	ret = cros_ec_check_result(ec_dev, ec_msg);
	if (ret)
		goto exit;

	len = ptr[1];
	sum = ptr[0] + ptr[1];
	if (len > ec_msg->insize) {
		dev_err(ec_dev->dev, "packet too long (%d bytes, expected %d)",
			len, ec_msg->insize);
		ret = -ENOSPC;
		goto exit;
	}

	/* copy response packet payload and compute checksum */
	for (i = 0; i < len; i++) {
		sum += ptr[i + 2];
		if (ec_msg->insize)
			ec_msg->indata[i] = ptr[i + 2];
	}
	sum &= 0xff;

	debug_packet(ec_dev->dev, "in", ptr, len + 3);

	if (sum != ptr[len + 2]) {
		dev_err(ec_dev->dev,
			"bad packet checksum, expected %02x, got %02x\n",
			sum, ptr[len + 2]);
		ret = -EBADMSG;
		goto exit;
	}

	ret = len;
exit:
	mutex_unlock(&ec_spi->lock);
	return ret;
}
Example #2
0
/**
 * cros_ec_cmd_xfer_spi - Transfer a message over SPI and receive the reply
 *
 * @ec_dev: ChromeOS EC device
 * @ec_msg: Message to transfer
 */
static int cros_ec_cmd_xfer_spi(struct cros_ec_device *ec_dev,
				struct cros_ec_command *ec_msg)
{
	struct cros_ec_spi *ec_spi = ec_dev->priv;
	struct spi_transfer trans;
	struct spi_message msg;
	int i, len;
	u8 *ptr;
	u8 *rx_buf;
	int sum;
	int ret = 0, final_ret;

	len = cros_ec_prepare_tx(ec_dev, ec_msg);
	dev_dbg(ec_dev->dev, "prepared, len=%d\n", len);

	/* If it's too soon to do another transaction, wait */
	if (ec_spi->last_transfer_ns) {
		unsigned long delay;	/* The delay completed so far */

		delay = ktime_get_ns() - ec_spi->last_transfer_ns;
		if (delay < EC_SPI_RECOVERY_TIME_NS)
			ndelay(EC_SPI_RECOVERY_TIME_NS - delay);
	}

	rx_buf = kzalloc(len, GFP_KERNEL);
	if (!rx_buf) {
		ret = -ENOMEM;
		goto exit;
	}

	/* Transmit phase - send our message */
	debug_packet(ec_dev->dev, "out", ec_dev->dout, len);
	memset(&trans, 0, sizeof(trans));
	trans.tx_buf = ec_dev->dout;
	trans.rx_buf = rx_buf;
	trans.len = len;
	trans.cs_change = 1;
	spi_message_init(&msg);
	spi_message_add_tail(&trans, &msg);
	ret = spi_sync(ec_spi->spi, &msg);

	/* Get the response */
	if (!ret) {
		/* Verify that EC can process command */
		for (i = 0; i < len; i++) {
			switch (rx_buf[i]) {
			case EC_SPI_PAST_END:
			case EC_SPI_RX_BAD_DATA:
			case EC_SPI_NOT_READY:
				ret = -EAGAIN;
				ec_msg->result = EC_RES_IN_PROGRESS;
			default:
				break;
			}
			if (ret)
				break;
		}
		if (!ret)
			ret = cros_ec_spi_receive_response(ec_dev,
					ec_msg->insize + EC_MSG_TX_PROTO_BYTES);
	} else {
		dev_err(ec_dev->dev, "spi transfer failed: %d\n", ret);
	}

	final_ret = terminate_request(ec_dev);
	if (!ret)
		ret = final_ret;
	if (ret < 0)
		goto exit;

	ptr = ec_dev->din;

	/* check response error code */
	ec_msg->result = ptr[0];
	ret = cros_ec_check_result(ec_dev, ec_msg);
	if (ret)
		goto exit;

	len = ptr[1];
	sum = ptr[0] + ptr[1];
	if (len > ec_msg->insize) {
		dev_err(ec_dev->dev, "packet too long (%d bytes, expected %d)",
			len, ec_msg->insize);
		ret = -ENOSPC;
		goto exit;
	}

	/* copy response packet payload and compute checksum */
	for (i = 0; i < len; i++) {
		sum += ptr[i + 2];
		if (ec_msg->insize)
			ec_msg->data[i] = ptr[i + 2];
	}
	sum &= 0xff;

	debug_packet(ec_dev->dev, "in", ptr, len + 3);

	if (sum != ptr[len + 2]) {
		dev_err(ec_dev->dev,
			"bad packet checksum, expected %02x, got %02x\n",
			sum, ptr[len + 2]);
		ret = -EBADMSG;
		goto exit;
	}

	ret = len;
exit:
	kfree(rx_buf);
	if (ec_msg->command == EC_CMD_REBOOT_EC)
		msleep(EC_REBOOT_DELAY_MS);

	return ret;
}
Example #3
0
/**
 * cros_ec_command_spi_xfer - Transfer a message over SPI and receive the reply
 *
 * @ec_dev: ChromeOS EC device
 * @ec_msg: Message to transfer
 */
static int cros_ec_command_spi_xfer(struct cros_ec_device *ec_dev,
				    struct cros_ec_msg *ec_msg)
{
	struct cros_ec_spi *ec_spi = ec_dev->priv;
	struct spi_transfer trans;
	struct spi_message msg;
	int i, len;
	u8 *ptr;
	int sum;
	int ret = 0, final_ret;
	struct timespec ts;

	len = cros_ec_prepare_tx(ec_dev, ec_msg);
	dev_dbg(ec_dev->dev, "prepared, len=%d\n", len);

	/* If it's too soon to do another transaction, wait */
	if (ec_spi->last_transfer_ns) {
		struct timespec ts;
		unsigned long delay;	/* The delay completed so far */

		ktime_get_ts(&ts);
		delay = timespec_to_ns(&ts) - ec_spi->last_transfer_ns;
		if (delay < EC_SPI_RECOVERY_TIME_NS)
			ndelay(delay);
	}

	/* Transmit phase - send our message */
	debug_packet(ec_dev->dev, "out", ec_dev->dout, len);
	memset(&trans, '\0', sizeof(trans));
	trans.tx_buf = ec_dev->dout;
	trans.len = len;
	trans.cs_change = 1;
	spi_message_init(&msg);
	spi_message_add_tail(&trans, &msg);
	ret = spi_sync(ec_spi->spi, &msg);

	/* Get the response */
	if (!ret) {
		ret = cros_ec_spi_receive_response(ec_dev,
				ec_msg->in_len + EC_MSG_TX_PROTO_BYTES);
	} else {
		dev_err(ec_dev->dev, "spi transfer failed: %d\n", ret);
	}

	/* turn off CS */
	spi_message_init(&msg);
	final_ret = spi_sync(ec_spi->spi, &msg);
	ktime_get_ts(&ts);
	ec_spi->last_transfer_ns = timespec_to_ns(&ts);
	if (!ret)
		ret = final_ret;
	if (ret < 0) {
		dev_err(ec_dev->dev, "spi transfer failed: %d\n", ret);
		return ret;
	}

	/* check response error code */
	ptr = ec_dev->din;
	if (ptr[0]) {
		dev_warn(ec_dev->dev, "command 0x%02x returned an error %d\n",
			 ec_msg->cmd, ptr[0]);
		debug_packet(ec_dev->dev, "in_err", ptr, len);
		return -EINVAL;
	}
	len = ptr[1];
	sum = ptr[0] + ptr[1];
	if (len > ec_msg->in_len) {
		dev_err(ec_dev->dev, "packet too long (%d bytes, expected %d)",
			len, ec_msg->in_len);
		return -ENOSPC;
	}

	/* copy response packet payload and compute checksum */
	for (i = 0; i < len; i++) {
		sum += ptr[i + 2];
		if (ec_msg->in_len)
			ec_msg->in_buf[i] = ptr[i + 2];
	}
	sum &= 0xff;

	debug_packet(ec_dev->dev, "in", ptr, len + 3);

	if (sum != ptr[len + 2]) {
		dev_err(ec_dev->dev,
			"bad packet checksum, expected %02x, got %02x\n",
			sum, ptr[len + 2]);
		return -EBADMSG;
	}

	return 0;
}
Example #4
0
/**
 * cros_ec_pkt_xfer_spi - Transfer a packet over SPI and receive the reply
 *
 * @ec_dev: ChromeOS EC device
 * @ec_msg: Message to transfer
 */
static int cros_ec_pkt_xfer_spi(struct cros_ec_device *ec_dev,
				struct cros_ec_command *ec_msg)
{
	struct ec_host_request *request;
	struct ec_host_response *response;
	struct cros_ec_spi *ec_spi = ec_dev->priv;
	struct spi_transfer trans, trans_delay;
	struct spi_message msg;
	int i, len;
	u8 *ptr;
	u8 *rx_buf;
	u8 sum;
	int ret = 0, final_ret;

	len = cros_ec_prepare_tx(ec_dev, ec_msg);
	request = (struct ec_host_request *)ec_dev->dout;
	dev_dbg(ec_dev->dev, "prepared, len=%d\n", len);

	/* If it's too soon to do another transaction, wait */
	if (ec_spi->last_transfer_ns) {
		unsigned long delay;	/* The delay completed so far */

		delay = ktime_get_ns() - ec_spi->last_transfer_ns;
		if (delay < EC_SPI_RECOVERY_TIME_NS)
			ndelay(EC_SPI_RECOVERY_TIME_NS - delay);
	}

	rx_buf = kzalloc(len, GFP_KERNEL);
	if (!rx_buf) {
		ret = -ENOMEM;
		goto exit;
	}

	/*
	 * Leave a gap between CS assertion and clocking of data to allow the
	 * EC time to wakeup.
	 */
	spi_message_init(&msg);
	if (ec_spi->start_of_msg_delay) {
		memset(&trans_delay, 0, sizeof(trans_delay));
		trans_delay.delay_usecs = ec_spi->start_of_msg_delay;
		spi_message_add_tail(&trans_delay, &msg);
	}

	/* Transmit phase - send our message */
	memset(&trans, 0, sizeof(trans));
	trans.tx_buf = ec_dev->dout;
	trans.rx_buf = rx_buf;
	trans.len = len;
	trans.cs_change = 1;
	spi_message_add_tail(&trans, &msg);
	ret = spi_sync(ec_spi->spi, &msg);

	/* Get the response */
	if (!ret) {
		/* Verify that EC can process command */
		for (i = 0; i < len; i++) {
			switch (rx_buf[i]) {
			case EC_SPI_PAST_END:
			case EC_SPI_RX_BAD_DATA:
			case EC_SPI_NOT_READY:
				ret = -EAGAIN;
				ec_msg->result = EC_RES_IN_PROGRESS;
			default:
				break;
			}
			if (ret)
				break;
		}
		if (!ret)
			ret = cros_ec_spi_receive_packet(ec_dev,
					ec_msg->insize + sizeof(*response));
	} else {
		dev_err(ec_dev->dev, "spi transfer failed: %d\n", ret);
	}

	final_ret = terminate_request(ec_dev);
	if (!ret)
		ret = final_ret;
	if (ret < 0)
		goto exit;

	ptr = ec_dev->din;

	/* check response error code */
	response = (struct ec_host_response *)ptr;
	ec_msg->result = response->result;

	ret = cros_ec_check_result(ec_dev, ec_msg);
	if (ret)
		goto exit;

	len = response->data_len;
	sum = 0;
	if (len > ec_msg->insize) {
		dev_err(ec_dev->dev, "packet too long (%d bytes, expected %d)",
			len, ec_msg->insize);
		ret = -EMSGSIZE;
		goto exit;
	}

	for (i = 0; i < sizeof(*response); i++)
		sum += ptr[i];

	/* copy response packet payload and compute checksum */
	memcpy(ec_msg->data, ptr + sizeof(*response), len);
	for (i = 0; i < len; i++)
		sum += ec_msg->data[i];

	if (sum) {
		dev_err(ec_dev->dev,
			"bad packet checksum, calculated %x\n",
			sum);
		ret = -EBADMSG;
		goto exit;
	}

	ret = len;
exit:
	kfree(rx_buf);
	if (ec_msg->command == EC_CMD_REBOOT_EC)
		msleep(EC_REBOOT_DELAY_MS);

	return ret;
}
Example #5
0
static int cros_ec_pkt_xfer_lpc(struct cros_ec_device *ec,
				struct cros_ec_command *msg)
{
	struct ec_host_request *request;
	struct ec_host_response response;
	u8 sum;
	int ret = 0;
	u8 *dout;

	ret = cros_ec_prepare_tx(ec, msg);

	/* Write buffer */
	cros_ec_lpc_write_bytes(EC_LPC_ADDR_HOST_PACKET, ret, ec->dout);

	request = (struct ec_host_request *)ec->dout;

	/* Here we go */
	sum = EC_COMMAND_PROTOCOL_3;
	cros_ec_lpc_write_bytes(EC_LPC_ADDR_HOST_CMD, 1, &sum);

	if (ec_response_timed_out()) {
		dev_warn(ec->dev, "EC responsed timed out\n");
		ret = -EIO;
		goto done;
	}

	/* Check result */
	msg->result = cros_ec_lpc_read_bytes(EC_LPC_ADDR_HOST_DATA, 1, &sum);
	ret = cros_ec_check_result(ec, msg);
	if (ret)
		goto done;

	/* Read back response */
	dout = (u8 *)&response;
	sum = cros_ec_lpc_read_bytes(EC_LPC_ADDR_HOST_PACKET, sizeof(response),
				     dout);

	msg->result = response.result;

	if (response.data_len > msg->insize) {
		dev_err(ec->dev,
			"packet too long (%d bytes, expected %d)",
			response.data_len, msg->insize);
		ret = -EMSGSIZE;
		goto done;
	}

	/* Read response and process checksum */
	sum += cros_ec_lpc_read_bytes(EC_LPC_ADDR_HOST_PACKET +
				      sizeof(response), response.data_len,
				      msg->data);

	if (sum) {
		dev_err(ec->dev,
			"bad packet checksum %02x\n",
			response.checksum);
		ret = -EBADMSG;
		goto done;
	}

	/* Return actual amount of data received */
	ret = response.data_len;
done:
	return ret;
}