4 * This file is part of BeRTOS.
6 * Bertos is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 * As a special exception, you may use this file as part of a free software
21 * library without restriction. Specifically, if other files instantiate
22 * templates or use macros or inline functions from this file, or you compile
23 * this file and link it with other files to produce an executable, this
24 * file does not by itself cause the resulting executable to be covered by
25 * the GNU General Public License. This exception does not however
26 * invalidate any other reasons why the executable file might be covered by
27 * the GNU General Public License.
29 * Copyright 2010 Develer S.r.l. (http://www.develer.com/)
33 * \brief Driver for the LPC23xx I2C (implementation)
37 #include "cfg/cfg_i2c.h"
39 #define LOG_LEVEL I2C_LOG_LEVEL
40 #define LOG_FORMAT I2C_LOG_FORMAT
44 #include <cfg/debug.h>
45 #include <cfg/macros.h> // BV()
46 #include <cfg/module.h>
48 #include <cpu/detect.h>
51 #include <drv/timer.h>
53 #include <drv/vic_lpc2.h> /* vic_handler_t */
55 #include <io/lpc23xx.h>
70 #define I2C_PCONP PCONP_PCI2C0
71 #define I2C_CONSET I20CONSET
72 #define I2C_CONCLR I20CONCLR
73 #define I2C_SCLH I20SCLH
74 #define I2C_SCLL I20SCLL
75 #define I2C_STAT I20STAT
76 #define I2C_DAT I20DAT
77 #define I2C_PINSEL_PORT PINSEL1
78 #define I2C_PINSEL I2C0_PINSEL
79 #define I2C_PINSEL_MASK I2C0_PINSEL_MASK
80 #define I2C_PCLKSEL PCLKSEL0
81 #define I2C_PCLK_MASK I2C0_PCLK_MASK
82 #define I2C_PCLK_DIV8 I2C0_PCLK_DIV8
88 #define GET_STATUS() ((uint8_t)I2C_STAT)
90 * Wait that SI bit is set.
92 * Note: this bit is set when the I2C state changes. However, entering
93 * state F8 does not set SI since there is nothing for an interrupt service
94 * routine to do in that case.
98 ticks_t start = timer_clock(); \
99 while( !(I2C_CONSET & BV(I2CON_SI)) ) \
101 if (timer_clock() - start > ms_to_ticks(CONFIG_I2C_START_TIMEOUT)) \
103 LOG_ERR("Timeout SI assert\n"); \
109 static void i2c_hw_restart(void)
111 // Clear all pending flags.
112 I2C_CONCLR = BV(I2CON_STAC) | BV(I2CON_SIC) | BV(I2CON_AAC);
114 // Set start and ack bit.
115 I2C_CONSET = BV(I2CON_STA);
121 static void i2c_hw_stop(void)
123 /* Set the stop bit */
124 I2C_CONSET = BV(I2CON_STO);
125 /* Clear pending flags */
126 I2C_CONCLR = BV(I2CON_STAC) | BV(I2CON_SIC) | BV(I2CON_AAC);
129 static void i2c_lpc2_put(I2c *i2c, uint8_t data)
133 // kprintf("w %02x\n", data);
135 I2C_CONCLR = BV(I2CON_SIC);
139 uint32_t status = GET_STATUS();
140 //kprintf("w %08lx\n", status);
142 /* if (status == I2C_STAT_DATA_ACK) */
144 /* Generate the stop if we finish to send all programmed bytes */
145 if (i2c->xfer_size == 1)
147 if (I2C_TEST_STOP(i2c->flags) == I2C_STOP)
151 if (status == I2C_STAT_DATA_NACK)
153 LOG_ERR("Data NACK\n");
154 i2c->errors |= I2C_NO_ACK;
157 else if ((status == I2C_STAT_ERROR) || (status == I2C_STAT_UNKNOW))
159 LOG_ERR("I2C error.\n");
160 i2c->errors |= I2C_ERR;
165 static uint8_t i2c_lpc2_get(I2c *i2c)
168 * Set ack bit if we want read more byte, otherwise
171 if (i2c->xfer_size > 1)
172 I2C_CONSET = BV(I2CON_AA);
174 I2C_CONCLR = BV(I2CON_AAC);
176 I2C_CONCLR = BV(I2CON_SIC);
180 uint32_t status = GET_STATUS();
181 uint8_t data = (uint8_t)(I2C_DAT & 0xFF);
183 // kprintf("r %02x\n", data);
184 // kprintf("r %08lx\n", status);
186 if (status == I2C_STAT_RDATA_ACK)
190 else if (status == I2C_STAT_RDATA_NACK)
193 * last byte to read generate the stop if
196 if (I2C_TEST_STOP(i2c->flags) == I2C_STOP)
201 else if ((status == I2C_STAT_ERROR) || (status == I2C_STAT_UNKNOW))
203 LOG_ERR("I2C error.\n");
204 i2c->errors |= I2C_ERR;
213 static void i2c_lpc2_start(struct I2c *i2c, uint16_t slave_addr)
215 if (I2C_TEST_START(i2c->flags) == I2C_START_W)
217 ticks_t start = timer_clock();
222 uint8_t status = GET_STATUS();
224 /* Start status ok, set addres and the R/W bit */
225 if ((status == I2C_STAT_SEND) || (status == I2C_STAT_RESEND))
226 I2C_DAT = slave_addr & ~I2C_READBIT;
228 /* Clear the start bit and clear the SI bit */
229 I2C_CONCLR = BV(I2CON_SIC) | BV(I2CON_STAC);
231 if (status == I2C_STAT_SLAW_ACK)
233 else if (status == I2C_STAT_ARB_LOST)
235 LOG_ERR("Arbitration lost\n");
236 i2c->errors |= I2C_ARB_LOST;
241 if (timer_clock() - start > ms_to_ticks(CONFIG_I2C_START_TIMEOUT))
243 LOG_ERR("Timeout on I2C START\n");
244 i2c->errors |= I2C_NO_ACK;
250 else if (I2C_TEST_START(i2c->flags) == I2C_START_R)
254 uint8_t status = GET_STATUS();
256 /* Start status ok, set addres and the R/W bit */
257 if ((status == I2C_STAT_SEND) || (status == I2C_STAT_RESEND))
258 I2C_DAT = slave_addr | I2C_READBIT;
260 /* Clear the start bit and clear the SI bit */
261 I2C_CONCLR = BV(I2CON_SIC) | BV(I2CON_STAC);
265 status = GET_STATUS();
267 if (status == I2C_STAT_SLAR_ACK)
270 if (status == I2C_STAT_SLAR_NACK)
272 LOG_ERR("SLAR NACK:%02x\n", status);
273 i2c->errors |= I2C_NO_ACK;
276 else if (status == I2C_STAT_ARB_LOST)
278 LOG_ERR("Arbitration lost\n");
279 i2c->errors |= I2C_ARB_LOST;
290 static const I2cVT i2c_lpc_vt =
292 .start = i2c_lpc2_start,
299 struct I2cHardware i2c_lpc2_hw =
306 * Initialize I2C module.
308 void i2c_hw_init(I2c *i2c, int dev, uint32_t clock)
310 i2c->hw = &i2c_lpc2_hw;
311 i2c->vt = &i2c_lpc_vt;
313 /* Enable I2C clock */
314 PCONP |= BV(I2C_PCONP);
316 ASSERT(clock <= 400000);
318 I2C_CONCLR = BV(I2CON_I2ENC) | BV(I2CON_STAC) | BV(I2CON_SIC) | BV(I2CON_AAC);
321 * Bit Frequency = Fplk / (I2C_I2SCLH + I2C_I2SCLL)
322 * value of I2SCLH and I2SCLL must be different
324 I2C_PCLKSEL &= ~I2C_PCLK_MASK;
325 I2C_PCLKSEL |= I2C_PCLK_DIV8;
327 I2C_SCLH = (((CPU_FREQ / 8) / clock) / 2) + 1;
328 I2C_SCLL = (((CPU_FREQ / 8) / clock) / 2);
330 ASSERT(I2C_SCLH > 4 || I2C_SCLL > 4);
332 /* Assign pins to SCL and SDA (P0_27, P0_28) */
333 I2C_PINSEL_PORT &= ~I2C_PINSEL_MASK;
334 I2C_PINSEL_PORT |= I2C_PINSEL;
337 I2C_CONSET = BV(I2CON_I2EN);