summaryrefslogtreecommitdiffstats
path: root/board/xilinx/xilinx_iic/xiic_l.c
blob: 6b7816373e2e1c198d922ada315f9f834958c1bb (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
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
/* $Id: xiic_l.c,v 1.2 2002/12/05 19:32:40 meinelte Exp $ */
/******************************************************************************
*
*	XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS"
*	AS A COURTESY TO YOU, SOLELY FOR USE IN DEVELOPING PROGRAMS AND
*	SOLUTIONS FOR XILINX DEVICES.  BY PROVIDING THIS DESIGN, CODE,
*	OR INFORMATION AS ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE,
*	APPLICATION OR STANDARD, XILINX IS MAKING NO REPRESENTATION
*	THAT THIS IMPLEMENTATION IS FREE FROM ANY CLAIMS OF INFRINGEMENT,
*	AND YOU ARE RESPONSIBLE FOR OBTAINING ANY RIGHTS YOU MAY REQUIRE
*	FOR YOUR IMPLEMENTATION.  XILINX EXPRESSLY DISCLAIMS ANY
*	WARRANTY WHATSOEVER WITH RESPECT TO THE ADEQUACY OF THE
*	IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OR
*	REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM CLAIMS OF
*	INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
*	FOR A PARTICULAR PURPOSE.
*
*	(c) Copyright 2002 Xilinx Inc.
*	All rights reserved.
*
******************************************************************************/
/*****************************************************************************/
/**
*
* @file xiic_l.c
*
* This file contains low-level driver functions that can be used to access the
* device.  The user should refer to the hardware device specification for more
* details of the device operation.
*
* <pre>
* MODIFICATION HISTORY:
*
* Ver	Who  Date     Changes
* ----- --- -------  -----------------------------------------------
* 1.01b jhl 5/13/02  First release
* 1.01b jhl 10/14/02 Corrected bug in the receive function, the setup of the
*		     interrupt status mask was not being done in the loop such
*		     that a read would sometimes fail on the last byte because
*		     the transmit error which should have been ignored was
*		     being used.  This would leave an extra byte in the FIFO
*		     and the bus throttled such that the next operation would
*		     also fail.	 Also updated the receive function to not
*		     disable the device after the last byte until after the
*		     bus transitions to not busy which is more consistent
*		     with the expected behavior.
* 1.01c ecm 12/05/02 new rev
* </pre>
*
****************************************************************************/

/***************************** Include Files *******************************/

#include "xbasic_types.h"
#include "xio.h"
#include "xipif_v1_23_b.h"
#include "xiic_l.h"

/************************** Constant Definitions ***************************/

/**************************** Type Definitions *****************************/


/***************** Macros (Inline Functions) Definitions *******************/


/******************************************************************************
*
* This macro clears the specified interrupt in the IPIF interrupt status
* register.  It is non-destructive in that the register is read and only the
* interrupt specified is cleared.  Clearing an interrupt acknowledges it.
*
* @param    BaseAddress contains the IPIF registers base address.
*
* @param    InterruptMask contains the interrupts to be disabled
*
* @return
*
* None.
*
* @note
*
* Signature: void XIic_mClearIisr(u32 BaseAddress,
*				  u32 InterruptMask);
*
******************************************************************************/
#define XIic_mClearIisr(BaseAddress, InterruptMask)		    \
    XIIF_V123B_WRITE_IISR((BaseAddress),			    \
	XIIF_V123B_READ_IISR(BaseAddress) & (InterruptMask))

/******************************************************************************
*
* This macro sends the address for a 7 bit address during both read and write
* operations. It takes care of the details to format the address correctly.
* This macro is designed to be called internally to the drivers.
*
* @param    SlaveAddress contains the address of the slave to send to.
*
* @param    Operation indicates XIIC_READ_OPERATION or XIIC_WRITE_OPERATION
*
* @return
*
* None.
*
* @note
*
* Signature: void XIic_mSend7BitAddr(u16 SlaveAddress, u8 Operation);
*
******************************************************************************/
#define XIic_mSend7BitAddress(BaseAddress, SlaveAddress, Operation)	    \
{									    \
    u8 LocalAddr = (u8)(SlaveAddress << 1);			    \
    LocalAddr = (LocalAddr & 0xFE) | (Operation);			    \
    XIo_Out8(BaseAddress + XIIC_DTR_REG_OFFSET, LocalAddr);		    \
}

/************************** Function Prototypes ****************************/

static unsigned RecvData (u32 BaseAddress, u8 * BufferPtr,
			  unsigned ByteCount);
static unsigned SendData (u32 BaseAddress, u8 * BufferPtr,
			  unsigned ByteCount);

/************************** Variable Definitions **************************/


/****************************************************************************/
/**
* Receive data as a master on the IIC bus.  This function receives the data
* using polled I/O and blocks until the data has been received.	 It only
* supports 7 bit addressing and non-repeated start modes of operation.	The
* user is responsible for ensuring the bus is not busy if multiple masters
* are present on the bus.
*
* @param    BaseAddress contains the base address of the IIC device.
* @param    Address contains the 7 bit IIC address of the device to send the
*	    specified data to.
* @param    BufferPtr points to the data to be sent.
* @param    ByteCount is the number of bytes to be sent.
*
* @return
*
* The number of bytes received.
*
* @note
*
* None
*
******************************************************************************/
unsigned XIic_Recv (u32 BaseAddress, u8 Address,
		    u8 * BufferPtr, unsigned ByteCount)
{
	u8 CntlReg;
	unsigned RemainingByteCount;

	/* Tx error is enabled incase the address (7 or 10) has no device to answer
	 * with Ack. When only one byte of data, must set NO ACK before address goes
	 * out therefore Tx error must not be enabled as it will go off immediately
	 * and the Rx full interrupt will be checked.  If full, then the one byte
	 * was received and the Tx error will be disabled without sending an error
	 * callback msg.
	 */
	XIic_mClearIisr (BaseAddress,
			 XIIC_INTR_RX_FULL_MASK | XIIC_INTR_TX_ERROR_MASK |
			 XIIC_INTR_ARB_LOST_MASK);

	/* Set receive FIFO occupancy depth for 1 byte (zero based)
	 */
	XIo_Out8 (BaseAddress + XIIC_RFD_REG_OFFSET, 0);

	/* 7 bit slave address, send the address for a read operation
	 * and set the state to indicate the address has been sent
	 */
	XIic_mSend7BitAddress (BaseAddress, Address, XIIC_READ_OPERATION);

	/* MSMS gets set after putting data in FIFO. Start the master receive
	 * operation by setting CR Bits MSMS to Master, if the buffer is only one
	 * byte, then it should not be acknowledged to indicate the end of data
	 */
	CntlReg = XIIC_CR_MSMS_MASK | XIIC_CR_ENABLE_DEVICE_MASK;
	if (ByteCount == 1) {
		CntlReg |= XIIC_CR_NO_ACK_MASK;
	}

	/* Write out the control register to start receiving data and call the
	 * function to receive each byte into the buffer
	 */
	XIo_Out8 (BaseAddress + XIIC_CR_REG_OFFSET, CntlReg);

	/* Clear the latched interrupt status for the bus not busy bit which must
	 * be done while the bus is busy
	 */
	XIic_mClearIisr (BaseAddress, XIIC_INTR_BNB_MASK);

	/* Try to receive the data from the IIC bus */

	RemainingByteCount = RecvData (BaseAddress, BufferPtr, ByteCount);
	/*
	 * The receive is complete, disable the IIC device and return the number of
	 * bytes that was received
	 */
	XIo_Out8 (BaseAddress + XIIC_CR_REG_OFFSET, 0);

	/* Return the number of bytes that was received */

	return ByteCount - RemainingByteCount;
}

/******************************************************************************
*
* Receive the specified data from the device that has been previously addressed
* on the IIC bus.  This function assumes that the 7 bit address has been sent
* and it should wait for the transmit of the address to complete.
*
* @param    BaseAddress contains the base address of the IIC device.
* @param    BufferPtr points to the buffer to hold the data that is received.
* @param    ByteCount is the number of bytes to be received.
*
* @return
*
* The number of bytes remaining to be received.
*
* @note
*
* This function does not take advantage of the receive FIFO because it is
* designed for minimal code space and complexity.  It contains loops that
* that could cause the function not to return if the hardware is not working.
*
* This function assumes that the calling function will disable the IIC device
* after this function returns.
*
******************************************************************************/
static unsigned RecvData (u32 BaseAddress, u8 * BufferPtr, unsigned ByteCount)
{
	u8 CntlReg;
	u32 IntrStatusMask;
	u32 IntrStatus;

	/* Attempt to receive the specified number of bytes on the IIC bus */

	while (ByteCount > 0) {
		/* Setup the mask to use for checking errors because when receiving one
		 * byte OR the last byte of a multibyte message an error naturally
		 * occurs when the no ack is done to tell the slave the last byte
		 */
		if (ByteCount == 1) {
			IntrStatusMask =
				XIIC_INTR_ARB_LOST_MASK | XIIC_INTR_BNB_MASK;
		} else {
			IntrStatusMask =
				XIIC_INTR_ARB_LOST_MASK |
				XIIC_INTR_TX_ERROR_MASK | XIIC_INTR_BNB_MASK;
		}

		/* Wait for the previous transmit and the 1st receive to complete
		 * by checking the interrupt status register of the IPIF
		 */
		while (1) {
			IntrStatus = XIIF_V123B_READ_IISR (BaseAddress);
			if (IntrStatus & XIIC_INTR_RX_FULL_MASK) {
				break;
			}
			/* Check the transmit error after the receive full because when
			 * sending only one byte transmit error will occur because of the
			 * no ack to indicate the end of the data
			 */
			if (IntrStatus & IntrStatusMask) {
				return ByteCount;
			}
		}

		CntlReg = XIo_In8 (BaseAddress + XIIC_CR_REG_OFFSET);

		/* Special conditions exist for the last two bytes so check for them
		 * Note that the control register must be setup for these conditions
		 * before the data byte which was already received is read from the
		 * receive FIFO (while the bus is throttled
		 */
		if (ByteCount == 1) {
			/* For the last data byte, it has already been read and no ack
			 * has been done, so clear MSMS while leaving the device enabled
			 * so it can get off the IIC bus appropriately with a stop.
			 */
			XIo_Out8 (BaseAddress + XIIC_CR_REG_OFFSET,
				  XIIC_CR_ENABLE_DEVICE_MASK);
		}

		/* Before the last byte is received, set NOACK to tell the slave IIC
		 * device that it is the end, this must be done before reading the byte
		 * from the FIFO
		 */
		if (ByteCount == 2) {
			/* Write control reg with NO ACK allowing last byte to
			 * have the No ack set to indicate to slave last byte read.
			 */
			XIo_Out8 (BaseAddress + XIIC_CR_REG_OFFSET,
				  CntlReg | XIIC_CR_NO_ACK_MASK);
		}

		/* Read in data from the FIFO and unthrottle the bus such that the
		 * next byte is read from the IIC bus
		 */
		*BufferPtr++ = XIo_In8 (BaseAddress + XIIC_DRR_REG_OFFSET);

		/* Clear the latched interrupt status so that it will be updated with
		 * the new state when it changes, this must be done after the receive
		 * register is read
		 */
		XIic_mClearIisr (BaseAddress, XIIC_INTR_RX_FULL_MASK |
				 XIIC_INTR_TX_ERROR_MASK |
				 XIIC_INTR_ARB_LOST_MASK);
		ByteCount--;
	}

	/* Wait for the bus to transition to not busy before returning, the IIC
	 * device cannot be disabled until this occurs.	 It should transition as
	 * the MSMS bit of the control register was cleared before the last byte
	 * was read from the FIFO.
	 */
	while (1) {
		if (XIIF_V123B_READ_IISR (BaseAddress) & XIIC_INTR_BNB_MASK) {
			break;
		}
	}

	return ByteCount;
}

/****************************************************************************/
/**
* Send data as a master on the IIC bus.	 This function sends the data
* using polled I/O and blocks until the data has been sent.  It only supports
* 7 bit addressing and non-repeated start modes of operation.  The user is
* responsible for ensuring the bus is not busy if multiple masters are present
* on the bus.
*
* @param    BaseAddress contains the base address of the IIC device.
* @param    Address contains the 7 bit IIC address of the device to send the
*	    specified data to.
* @param    BufferPtr points to the data to be sent.
* @param    ByteCount is the number of bytes to be sent.
*
* @return
*
* The number of bytes sent.
*
* @note
*
* None
*
******************************************************************************/
unsigned XIic_Send (u32 BaseAddress, u8 Address,
		    u8 * BufferPtr, unsigned ByteCount)
{
	unsigned RemainingByteCount;

	/* Put the address into the FIFO to be sent and indicate that the operation
	 * to be performed on the bus is a write operation
	 */
	XIic_mSend7BitAddress (BaseAddress, Address, XIIC_WRITE_OPERATION);

	/* Clear the latched interrupt status so that it will be updated with the
	 * new state when it changes, this must be done after the address is put
	 * in the FIFO
	 */
	XIic_mClearIisr (BaseAddress, XIIC_INTR_TX_EMPTY_MASK |
			 XIIC_INTR_TX_ERROR_MASK | XIIC_INTR_ARB_LOST_MASK);

	/* MSMS must be set after putting data into transmit FIFO, indicate the
	 * direction is transmit, this device is master and enable the IIC device
	 */
	XIo_Out8 (BaseAddress + XIIC_CR_REG_OFFSET,
		  XIIC_CR_MSMS_MASK | XIIC_CR_DIR_IS_TX_MASK |
		  XIIC_CR_ENABLE_DEVICE_MASK);

	/* Clear the latched interrupt
	 * status for the bus not busy bit which must be done while the bus is busy
	 */
	XIic_mClearIisr (BaseAddress, XIIC_INTR_BNB_MASK);

	/* Send the specified data to the device on the IIC bus specified by the
	 * the address
	 */
	RemainingByteCount = SendData (BaseAddress, BufferPtr, ByteCount);

	/*
	 * The send is complete, disable the IIC device and return the number of
	 * bytes that was sent
	 */
	XIo_Out8 (BaseAddress + XIIC_CR_REG_OFFSET, 0);

	return ByteCount - RemainingByteCount;
}

/******************************************************************************
*
* Send the specified buffer to the device that has been previously addressed
* on the IIC bus.  This function assumes that the 7 bit address has been sent
* and it should wait for the transmit of the address to complete.
*
* @param    BaseAddress contains the base address of the IIC device.
* @param    BufferPtr points to the data to be sent.
* @param    ByteCount is the number of bytes to be sent.
*
* @return
*
* The number of bytes remaining to be sent.
*
* @note
*
* This function does not take advantage of the transmit FIFO because it is
* designed for minimal code space and complexity.  It contains loops that
* that could cause the function not to return if the hardware is not working.
*
******************************************************************************/
static unsigned SendData (u32 BaseAddress, u8 * BufferPtr, unsigned ByteCount)
{
	u32 IntrStatus;

	/* Send the specified number of bytes in the specified buffer by polling
	 * the device registers and blocking until complete
	 */
	while (ByteCount > 0) {
		/* Wait for the transmit to be empty before sending any more data
		 * by polling the interrupt status register
		 */
		while (1) {
			IntrStatus = XIIF_V123B_READ_IISR (BaseAddress);

			if (IntrStatus & (XIIC_INTR_TX_ERROR_MASK |
					  XIIC_INTR_ARB_LOST_MASK |
					  XIIC_INTR_BNB_MASK)) {
				return ByteCount;
			}

			if (IntrStatus & XIIC_INTR_TX_EMPTY_MASK) {
				break;
			}
		}
		/* If there is more than one byte to send then put the next byte to send
		 * into the transmit FIFO
		 */
		if (ByteCount > 1) {
			XIo_Out8 (BaseAddress + XIIC_DTR_REG_OFFSET,
				  *BufferPtr++);
		} else {
			/* Set the stop condition before sending the last byte of data so that
			 * the stop condition will be generated immediately following the data
			 * This is done by clearing the MSMS bit in the control register.
			 */
			XIo_Out8 (BaseAddress + XIIC_CR_REG_OFFSET,
				  XIIC_CR_ENABLE_DEVICE_MASK |
				  XIIC_CR_DIR_IS_TX_MASK);

			/* Put the last byte to send in the transmit FIFO */

			XIo_Out8 (BaseAddress + XIIC_DTR_REG_OFFSET,
				  *BufferPtr++);
		}

		/* Clear the latched interrupt status register and this must be done after
		 * the transmit FIFO has been written to or it won't clear
		 */
		XIic_mClearIisr (BaseAddress, XIIC_INTR_TX_EMPTY_MASK);

		/* Update the byte count to reflect the byte sent and clear the latched
		 * interrupt status so it will be updated for the new state
		 */
		ByteCount--;
	}

	/* Wait for the bus to transition to not busy before returning, the IIC
	 * device cannot be disabled until this occurs.
	 * Note that this is different from a receive operation because the stop
	 * condition causes the bus to go not busy.
	 */
	while (1) {
		if (XIIF_V123B_READ_IISR (BaseAddress) & XIIC_INTR_BNB_MASK) {
			break;
		}
	}

	return ByteCount;
}
OpenPOWER on IntegriCloud