summaryrefslogtreecommitdiff
path: root/cpu/at91rm9200/i2c.c
blob: 433dd32a23112057fcc3602a20f6a50e573db798 (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
/*
 *  i2c Support for Atmel's AT91RM9200 Two-Wire Interface
 *
 *  (c) Rick Bronson
 *
 *  Borrowed heavily from original work by:
 *  Copyright (c) 2000 Philip Edelbrock <phil@stimpy.netroedge.com>
 *
 *  Modified to work with u-boot by (C) 2004 Gary Jennejohn garyj@denx.de
 *
 *  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., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
*/
#include <common.h>

#ifdef CONFIG_HARD_I2C

#include <i2c.h>
#include <asm/io.h>
#include <asm/arch/hardware.h>

#include <at91rm9200_i2c.h>

/* define DEBUG */

/*
 * Poll the i2c status register until the specified bit is set.
 * Returns 0 if timed out (100 msec)
 */
static short at91_poll_status(AT91PS_TWI twi, unsigned long bit) {
	int loop_cntr = 10000;
	do {
		udelay(10);
	} while (!(twi->TWI_SR & bit) && (--loop_cntr > 0));

	return (loop_cntr > 0);
}

/*
 * Generic i2c master transfer entrypoint
 *
 * rw == 1 means that this is a read
 */
static int
at91_xfer(unsigned char chip, unsigned int addr, int alen,
						unsigned char *buffer, int len, int rw)
{
	AT91PS_TWI twi = (AT91PS_TWI) AT91_TWI_BASE;
	int length;
	unsigned char *buf;
	/* Set the TWI Master Mode Register */
	twi->TWI_MMR = (chip << 16) | (alen << 8)
		| ((rw == 1) ? AT91C_TWI_MREAD : 0);

	/* Set TWI Internal Address Register with first messages data field */
	/* only one address byte is supported  */
	if (alen > 0)
		twi->TWI_IADR = addr & 0xff;

	length = len;
	buf = buffer;
	if (length && buf) {	/* sanity check */
		if (rw) {
			twi->TWI_CR = AT91C_TWI_START;
			while (length--) {
				if (!length)
					twi->TWI_CR = AT91C_TWI_STOP;
				/* Wait until transfer is finished */
				if (!at91_poll_status(twi, AT91C_TWI_RXRDY)) {
					debug ("at91_i2c: timeout 1\n");
					return 1;
				}
				*buf++ = twi->TWI_RHR;
			}
			if (!at91_poll_status(twi, AT91C_TWI_TXCOMP)) {
				debug ("at91_i2c: timeout 2\n");
				return 1;
			}
		} else {
			twi->TWI_CR = AT91C_TWI_START;
			while (length--) {
				twi->TWI_THR = *buf++;
				if (!length)
					twi->TWI_CR = AT91C_TWI_STOP;
				if (!at91_poll_status(twi, AT91C_TWI_TXRDY)) {
					debug ("at91_i2c: timeout 3\n");
					return 1;
				}
			}
			/* Wait until transfer is finished */
			if (!at91_poll_status(twi, AT91C_TWI_TXCOMP)) {
				debug ("at91_i2c: timeout 4\n");
				return 1;
			}
		}
	}
	return 0;
}

int
i2c_probe(unsigned char chip)
{
	char buffer[1];

	return at91_xfer(chip, 0, 0, buffer, 1, 1);
}

int
i2c_read (unsigned char chip, unsigned int addr, int alen,
	  unsigned char *buffer, int len)
{
#ifdef CFG_I2C_EEPROM_ADDR_OVERFLOW
	/* we only allow one address byte */
	if (alen > 1)
		return 1;
	/* XXX assume an ATMEL AT24C16 */
	if (alen == 1) {
#if 0 /* EEPROM code already sets this correctly */
		chip |= (addr >> 8) & 0xff;
#endif
		addr = addr & 0xff;
	}
#endif
	return at91_xfer(chip, addr, alen, buffer, len, 1);
}

int
i2c_write(unsigned char chip, unsigned int addr, int alen,
							unsigned char *buffer, int len)
{
	int i;
	unsigned char *buf;

#ifdef CFG_I2C_EEPROM_ADDR_OVERFLOW
	/* we only allow one address byte */
	if (alen > 1)
		return 1;
	/* XXX assume an ATMEL AT24C16 */
	if (alen == 1) {
		buf = buffer;
		/* do single byte writes */
		for (i = 0; i < len; i++) {
#if 0 /* EEPROM code already sets this correctly */
			chip |= (addr >> 8) & 0xff;
#endif
			addr = addr & 0xff;
			if (at91_xfer(chip, addr, alen, buf++, 1, 0))
				return 1;
			addr++;
		}
		return 0;
	}
#endif
	return at91_xfer(chip, addr, alen, buffer, len, 0);
}

/*
 * Main initialization routine
 */
void
i2c_init(int speed, int slaveaddr)
{
	AT91PS_TWI twi = (AT91PS_TWI) AT91_TWI_BASE;

	*AT91C_PIOA_PDR = AT91C_PA25_TWD | AT91C_PA26_TWCK;
	*AT91C_PIOA_ASR = AT91C_PA25_TWD | AT91C_PA26_TWCK;
	*AT91C_PIOA_MDER = AT91C_PA25_TWD | AT91C_PA26_TWCK;
	*AT91C_PMC_PCER = 1 << AT91C_ID_TWI;		/* enable peripheral clock */

	twi->TWI_IDR = 0x3ff;				/* Disable all interrupts */
	twi->TWI_CR = AT91C_TWI_SWRST;			/* Reset peripheral */
	twi->TWI_CR = AT91C_TWI_MSEN | AT91C_TWI_SVDIS;	/* Set Master mode */

	/* Here, CKDIV = 1 and CHDIV=CLDIV  ==> CLDIV = CHDIV = 1/4*((Fmclk/FTWI) -6) */
	twi->TWI_CWGR = AT91C_TWI_CKDIV1 | AT91C_TWI_CLDIV3 | (AT91C_TWI_CLDIV3 << 8);

	debug ("Found AT91 i2c\n");
	return;
}
#endif /* CONFIG_HARD_I2C */