aboutsummaryrefslogtreecommitdiffstats
path: root/firmware/libboard/common/source/boardver_adc.c
blob: f204aa151c384603c25bdb5b8aa8d4e529e3c787 (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
/* This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307, USA
 */
#include "board.h"
#include "boardver_adc.h"

/* FIXME: share this with mode_cardemu.c */
#define UV_PER_LSB	((3300 * 1000) / 4096)
static uint32_t adc2uv(uint16_t adc)
{
	uint32_t uv = (uint32_t) adc * UV_PER_LSB;
	return uv;
}

/***********************************************************************
 * ADC for board version detection
 ***********************************************************************/

#ifdef PIN_VERSION_DET

static int adc_sam3s_reva_errata = 0;

static const Pin pin_version_det = PIN_VERSION_DET;

/* Warning: Don't call this while other code (like the SIM VCC voltage
 * reading) is running.  The idea is you call this once during board
 * startup and cache the result in a (global) variable if you need it
 * later throughout the code */
int get_board_version_adc(void)
{
	uint32_t chip_arch = CHIPID->CHIPID_CIDR & CHIPID_CIDR_ARCH_Msk;
	uint32_t chip_ver = CHIPID->CHIPID_CIDR & CHIPID_CIDR_VERSION_Msk;
	uint16_t sample;
	uint32_t uv;

	PIO_Configure(&pin_version_det, 1);

	PMC_EnablePeripheral(ID_ADC);

	ADC->ADC_CR |= ADC_CR_SWRST;
	if (chip_ver == 0 &&
	    (chip_arch == CHIPID_CIDR_ARCH_SAM3SxA ||
	     chip_arch == CHIPID_CIDR_ARCH_SAM3SxB ||
	     chip_arch == CHIPID_CIDR_ARCH_SAM3SxC)) {
		TRACE_INFO("Enabling Rev.A ADC Errata work-around\r\n");
		adc_sam3s_reva_errata = 1;
	}

	if (adc_sam3s_reva_errata) {
		/* Errata Work-Around to clear EOCx flags */
		volatile uint32_t foo;
		int i;
		for (i = 0; i < 16; i++)
			foo = ADC->ADC_CDR[i];
	}

	/* Initialize ADC for AD2, fADC=48/24=2MHz */
	ADC->ADC_MR = ADC_MR_TRGEN_DIS | ADC_MR_LOWRES_BITS_12 |
		      ADC_MR_SLEEP_NORMAL | ADC_MR_FWUP_OFF |
		      ADC_MR_FREERUN_OFF | ADC_MR_PRESCAL(23) |
		      ADC_MR_STARTUP_SUT8 | ADC_MR_SETTLING(3) |
		      ADC_MR_ANACH_NONE | ADC_MR_TRACKTIM(4) |
		      ADC_MR_TRANSFER(1) | ADC_MR_USEQ_NUM_ORDER;
	/* enable AD2 channel only */
	ADC->ADC_CHER = ADC_CHER_CH2;

	/* Make sure we don't use interrupts as that's what the SIM card
	 * VCC ADC code is using */
	ADC->ADC_IER = 0;
	NVIC_DisableIRQ(ADC_IRQn);

	ADC->ADC_CR |= ADC_CR_START;

	/* busy-wait, actually read the value */
	do { } while (!(ADC->ADC_ISR & ADC_ISR_EOC2));
	/* convert to voltage */
	sample = ADC->ADC_CDR[2];
	uv = adc2uv(sample);
	TRACE_INFO("VERSION_DET ADC=%u => %u uV\r\n", sample, uv);

	/* FIXME: convert to board version based on thresholds */

	return 0;
}

#endif /* PIN_VERSION_DET */