summaryrefslogtreecommitdiff
path: root/board/zinger/hardware.c
blob: c19e6f0f6be6e9031e8a3c314c0b441daf2a3b31 (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
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
/* 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.
 */
/* Hardware initialization and common functions */

#include "adc.h"
#include "common.h"
#include "cpu.h"
#include "registers.h"
#include "task.h"
#include "timer.h"
#include "util.h"
#include "watchdog.h"

static void system_init(void)
{
	/* Enable access to RCC CSR register and RTC backup registers */
	STM32_PWR_CR |= BIT(8);

	/* switch on LSI */
	STM32_RCC_CSR |= BIT(0);
	/* Wait for LSI to be ready */
	while (!(STM32_RCC_CSR & BIT(1)))
		;
	/* re-configure RTC if needed */
	if ((STM32_RCC_BDCR & 0x00018300) != 0x00008200) {
		/* the RTC settings are bad, we need to reset it */
		STM32_RCC_BDCR |= 0x00010000;
		/* Enable RTC and use LSI as clock source */
		STM32_RCC_BDCR = (STM32_RCC_BDCR & ~0x00018300) | 0x00008200;
	}
}

static void power_init(void)
{
	/* enable SYSCFG, COMP, ADC, SPI1, USART1 */
	STM32_RCC_APB2ENR = 0x00005201;
	/* enable TIM2, TIM3, TIM14, PWR */
	STM32_RCC_APB1ENR = 0x10000103;
	/* enable DMA, SRAM, CRC, GPA, GPB, GPF */
	STM32_RCC_AHBENR  = 0x460045;
}

/* GPIO setting helpers */
#define OUT(n) (1 << ((n) * 2))
#define AF(n) (2 << ((n) * 2))
#define ANALOG(n) (3 << ((n) * 2))
#define HIGH(n) (1 << (n))
#define ODR(n) (1 << (n))
#define HISPEED(n) (3 << ((n) * 2))
#define AFx(n, x) (x << (((n) % 8) * 4))

static void pins_init(void)
{
	/* Pin usage:
	 * PA0  (OUT - GPIO)       : Wakeup on Vnc / Threshold
	 * PA1  (ANALOG - ADC_IN1) : CC sense
	 * PA2  (ANALOG - ADC_IN2) : Current sense
	 * PA3  (ANALOG - ADC_IN3) : Voltage sense
	 * PA4  (OUT - OD GPIO)    : PD TX enable
	 * PA5  (AF0 - SPI1_SCK)   : TX clock in
	 * PA6  (AF0 - SPI1_MISO)  : PD TX
	 * PA7  (AF5 - TIM3_CH2)   : PD RX
	 * PA9  (AF1 - UART1_TX)   : [DEBUG] UART TX
	 * PA10 (AF1 - UART1_RX)   : [DEBUG] UART RX
	 * PA13 (OUT - GPIO)       : voltage select[0]
	 * PA14 (OUT - GPIO)       : voltage select[1]
	 * PB1  (AF0 - TIM14_CH1)  : TX clock out
	 * PF0  (OUT - GPIO)       : LM5050 FET driver off
	 * PF1  (OUT - GPIO)       : discharge FET
	 */

	/*
	 * Clear power control/status register to disable wakeup
	 * pin A0, so that we can change it to an output.
	 */
	STM32_PWR_CSR = 0;
	STM32_PWR_CR |= 0xc;

	STM32_GPIO_ODR(GPIO_A) = HIGH(0) | HIGH(4);
	STM32_GPIO_AFRL(GPIO_A) = AFx(7, 1);
	STM32_GPIO_AFRH(GPIO_A) = AFx(9, 1) | AFx(10, 1);
	STM32_GPIO_OTYPER(GPIO_A) = ODR(4);
	STM32_GPIO_OSPEEDR(GPIO_A) = HISPEED(5) | HISPEED(6) | HISPEED(7);
	STM32_GPIO_MODER(GPIO_A) = OUT(0) | ANALOG(1) | ANALOG(2) | ANALOG(3)
				 | OUT(4) | AF(5) /*| AF(6)*/ | AF(7) | AF(9)
				 | AF(10) | OUT(13) | OUT(14);
	/* set PF0 / PF1 as output */
	STM32_GPIO_ODR(GPIO_F) = 0;
	STM32_GPIO_MODER(GPIO_F) = OUT(0) | OUT(1);
	STM32_GPIO_OTYPER(GPIO_F) = 0;

	/* Set PB1 as AF0 (TIM14_CH1) */
	STM32_GPIO_OSPEEDR(GPIO_B) = HISPEED(1);
	STM32_GPIO_MODER(GPIO_B) = AF(1);
}

static void adc_init(void)
{
	/* Only do the calibration if the ADC is off  */
	if (!(STM32_ADC_CR & 1)) {
		/* ADC calibration */
		STM32_ADC_CR = STM32_ADC_CR_ADCAL; /* set ADCAL = 1, ADC off */
		/* wait for the end of calibration */
		while (STM32_ADC_CR & STM32_ADC_CR_ADCAL)
			;
	}
	/* Single conversion, right aligned, 12-bit */
	STM32_ADC_CFGR1 = BIT(12); /* BIT(15) => AUTOOFF */;
	/* clock is ADCCLK (ADEN must be off when writing this reg) */
	STM32_ADC_CFGR2 = 0;
	/* Sampling time : 71.5 ADC clock cycles, about 5us */
	STM32_ADC_SMPR = 6;

	/*
	 * ADC enable (note: takes 4 ADC clocks between end of calibration
	 * and setting ADEN).
	 */
	STM32_ADC_CR = STM32_ADC_CR_ADEN;
	while (!(STM32_ADC_ISR & STM32_ADC_ISR_ADRDY))
		STM32_ADC_CR = STM32_ADC_CR_ADEN;
	/* Disable interrupts */
	STM32_ADC_IER = 0;
	/* Analog watchdog IRQ */
	task_enable_irq(STM32_IRQ_ADC_COMP);
}

static void uart_init(void)
{
	/* set baudrate */
	STM32_USART_BRR(UARTN_BASE) =
		DIV_ROUND_NEAREST(CPU_CLOCK, CONFIG_UART_BAUD_RATE);
	/* UART enabled, 8 Data bits, oversampling x16, no parity */
	STM32_USART_CR1(UARTN_BASE) =
		STM32_USART_CR1_UE | STM32_USART_CR1_TE | STM32_USART_CR1_RE;
	/* 1 stop bit, no fancy stuff */
	STM32_USART_CR2(UARTN_BASE) = 0x0000;
	/* DMA disabled, special modes disabled, error interrupt disabled */
	STM32_USART_CR3(UARTN_BASE) = 0x0000;
}

static void timers_init(void)
{
	/* TIM2 is a 32-bit free running counter with 1Mhz frequency */
	STM32_TIM_CR2(2) = 0x0000;
	STM32_TIM32_ARR(2) = 0xFFFFFFFF;
	STM32_TIM_PSC(2) = CPU_CLOCK / 1000000 - 1;
	STM32_TIM_EGR(2) = 0x0001; /* Reload the pre-scaler */
	STM32_TIM_CR1(2) = 1;
	STM32_TIM32_CNT(2) = 0x00000000;
	STM32_TIM_SR(2) = 0; /* Clear pending interrupts */
	STM32_TIM_DIER(2) = 1; /* Overflow interrupt */
	task_enable_irq(STM32_IRQ_TIM2);
}

static void irq_init(void)
{
	/* clear all pending interrupts */
	CPU_NVIC_UNPEND(0) = 0xffffffff;
	/* enable global interrupts */
	asm("cpsie i");
}

extern void runtime_init(void);
void hardware_init(void)
{
	uint32_t raw_cause = STM32_RCC_CSR;
	uint32_t pwr_status = STM32_PWR_CSR;

	power_init();

	/* Clear the hardware reset cause by setting the RMVF bit */
	STM32_RCC_CSR |= BIT(24);
	/* Clear SBF in PWR_CSR */
	STM32_PWR_CR |= BIT(3);

	/*
	 * WORKAROUND: as we cannot de-activate the watchdog during
	 * long hibernation, we are woken-up once by the watchdog and
	 * go back to hibernate if we detect that condition, without
	 * watchdog initialized this time.
	 * The RTC deadline (if any) is already set.
	 */
	if ((pwr_status & 0x2) && (raw_cause & 0x60000000))
		__enter_hibernate(0, 0);

	system_init();
	runtime_init(); /* sets clock */
	pins_init();
	uart_init();
	timers_init();
	watchdog_init();
	adc_init();
	irq_init();
}

static int watchdog_ain_id, watchdog_ain_high, watchdog_ain_low;

static int adc_enable_last_watchdog(void)
{
	return adc_enable_watchdog(watchdog_ain_id, watchdog_ain_high,
			    watchdog_ain_low);
}

static inline int adc_watchdog_enabled(void)
{
	return STM32_ADC_CFGR1 & BIT(23);
}

int adc_read_channel(enum adc_channel ch)
{
	int value;
	int watchdog_enabled = adc_watchdog_enabled();

	if (watchdog_enabled)
		adc_disable_watchdog();

	/* Select channel to convert */
	STM32_ADC_CHSELR = 1 << ch;
	/* Clear flags */
	STM32_ADC_ISR = 0x8e;
	/* Start conversion */
	STM32_ADC_CR |= BIT(2); /* ADSTART */
	/* Wait for end of conversion */
	while (!(STM32_ADC_ISR & BIT(2)))
		;
	/* read converted value */
	value = STM32_ADC_DR;

	if (watchdog_enabled)
		adc_enable_last_watchdog();

	return value;
}

int adc_enable_watchdog(int ch, int high, int low)
{
	/* store last watchdog setup */
	watchdog_ain_id = ch;
	watchdog_ain_high = high;
	watchdog_ain_low = low;

	/* Set thresholds */
	STM32_ADC_TR = ((high & 0xfff) << 16) | (low & 0xfff);
	/* Select channel to convert */
	STM32_ADC_CHSELR = 1 << ch;
	/* Clear flags */
	STM32_ADC_ISR = 0x8e;
	/* Set Watchdog enable bit on a single channel / continuous mode */
	STM32_ADC_CFGR1 = (ch << 26) | BIT(23) | BIT(22)
			|  BIT(13) | BIT(12);
	/* Enable watchdog interrupt */
	STM32_ADC_IER = BIT(7);
	/* Start continuous conversion */
	STM32_ADC_CR |= BIT(2); /* ADSTART */

	return EC_SUCCESS;
}

int adc_disable_watchdog(void)
{
	/* Stop on-going conversion */
	STM32_ADC_CR |= BIT(4); /* ADSTP */
	/* Wait for conversion to stop */
	while (STM32_ADC_CR & BIT(4))
		;
	/* CONT=0 -> continuous mode off / Clear Watchdog enable */
	STM32_ADC_CFGR1 = BIT(12);
	/* Disable interrupt */
	STM32_ADC_IER = 0;
	/* Clear flags */
	STM32_ADC_ISR = 0x8e;

	return EC_SUCCESS;
}

/* ---- flash handling ---- */

/*
 * Approximate number of CPU cycles per iteration of the loop when polling
 * the flash status
 */
#define CYCLE_PER_FLASH_LOOP 10

/* Flash page programming timeout.  This is 2x the datasheet max. */
#define FLASH_TIMEOUT_US 16000
#define FLASH_TIMEOUT_LOOP \
	(FLASH_TIMEOUT_US * (CPU_CLOCK / SECOND) / CYCLE_PER_FLASH_LOOP)

/* Flash unlocking keys */
#define KEY1    0x45670123
#define KEY2    0xCDEF89AB

/* Lock bits for FLASH_CR register */
#define PG       BIT(0)
#define PER      BIT(1)
#define OPTPG    BIT(4)
#define OPTER    BIT(5)
#define STRT     BIT(6)
#define CR_LOCK  BIT(7)
#define OPTWRE   BIT(9)

int crec_flash_physical_write(int offset, int size, const char *data)
{
	uint16_t *address = (uint16_t *)(CONFIG_PROGRAM_MEMORY_BASE + offset);
	int res = EC_SUCCESS;
	int i;

	if ((uint32_t)address >
	    CONFIG_PROGRAM_MEMORY_BASE + CONFIG_FLASH_SIZE_BYTES)
		return EC_ERROR_INVAL;

	/* unlock CR if needed */
	if (STM32_FLASH_CR & CR_LOCK) {
		STM32_FLASH_KEYR = KEY1;
		STM32_FLASH_KEYR = KEY2;
	}

	/* Clear previous error status */
	STM32_FLASH_SR = 0x34;
	/* set the ProGram bit */
	STM32_FLASH_CR |= PG;

	for (; size > 0; size -= sizeof(uint16_t)) {
		/* wait to be ready  */
		for (i = 0; (STM32_FLASH_SR & 1) && (i < FLASH_TIMEOUT_LOOP);
		     i++)
			;
		/* write the half word */
		*address++ = data[0] + (data[1] << 8);
		data += 2;
		/* Wait for writes to complete */
		for (i = 0; (STM32_FLASH_SR & 1) && (i < FLASH_TIMEOUT_LOOP);
		     i++)
			;
		if (i == FLASH_TIMEOUT_LOOP) {
			res = EC_ERROR_TIMEOUT;
			goto exit_wr;
		}
		/* Check for error conditions - erase failed, voltage error,
		 * protection error */
		if (STM32_FLASH_SR & 0x14) {
			res = EC_ERROR_UNKNOWN;
			goto exit_wr;
		}
	}

exit_wr:
	STM32_FLASH_CR &= ~PG;
	STM32_FLASH_CR = CR_LOCK;

	return res;
}

int crec_flash_physical_erase(int offset, int size)
{
	int res = EC_SUCCESS;

	/* unlock CR if needed */
	if (STM32_FLASH_CR & CR_LOCK) {
		STM32_FLASH_KEYR = KEY1;
		STM32_FLASH_KEYR = KEY2;
	}

	/* Clear previous error status */
	STM32_FLASH_SR = 0x34;
	/* set PER bit */
	STM32_FLASH_CR |= PER;

	for (; size > 0; size -= CONFIG_FLASH_ERASE_SIZE,
	     offset += CONFIG_FLASH_ERASE_SIZE) {
		int i;
		/* select page to erase */
		STM32_FLASH_AR = CONFIG_PROGRAM_MEMORY_BASE + offset;
		/* set STRT bit : start erase */
		STM32_FLASH_CR |= STRT;


		/* Wait for erase to complete */
		for (i = 0; (STM32_FLASH_SR & 1) && (i < FLASH_TIMEOUT_LOOP);
		     i++)
			;
		if (i == FLASH_TIMEOUT_LOOP) {
			res = EC_ERROR_TIMEOUT;
			goto exit_er;
		}

		/*
		 * Check for error conditions - erase failed, voltage error,
		 * protection error
		 */
		if (STM32_FLASH_SR & 0x14) {
			res = EC_ERROR_UNKNOWN;
			goto exit_er;
		}
	}

exit_er:
	STM32_FLASH_CR &= ~PER;
	STM32_FLASH_CR = CR_LOCK;

	return res;
}

static void unlock_erase_optb(void)
{
	int i;

	/* Clear previous error status */
	STM32_FLASH_SR = 0x34;

	/* wait to be ready  */
	for (i = 0; (STM32_FLASH_SR & 1) && (i < FLASH_TIMEOUT_LOOP); i++)
		;

	/* Unlock the option bytes access */
	if (STM32_FLASH_CR & CR_LOCK) {
		STM32_FLASH_KEYR = KEY1;
		STM32_FLASH_KEYR = KEY2;
	}
	if (!(STM32_FLASH_CR & OPTWRE)) {
		STM32_FLASH_OPTKEYR = KEY1;
		STM32_FLASH_OPTKEYR = KEY2;
	}
	/* Must be set in 2 separate lines. */
	STM32_FLASH_CR |= OPTER;
	STM32_FLASH_CR |= STRT;

	/* wait to be ready  */
	for (i = 0; (STM32_FLASH_SR & 1) && (i < FLASH_TIMEOUT_LOOP); i++)
		;
	/* reset erasing bits */
	STM32_FLASH_CR = OPTWRE;
}


static void write_optb(int byte, uint8_t value)
{
	volatile int16_t *hword = (uint16_t *)(STM32_OPTB_BASE + byte);
	int i;

	/* Clear previous error status */
	STM32_FLASH_SR = 0x34;

	/* set OPTPG bit */
	STM32_FLASH_CR |= OPTPG;

	*hword = ((~value) << STM32_OPTB_COMPL_SHIFT) | value;

	/* reset OPTPG bit */
	STM32_FLASH_CR = OPTWRE;

	/* wait to be ready  */
	for (i = 0; (STM32_FLASH_SR & 1) && (i < FLASH_TIMEOUT_LOOP); i++)
		;
}

void flash_physical_permanent_protect(void)
{
	unlock_erase_optb();
	/* protect the 16KB RO partition against write/erase in WRP0 */
	write_optb(8, 0xF0);
	/* Set RDP to level 1 to prevent disabling the protection */
	write_optb(0, 0x11);
	/* Reset by using OBL_LAUNCH to take changes into account */
	asm volatile("cpsid i");
	STM32_FLASH_CR |= FLASH_CR_OBL_LAUNCH;
	/* Spin and wait for reboot; should never return */
	while (1)
		;
}

int flash_physical_is_permanently_protected(void)
{
	/* if RDP is still at level 0, the flash protection is not in place */
	return (STM32_FLASH_OBR & STM32_FLASH_OBR_RDP_MASK) &&
		/* the low 16KB (RO partition) are write-protected */
		!(STM32_FLASH_WRPR & 0xF);
}