summaryrefslogtreecommitdiff
path: root/chip/stm32/i2c_ite_flash_support.c
blob: 916a8c364c7bde605e54fcc349df32e2541bdc08 (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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
/* Copyright 2020 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.
 */
/* STM implementation for flashing ITE-based ECs over i2c */

#include "i2c_ite_flash_support.h"
#include "i2c.h"
#include "registers.h"
#include "time.h"

/*
 * As of 2018-11-27 the default for both is 60 bytes.  These larger values allow
 * for reflashing of ITE EC chips over I2C
 * (https://issuetracker.google.com/79684405) in reasonably speedy fashion.  If
 * the EC firmware defaults are ever raised significantly, consider removing
 * these overrides.
 *
 * As of 2018-11-27 the actual maximum write size supported by the I2C-over-USB
 * protocol is (1<<12)-1, and the maximum read size supported is
 * (1<<15)-1.  However compile time assertions require that these values be
 * powers of 2 after overheads are included.  Thus, the write limit set here
 * /should/ be (1<<12)-4 and the read limit should be (1<<15)-6, however those
 * ideal limits are not actually possible because stm32 lacks sufficient
 * spare memory for them.  With symmetrical limits, the maximum that currently
 * fits is (1<<11)-4 write limit and (1<<11)-6 read limit, leaving 1404 bytes of
 * RAM available.
 *
 * However even with a sufficiently large write value here, the maximum that
 * actually works as of 2018-12-03 is 255 bytes.  Additionally, ITE EC firmware
 * image verification requires exactly 256 byte reads.  Thus the only useful
 * powers-of-2-minus-overhead limits to set here are (1<<9)-4 writes and
 * (1<<9)-6 reads, leaving 6012 bytes of RAM available, down from 7356 bytes of
 * RAM available with the default 60 byte limits.
 */
#if CONFIG_USB_I2C_MAX_WRITE_COUNT != ((1<<9) - 4)
#error Must set CONFIG_USB_I2C_MAX_WRITE_COUNT to ((1<<9) - 4)
#endif
#if CONFIG_USB_I2C_MAX_READ_COUNT != ((1<<9) - 6)
#error Must set CONFIG_USB_I2C_MAX_WRITE_COUNT to ((1<<9) - 6)
#endif

/*
 * iteflash requires 256 byte reads for verifying ITE EC firmware.  Without this
 * the limit is CONFIG_I2C_CHIP_MAX_TRANSFER_SIZE which is 255 for STM32F0 due
 * to an 8 bit field, per src/platform/ec/include/config.h comment.
 */
#ifndef CONFIG_I2C_XFER_LARGE_TRANSFER
#error Must define CONFIG_I2C_XFER_LARGE_TRANSFER
#endif

#define KHz 1000
#define MHz (1000 * KHz)

/*
 * These constants are values that one might want to try changing if
 * enable_ite_dfu stops working, or does not work on a new ITE EC chip revision.
 */

#define ITE_DFU_I2C_CMD_ADDR_FLAGS 0x5A
#define ITE_DFU_I2C_DATA_ADDR_FLAGS 0x35

#define SMCLK_WAVEFORM_PERIOD_HZ (100 * KHz)
#define SMDAT_WAVEFORM_PERIOD_HZ (200 * KHz)

#define START_DELAY_MS 5
#define SPECIAL_WAVEFORM_MS 50
#define PLL_STABLE_MS 10

/*
 * Digital line levels to hold before (PRE_) or after (POST_) sending the
 * special waveforms.  0 for low, 1 for high.
 */
#define SMCLK_PRE_LEVEL 0
#define SMDAT_PRE_LEVEL 0
#define SMCLK_POST_LEVEL 0
#define SMDAT_POST_LEVEL 0

/* The caller should hold the i2c_lock() for ite_dfu_config.i2c_port. */
static int ite_i2c_read_register(uint8_t register_offset, uint8_t *output)
{
	/*
	 * Ideally the write and read would be done in one I2C transaction, as
	 * is normally done when reading from the same I2C address that the
	 * write was sent to.  The ITE EC is abnormal in that regard, with its
	 * different 7-bit addresses for writes vs reads.
	 *
	 * i2c_xfer() does not support that.  Its I2C_XFER_START and
	 * I2C_XFER_STOP flag bits do not cleanly support that scenario, they
	 * are for continuing transfers without either of STOP or START
	 * in-between.
	 *
	 * For what it's worth, the iteflash.c FTDI-based implementation of this
	 * does the same thing, issuing a STOP between the write and read.  This
	 * works, even if perhaps it should not.
	 */
	int ret;
	/* Tell the ITE EC which register we want to read. */
	ret = i2c_xfer_unlocked(ite_dfu_config.i2c_port,
				ITE_DFU_I2C_CMD_ADDR_FLAGS,
				&register_offset, sizeof(register_offset),
				NULL, 0, I2C_XFER_SINGLE);
	if (ret != EC_SUCCESS)
		return ret;
	/* Read in the 1 byte register value. */
	ret = i2c_xfer_unlocked(ite_dfu_config.i2c_port,
				ITE_DFU_I2C_DATA_ADDR_FLAGS,
				NULL, 0,
				output, sizeof(*output), I2C_XFER_SINGLE);
	return ret;
}

/* Helper function to read ITE chip ID, for verifying ITE DFU mode. */
static int cprint_ite_chip_id(void)
{
	/*
	 * Per i2c_read8() implementation, use an array even for single byte
	 * reads to ensure alignment for DMA on STM32.
	 */
	uint8_t chipid1[1];
	uint8_t chipid2[1];
	uint8_t chipver[1];

	int ret;
	int chip_version;
	int flash_kb;

	i2c_lock(ite_dfu_config.i2c_port, 1);

	/* Read the CHIPID1 register. */
	ret = ite_i2c_read_register(0x00, chipid1);
	if (ret != EC_SUCCESS)
		goto unlock;

	/* Read the CHIPID2 register. */
	ret = ite_i2c_read_register(0x01, chipid2);
	if (ret != EC_SUCCESS)
		goto unlock;

	/* Read the CHIPVER register. */
	ret = ite_i2c_read_register(0x02, chipver);

unlock:
	i2c_lock(ite_dfu_config.i2c_port, 0);
	if (ret != EC_SUCCESS)
		return ret;

	/*
	 * Compute chip version and embedded flash size from the CHIPVER value.
	 *
	 * Chip version is mapping from bit 3-0
	 * Flash size is mapping from bit 7-4
	 *
	 * Chip Version (bits 3-0)
	 * 0: AX
	 * 1: BX
	 * 2: CX
	 * 3: DX
	 *
	 * CX or prior flash size (bits 7-4)
	 * 0:128KB
	 * 4:192KB
	 * 8:256KB
	 *
	 * DX flash size (bits 7-4)
	 * 0:128KB
	 * 2:192KB
	 * 4:256KB
	 * 6:384KB
	 * 8:512KB
	 */
	chip_version = chipver[0] & 0x07;
	if (chip_version < 0x3) {
		/* Chip version is CX or earlier. */
		switch (chipver[0] >> 4) {
		case 0:
			flash_kb = 128;
			break;
		case 4:
			flash_kb = 192;
			break;
		case 8:
			flash_kb = 256;
			break;
		default:
			flash_kb = -2;
		}
	} else if (chip_version == 0x3) {
		/* Chip version is DX. */
		switch (chipver[0] >> 4) {
		case 0:
			flash_kb = 128;
			break;
		case 2:
			flash_kb = 192;
			break;
		case 4:
			flash_kb = 256;
			break;
		case 6:
			flash_kb = 384;
			break;
		case 8:
			flash_kb = 512;
			break;
		default:
			flash_kb = -3;
		}
	} else {
		/* Unrecognized chip version. */
		flash_kb = -1;
	}

	ccprintf("ITE EC info: CHIPID1=0x%02X CHIPID2=0x%02X CHIPVER=0x%02X ",
		chipid1[0], chipid2[0], chipver[0]);
	ccprintf("version=%d flash_bytes=%d\n", chip_version, flash_kb << 10);

	/*
	 * IT8320_eflash_SMBus_Programming_Guide.pdf says it is an error if
	 * CHIPID1 != 0x83.
	 */
	if (chipid1[0] != 0x83)
		ret = EC_ERROR_HW_INTERNAL;

	return ret;
}

/* Enable ITE direct firmware update (DFU) mode. */
static int command_enable_ite_dfu(int argc, char **argv)
{
	if (argc > 1)
		return EC_ERROR_PARAM_COUNT;

	/* Ensure we can perform the dfu operation */
	if (ite_dfu_config.access_allow && !ite_dfu_config.access_allow())
		return EC_ERROR_ACCESS_DENIED;

	/* Enable peripheral clocks. */
	STM32_RCC_APB2ENR |=
		STM32_RCC_APB2ENR_TIM16EN | STM32_RCC_APB2ENR_TIM17EN;

	/* Reset timer registers which are not otherwise set below. */
	STM32_TIM_CR2(16) = 0x0000;
	STM32_TIM_CR2(17) = 0x0000;
	STM32_TIM_DIER(16) = 0x0000;
	STM32_TIM_DIER(17) = 0x0000;
	STM32_TIM_SR(16) = 0x0000;
	STM32_TIM_SR(17) = 0x0000;
	STM32_TIM_CNT(16) = 0x0000;
	STM32_TIM_CNT(17) = 0x0000;
	STM32_TIM_RCR(16) = 0x0000;
	STM32_TIM_RCR(17) = 0x0000;
	STM32_TIM_DCR(16) = 0x0000;
	STM32_TIM_DCR(17) = 0x0000;
	STM32_TIM_DMAR(16) = 0x0000;
	STM32_TIM_DMAR(17) = 0x0000;

	/* Prescale to 1 MHz and use ARR to achieve NNN KHz periods. */
	/* This approach is seen in STM's documentation. */
	STM32_TIM_PSC(16) = (CPU_CLOCK / MHz) - 1;
	STM32_TIM_PSC(17) = (CPU_CLOCK / MHz) - 1;

	/* Set the waveform periods based on 1 MHz prescale. */
	STM32_TIM_ARR(16) = (MHz / SMCLK_WAVEFORM_PERIOD_HZ) - 1;
	STM32_TIM_ARR(17) = (MHz / SMDAT_WAVEFORM_PERIOD_HZ) - 1;

	/* Set output compare 1 mode to PWM mode 1 and enable preload. */
	STM32_TIM_CCMR1(16) =
		STM32_TIM_CCMR1_OC1M_PWM_MODE_1 | STM32_TIM_CCMR1_OC1PE;
	STM32_TIM_CCMR1(17) =
		STM32_TIM_CCMR1_OC1M_PWM_MODE_1 | STM32_TIM_CCMR1_OC1PE;

	/*
	 * Enable output compare 1 (or its N counterpart). Note that if only
	 * OC1N is enabled, then it is not complemented. From datasheet:
	 * "When only OCxN is enabled (CCxE=0, CCxNE=1), it is not complemented"
	 *
	 * Note: we want the rising edge of SDA to be in the middle of SCL, so
	 * invert the SDA (faster) signal.
	 */
	if (ite_dfu_config.use_complement_timer_channel) {
		STM32_TIM_CCER(16) = STM32_TIM_CCER_CC1NE;
		STM32_TIM_CCER(17) = STM32_TIM_CCER_CC1NE |
				     STM32_TIM_CCER_CC1NP;
	} else {
		STM32_TIM_CCER(16) = STM32_TIM_CCER_CC1E;
		STM32_TIM_CCER(17) = STM32_TIM_CCER_CC1E | STM32_TIM_CCER_CC1P;
	}

	/* Enable main output. */
	STM32_TIM_BDTR(16) = STM32_TIM_BDTR_MOE;
	STM32_TIM_BDTR(17) = STM32_TIM_BDTR_MOE;

	/* Update generation (reinitialize counters). */
	STM32_TIM_EGR(16) = STM32_TIM_EGR_UG;
	STM32_TIM_EGR(17) = STM32_TIM_EGR_UG;

	/* Set duty cycle to 0% or 100%, pinning each channel low or high. */
	STM32_TIM_CCR1(16) = SMCLK_PRE_LEVEL ? 0xFFFF : 0x0000;
	STM32_TIM_CCR1(17) = SMDAT_PRE_LEVEL ? 0xFFFF : 0x0000;

	/* Enable timer counters. */
	STM32_TIM_CR1(16) = STM32_TIM_CR1_CEN;
	STM32_TIM_CR1(17) = STM32_TIM_CR1_CEN;

	/* Set GPIO to alternate mode TIM(16|17)_CH1(N)? */
	gpio_config_pin(MODULE_I2C_TIMERS, ite_dfu_config.scl, 1);
	gpio_config_pin(MODULE_I2C_TIMERS, ite_dfu_config.sda, 1);

	msleep(START_DELAY_MS);

	/* Set pulse width to half of waveform period. */
	STM32_TIM_CCR1(16) = (MHz / SMCLK_WAVEFORM_PERIOD_HZ) / 2;
	STM32_TIM_CCR1(17) = (MHz / SMDAT_WAVEFORM_PERIOD_HZ) / 2;

	msleep(SPECIAL_WAVEFORM_MS);

	/* Set duty cycle to 0% or 100%, pinning each channel low or high. */
	STM32_TIM_CCR1(16) = SMCLK_POST_LEVEL ? 0xFFFF : 0x0000;
	STM32_TIM_CCR1(17) = SMDAT_POST_LEVEL ? 0xFFFF : 0x0000;

	msleep(PLL_STABLE_MS);

	/* Set GPIO back to alternate mode I2C. */
	gpio_config_pin(MODULE_I2C, ite_dfu_config.scl, 1);
	gpio_config_pin(MODULE_I2C, ite_dfu_config.sda, 1);

	/* Disable timer counters. */
	STM32_TIM_CR1(16) = 0x0000;
	STM32_TIM_CR1(17) = 0x0000;

	/* Disable peripheral clocks. */
	STM32_RCC_APB2ENR &=
		~(STM32_RCC_APB2ENR_TIM16EN | STM32_RCC_APB2ENR_TIM17EN);

	return cprint_ite_chip_id();
}
DECLARE_CONSOLE_COMMAND(
	enable_ite_dfu, command_enable_ite_dfu, "",
	"Enable ITE Direct Firmware Update (DFU) mode");

/* Read ITE chip ID.  Can be used to verify ITE DFU mode. */
static int command_get_ite_chipid(int argc, char **argv)
{
	if (argc > 1)
		return EC_ERROR_PARAM_COUNT;

	/* Ensure we can perform the dfu operation */
	if (ite_dfu_config.access_allow && !ite_dfu_config.access_allow())
		return EC_ERROR_ACCESS_DENIED;

	return cprint_ite_chip_id();
}
DECLARE_CONSOLE_COMMAND(
	get_ite_chipid, command_get_ite_chipid, "",
	"Read ITE EC chip ID, version, flash size (must be in DFU mode)");