1 //==========================================================================
5 // I2C driver for LPC2xxx
7 //==========================================================================
8 //####ECOSGPLCOPYRIGHTBEGIN####
9 // -------------------------------------------
10 // This file is part of eCos, the Embedded Configurable Operating System.
11 // Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
13 // eCos is free software; you can redistribute it and/or modify it under
14 // the terms of the GNU General Public License as published by the Free
15 // Software Foundation; either version 2 or (at your option) any later version.
17 // eCos is distributed in the hope that it will be useful, but WITHOUT ANY
18 // WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22 // You should have received a copy of the GNU General Public License along
23 // with eCos; if not, write to the Free Software Foundation, Inc.,
24 // 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
26 // As a special exception, if other files instantiate templates or use macros
27 // or inline functions from this file, or you compile this file and link it
28 // with other works to produce a work based on this file, this file does not
29 // by itself cause the resulting work to be covered by the GNU General Public
30 // License. However the source code for this file must still be made available
31 // in accordance with section (3) of the GNU General Public License.
33 // This exception does not invalidate any other reasons why a work based on
34 // this file might be covered by the GNU General Public License.
36 // Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
37 // at http://sources.redhat.com/ecos/ecos-license/
38 // -------------------------------------------
39 //####ECOSGPLCOPYRIGHTEND####
40 //==========================================================================
41 //#####DESCRIPTIONBEGIN####
43 // Author(s): Hans Rosenfeld <rosenfeld@grumpf.hope-2000.org>
49 //####DESCRIPTIONEND####
51 //==========================================================================
53 #include <pkgconf/system.h>
54 #include <pkgconf/devs_i2c_arm_lpc2xxx.h>
56 #include <cyg/infra/cyg_type.h>
57 #include <cyg/infra/diag.h>
58 #include <cyg/io/i2c.h>
59 #include <cyg/hal/hal_arch.h>
60 #include <cyg/hal/hal_io.h>
61 #include <cyg/hal/hal_intr.h>
62 #include <cyg/hal/drv_api.h>
65 * According to the Users Manual the LPC2xxx I2C module is very
66 * similar to the I2C module of the Philips 8xC552/556 controllers. I
67 * guess it is used in other Philips/NXP controllers, too. Using these
68 * macros should make it easier to split off the common parts of the
69 * driver once it's necessary.
73 #define I2C_INTR CYGNUM_HAL_INTERRUPT_I2C
74 #define I2C_FREQ (CYGNUM_HAL_ARM_LPC2XXX_CLOCK_SPEED / \
75 CYGNUM_HAL_ARM_LPC2XXX_VPBDIV)
76 #define I2C_BASE CYGARC_HAL_LPC2XXX_REG_I2_BASE
78 #define I2C_CONSET CYGARC_HAL_LPC2XXX_REG_I2CONSET
79 #define I2C_CONCLR CYGARC_HAL_LPC2XXX_REG_I2CONCLR
80 #define I2C_CON I2C_CONSET
81 #define I2C_STAT CYGARC_HAL_LPC2XXX_REG_I2STAT
82 #define I2C_DAT CYGARC_HAL_LPC2XXX_REG_I2DAT
83 #define I2C_ADR CYGARC_HAL_LPC2XXX_REG_I2ADR
84 #define I2C_SCLH CYGARC_HAL_LPC2XXX_REG_I2SCLH
85 #define I2C_SCLL CYGARC_HAL_LPC2XXX_REG_I2SCLL
87 #define I2C_R8(r, x) HAL_READ_UINT8 (I2C_BASE + (r), (x))
88 #define I2C_W8(r, x) HAL_WRITE_UINT8 (I2C_BASE + (r), (x))
89 #define I2C_R16(r, x) HAL_READ_UINT16 (I2C_BASE + (r), (x))
90 #define I2C_W16(r, x) HAL_WRITE_UINT16(I2C_BASE + (r), (x))
92 /* Special case for setting/clearing bits in I2C_CON */
93 #define SET_CON(x) I2C_W8(I2C_CONSET, (x))
94 #define CLR_CON(x) I2C_W8(I2C_CONCLR, (x))
96 #define CON_EN CYGARC_HAL_LPC2XXX_REG_I2CONSET_I2EN
97 #define CON_STA CYGARC_HAL_LPC2XXX_REG_I2CONSET_STA
98 #define CON_STO CYGARC_HAL_LPC2XXX_REG_I2CONSET_STO
99 #define CON_SI CYGARC_HAL_LPC2XXX_REG_I2CONSET_SI
100 #define CON_AA CYGARC_HAL_LPC2XXX_REG_I2CONSET_AA
102 static cyg_uint8 i2c_addr;
103 static cyg_uint32 i2c_count = 0;
104 static const cyg_uint8* i2c_txbuf = NULL;
105 static cyg_uint8* i2c_rxbuf = NULL;
106 static cyg_bool i2c_rxnak;
109 static cyg_uint32 i2c_flag = 0;
110 static cyg_uint32 i2c_delay;
112 static cyg_drv_mutex_t i2c_lock;
113 static cyg_drv_cond_t i2c_wait;
114 static cyg_handle_t i2c_hand;
115 static cyg_interrupt i2c_data;
117 #define I2C_FLAG_FINISH 1 /* transfer finished */
118 #define I2C_FLAG_ACT 2 /* bus still active, no STOP condition sent */
119 #define I2C_FLAG_ERROR (1<<31) /* one of the following errors occured: */
120 #define I2C_FLAG_ADDR (1<<30) /* - address was not ACKed */
121 #define I2C_FLAG_DATA (1<<29) /* - data was not ACKed */
122 #define I2C_FLAG_LOST (1<<28) /* - bus arbitration was lost */
123 #define I2C_FLAG_BUF (1<<27) /* - no buffer for reading or writing */
124 #define I2C_FLAG_UNK (1<<26) /* - unknown I2C status */
127 * set up I2C bus timing
128 * I2C clock period is in PCLK ticks
131 i2c_lpc2xxx_delay(cyg_uint32 delay)
135 if(delay == i2c_delay)
138 period = I2C_FREQ / (1000000000 / delay);
141 I2C_W16(I2C_SCLL, period);
142 I2C_W16(I2C_SCLH, period);
147 * The ISR does the actual work. It is not that much work to justify
148 * putting it in the DSR, and it is also not clear whether this would
149 * even work. If an error occurs we try to leave the bus in the same
150 * state as we would if there was no error.
153 i2c_lpc2xxx_isr(cyg_vector_t vec, cyg_addrword_t data)
156 I2C_R8(I2C_STAT, status);
159 case 0x08: /* START sent, send Addr+R/W */
160 case 0x10: /* ReSTART sent, send Addr+R/W */
162 I2C_W8(I2C_DAT, i2c_addr);
165 case 0x18: /* Addr ACKed, send data */
166 case 0x28: /* Data ACKed, send more */
168 i2c_flag = I2C_FLAG_FINISH;
169 cyg_drv_interrupt_mask_intunsafe(vec);
170 cyg_drv_interrupt_acknowledge(vec);
171 return CYG_ISR_HANDLED | CYG_ISR_CALL_DSR;
174 if(i2c_txbuf == NULL) {
175 i2c_flag = I2C_FLAG_ERROR | I2C_FLAG_BUF;
176 cyg_drv_interrupt_mask_intunsafe(vec);
177 cyg_drv_interrupt_acknowledge(vec);
178 return CYG_ISR_HANDLED | CYG_ISR_CALL_DSR;
181 I2C_W8(I2C_DAT, *i2c_txbuf);
186 case 0x50: /* Data ACKed, receive more */
187 case 0x58: /* Data not ACKed, end reception */
188 if(i2c_rxbuf == NULL) {
189 i2c_flag = I2C_FLAG_ERROR | I2C_FLAG_BUF;
190 cyg_drv_interrupt_mask_intunsafe(vec);
191 cyg_drv_interrupt_acknowledge(vec);
192 return CYG_ISR_HANDLED | CYG_ISR_CALL_DSR;
195 I2C_R8(I2C_DAT, *i2c_rxbuf);
198 case 0x40: /* Addr ACKed, receive data */
199 if(status == 0x58 || i2c_count == 0) {
200 i2c_flag = I2C_FLAG_FINISH;
201 cyg_drv_interrupt_mask_intunsafe(vec);
202 cyg_drv_interrupt_acknowledge(vec);
203 return CYG_ISR_HANDLED | CYG_ISR_CALL_DSR;
206 if(i2c_count == 1 && i2c_rxnak)
212 case 0x20: /* Addr not ACKed */
214 i2c_flag = I2C_FLAG_ERROR | I2C_FLAG_ADDR;
215 cyg_drv_interrupt_mask_intunsafe(vec);
216 cyg_drv_interrupt_acknowledge(vec);
217 return CYG_ISR_HANDLED | CYG_ISR_CALL_DSR;
219 case 0x30: /* Data not ACKed */
222 i2c_flag = I2C_FLAG_ERROR | I2C_FLAG_DATA;
223 cyg_drv_interrupt_mask_intunsafe(vec);
224 cyg_drv_interrupt_acknowledge(vec);
225 return CYG_ISR_HANDLED | CYG_ISR_CALL_DSR;
227 case 0x38: /* Arbitration lost */
228 i2c_flag = I2C_FLAG_ERROR | I2C_FLAG_LOST;
230 default: /* lots of unused states... */
231 i2c_flag = I2C_FLAG_ERROR | I2C_FLAG_UNK;
236 cyg_drv_interrupt_acknowledge(vec);
237 return CYG_ISR_HANDLED | CYG_ISR_CALL_DSR;
241 i2c_lpc2xxx_dsr(cyg_vector_t vec, cyg_ucount32 count, cyg_addrword_t data)
244 cyg_drv_cond_signal(&i2c_wait);
248 * Initialize driver & hardware state
251 i2c_lpc2xxx_init(struct cyg_i2c_bus *bus)
253 cyg_uint32 addr, tmp;
255 /* enable I2C pins */
256 addr = CYGARC_HAL_LPC2XXX_REG_PIN_BASE + CYGARC_HAL_LPC2XXX_REG_PINSEL0;
257 HAL_READ_UINT32(addr, tmp);
259 HAL_WRITE_UINT32(addr, tmp);
261 cyg_drv_mutex_init(&i2c_lock);
262 cyg_drv_cond_init(&i2c_wait, &i2c_lock);
263 cyg_drv_interrupt_create(I2C_INTR, 0, (cyg_addrword_t) 0, &i2c_lpc2xxx_isr,
264 &i2c_lpc2xxx_dsr, &i2c_hand, &i2c_data);
265 cyg_drv_interrupt_attach(i2c_hand);
267 CLR_CON(CON_EN | CON_STA | CON_SI | CON_AA);
273 * transmit a buffer to a device
276 i2c_lpc2xxx_tx(const cyg_i2c_device *dev,
278 const cyg_uint8 *tx_data,
282 i2c_lpc2xxx_delay(dev->i2c_delay);
284 i2c_addr = dev->i2c_address << 1;
289 * for a repeated start the SI bit has to be reset
290 * if we continue a previous transfer, load the next byte
292 if(send_start && i2c_flag == I2C_FLAG_ACT) {
295 } else if(send_start) {
298 I2C_W8(I2C_DAT, *i2c_txbuf);
307 * the isr will do most of the work, and the dsr will signal when an
308 * error occured or the transfer finished
310 cyg_drv_mutex_lock(&i2c_lock);
312 cyg_drv_interrupt_unmask(I2C_INTR);
313 while(!(i2c_flag & (I2C_FLAG_FINISH|I2C_FLAG_ERROR)))
314 cyg_drv_cond_wait(&i2c_wait);
315 cyg_drv_interrupt_mask(I2C_INTR);
316 cyg_drv_dsr_unlock();
317 cyg_drv_mutex_unlock(&i2c_lock);
319 /* too bad we have no way to tell the caller */
320 if(i2c_flag & I2C_FLAG_ERROR)
321 diag_printf("I2C TX error flag: %x\n", i2c_flag);
325 CLR_CON(CON_SI | CON_STA);
326 } else i2c_flag = I2C_FLAG_ACT;
338 * receive into a buffer from a device
341 i2c_lpc2xxx_rx(const cyg_i2c_device *dev,
348 i2c_lpc2xxx_delay(dev->i2c_delay);
350 i2c_addr = dev->i2c_address << 1 | 1;
353 i2c_rxnak = send_nak;
356 * for a repeated start the SI bit has to be reset
357 * if we continue a previous transfer, start reception
359 if(send_start && i2c_flag == I2C_FLAG_ACT) {
362 } else if(send_start)
368 * the isr will do most of the work, and the dsr will signal when an
369 * error occured or the transfer finished
371 cyg_drv_mutex_lock(&i2c_lock);
373 cyg_drv_interrupt_unmask(I2C_INTR);
374 while(!(i2c_flag & (I2C_FLAG_FINISH|I2C_FLAG_ERROR)))
375 cyg_drv_cond_wait(&i2c_wait);
376 cyg_drv_interrupt_mask(I2C_INTR);
377 cyg_drv_dsr_unlock();
378 cyg_drv_mutex_unlock(&i2c_lock);
380 /* too bad we have no way to tell the caller */
381 if(i2c_flag & I2C_FLAG_ERROR)
382 diag_printf("I2C RX error flag: %x\n", i2c_flag);
386 CLR_CON(CON_SI | CON_STA);
387 } else i2c_flag = I2C_FLAG_ACT;
404 i2c_lpc2xxx_stop(const cyg_i2c_device *dev)
409 CYG_I2C_BUS(cyg_i2c_lpc2xxx_bus,