1/* 2 * Copyright (c) 2004-2007 Marcus Overhagen <marcus@overhagen.de> 3 * 4 * Permission is hereby granted, free of charge, to any person 5 * obtaining a copy of this software and associated documentation 6 * files (the "Software"), to deal in the Software without restriction, 7 * including without limitation the rights to use, copy, modify, 8 * merge, publish, distribute, sublicense, and/or sell copies of 9 * the Software, and to permit persons to whom the Software is 10 * furnished to do so, subject to the following conditions: 11 * 12 * The above copyright notice and this permission notice shall be 13 * included in all copies or substantial portions of the Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 17 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 * OTHER DEALINGS IN THE SOFTWARE. 23 */ 24 25#include <stdlib.h> 26#include <string.h> 27#include <KernelExport.h> 28#include "i2c_core.h" 29 30#define TRACE_I2C 31#ifdef TRACE_I2C 32 #define TRACE dprintf 33#else 34 #define TRACE(a...) 35#endif 36 37static status_t i2c_writebyte(i2c_bus *bus, uint8 byte, int *ack); 38static status_t i2c_readbyte(i2c_bus *bus, uint8 *pbyte); 39static status_t i2c_read_unlocked(i2c_bus *bus, int address, void *data, int size); 40static status_t i2c_write_unlocked(i2c_bus *bus, int address, const void *data, int size); 41 42 43struct _i2c_bus 44{ 45 void *cookie; 46 bigtime_t delay; 47 bigtime_t timeout; 48 i2c_set_scl set_scl; 49 i2c_set_sda set_sda; 50 i2c_get_scl get_scl; 51 i2c_get_sda get_sda; 52 sem_id sem; 53}; 54 55 56i2c_bus * 57i2c_create_bus(void *cookie, 58 int frequency, 59 bigtime_t timeout, 60 i2c_set_scl set_scl, 61 i2c_set_sda set_sda, 62 i2c_get_scl get_scl, 63 i2c_get_sda get_sda) 64{ 65 i2c_bus *bus = malloc(sizeof(i2c_bus)); 66 if (!bus) 67 return NULL; 68 69 bus->sem = create_sem(1, "i2c bus access"); 70 if (bus->sem < 0) { 71 free(bus); 72 return NULL; 73 } 74 75 bus->cookie = cookie; 76 bus->delay = 1000000 / frequency; 77 if (bus->delay == 0) 78 bus->delay = 1; 79 bus->timeout = timeout; 80 bus->set_scl = set_scl; 81 bus->set_sda = set_sda; 82 bus->get_scl = get_scl; 83 bus->get_sda = get_sda; 84 85 set_scl(cookie, 1); 86 set_sda(cookie, 1); 87 88 return bus; 89} 90 91 92void 93i2c_delete_bus(i2c_bus *bus) 94{ 95 if (!bus) 96 return; 97 delete_sem(bus->sem); 98 free(bus); 99} 100 101 102static inline void 103set_sda_low(i2c_bus *bus) 104{ 105 bus->set_sda(bus->cookie, 0); 106 snooze(bus->delay); 107} 108 109 110static inline void 111set_sda_high(i2c_bus *bus) 112{ 113 bus->set_sda(bus->cookie, 1); 114 snooze(bus->delay); 115} 116 117 118static inline void 119set_scl_low(i2c_bus *bus) 120{ 121 bus->set_scl(bus->cookie, 0); 122 snooze(bus->delay); 123} 124 125 126static inline status_t 127set_scl_high(i2c_bus *bus) 128{ 129 bigtime_t end = system_time() + bus->timeout; 130 bus->set_scl(bus->cookie, 1); 131 while (0 == bus->get_scl(bus->cookie)) { 132 if (system_time() > end) 133 return B_TIMED_OUT; 134 snooze(5); 135 } 136 snooze(bus->delay); 137 return B_OK; 138} 139 140 141static inline void 142i2c_start(i2c_bus *bus) 143{ 144 set_sda_low(bus); 145 set_scl_low(bus); 146} 147 148 149static inline void 150i2c_stop(i2c_bus *bus) 151{ 152 set_sda_low(bus); 153 set_scl_high(bus); 154 set_sda_high(bus); 155} 156 157 158static inline status_t 159i2c_start_address(i2c_bus *bus, int address, int read /* 1 = read, 0 = write */) 160{ 161 status_t res; 162 uint8 addr; 163 int ack; 164 int i; 165 166// TRACE("i2c_start_address: enter\n"); 167 168 addr = (address << 1) | (read & 1); 169 for (i = 0; i < 5; i++) { 170 i2c_start(bus); 171 res = i2c_writebyte(bus, addr, &ack); 172 if (res == B_OK) { 173 if (ack) 174 break; 175 res = B_ERROR; 176 } 177 if (res == B_TIMED_OUT) 178 break; 179 i2c_stop(bus); 180 snooze(50); 181 } 182 183// TRACE("i2c_start_address: %s, ack %d, i %d\n", strerror(res), ack, i); 184 return res; 185} 186 187 188/* write one byte and the ack/nack 189 * return values: 190 * B_OK => byte transmitted 191 * B_ERROR => arbitration lost 192 * B_TIMED_OUT => time out 193 */ 194status_t 195i2c_writebyte(i2c_bus *bus, uint8 byte, int *ack) 196{ 197 int has_arbitration; 198 199 int i; 200 has_arbitration = 1; 201 for (i = 7; i >= 0; i--) { 202 int bit = (byte >> i) & 1; 203 if (has_arbitration) { 204 if (bit) 205 set_sda_high(bus); 206 else 207 set_sda_low(bus); 208 } 209 if (set_scl_high(bus) != B_OK) { 210 set_sda_high(bus); // avoid blocking the bus 211 TRACE("i2c_writebyte timeout at bit %d\n", i); 212 return B_TIMED_OUT; 213 } 214 if (has_arbitration) { 215 if (bit == 1 && 0 == bus->get_sda(bus->cookie)) { 216 has_arbitration = 0; 217// TRACE("i2c_writebyte lost arbitration at bit %d\n", i); 218 } 219 } 220 set_scl_low(bus); 221 } 222 set_sda_high(bus); 223 if (set_scl_high(bus) != B_OK) { 224 TRACE("i2c_writebyte timeout at ack\n"); 225 return B_TIMED_OUT; 226 } 227 *ack = 0 == bus->get_sda(bus->cookie); 228 set_scl_low(bus); 229 230 return has_arbitration ? B_OK : B_ERROR; 231} 232 233 234/* read one byte, don't generate ack 235 */ 236status_t 237i2c_readbyte(i2c_bus *bus, uint8 *pbyte) 238{ 239 int i; 240 uint8 byte; 241 242 set_sda_high(bus); 243 byte = 0; 244 for (i = 7; i >= 0; i--) { 245 if (set_scl_high(bus) != B_OK) { 246 TRACE("i2c_readbyte timeout at bit %d\n", i); 247 return B_TIMED_OUT; 248 } 249 byte = (byte << 1) | bus->get_sda(bus->cookie); 250 set_scl_low(bus); 251 } 252 *pbyte = byte; 253 return B_OK; 254} 255 256 257status_t 258i2c_read_unlocked(i2c_bus *bus, int address, void *data, int size) 259{ 260 status_t status; 261 uint8 *bytes; 262 263 if (size <= 0) 264 return B_BAD_VALUE; 265 266 status = i2c_start_address(bus, address, 1 /* 1 = read, 0 = write */); 267 if (status != B_OK) { 268 TRACE("i2c_read: error on i2c_start_address: %s\n", strerror(status)); 269 return status; 270 } 271 272 bytes = data; 273 while (size > 0) { 274 if (i2c_readbyte(bus, bytes) != B_OK) { // timeout 275 TRACE("i2c_read: timeout error on byte %ld\n", bytes - (uint8 *)data); 276 return B_TIMED_OUT; 277 } 278 if (size > 0) 279 set_sda_low(bus); // ack 280 else 281 set_sda_high(bus); // nack 282 283 if (set_scl_high(bus) != B_OK) { 284 set_sda_high(bus); // avoid blocking the bus 285 TRACE("i2c_read: timeout at ack\n"); 286 return B_TIMED_OUT; 287 } 288 set_scl_low(bus); 289 set_sda_high(bus); 290 291 size--; 292 bytes++; 293 } 294 295 i2c_stop(bus); 296 297 return B_OK; 298} 299 300 301status_t 302i2c_write_unlocked(i2c_bus *bus, int address, const void *data, int size) 303{ 304 const uint8 *bytes; 305 status_t status; 306 int ack; 307 308 if (size <= 0) 309 return B_BAD_VALUE; 310 311 status = i2c_start_address(bus, address, 0 /* 1 = read, 0 = write */); 312 if (status != B_OK) { 313 TRACE("i2c_write: error on i2c_start_address: %s\n", strerror(status)); 314 return status; 315 } 316 317 bytes = data; 318 while (size > 0) { 319 status = i2c_writebyte(bus, *bytes, &ack); 320 if (status == B_TIMED_OUT) { 321 TRACE("i2c_write: timeout error on byte %ld\n", bytes - (uint8 *)data); 322 return B_TIMED_OUT; 323 } 324 if (status != B_OK) { 325 TRACE("i2c_write: arbitration lost on byte %ld\n", bytes - (uint8 *)data); 326 break; 327 } 328 if (!ack) { 329 TRACE("i2c_write: error, got NACK on byte %ld\n", bytes - (uint8 *)data); 330 break; 331 } 332 bytes++; 333 size--; 334 } 335 i2c_stop(bus); 336 337 if (status != B_OK) 338 return status; 339 340 return ack ? B_OK : B_ERROR; 341} 342 343 344status_t 345i2c_read(i2c_bus *bus, int address, void *data, int size) 346{ 347 status_t res; 348 acquire_sem(bus->sem); 349 res = i2c_read_unlocked(bus, address, data, size); 350 release_sem(bus->sem); 351 return res; 352} 353 354 355status_t 356i2c_write(i2c_bus *bus, int address, const void *data, int size) 357{ 358 status_t res; 359 acquire_sem(bus->sem); 360 res = i2c_write_unlocked(bus, address, data, size); 361 release_sem(bus->sem); 362 return res; 363} 364 365 366status_t 367i2c_xfer(i2c_bus *bus, int address, 368 const void *write_data, int write_size, 369 void *read_data, int read_size) 370{ 371 status_t res; 372 373 acquire_sem(bus->sem); 374 375 if (write_data != 0 && write_size != 0) { 376 res = i2c_write_unlocked(bus, address, write_data, write_size); 377 if (res != B_OK) { 378 release_sem(bus->sem); 379 return res; 380 } 381 } 382 383 if (read_data != 0 && read_size != 0) { 384 res = i2c_read_unlocked(bus, address, read_data, read_size); 385 if (res != B_OK) { 386 release_sem(bus->sem); 387 return res; 388 } 389 } 390 391 release_sem(bus->sem); 392 393 return B_OK; 394} 395