315 lines
6.8 KiB
C
315 lines
6.8 KiB
C
/* Copyright 2014 The Chromium OS Authors. All rights reserved.
|
|
* Use of this source code is governed by a BSD-style license that can be
|
|
* found in the LICENSE file.
|
|
*
|
|
* Megachips DisplayPort to HDMI protocol converter / level shifter driver.
|
|
*/
|
|
|
|
#include "config.h"
|
|
#include "console.h"
|
|
#include "common.h"
|
|
#include "ec_commands.h"
|
|
#include "mcdp28x0.h"
|
|
#include "queue.h"
|
|
#include "queue_policies.h"
|
|
#include "timer.h"
|
|
#include "usart-stm32f0.h"
|
|
#include "util.h"
|
|
|
|
#define CPRINTF(format, args...) cprintf(CC_USBPD, format, ## args)
|
|
|
|
static uint8_t mcdp_inbuf[MCDP_INBUF_MAX];
|
|
|
|
#undef MCDP_DEBUG
|
|
|
|
#ifdef MCDP_DEBUG
|
|
static inline void print_buffer(uint8_t *buf, int cnt)
|
|
{
|
|
int i;
|
|
CPRINTF("buf:");
|
|
for (i = 0; i < cnt; i++) {
|
|
if (i && !(i % 4))
|
|
CPRINTF("\n ");
|
|
CPRINTF("[%02d]0x%02x ", i, buf[i]);
|
|
}
|
|
CPRINTF("\n");
|
|
}
|
|
#else
|
|
static inline void print_buffer(uint8_t *buf, int cnt) {}
|
|
#endif
|
|
|
|
static struct usart_config const usart_mcdp;
|
|
|
|
struct queue const usart_mcdp_rx_queue = QUEUE_DIRECT(MCDP_INBUF_MAX,
|
|
uint8_t,
|
|
usart_mcdp.producer,
|
|
null_consumer);
|
|
struct queue const usart_mcdp_tx_queue = QUEUE_DIRECT(MCDP_OUTBUF_MAX,
|
|
uint8_t,
|
|
null_producer,
|
|
usart_mcdp.consumer);
|
|
|
|
static struct usart_config const usart_mcdp = USART_CONFIG(CONFIG_MCDP28X0,
|
|
usart_rx_interrupt,
|
|
usart_tx_interrupt,
|
|
115200,
|
|
0,
|
|
usart_mcdp_rx_queue,
|
|
usart_mcdp_tx_queue);
|
|
|
|
/**
|
|
* Compute checksum.
|
|
*
|
|
* @seed initial value of checksum.
|
|
* @msg message bytes to compute checksum on.
|
|
* @cnt count of message bytes.
|
|
* @return partial checksum.
|
|
*/
|
|
static uint8_t compute_checksum(uint8_t seed, const uint8_t *msg, int cnt)
|
|
{
|
|
int i;
|
|
uint8_t chksum = seed;
|
|
|
|
for (i = 0; i < cnt; i++)
|
|
chksum += msg[i];
|
|
return ~chksum + 1;
|
|
}
|
|
|
|
/**
|
|
* transmit message over serial
|
|
*
|
|
* Packet consists of:
|
|
* msg[0] == length of entire packet
|
|
* msg[1] == 1st message byte (typically command)
|
|
* msg[cnt+1] == last message byte
|
|
* msg[cnt+2] == checksum
|
|
*
|
|
* @msg message bytes
|
|
* @cnt count of message bytes
|
|
* @return zero if success, error code otherwise
|
|
*/
|
|
static int tx_serial(const uint8_t *msg, int cnt)
|
|
{
|
|
uint8_t out = cnt + 2;
|
|
/* 1st byte (not in msg) is always cnt + 2, so seed chksum with that */
|
|
uint8_t chksum = compute_checksum(cnt + 2, msg, cnt);
|
|
|
|
if (queue_add_unit(&usart_mcdp_tx_queue, &out) != 1)
|
|
return MCDP_ERROR_TX_CNT;
|
|
|
|
if (queue_add_units(&usart_mcdp_tx_queue, msg, cnt) != cnt)
|
|
return MCDP_ERROR_TX_BODY;
|
|
|
|
print_buffer((uint8_t *)msg, cnt);
|
|
|
|
if (queue_add_unit(&usart_mcdp_tx_queue, &chksum) != 1)
|
|
return MCDP_ERROR_TX_CHKSUM;
|
|
|
|
return MCDP_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* receive message over serial
|
|
*
|
|
* While definitive documentation is lacking its believed the following receive
|
|
* packet is always true.
|
|
*
|
|
* Packet consists of:
|
|
* msg[0] == length of entire packet
|
|
* msg[1] == 1st message byte (typically command)
|
|
* msg[cnt+2] == last message byte
|
|
* msg[cnt+3] == checksum
|
|
*
|
|
* @msg pointer to buffer to read message into
|
|
* @cnt count of message bytes
|
|
* @return zero if success, error code otherwise
|
|
*/
|
|
static int rx_serial(uint8_t *msg, int cnt)
|
|
{
|
|
size_t read;
|
|
int retry = 2;
|
|
|
|
read = queue_remove_units(&usart_mcdp_rx_queue, msg, cnt);
|
|
while ((read < cnt) && retry) {
|
|
usleep(100*MSEC);
|
|
read += queue_remove_units(&usart_mcdp_rx_queue, msg + read,
|
|
cnt - read);
|
|
retry--;
|
|
}
|
|
|
|
print_buffer(msg, cnt);
|
|
|
|
/* Some response sizes are dynamic so shrink cnt accordingly. */
|
|
if (cnt > msg[0])
|
|
cnt = msg[0];
|
|
|
|
if (msg[cnt-1] != compute_checksum(0, msg, cnt-1))
|
|
return MCDP_ERROR_CHKSUM;
|
|
|
|
if (read != cnt) {
|
|
CPRINTF("rx_serial: read bytes %d != %d cnt\n", read, cnt);
|
|
return MCDP_ERROR_RX_BYTES;
|
|
}
|
|
|
|
return MCDP_SUCCESS;
|
|
}
|
|
|
|
static int rx_serial_ack(void)
|
|
{
|
|
int rv = rx_serial(mcdp_inbuf, 3);
|
|
if (rv)
|
|
return rv;
|
|
|
|
if (mcdp_inbuf[1] != MCDP_CMD_ACK)
|
|
return MCDP_ERROR_RX_ACK;
|
|
|
|
return rv;
|
|
}
|
|
|
|
void mcdp_enable(void)
|
|
{
|
|
usart_init(&usart_mcdp);
|
|
}
|
|
|
|
void mcdp_disable(void)
|
|
{
|
|
usart_shutdown(&usart_mcdp);
|
|
}
|
|
|
|
int mcdp_get_info(struct mcdp_info *info)
|
|
{
|
|
const uint8_t msg[2] = {MCDP_CMD_APPSTEST, 0x28};
|
|
int rv = tx_serial(msg, sizeof(msg));
|
|
|
|
if (rv)
|
|
return rv;
|
|
|
|
rv = rx_serial_ack();
|
|
if (rv)
|
|
return rv;
|
|
|
|
/* chksum is unreliable ... don't check */
|
|
rx_serial(mcdp_inbuf, MCDP_RSP_LEN(MCDP_LEN_GETINFO));
|
|
|
|
memcpy(info, &mcdp_inbuf[2], MCDP_LEN_GETINFO);
|
|
|
|
return rv;
|
|
}
|
|
|
|
#ifdef CONFIG_CMD_MCDP
|
|
static int mcdp_get_dev_id(char *dev, uint8_t dev_id, int dev_cnt)
|
|
{
|
|
uint8_t msg[2];
|
|
int rv;
|
|
msg[0] = MCDP_CMD_GETDEVID;
|
|
msg[1] = dev_id;
|
|
|
|
rv = tx_serial(msg, sizeof(msg));
|
|
if (rv)
|
|
return rv;
|
|
|
|
rv = rx_serial(mcdp_inbuf, sizeof(mcdp_inbuf));
|
|
if (rv)
|
|
return rv;
|
|
|
|
memcpy(dev, &mcdp_inbuf[2], mcdp_inbuf[0] - 3);
|
|
dev[mcdp_inbuf[0] - 3] = '\0';
|
|
return EC_SUCCESS;
|
|
}
|
|
|
|
static int mcdp_appstest(uint8_t cmd, int paramc, char **paramv)
|
|
{
|
|
uint8_t msg[6];
|
|
char *e;
|
|
int i;
|
|
int rv = MCDP_SUCCESS;
|
|
|
|
/* setup any appstest params */
|
|
msg[0] = MCDP_CMD_APPSTESTPARAM;
|
|
for (i = 0; i < paramc; i++) {
|
|
uint32_t param = strtoi(paramv[i], &e, 10);
|
|
if (*e)
|
|
return EC_ERROR_PARAM1;
|
|
msg[1] = i + 1;
|
|
msg[2] = (param >> 24) & 0xff;
|
|
msg[3] = (param >> 16) & 0xff;
|
|
msg[4] = (param >> 8) & 0xff;
|
|
msg[5] = (param >> 0) & 0xff;
|
|
rv = tx_serial(msg, sizeof(msg));
|
|
if (rv)
|
|
return rv;
|
|
|
|
rv = rx_serial_ack();
|
|
if (rv)
|
|
return rv;
|
|
}
|
|
|
|
msg[0] = MCDP_CMD_APPSTEST;
|
|
msg[1] = cmd;
|
|
rv = tx_serial(msg, 2);
|
|
if (rv)
|
|
return rv;
|
|
|
|
rv = rx_serial_ack();
|
|
if (rv)
|
|
return rv;
|
|
|
|
/* magic */
|
|
rx_serial(mcdp_inbuf, sizeof(mcdp_inbuf));
|
|
rx_serial(mcdp_inbuf, sizeof(mcdp_inbuf));
|
|
|
|
return EC_SUCCESS;
|
|
}
|
|
|
|
int command_mcdp(int argc, char **argv)
|
|
{
|
|
int rv = EC_SUCCESS;
|
|
char *e;
|
|
|
|
if (argc < 2)
|
|
return EC_ERROR_PARAM_COUNT;
|
|
|
|
mcdp_enable();
|
|
if (!strncasecmp(argv[1], "info", 4)) {
|
|
struct mcdp_info info;
|
|
rv = mcdp_get_info(&info);
|
|
if (!rv)
|
|
ccprintf("family:%04x chipid:%04x irom:%d.%d.%d "
|
|
"fw:%d.%d.%d\n",
|
|
MCDP_FAMILY(info.family),
|
|
MCDP_CHIPID(info.chipid),
|
|
info.irom.major, info.irom.minor,
|
|
info.irom.build,
|
|
info.fw.major, info.fw.minor, info.fw.build);
|
|
} else if (!strncasecmp(argv[1], "devid", 4)) {
|
|
uint8_t dev_id = strtoi(argv[2], &e, 10);
|
|
char dev[32];
|
|
if (*e)
|
|
return EC_ERROR_PARAM2;
|
|
else
|
|
rv = mcdp_get_dev_id(dev, dev_id, 32);
|
|
if (!rv)
|
|
ccprintf("devid[%d] = %s\n", dev_id, dev);
|
|
} else if (!strncasecmp(argv[1], "appstest", 4)) {
|
|
uint8_t cmd = strtoi(argv[2], &e, 10);
|
|
if (*e)
|
|
return EC_ERROR_PARAM2;
|
|
else
|
|
rv = mcdp_appstest(cmd, argc - 3, &argv[3]);
|
|
if (!rv)
|
|
ccprintf("appstest[%d] completed\n", cmd);
|
|
} else {
|
|
return EC_ERROR_PARAM1;
|
|
}
|
|
|
|
mcdp_disable();
|
|
if (rv)
|
|
ccprintf("mcdp_error:%d\n", rv);
|
|
|
|
return EC_SUCCESS;
|
|
}
|
|
DECLARE_CONSOLE_COMMAND(mcdp, command_mcdp,
|
|
"info|devid <id>|appstest <cmd> [<params>]",
|
|
"USB PD");
|
|
#endif /* CONFIG_CMD_MCDP */
|