summaryrefslogtreecommitdiffstats
path: root/arch/ppc/boot/simple/iic.c
blob: 5e91489426b46485a787a51520f7b207e6aaa3d5 (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
/* Minimal support functions to read configuration from IIC EEPROMS
 * on MPC8xx boards.  Originally written for RPGC RPX-Lite.
 * Dan Malek (dmalek@jlc.net).
 */
#include <linux/types.h>
#include <asm/uaccess.h>
#include <asm/mpc8xx.h>
#include <asm/cpm1.h>


/* IIC functions.
 * These are just the basic master read/write operations so we can
 * examine serial EEPROM.
 */
void	iic_read(uint devaddr, u_char *buf, uint offset, uint count);

static	int	iic_init_done;

static void
iic_init(void)
{
	volatile iic_t *iip;
	volatile i2c8xx_t *i2c;
	volatile cpm8xx_t	*cp;
	volatile immap_t	*immap;
	uint	dpaddr;

	immap = (immap_t *)IMAP_ADDR;
	cp = (cpm8xx_t *)&(immap->im_cpm);

	/* Reset the CPM.  This is necessary on the 860 processors
	 * that may have started the SCC1 ethernet without relocating
	 * the IIC.
	 * This also stops the Ethernet in case we were loaded by a
	 * BOOTP rom monitor.
	 */
	cp->cp_cpcr = (CPM_CR_RST | CPM_CR_FLG);

	/* Wait for it.
	*/
	while (cp->cp_cpcr & (CPM_CR_RST | CPM_CR_FLG));

	/* Remove any microcode patches.  We will install our own
	 * later.
	 */
	cp->cp_cpmcr1 = 0;
	cp->cp_cpmcr2 = 0;
	cp->cp_cpmcr3 = 0;
	cp->cp_cpmcr4 = 0;
	cp->cp_rccr = 0;

	iip = (iic_t *)&cp->cp_dparam[PROFF_IIC];
	i2c = (i2c8xx_t *)&(immap->im_i2c);

	/* Initialize Port B IIC pins.
	*/
	cp->cp_pbpar |= 0x00000030;
	cp->cp_pbdir |= 0x00000030;
	cp->cp_pbodr |= 0x00000030;

	/* Initialize the parameter ram.
	*/

	/* Allocate space for a two transmit and one receive buffer
	 * descriptor in the DP ram.
	 * For now, this address seems OK, but it may have to
	 * change with newer versions of the firmware.
	 */
	dpaddr = 0x0840;

	/* Set up the IIC parameters in the parameter ram.
	*/
	iip->iic_tbase = dpaddr;
	iip->iic_rbase = dpaddr + (2 * sizeof(cbd_t));

	iip->iic_tfcr = SMC_EB;
	iip->iic_rfcr = SMC_EB;

	/* This should really be done by the reader/writer.
	*/
	iip->iic_mrblr = 128;

	/* Initialize Tx/Rx parameters.
	*/
	cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_I2C, CPM_CR_INIT_TRX) | CPM_CR_FLG;
	while (cp->cp_cpcr & CPM_CR_FLG);

	/* Select an arbitrary address.  Just make sure it is unique.
	*/
	i2c->i2c_i2add = 0x34;

	/* Make clock run maximum slow.
	*/
	i2c->i2c_i2brg = 7;

	/* Disable interrupts.
	*/
	i2c->i2c_i2cmr = 0;
	i2c->i2c_i2cer = 0xff;

	/* Enable SDMA.
	*/
	immap->im_siu_conf.sc_sdcr = 1;

	iic_init_done = 1;
}

/* Read from IIC.
 * Caller provides device address, memory buffer, and byte count.
 */
static	u_char	iitemp[32];

void
iic_read(uint devaddr, u_char *buf, uint offset, uint count)
{
	volatile iic_t		*iip;
	volatile i2c8xx_t	*i2c;
	volatile cbd_t		*tbdf, *rbdf;
	volatile cpm8xx_t	*cp;
	volatile immap_t	*immap;
	u_char			*tb;
	uint			temp;

	/* If the interface has not been initialized, do that now.
	*/
	if (!iic_init_done)
		iic_init();

	immap = (immap_t *)IMAP_ADDR;
	cp = (cpm8xx_t *)&(immap->im_cpm);

	iip = (iic_t *)&cp->cp_dparam[PROFF_IIC];
	i2c = (i2c8xx_t *)&(immap->im_i2c);

	tbdf = (cbd_t *)&cp->cp_dpmem[iip->iic_tbase];
	rbdf = (cbd_t *)&cp->cp_dpmem[iip->iic_rbase];

	/* Send a "dummy write" operation.  This is a write request with
	 * only the offset sent, followed by another start condition.
	 * This will ensure we start reading from the first location
	 * of the EEPROM.
	 */
	tb = iitemp;
	tb = (u_char *)(((uint)tb + 15) & ~15);
	tbdf->cbd_bufaddr = (int)tb;
	*tb = devaddr & 0xfe;	/* Device address */
	*(tb+1) = offset;		/* Offset */
	tbdf->cbd_datlen = 2;		/* Length */
	tbdf->cbd_sc =
	      BD_SC_READY | BD_SC_LAST | BD_SC_WRAP | BD_IIC_START;

	i2c->i2c_i2mod = 1;	/* Enable */
	i2c->i2c_i2cer = 0xff;
	i2c->i2c_i2com = 0x81;	/* Start master */

	/* Wait for IIC transfer.
	*/
#if 0
	while ((i2c->i2c_i2cer & 3) == 0);

	if (tbdf->cbd_sc & BD_SC_READY)
		printf("IIC ra complete but tbuf ready\n");
#else
	temp = 10000000;
	while ((tbdf->cbd_sc & BD_SC_READY) && (temp != 0))
		temp--;
#if 0
	/* We can't do this...there is no serial port yet!
	*/
	if (temp == 0) {
		printf("Timeout reading EEPROM\n");
		return;
	}
#endif
#endif
	
	/* Chip errata, clear enable.
	*/
	i2c->i2c_i2mod = 0;

	/* To read, we need an empty buffer of the proper length.
	 * All that is used is the first byte for address, the remainder
	 * is just used for timing (and doesn't really have to exist).
	 */
	tbdf->cbd_bufaddr = (int)tb;
	*tb = devaddr | 1;	/* Device address */
	rbdf->cbd_bufaddr = (uint)buf;		/* Desination buffer */
	tbdf->cbd_datlen = rbdf->cbd_datlen = count + 1;	/* Length */
	tbdf->cbd_sc = BD_SC_READY | BD_SC_LAST | BD_SC_WRAP | BD_IIC_START;
	rbdf->cbd_sc = BD_SC_EMPTY | BD_SC_WRAP;

	/* Chip bug, set enable here.
	*/
	i2c->i2c_i2mod = 1;	/* Enable */
	i2c->i2c_i2cer = 0xff;
	i2c->i2c_i2com = 0x81;	/* Start master */

	/* Wait for IIC transfer.
	*/
#if 0
	while ((i2c->i2c_i2cer & 1) == 0);

	if (rbdf->cbd_sc & BD_SC_EMPTY)
		printf("IIC read complete but rbuf empty\n");
#else
	temp = 10000000;
	while ((tbdf->cbd_sc & BD_SC_READY) && (temp != 0))
		temp--;
#endif
	
	/* Chip errata, clear enable.
	*/
	i2c->i2c_i2mod = 0;
}
OpenPOWER on IntegriCloud