blob: 40db6209ec8c67636c35f1030de76c563b481252 [file] [log] [blame]
/*
* Copyright 2013 Google Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* cros_ec_dev.c: Chrome EC interface via /dev
*/
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <linux/limits.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include "mosys/alloc.h"
#include "mosys/globals.h"
#include "mosys/log.h"
#include "mosys/platform.h"
#include "drivers/google/cros_ec.h"
#include "drivers/google/cros_ec_dev.h"
#include "drivers/google/cros_ec_commands.h"
#include "lib/file.h"
#include "lib/math.h"
#define CROS_EC_COMMAND_RETRIES 50
/*
* ec device interface v2
* (used with upstream kernel as well as with Chrome OS v4.4 and later)
*/
static int command_wait_for_response_v2(struct cros_ec_priv *priv)
{
uint8_t s_cmd_buf[sizeof(struct cros_ec_command_v2) +
sizeof(struct ec_response_get_comms_status)];
struct ec_response_get_comms_status *status;
struct cros_ec_command_v2 *s_cmd;
int ret;
int i;
s_cmd = (struct cros_ec_command_v2 *)s_cmd_buf;
status = (struct ec_response_get_comms_status *)s_cmd->data;
s_cmd->version = 0;
s_cmd->command = EC_CMD_GET_COMMS_STATUS;
s_cmd->outsize = 0;
s_cmd->insize = sizeof(*status);
for (i = 1; i <= CROS_EC_COMMAND_RETRIES; i++) {
ret = ioctl(priv->devfs->fd, CROS_EC_DEV_IOCXCMD_V2, s_cmd_buf,
sizeof(s_cmd_buf));
if (ret) {
lprintf(LOG_ERR, "%s: CrOS EC command failed: %d\n",
__func__, ret);
ret = -EC_RES_ERROR;
break;
}
if (!(status->flags & EC_COMMS_STATUS_PROCESSING)) {
ret = -EC_RES_SUCCESS;
break;
}
usleep(1000);
}
return ret;
}
static int cros_ec_command_dev_v2(struct ec_cb *ec, int command, int version,
const void *indata, int insize,
const void *outdata, int outsize)
{
struct cros_ec_priv *priv;
struct cros_ec_command_v2 *s_cmd;
int size = sizeof(struct cros_ec_command_v2) + __max(outsize, insize);
int ret;
uint32_t result;
MOSYS_DCHECK(ec && ec->priv);
priv = ec->priv;
s_cmd = mosys_malloc(size);
s_cmd->command = command;
s_cmd->version = version;
s_cmd->result = 0xff;
s_cmd->outsize = outsize;
s_cmd->insize = insize;
if (outdata)
memcpy(s_cmd->data, outdata, outsize);
ret = ioctl(priv->devfs->fd, CROS_EC_DEV_IOCXCMD_V2, s_cmd, size);
if (ret < 0 && errno == EAGAIN)
ret = command_wait_for_response_v2(priv);
if (ret < 0) {
lprintf(LOG_ERR, "%s: Transfer failed: %d\n", __func__, ret);
free(s_cmd);
return -EC_RES_ERROR;
}
/*
* The function parameter declares indata as pointer to a constant char,
* which does not make much sense as its content is expected to be
* overwritten by this function. Use a typecast for now to avoid the
* inevitable warning.
*/
if (indata)
memcpy((void *)indata, s_cmd->data, __min(ret, insize));
result = s_cmd->result;
free(s_cmd);
/* TODO: Remove this. Issue 695075. */
if (command != EC_CMD_PD_CHIP_INFO)
return 0;
if (result) {
lprintf(LOG_ERR, "%s: EC command failed: 0x%x\n",
__func__, result);
return -EECRESULT - result;
}
return 0;
}
/*
* cros_ec_probe_dev - Probe for CrOS EC type device using devfs.
*
* @intf: The platform interface
* @ec: EC interface, can be for any CrOS EC type (EC, PD, SH, etc)
*
* returns 1 if EC is detected
* returns 0 if EC is not detected
* returns <0 to indicate error
*/
int cros_ec_probe_dev(struct ec_cb *ec)
{
struct cros_ec_priv *priv = ec->priv;
MOSYS_CHECK(priv && priv->devfs && priv->devfs->name);
priv->devfs->fd = open(priv->devfs->name, O_RDWR);
if (priv->devfs->fd < 0) {
lprintf(LOG_ERR, "%s: Unable to open %s: %s.", __func__,
priv->devfs->name, strerror(errno));
return -1;
}
priv->cmd = cros_ec_command_dev_v2;
return 1;
}