1/***********************license start*************** 2 * Copyright (c) 2003-2010 Cavium Networks (support@cavium.com). All rights 3 * reserved. 4 * 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are 8 * met: 9 * 10 * * Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 13 * * Redistributions in binary form must reproduce the above 14 * copyright notice, this list of conditions and the following 15 * disclaimer in the documentation and/or other materials provided 16 * with the distribution. 17 18 * * Neither the name of Cavium Networks nor the names of 19 * its contributors may be used to endorse or promote products 20 * derived from this software without specific prior written 21 * permission. 22 23 * This Software, including technical data, may be subject to U.S. export control 24 * laws, including the U.S. Export Administration Act and its associated 25 * regulations, and may be subject to export or import regulations in other 26 * countries. 27 28 * TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS" 29 * AND WITH ALL FAULTS AND CAVIUM NETWORKS MAKES NO PROMISES, REPRESENTATIONS OR 30 * WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO 31 * THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR 32 * DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM 33 * SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE, 34 * MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF 35 * VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR 36 * CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT OF USE OR 37 * PERFORMANCE OF THE SOFTWARE LIES WITH YOU. 38 ***********************license end**************************************/ 39 40 41 42 43 44 45 46/** 47 * @file 48 * 49 * Interface to the TWSI / I2C bus 50 * 51 * <hr>$Revision: 49448 $<hr> 52 * 53 */ 54#ifdef CVMX_BUILD_FOR_LINUX_KERNEL 55#include <linux/i2c.h> 56 57#include <asm/octeon/cvmx.h> 58#include <asm/octeon/cvmx-twsi.h> 59#else 60#include "cvmx.h" 61#include "cvmx-twsi.h" 62#include "cvmx-csr-db.h" 63#endif 64 65//#define PRINT_TWSI_CONFIG 66#ifdef PRINT_TWSI_CONFIG 67#define twsi_printf printf 68#else 69#define twsi_printf(...) 70#define cvmx_csr_db_decode(...) 71#endif 72 73#ifdef CVMX_BUILD_FOR_LINUX_KERNEL 74static struct i2c_adapter *__cvmx_twsix_get_adapter(int twsi_id) 75{ 76# if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) 77 struct octeon_i2c { 78 wait_queue_head_t queue; 79 struct i2c_adapter adap; 80 int irq; 81 int twsi_freq; 82 int sys_freq; 83 resource_size_t twsi_phys; 84 void __iomem *twsi_base; 85 resource_size_t regsize; 86 struct device *dev; 87 int broken_irq_mode; 88 }; 89 struct i2c_adapter *adapter; 90 struct octeon_i2c *i2c; 91 92 adapter = i2c_get_adapter(0); 93 if (adapter == NULL) 94 return NULL; 95 i2c = container_of(adapter, struct octeon_i2c, adap); 96 return &i2c[twsi_id].adap; 97#else 98 return NULL; 99#endif 100} 101#endif 102 103 104/** 105 * Do a twsi read from a 7 bit device address using an (optional) internal address. 106 * Up to 8 bytes can be read at a time. 107 * 108 * @param twsi_id which Octeon TWSI bus to use 109 * @param dev_addr Device address (7 bit) 110 * @param internal_addr 111 * Internal address. Can be 0, 1 or 2 bytes in width 112 * @param num_bytes Number of data bytes to read 113 * @param ia_width_bytes 114 * Internal address size in bytes (0, 1, or 2) 115 * @param data Pointer argument where the read data is returned. 116 * 117 * @return read data returned in 'data' argument 118 * Number of bytes read on success 119 * -1 on failure 120 */ 121int cvmx_twsix_read_ia(int twsi_id, uint8_t dev_addr, uint16_t internal_addr, int num_bytes, int ia_width_bytes, uint64_t *data) 122{ 123#ifdef CVMX_BUILD_FOR_LINUX_KERNEL 124# if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) 125 struct i2c_adapter *adapter; 126 u8 data_buf[8]; 127 u8 addr_buf[8]; 128 struct i2c_msg msg[2]; 129 uint64_t r; 130 int i, j; 131 132 if (ia_width_bytes == 0) 133 return cvmx_twsix_read(twsi_id, dev_addr, num_bytes, data); 134 135 BUG_ON(ia_width_bytes > 2); 136 BUG_ON(num_bytes > 8 || num_bytes < 1); 137 138 adapter = __cvmx_twsix_get_adapter(twsi_id); 139 if (adapter == NULL) 140 return -1; 141 142 for (j = 0, i = ia_width_bytes - 1; i >= 0; i--, j++) 143 addr_buf[j] = (u8)(internal_addr >> (i * 8)); 144 145 msg[0].addr = dev_addr; 146 msg[0].flags = 0; 147 msg[0].len = ia_width_bytes; 148 msg[0].buf = addr_buf; 149 150 msg[1].addr = dev_addr; 151 msg[1].flags = I2C_M_RD; 152 msg[1].len = num_bytes; 153 msg[1].buf = data_buf; 154 155 i = i2c_transfer(adapter, msg, 2); 156 157 i2c_put_adapter(adapter); 158 159 if (i == 2) { 160 r = 0; 161 for (i = 0; i < num_bytes; i++) 162 r = (r << 8) | data_buf[i]; 163 *data = r; 164 return num_bytes; 165 } else { 166 return -1; 167 } 168# else 169 BUG(); /* The I2C driver is not compiled in */ 170# endif 171#else 172 cvmx_mio_twsx_sw_twsi_t sw_twsi_val; 173 cvmx_mio_twsx_sw_twsi_ext_t twsi_ext; 174 175 if (num_bytes < 1 || num_bytes > 8 || !data || ia_width_bytes < 0 || ia_width_bytes > 2) 176 return -1; 177 178 twsi_ext.u64 = 0; 179 sw_twsi_val.u64 = 0; 180 sw_twsi_val.s.v = 1; 181 sw_twsi_val.s.r = 1; 182 sw_twsi_val.s.sovr = 1; 183 sw_twsi_val.s.size = num_bytes - 1; 184 sw_twsi_val.s.a = dev_addr; 185 186 if (ia_width_bytes > 0) { 187 sw_twsi_val.s.op = 1; 188 sw_twsi_val.s.ia = (internal_addr >> 3) & 0x1f; 189 sw_twsi_val.s.eop_ia = internal_addr & 0x7; 190 } 191 if (ia_width_bytes == 2) { 192 sw_twsi_val.s.eia = 1; 193 twsi_ext.s.ia = internal_addr >> 8; 194 cvmx_write_csr(CVMX_MIO_TWSX_SW_TWSI_EXT(twsi_id), twsi_ext.u64); 195 } 196 197 cvmx_csr_db_decode(cvmx_get_proc_id(), CVMX_MIO_TWSX_SW_TWSI(twsi_id), sw_twsi_val.u64); 198 cvmx_write_csr(CVMX_MIO_TWSX_SW_TWSI(twsi_id), sw_twsi_val.u64); 199 while (((cvmx_mio_twsx_sw_twsi_t)(sw_twsi_val.u64 = cvmx_read_csr(CVMX_MIO_TWSX_SW_TWSI(twsi_id)))).s.v) 200 ; 201 twsi_printf("Results:\n"); 202 cvmx_csr_db_decode(cvmx_get_proc_id(), CVMX_MIO_TWSX_SW_TWSI(twsi_id), sw_twsi_val.u64); 203 if (!sw_twsi_val.s.r) 204 return -1; 205 206 *data = (sw_twsi_val.s.d & (0xFFFFFFFF >> (32 - num_bytes*8))); 207 if (num_bytes > 4) { 208 twsi_ext.u64 = cvmx_read_csr(CVMX_MIO_TWSX_SW_TWSI_EXT(twsi_id)); 209 *data |= ((unsigned long long)(twsi_ext.s.d & (0xFFFFFFFF >> (32 - num_bytes*8))) << 32); 210 } 211 return num_bytes; 212#endif 213} 214 215/** 216 * Read from a TWSI device (7 bit device address only) without generating any 217 * internal addresses. 218 * Read from 1-8 bytes and returns them in the data pointer. 219 * 220 * @param twsi_id TWSI interface on Octeon to use 221 * @param dev_addr TWSI device address (7 bit only) 222 * @param num_bytes number of bytes to read 223 * @param data Pointer to data read from TWSI device 224 * 225 * @return Number of bytes read on success 226 * -1 on error 227 */ 228int cvmx_twsix_read(int twsi_id, uint8_t dev_addr, int num_bytes, uint64_t *data) 229{ 230#ifdef CVMX_BUILD_FOR_LINUX_KERNEL 231# if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) 232 struct i2c_adapter *adapter; 233 u8 data_buf[8]; 234 struct i2c_msg msg[1]; 235 uint64_t r; 236 int i; 237 238 BUG_ON(num_bytes > 8 || num_bytes < 1); 239 240 adapter = __cvmx_twsix_get_adapter(twsi_id); 241 if (adapter == NULL) 242 return -1; 243 244 msg[0].addr = dev_addr; 245 msg[0].flags = I2C_M_RD; 246 msg[0].len = num_bytes; 247 msg[0].buf = data_buf; 248 249 i = i2c_transfer(adapter, msg, 1); 250 251 i2c_put_adapter(adapter); 252 253 if (i == 1) { 254 r = 0; 255 for (i = 0; i < num_bytes; i++) 256 r = (r << 8) | data_buf[i]; 257 *data = r; 258 return num_bytes; 259 } else { 260 return -1; 261 } 262# else 263 BUG(); /* The I2C driver is not compiled in */ 264# endif 265#else 266 cvmx_mio_twsx_sw_twsi_t sw_twsi_val; 267 cvmx_mio_twsx_sw_twsi_ext_t twsi_ext; 268 269 if (num_bytes > 8 || num_bytes < 1) 270 return -1; 271 272 sw_twsi_val.u64 = 0; 273 sw_twsi_val.s.v = 1; 274 sw_twsi_val.s.r = 1; 275 sw_twsi_val.s.a = dev_addr; 276 sw_twsi_val.s.sovr = 1; 277 sw_twsi_val.s.size = num_bytes - 1; 278 279 cvmx_csr_db_decode(cvmx_get_proc_id(), CVMX_MIO_TWSX_SW_TWSI(twsi_id), sw_twsi_val.u64); 280 cvmx_write_csr(CVMX_MIO_TWSX_SW_TWSI(twsi_id), sw_twsi_val.u64); 281 while (((cvmx_mio_twsx_sw_twsi_t)(sw_twsi_val.u64 = cvmx_read_csr(CVMX_MIO_TWSX_SW_TWSI(twsi_id)))).s.v) 282 ; 283 twsi_printf("Results:\n"); 284 cvmx_csr_db_decode(cvmx_get_proc_id(), CVMX_MIO_TWSX_SW_TWSI(twsi_id), sw_twsi_val.u64); 285 if (!sw_twsi_val.s.r) 286 return -1; 287 288 *data = (sw_twsi_val.s.d & (0xFFFFFFFF >> (32 - num_bytes*8))); 289 if (num_bytes > 4) { 290 twsi_ext.u64 = cvmx_read_csr(CVMX_MIO_TWSX_SW_TWSI_EXT(twsi_id)); 291 *data |= ((unsigned long long)(twsi_ext.s.d & (0xFFFFFFFF >> (32 - num_bytes*8))) << 32); 292 } 293 return num_bytes; 294#endif 295} 296 297/** 298 * Perform a twsi write operation to a 7 bit device address. 299 * 300 * Note that many eeprom devices have page restrictions regarding address boundaries 301 * that can be crossed in one write operation. This is device dependent, and this routine 302 * does nothing in this regard. 303 * This command does not generate any internal addressess. 304 * 305 * @param twsi_id Octeon TWSI interface to use 306 * @param dev_addr TWSI device address 307 * @param num_bytes Number of bytes to write (between 1 and 8 inclusive) 308 * @param data Data to write 309 * 310 * @return 0 on success 311 * -1 on failure 312 */ 313int cvmx_twsix_write(int twsi_id, uint8_t dev_addr, int num_bytes, uint64_t data) 314{ 315#ifdef CVMX_BUILD_FOR_LINUX_KERNEL 316# if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) 317 struct i2c_adapter *adapter; 318 u8 data_buf[8]; 319 struct i2c_msg msg[1]; 320 int i, j; 321 322 BUG_ON(num_bytes > 8 || num_bytes < 1); 323 324 adapter = __cvmx_twsix_get_adapter(twsi_id); 325 if (adapter == NULL) 326 return -1; 327 328 for (j = 0, i = num_bytes - 1; i >= 0; i--, j++) 329 data_buf[j] = (u8)(data >> (i * 8)); 330 331 msg[1].addr = dev_addr; 332 msg[1].flags = 0; 333 msg[1].len = num_bytes; 334 msg[1].buf = data_buf; 335 336 i = i2c_transfer(adapter, msg, 1); 337 338 i2c_put_adapter(adapter); 339 340 if (i == 1) 341 return num_bytes; 342 else 343 return -1; 344# else 345 BUG(); /* The I2C driver is not compiled in */ 346# endif 347#else 348 cvmx_mio_twsx_sw_twsi_t sw_twsi_val; 349 350 if (num_bytes > 8 || num_bytes < 1) 351 return -1; 352 353 sw_twsi_val.u64 = 0; 354 sw_twsi_val.s.v = 1; 355 sw_twsi_val.s.a = dev_addr; 356 sw_twsi_val.s.d = data & 0xffffffff; 357 sw_twsi_val.s.sovr = 1; 358 sw_twsi_val.s.size = num_bytes - 1; 359 if (num_bytes > 4) { 360 /* Upper four bytes go into a separate register */ 361 cvmx_mio_twsx_sw_twsi_ext_t twsi_ext; 362 twsi_ext.u64 = 0; 363 twsi_ext.s.d = data >> 32; 364 cvmx_write_csr(CVMX_MIO_TWSX_SW_TWSI_EXT(twsi_id), twsi_ext.u64); 365 } 366 cvmx_csr_db_decode(cvmx_get_proc_id(), CVMX_MIO_TWSX_SW_TWSI(twsi_id), sw_twsi_val.u64); 367 cvmx_write_csr(CVMX_MIO_TWSX_SW_TWSI(twsi_id), sw_twsi_val.u64); 368 while (((cvmx_mio_twsx_sw_twsi_t)(sw_twsi_val.u64 = cvmx_read_csr(CVMX_MIO_TWSX_SW_TWSI(twsi_id)))).s.v) 369 ; 370 twsi_printf("Results:\n"); 371 cvmx_csr_db_decode(cvmx_get_proc_id(), CVMX_MIO_TWSX_SW_TWSI(twsi_id), sw_twsi_val.u64); 372 if (!sw_twsi_val.s.r) 373 return -1; 374 375 return 0; 376#endif 377} 378 379/** 380 * Write 1-8 bytes to a TWSI device using an internal address. 381 * 382 * @param twsi_id which TWSI interface on Octeon to use 383 * @param dev_addr TWSI device address (7 bit only) 384 * @param internal_addr 385 * TWSI internal address (0, 8, or 16 bits) 386 * @param num_bytes Number of bytes to write (1-8) 387 * @param ia_width_bytes 388 * internal address width, in bytes (0, 1, 2) 389 * @param data Data to write. Data is written MSB first on the twsi bus, and only the lower 390 * num_bytes bytes of the argument are valid. (If a 2 byte write is done, only 391 * the low 2 bytes of the argument is used. 392 * 393 * @return Number of bytes read on success, 394 * -1 on error 395 */ 396int cvmx_twsix_write_ia(int twsi_id, uint8_t dev_addr, uint16_t internal_addr, int num_bytes, int ia_width_bytes, uint64_t data) 397{ 398#ifdef CVMX_BUILD_FOR_LINUX_KERNEL 399# if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) 400 struct i2c_adapter *adapter; 401 u8 data_buf[8]; 402 u8 addr_buf[8]; 403 struct i2c_msg msg[2]; 404 int i, j; 405 406 if (ia_width_bytes == 0) 407 return cvmx_twsix_write(twsi_id, dev_addr, num_bytes, data); 408 409 BUG_ON(ia_width_bytes > 2); 410 BUG_ON(num_bytes > 8 || num_bytes < 1); 411 412 adapter = __cvmx_twsix_get_adapter(twsi_id); 413 if (adapter == NULL) 414 return -1; 415 416 417 for (j = 0, i = ia_width_bytes - 1; i >= 0; i--, j++) 418 addr_buf[j] = (u8)(internal_addr >> (i * 8)); 419 420 for (j = 0, i = num_bytes - 1; i >= 0; i--, j++) 421 data_buf[j] = (u8)(data >> (i * 8)); 422 423 msg[0].addr = dev_addr; 424 msg[0].flags = 0; 425 msg[0].len = ia_width_bytes; 426 msg[0].buf = addr_buf; 427 428 msg[1].addr = dev_addr; 429 msg[1].flags = 0; 430 msg[1].len = num_bytes; 431 msg[1].buf = data_buf; 432 433 i = i2c_transfer(adapter, msg, 2); 434 435 i2c_put_adapter(adapter); 436 437 if (i == 2) { 438 /* Poll until reads succeed, or polling times out */ 439 int to = 100; 440 while (to-- > 0) { 441 uint64_t data; 442 if (cvmx_twsix_read(twsi_id, dev_addr, 1, &data) >= 0) 443 break; 444 } 445 } 446 447 if (i == 2) 448 return num_bytes; 449 else 450 return -1; 451# else 452 BUG(); /* The I2C driver is not compiled in */ 453# endif 454#else 455 cvmx_mio_twsx_sw_twsi_t sw_twsi_val; 456 cvmx_mio_twsx_sw_twsi_ext_t twsi_ext; 457 int to; 458 459 if (num_bytes < 1 || num_bytes > 8 || ia_width_bytes < 0 || ia_width_bytes > 2) 460 return -1; 461 462 twsi_ext.u64 = 0; 463 464 sw_twsi_val.u64 = 0; 465 sw_twsi_val.s.v = 1; 466 sw_twsi_val.s.sovr = 1; 467 sw_twsi_val.s.size = num_bytes - 1; 468 sw_twsi_val.s.a = dev_addr; 469 sw_twsi_val.s.d = 0xFFFFFFFF & data; 470 471 if (ia_width_bytes > 0) { 472 sw_twsi_val.s.op = 1; 473 sw_twsi_val.s.ia = (internal_addr >> 3) & 0x1f; 474 sw_twsi_val.s.eop_ia = internal_addr & 0x7; 475 } 476 if (ia_width_bytes == 2) { 477 sw_twsi_val.s.eia = 1; 478 twsi_ext.s.ia = internal_addr >> 8; 479 } 480 if (num_bytes > 4) 481 twsi_ext.s.d = data >> 32; 482 483 twsi_printf("%s: twsi_id=%x, dev_addr=%x, internal_addr=%x\n\tnum_bytes=%d, ia_width_bytes=%d, data=%lx\n", 484 __FUNCTION__, twsi_id, dev_addr, internal_addr, num_bytes, ia_width_bytes, data); 485 cvmx_csr_db_decode(cvmx_get_proc_id(), CVMX_MIO_TWSX_SW_TWSI_EXT(twsi_id), twsi_ext.u64); 486 cvmx_write_csr(CVMX_MIO_TWSX_SW_TWSI_EXT(twsi_id), twsi_ext.u64); 487 cvmx_csr_db_decode(cvmx_get_proc_id(), CVMX_MIO_TWSX_SW_TWSI(twsi_id), sw_twsi_val.u64); 488 cvmx_write_csr(CVMX_MIO_TWSX_SW_TWSI(twsi_id), sw_twsi_val.u64); 489 while (((cvmx_mio_twsx_sw_twsi_t)(sw_twsi_val.u64 = cvmx_read_csr(CVMX_MIO_TWSX_SW_TWSI(twsi_id)))).s.v) 490 ; 491 twsi_printf("Results:\n"); 492 cvmx_csr_db_decode(cvmx_get_proc_id(), CVMX_MIO_TWSX_SW_TWSI(twsi_id), sw_twsi_val.u64); 493 494 /* Poll until reads succeed, or polling times out */ 495 to = 100; 496 while (to-- > 0) { 497 uint64_t data; 498 if (cvmx_twsix_read(twsi_id, dev_addr, 1, &data) >= 0) 499 break; 500 } 501 if (to <= 0) 502 return -1; 503 504 return num_bytes; 505#endif 506} 507