blob: d0a70ddfa95168d47baa5649a1dc62a85556986f [file] [log] [blame]
/*
* Chromium OS mkbp driver - SPI interface
*
* Copyright (c) 2012 The Chromium OS Authors.
* See file CREDITS for list of people who contributed to this
* project.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
/*
* The Matrix Keyboard Protocol driver handles talking to the keyboard
* controller chip. Mostly this is for keyboard functions, but some other
* things have slipped in, so we provide generic services to talk to the
* KBC.
*/
#include <common.h>
#include <mkbp.h>
#include <spi.h>
#ifdef DEBUG_TRACE
#define debug_trace(fmt, b...) debug(fmt, #b)
#else
#define debug_trace(fmt, b...)
#endif
/**
* Send a command to a LPC MKBP device and return the reply.
*
* The device's internal input/output buffers are used.
*
* @param dev MKBP device
* @param cmd Command to send (EC_CMD_...)
* @param cmd_version Version of command to send (EC_VER_...)
* @param dout Output data (may be NULL If dout_len=0)
* @param dout_len Size of output data in bytes
* @param din Response data (may be NULL If din_len=0)
* @param din_len Maximum size of response in bytes
* @return number of bytes in response, or -1 on error
*/
int mkbp_spi_command(struct mkbp_dev *dev, uint8_t cmd, int cmd_version,
const uint8_t *dout, int dout_len,
uint8_t *din, int din_len)
{
int in_bytes = din_len + MSG_PROTO_BYTES;
const uint8_t *p, *end;
int len = 0;
int rv;
/*
* Sanity-check input size to make sure it plus transaction overhead
* fits in the internal device buffer.
*/
if (in_bytes > sizeof(dev->din)) {
debug("%s: Cannot receive %d bytes\n", __func__, din_len);
return -1;
}
/* Clear input buffer so we don't get false hits for MSG_HEADER */
memset(dev->din, '\0', in_bytes);
if (spi_claim_bus(dev->spi)) {
debug("%s: Cannot claim SPI bus\n", __func__);
return -1;
}
/* Send command */
#ifdef CONFIG_NEW_SPI_XFER
rv = spi_xfer(dev->spi, &cmd, 8, dev->din, 8));
#else
rv = spi_xfer(dev->spi, 8, &cmd, dev->din,
SPI_XFER_BEGIN | SPI_XFER_END);
#endif
if (rv) {
debug("%s: Cannot send command via SPI\n", __func__);
spi_release_bus(dev->spi);
return -1;
}
/* Send output data and receive input data */
#ifdef CONFIG_NEW_SPI_XFER
rv = spi_xfer(dev->spi, dout, dout_len * 8,
dev->din, in_bytes * 8)) {
#else
/*
* TODO(sjg): Old SPI XFER doesn't support sending params before
* reading response, so this will probably clock the wrong amount of
* data.
*/
rv = spi_xfer(dev->spi, max(dout_len, in_bytes) * 8,
dev->dout, dev->din,
SPI_XFER_BEGIN | SPI_XFER_END);
#endif
spi_release_bus(dev->spi);
if (rv) {
debug("%s: Cannot complete SPI transfer\n", __func__);
return -1;
}
/* Scan to start of reply */
for (p = dev->din, end = p + in_bytes; p < end; p++) {
if (*p == MSG_HEADER)
break;
}
if (end - p > 2)
len = p[1] + (p[2] << 8);
else
debug("%s: Message header not found\n", __func__);
if (!len || len > end - p) {
debug("%s: Message received is too short, len=%d, bytes=%d\n",
__func__, len, end - p);
len = -1;
}
if (len > 0) {
p += MSG_HEADER_BYTES;
len -= MSG_PROTO_BYTES; /* remove header, checksum, trailer */
/* Response code is first byte of message */
if (p[0] != EC_RES_SUCCESS)
return -(int)(p[0]);
/* Anything else is the response data */
len = min(len - 1, din_len);
if (len)
memcpy(din, p + 1, len);
}
return len;
}
/**
* Initialize SPI protocol.
*
* @param dev MKBP device
* @param blob Device tree blob
* @return 0 if ok, -1 on error
*/
int mkbp_spi_init(struct mkbp_dev *dev, const void *blob)
{
/* Decode interface-specific FDT params */
dev->max_frequency = fdtdec_get_int(blob, dev->node,
"spi-max-frequency", 500000);
dev->cs = fdtdec_get_int(blob, dev->node, "reg", 0);
dev->spi = spi_setup_slave_fdt(blob, dev->parent_node,
dev->cs, dev->max_frequency, 0);
if (!dev->spi) {
debug("%s: Could not setup SPI slave\n", __func__);
return -1;
}
dev->cmd_version_is_supported = 0;
return 0;
}