1/******************************************************************************
2 *
3 * Filename: eeprom.c
4 *
5 * Instantiation of eeprom routines
6 *
7 * Revision information:
8 *
9 * 28AUG2004	kb_admin	initial creation - adapted from Atmel sources
10 * 12JAN2005	kb_admin	fixed clock generation, write polling, init
11 *
12 * BEGIN_KBDD_BLOCK
13 * No warranty, expressed or implied, is included with this software.  It is
14 * provided "AS IS" and no warranty of any kind including statutory or aspects
15 * relating to merchantability or fitness for any purpose is provided.  All
16 * intellectual property rights of others is maintained with the respective
17 * owners.  This software is not copyrighted and is intended for reference
18 * only.
19 * END_BLOCK
20 *
21 * $FreeBSD$
22 *****************************************************************************/
23
24#include "at91rm9200_lowlevel.h"
25#include "at91rm9200.h"
26#include "lib.h"
27#include "ee.h"
28
29/******************************* GLOBALS *************************************/
30
31
32/*********************** PRIVATE FUNCTIONS/DATA ******************************/
33
34
35/* Use a macro to calculate the TWI clock generator value to save code space. */
36#define AT91C_TWSI_CLOCK	100000
37#define TWSI_EEPROM_ADDRESS	0x40
38
39#define TWI_CLK_BASE_DIV	((AT91C_MASTER_CLOCK/(4*AT91C_TWSI_CLOCK)) - 2)
40#define SET_TWI_CLOCK	((0x00010000) | (TWI_CLK_BASE_DIV) | (TWI_CLK_BASE_DIV << 8))
41
42
43/*************************** GLOBAL FUNCTIONS ********************************/
44
45
46/*
47 * .KB_C_FN_DEFINITION_START
48 * void InitEEPROM(void)
49 *  This global function initializes the EEPROM interface (TWI).  Intended
50 * to be called a single time.
51 * .KB_C_FN_DEFINITION_END
52 */
53void
54EEInit(void)
55{
56
57	AT91PS_TWI twiPtr = (AT91PS_TWI)AT91C_BASE_TWI;
58
59	AT91PS_PIO pPio = (AT91PS_PIO)AT91C_BASE_PIOA;
60	AT91PS_PMC pPMC = (AT91PS_PMC)AT91C_BASE_PMC;
61
62	pPio->PIO_ASR = AT91C_PIO_PA25 | AT91C_PIO_PA26;
63	pPio->PIO_PDR = AT91C_PIO_PA25 | AT91C_PIO_PA26;
64
65	pPio->PIO_MDDR = ~AT91C_PIO_PA25;
66	pPio->PIO_MDER = AT91C_PIO_PA25;
67
68	pPMC->PMC_PCER = 1u << AT91C_ID_TWI;
69
70	twiPtr->TWI_IDR = 0xffffffffu;
71	twiPtr->TWI_CR = AT91C_TWI_SWRST;
72	twiPtr->TWI_CR = AT91C_TWI_MSEN | AT91C_TWI_SVDIS;
73
74	twiPtr->TWI_CWGR = SET_TWI_CLOCK;
75}
76
77static inline unsigned
78iicaddr(unsigned ee_off)
79{
80    return (TWSI_EEPROM_ADDRESS | ((ee_off >> 8) & 0x7));
81}
82
83
84/*
85 * .KB_C_FN_DEFINITION_START
86 * void ReadEEPROM(unsigned ee_addr, char *data_addr, unsigned size)
87 *  This global function reads data from the eeprom at ee_addr storing data
88 * to data_addr for size bytes.  Assume the TWI has been initialized.
89 * This function does not utilize the page read mode to simplify the code.
90 * .KB_C_FN_DEFINITION_END
91 */
92void
93EERead(unsigned ee_off, char *data_addr, unsigned size)
94{
95	const AT91PS_TWI 	twiPtr = AT91C_BASE_TWI;
96	unsigned int status;
97
98	if ((ee_off & ~0xff) != ((ee_off + size) & ~0xff)) {
99		printf("Crosses page boundary: 0x%x 0x%x\n", ee_off, size);
100		return;
101	}
102
103	status = twiPtr->TWI_SR;
104	status = twiPtr->TWI_RHR;
105	twiPtr->TWI_MMR = (iicaddr(ee_off) << 16) | AT91C_TWI_IADRSZ_1_BYTE |
106	    AT91C_TWI_MREAD;
107	twiPtr->TWI_IADR = ee_off & 0xff;
108	twiPtr->TWI_CR = AT91C_TWI_START;
109	while (size-- > 1) {
110		while (!(twiPtr->TWI_SR & AT91C_TWI_RXRDY))
111			continue;
112		*(data_addr++) = twiPtr->TWI_RHR;
113	}
114	twiPtr->TWI_CR = AT91C_TWI_STOP;
115	status = twiPtr->TWI_SR;
116	while (!(twiPtr->TWI_SR & AT91C_TWI_TXCOMP))
117		continue;
118	*data_addr = twiPtr->TWI_RHR;
119}
120
121
122/*
123 * .KB_C_FN_DEFINITION_START
124 * void WriteEEPROM(unsigned ee_off, char *data_addr, unsigned size)
125 *  This global function writes data to the eeprom at ee_off using data
126 * from data_addr for size bytes.  Assume the TWI has been initialized.
127 * This function does not utilize the page write mode as the write time is
128 * much greater than the time required to access the device for byte-write
129 * functionality.  This allows the function to be much simpler.
130 * .KB_C_FN_DEFINITION_END
131 */
132void
133EEWrite(unsigned ee_off, const char *data_addr, unsigned size)
134{
135	const AT91PS_TWI 	twiPtr = AT91C_BASE_TWI;
136	unsigned		status;
137	char			test_data;
138
139	while (size--) {
140		// Set the TWI Master Mode Register
141		twiPtr->TWI_MMR = (iicaddr(ee_off) << 16) |
142		    AT91C_TWI_IADRSZ_1_BYTE;
143		twiPtr->TWI_IADR = ee_off++;
144		status = twiPtr->TWI_SR;
145
146		// Load one data byte
147		twiPtr->TWI_THR = *(data_addr++);
148		twiPtr->TWI_CR = AT91C_TWI_START;
149		while (!(twiPtr->TWI_SR & AT91C_TWI_TXRDY))
150			continue;
151		twiPtr->TWI_CR = AT91C_TWI_STOP;
152		status = twiPtr->TWI_SR;
153		while (!(twiPtr->TWI_SR & AT91C_TWI_TXCOMP))
154			continue;
155
156		// wait for write operation to complete, it is done once
157		// we can read it back...
158		EERead(ee_off, &test_data, 1);
159	}
160}
161