summaryrefslogtreecommitdiffstats
path: root/sys/boot/arm/at91/libat91/eeprom.c
blob: 8af411ba31fe6f2d603a98097d818f291644078b (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
/******************************************************************************
 *
 * Filename: eeprom.c
 *
 * Instantiation of eeprom routines
 *
 * Revision information:
 *
 * 28AUG2004	kb_admin	initial creation - adapted from Atmel sources
 * 12JAN2005	kb_admin	fixed clock generation, write polling, init
 *
 * BEGIN_KBDD_BLOCK
 * No warranty, expressed or implied, is included with this software.  It is
 * provided "AS IS" and no warranty of any kind including statutory or aspects
 * relating to merchantability or fitness for any purpose is provided.  All
 * intellectual property rights of others is maintained with the respective
 * owners.  This software is not copyrighted and is intended for reference
 * only.
 * END_BLOCK
 *
 * $FreeBSD$
 *****************************************************************************/

#include "at91rm9200_lowlevel.h"
#include "at91rm9200.h"
#include "lib.h"

/******************************* GLOBALS *************************************/


/*********************** PRIVATE FUNCTIONS/DATA ******************************/


/* Use a macro to calculate the TWI clock generator value to save code space. */
#define AT91C_TWSI_CLOCK	100000
#define TWSI_EEPROM_ADDRESS	0x50

#define TWI_CLK_BASE_DIV	((AT91C_MASTER_CLOCK/(4*AT91C_TWSI_CLOCK)) - 2)
#define SET_TWI_CLOCK	((0x00010000) | (TWI_CLK_BASE_DIV) | (TWI_CLK_BASE_DIV << 8))


/*************************** GLOBAL FUNCTIONS ********************************/


/*
 * .KB_C_FN_DEFINITION_START
 * void InitEEPROM(void)
 *  This global function initializes the EEPROM interface (TWI).  Intended
 * to be called a single time.
 * .KB_C_FN_DEFINITION_END
 */
void
InitEEPROM(void)
{

	AT91PS_TWI twiPtr = (AT91PS_TWI)AT91C_BASE_TWI;

	AT91PS_PIO pPio = (AT91PS_PIO)AT91C_BASE_PIOA;
	AT91PS_PMC pPMC = (AT91PS_PMC)AT91C_BASE_PMC;

	pPio->PIO_ASR = AT91C_PA25_TWD | AT91C_PA26_TWCK;
	pPio->PIO_PDR = AT91C_PA25_TWD | AT91C_PA26_TWCK;

	pPio->PIO_MDDR = ~AT91C_PA25_TWD;
	pPio->PIO_MDER = AT91C_PA25_TWD;

	pPMC->PMC_PCER = 1u << AT91C_ID_TWI;

	twiPtr->TWI_IDR = 0xffffffffu;
	twiPtr->TWI_CR = AT91C_TWI_SWRST;
	twiPtr->TWI_CR = AT91C_TWI_MSEN | AT91C_TWI_SVDIS;

	twiPtr->TWI_CWGR = SET_TWI_CLOCK;
}


/*
 * .KB_C_FN_DEFINITION_START
 * void ReadEEPROM(unsigned ee_addr, char *data_addr, unsigned size)
 *  This global function reads data from the eeprom at ee_addr storing data
 * to data_addr for size bytes.  Assume the TWI has been initialized.
 * This function does not utilize the page read mode to simplify the code.
 * .KB_C_FN_DEFINITION_END
 */
int
ReadEEPROM(unsigned ee_off, char *data_addr, unsigned size)
{
	const AT91PS_TWI 	twiPtr = AT91C_BASE_TWI;
	unsigned int status;
	unsigned int count;

	status = twiPtr->TWI_SR;
	status = twiPtr->TWI_RHR;

	// Set the TWI Master Mode Register
	twiPtr->TWI_MMR = (TWSI_EEPROM_ADDRESS << 16) |
	    AT91C_TWI_IADRSZ_2_BYTE | AT91C_TWI_MREAD;

	// Set TWI Internal Address Register
	twiPtr->TWI_IADR = ee_off;

	// Start transfer
	twiPtr->TWI_CR = AT91C_TWI_START;

	status = twiPtr->TWI_SR;

	while (size-- > 1){
		// Wait RHR Holding register is full
		count = 1000000;
		while (!(twiPtr->TWI_SR & AT91C_TWI_RXRDY) && --count > 0)
			continue;
		if (count <= 0)
			return -1;

		// Read byte
		*(data_addr++) = twiPtr->TWI_RHR;
	}

	twiPtr->TWI_CR = AT91C_TWI_STOP;

	status = twiPtr->TWI_SR;

	// Wait transfer is finished
	while (!(twiPtr->TWI_SR & AT91C_TWI_TXCOMP))
		continue;

	// Read last byte
	*data_addr = twiPtr->TWI_RHR;
	return 0;
}


/*
 * .KB_C_FN_DEFINITION_START
 * void WriteEEPROM(unsigned ee_off, char *data_addr, unsigned size)
 *  This global function writes data to the eeprom at ee_off using data
 * from data_addr for size bytes.  Assume the TWI has been initialized.
 * This function does not utilize the page write mode as the write time is
 * much greater than the time required to access the device for byte-write
 * functionality.  This allows the function to be much simpler.
 * .KB_C_FN_DEFINITION_END
 */
void
WriteEEPROM(unsigned ee_off, char *data_addr, unsigned size)
{
	const AT91PS_TWI 	twiPtr = AT91C_BASE_TWI;
	unsigned		status;
	char			test_data;

	while (size--) {
		if (!(ee_off & 0x3f))
			putchar('.');

		// Set the TWI Master Mode Register
		twiPtr->TWI_MMR = ((TWSI_EEPROM_ADDRESS << 16) |
		    AT91C_TWI_IADRSZ_2_BYTE) & ~AT91C_TWI_MREAD;

		// Set TWI Internal Address Register
		twiPtr->TWI_IADR = ee_off++;

		status = twiPtr->TWI_SR;

		twiPtr->TWI_THR = *(data_addr++);

		twiPtr->TWI_CR = AT91C_TWI_START;

		// Wait transfer is finished
		while (!(twiPtr->TWI_SR & AT91C_TWI_TXRDY))
			continue;

		twiPtr->TWI_CR = AT91C_TWI_STOP;

		status = twiPtr->TWI_SR;

		// Wait transfer is finished
		while (!(twiPtr->TWI_SR & AT91C_TWI_TXCOMP))
			continue;

		// wait for write operation to complete
		ReadEEPROM(ee_off, &test_data, 1);
	}

	putchar('\r');
	putchar('\n');
}
OpenPOWER on IntegriCloud