BeRTOS
|
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 }