summaryrefslogtreecommitdiff
path: root/driver/mcdp28x0.c
blob: de334b5c59deeffb70c5995e5f355fe49eb15589 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
/* Copyright (c) 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 "mcdp28x0.h"
#include "timer.h"
#include "usart-stm32f0.h"
#include "util.h"

#define CPRINTF(format, args...) cprintf(CC_USBPD, format, ## args)

#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

USART_CONFIG(usart_mcdp, CONFIG_MCDP28X0, 115200, MCDP_INBUF_MAX,
	     MCDP_OUTBUF_MAX, NULL, NULL);

/**
 * Compute checksum.
 *
 * @msg message bytes to compute checksum on.
 * @cnt count of message bytes.
 * @return partial checksum.
 */
static uint8_t compute_checksum(const uint8_t *msg, int cnt)
{
	int i;
	/* 1st byte (not in msg) is always cnt + 2, so seed chksum with that */
	uint8_t chksum = cnt + 2;

	for (i = 0; i < cnt; i++)
		chksum += msg[i];
	return ~chksum + 1;
}

/**
 * transmit message over serial
 *
 * Packet consists of:
 *   msg[0]     == length including this byte + cnt + chksum
 *   msg[1]     == 1st message byte
 *   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;
	uint8_t chksum = compute_checksum(msg, cnt);

	if (out_stream_write(&usart_mcdp.out, &out, 1) != 1)
		return EC_ERROR_UNKNOWN;

	if (out_stream_write(&usart_mcdp.out, msg, cnt) != cnt)
		return EC_ERROR_UNKNOWN;

	if (out_stream_write(&usart_mcdp.out, &chksum, 1) != 1)
		return EC_ERROR_UNKNOWN;

	print_buffer(usart_mcdp_tx_buffer, cnt + 2);

	return EC_SUCCESS;
}

/**
 * receive message over serial
 *
 * @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 = in_stream_read(&usart_mcdp.in, msg, cnt);
	while ((read < cnt) && retry) {
		usleep(100*MSEC);
		read += in_stream_read(&usart_mcdp.in, msg + read,
				       cnt - read);
		retry--;
	}

	print_buffer(msg, cnt);

	return !(read == cnt);
}

void mcdp_enable(void)
{
	usart_init(&usart_mcdp);
}

void mcdp_disable(void)
{
	usart_shutdown(&usart_mcdp);
}

int mcdp_get_info(struct mcdp_info  *info)
{
	uint8_t inbuf[MCDP_RSP_LEN(MCDP_LEN_GETINFO)];
	const uint8_t msg[2] = {MCDP_CMD_GETINFO, 0x00}; /* cmd + msg type */

	if (tx_serial(msg, sizeof(msg)))
		return EC_ERROR_UNKNOWN;

	if (rx_serial(inbuf, sizeof(inbuf)))
		return EC_ERROR_UNKNOWN;

	/* check length & returned command ... no checksum provided */
	if (((inbuf[0] - 1) != sizeof(inbuf)) ||
	    (inbuf[1] != MCDP_CMD_GETINFO))
		return EC_ERROR_UNKNOWN;

	memcpy(info, &inbuf[2], MCDP_LEN_GETINFO);

#ifdef MCDP_DEBUG
	CPRINTF("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);
#endif
	return EC_SUCCESS;
}