BeRTOS
i2c_lpc2.c
Go to the documentation of this file.
00001 
00038 #include "cfg/cfg_i2c.h"
00039 
00040 #define LOG_LEVEL  I2C_LOG_LEVEL
00041 #define LOG_FORMAT I2C_LOG_FORMAT
00042 
00043 #include <cfg/log.h>
00044 
00045 #include <cfg/debug.h>
00046 #include <cfg/macros.h> // BV()
00047 
00048 #include <cpu/detect.h>
00049 #include <cpu/irq.h>
00050 #include <cpu/power.h>
00051 
00052 #include <drv/timer.h>
00053 #include <drv/i2c.h>
00054 
00055 #include <io/lpc23xx.h>
00056 
00057 struct I2cHardware
00058 {
00059     uint32_t base;
00060     uint32_t pconp;
00061     uint32_t pinsel_port;
00062     uint32_t pinsel;
00063     uint32_t pinsel_mask;
00064     uint32_t pclksel;
00065     uint32_t pclk_mask;
00066     uint32_t pclk_div;
00067 };
00068 
00069 /*
00070  * Wait that SI bit is set.
00071  *
00072  * Note: this bit is set when the I2C state changes. However, entering
00073  * state F8 does not set SI since there is nothing for an interrupt service
00074  * routine to do in that case.
00075  */
00076 #define WAIT_SI(i2c) \
00077         do { \
00078             ticks_t start = timer_clock(); \
00079             while( !(HWREG(i2c->hw->base + I2C_CONSET_OFF) & BV(I2CON_SI)) ) \
00080             { \
00081                 if (timer_clock() - start > ms_to_ticks(CONFIG_I2C_START_TIMEOUT)) \
00082                 { \
00083                     LOG_ERR("Timeout SI assert\n"); \
00084                     LOG_ERR("[%08lx]\n", HWREG(i2c->hw->base + I2C_STAT_OFF)); \
00085                     break; \
00086                 } \
00087                 cpu_relax(); \
00088             } \
00089     } while (0)
00090 
00091 static void i2c_hw_restart(I2c *i2c)
00092 {
00093     // Clear all pending flags.
00094     HWREG(i2c->hw->base + I2C_CONCLR_OFF) = BV(I2CON_STAC) | BV(I2CON_SIC) | BV(I2CON_AAC);
00095 
00096     // Set start and ack bit.
00097     HWREG(i2c->hw->base + I2C_CONSET_OFF) = BV(I2CON_STA);
00098 
00099     WAIT_SI(i2c);
00100 }
00101 
00102 
00103 static void i2c_hw_stop(I2c *i2c)
00104 {
00105     /* Set the stop bit */
00106     HWREG(i2c->hw->base + I2C_CONSET_OFF) = BV(I2CON_STO);
00107     /* Clear pending flags */
00108     HWREG(i2c->hw->base + I2C_CONCLR_OFF) = BV(I2CON_STAC) | BV(I2CON_SIC) | BV(I2CON_AAC);
00109 }
00110 
00111 static void i2c_lpc2_putc(I2c *i2c, uint8_t data)
00112 {
00113     HWREG(i2c->hw->base + I2C_DAT_OFF) = data;
00114     HWREG(i2c->hw->base + I2C_CONCLR_OFF) = BV(I2CON_SIC);
00115 
00116     WAIT_SI(i2c);
00117 
00118     uint32_t status = HWREG(i2c->hw->base + I2C_STAT_OFF);
00119 
00120 
00121     /* Generate the stop if we finish to send all programmed bytes */
00122     if (i2c->xfer_size == 1)
00123     {
00124         if (I2C_TEST_STOP(i2c->flags) == I2C_STOP)
00125             i2c_hw_stop(i2c);
00126     }
00127 
00128     if (status == I2C_STAT_DATA_NACK)
00129     {
00130         LOG_ERR("Data NACK\n");
00131         i2c->errors |= I2C_NO_ACK;
00132         i2c_hw_stop(i2c);
00133     }
00134     else if ((status == I2C_STAT_ERROR) || (status == I2C_STAT_UNKNOW))
00135     {
00136         LOG_ERR("I2C error.\n");
00137         i2c->errors |= I2C_ERR;
00138         i2c_hw_stop(i2c);
00139     }
00140 }
00141 
00142 static uint8_t i2c_lpc2_getc(I2c *i2c)
00143 {
00144     /*
00145      * Set ack bit if we want read more byte, otherwise
00146      * we disable it
00147      */
00148     if (i2c->xfer_size > 1)
00149         HWREG(i2c->hw->base + I2C_CONSET_OFF) = BV(I2CON_AA);
00150     else
00151         HWREG(i2c->hw->base + I2C_CONCLR_OFF) = BV(I2CON_AAC);
00152 
00153     HWREG(i2c->hw->base + I2C_CONCLR_OFF) = BV(I2CON_SIC);
00154 
00155     WAIT_SI(i2c);
00156 
00157     uint32_t status = HWREG(i2c->hw->base + I2C_STAT_OFF);
00158     uint8_t data = (uint8_t)HWREG(i2c->hw->base + I2C_DAT_OFF);
00159 
00160     if (status == I2C_STAT_RDATA_ACK)
00161     {
00162         return data;
00163     }
00164     else if (status == I2C_STAT_RDATA_NACK)
00165     {
00166         /*
00167          * last byte to read generate the stop if
00168          * required
00169          */
00170         if (I2C_TEST_STOP(i2c->flags) == I2C_STOP)
00171             i2c_hw_stop(i2c);
00172 
00173         return data;
00174     }
00175     else if ((status == I2C_STAT_ERROR) || (status == I2C_STAT_UNKNOW))
00176     {
00177         LOG_ERR("I2C error.\n");
00178         i2c->errors |= I2C_ERR;
00179         i2c_hw_stop(i2c);
00180     }
00181 
00182     return 0xFF;
00183 }
00184 
00185 static void i2c_lpc2_start(struct I2c *i2c, uint16_t slave_addr)
00186 {
00187     if (I2C_TEST_START(i2c->flags) == I2C_START_W)
00188     {
00189         ticks_t start = timer_clock();
00190         while (true)
00191         {
00192             i2c_hw_restart(i2c);
00193 
00194             uint8_t status = HWREG(i2c->hw->base + I2C_STAT_OFF);
00195 
00196             /* Start status ok, set addres and the R/W bit */
00197             if ((status == I2C_STAT_SEND) || (status == I2C_STAT_RESEND))
00198                 HWREG(i2c->hw->base + I2C_DAT_OFF) = slave_addr & ~I2C_READBIT;
00199 
00200             /* Clear the start bit and clear the SI bit */
00201             HWREG(i2c->hw->base + I2C_CONCLR_OFF) = BV(I2CON_SIC) | BV(I2CON_STAC);
00202 
00203             if (status == I2C_STAT_SLAW_ACK)
00204                 break;
00205 
00206             if (status == I2C_STAT_ARB_LOST)
00207             {
00208                 LOG_ERR("Arbitration lost\n");
00209                 i2c->errors |= I2C_ARB_LOST;
00210                 i2c_hw_stop(i2c);
00211             }
00212 
00213             if (timer_clock() - start > ms_to_ticks(CONFIG_I2C_START_TIMEOUT))
00214             {
00215                 LOG_ERR("Timeout on I2C START\n");
00216                 i2c->errors |= I2C_NO_ACK;
00217                 i2c_hw_stop(i2c);
00218                 break;
00219             }
00220         }
00221     }
00222     else if (I2C_TEST_START(i2c->flags) == I2C_START_R)
00223     {
00224         i2c_hw_restart(i2c);
00225 
00226         uint8_t status = HWREG(i2c->hw->base + I2C_STAT_OFF);
00227 
00228         /* Start status ok, set addres and the R/W bit */
00229         if ((status == I2C_STAT_SEND) || (status == I2C_STAT_RESEND))
00230             HWREG(i2c->hw->base + I2C_DAT_OFF) = slave_addr | I2C_READBIT;
00231 
00232         /* Clear the start bit and clear the SI bit */
00233         HWREG(i2c->hw->base + I2C_CONCLR_OFF) = BV(I2CON_SIC) | BV(I2CON_STAC);
00234 
00235         WAIT_SI(i2c);
00236 
00237         status = HWREG(i2c->hw->base + I2C_STAT_OFF);
00238 
00239         if (status == I2C_STAT_SLAR_NACK)
00240         {
00241             LOG_ERR("SLAR NACK:%02x\n", status);
00242             i2c->errors |= I2C_NO_ACK;
00243             i2c_hw_stop(i2c);
00244         }
00245 
00246         if (status == I2C_STAT_ARB_LOST)
00247         {
00248             LOG_ERR("Arbitration lost\n");
00249             i2c->errors |= I2C_ARB_LOST;
00250             i2c_hw_stop(i2c);
00251         }
00252     }
00253     else
00254     {
00255         ASSERT(0);
00256     }
00257 }
00258 
00259 static const I2cVT i2c_lpc_vt =
00260 {
00261     .start = i2c_lpc2_start,
00262     .getc = i2c_lpc2_getc,
00263     .putc = i2c_lpc2_putc,
00264     .write = i2c_genericWrite,
00265     .read = i2c_genericRead,
00266 };
00267 
00268 static struct I2cHardware i2c_lpc2_hw[] =
00269 {
00270     { /* I2C0 */
00271         .base = I2C0_BASE_ADDR,
00272         .pconp = BV(PCONP_PCI2C0),
00273         .pinsel_port = PINSEL1_OFF,
00274         .pinsel = I2C0_PINSEL,
00275         .pinsel_mask = I2C0_PINSEL_MASK,
00276         .pclksel = PCLKSEL0_OFF,
00277         .pclk_mask = I2C0_PCLK_MASK,
00278         .pclk_div = I2C0_PCLK_DIV8,
00279     },
00280     { /* I2C1 */
00281         .base = I2C1_BASE_ADDR,
00282         .pconp = BV(PCONP_PCI2C1),
00283         .pinsel_port = PINSEL0_OFF,
00284         .pinsel = I2C1_PINSEL,
00285         .pinsel_mask = I2C1_PINSEL_MASK,
00286         .pclksel = PCLKSEL1_OFF,
00287         .pclk_mask = I2C1_PCLK_MASK,
00288         .pclk_div = I2C1_PCLK_DIV8,
00289     },
00290     { /* I2C2 */
00291         .base = I2C2_BASE_ADDR,
00292         .pconp = BV(PCONP_PCI2C2),
00293         .pinsel_port = PINSEL0_OFF,
00294         .pinsel = I2C2_PINSEL,
00295         .pinsel_mask = I2C2_PINSEL_MASK,
00296         .pclksel = PCLKSEL1_OFF,
00297         .pclk_mask = I2C2_PCLK_MASK,
00298         .pclk_div = I2C2_PCLK_DIV8,
00299     },
00300 };
00301 
00305 void i2c_hw_init(I2c *i2c, int dev, uint32_t clock)
00306 {
00307     i2c->hw = &i2c_lpc2_hw[dev];
00308     i2c->vt = &i2c_lpc_vt;
00309 
00310     /* Enable I2C clock */
00311     PCONP |= i2c->hw->pconp;
00312 
00313     ASSERT(clock <= 400000);
00314 
00315     HWREG(i2c->hw->base + I2C_CONCLR_OFF) = BV(I2CON_I2ENC) | BV(I2CON_STAC) | BV(I2CON_SIC) | BV(I2CON_AAC);
00316 
00317     /*
00318      * Bit Frequency = Fplk / (I2C_I2SCLH + I2C_I2SCLL)
00319      * value of I2SCLH and I2SCLL must be different
00320      */
00321     HWREG(SCB_BASE_ADDR + i2c->hw->pclksel) &= ~i2c->hw->pclk_mask;
00322     HWREG(SCB_BASE_ADDR + i2c->hw->pclksel) |= i2c->hw->pclk_div;
00323 
00324     HWREG(i2c->hw->base + I2C_SCLH_OFF) = (((CPU_FREQ / 8) / clock) / 2) + 1;
00325     HWREG(i2c->hw->base + I2C_SCLL_OFF) = (((CPU_FREQ / 8) / clock) / 2);
00326 
00327     ASSERT(HWREG(i2c->hw->base + I2C_SCLH_OFF) > 4);
00328     ASSERT(HWREG(i2c->hw->base + I2C_SCLL_OFF) > 4);
00329 
00330     /* Assign pins to SCL and SDA */
00331     HWREG(PINSEL_BASE_ADDR + i2c->hw->pinsel_port) &= ~i2c->hw->pinsel_mask;
00332     HWREG(PINSEL_BASE_ADDR + i2c->hw->pinsel_port) |= i2c->hw->pinsel;
00333 
00334     // Enable I2C
00335     HWREG(i2c->hw->base + I2C_CONSET_OFF) = BV(I2CON_I2EN);
00336 }