atii2c.c revision 32b578d3
1/* 2 * Copyright 2003 through 2004 by Marc Aurele La France (TSI @ UQV), tsi@xfree86.org 3 * 4 * Permission to use, copy, modify, distribute, and sell this software and its 5 * documentation for any purpose is hereby granted without fee, provided that 6 * the above copyright notice appear in all copies and that both that copyright 7 * notice and this permission notice appear in supporting documentation, and 8 * that the name of Marc Aurele La France not be used in advertising or 9 * publicity pertaining to distribution of the software without specific, 10 * written prior permission. Marc Aurele La France makes no representations 11 * about the suitability of this software for any purpose. It is provided 12 * "as-is" without express or implied warranty. 13 * 14 * MARC AURELE LA FRANCE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 15 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO 16 * EVENT SHALL MARC AURELE LA FRANCE BE LIABLE FOR ANY SPECIAL, INDIRECT OR 17 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 18 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 19 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 20 * PERFORMANCE OF THIS SOFTWARE. 21 */ 22 23#ifdef HAVE_CONFIG_H 24#include "config.h" 25#endif 26 27#include "atii2c.h" 28#include "atimach64i2c.h" 29#include "atistruct.h" 30 31#include "xf86.h" 32 33/* This is derived from GATOS code, with a liberal sprinkling of bug fixes */ 34 35/* 36 * Some local macros for use by the mid-level I2C functions. 37 */ 38 39#define ATII2CDelay \ 40 (*pI2CBus->I2CUDelay)(pI2CBus, pI2CBus->HoldTime) 41 42 43#define ATII2CSCLDirOff \ 44 if (pATII2C->SCLDir != 0) \ 45 (*pATII2C->I2CSetBits)(pATII2C, pATI, \ 46 pATII2C->I2CCur & ~pATII2C->SCLDir) 47 48#define ATII2CSCLDirOn \ 49 if (pATII2C->SCLDir != 0) \ 50 (*pATII2C->I2CSetBits)(pATII2C, pATI, \ 51 pATII2C->I2CCur | pATII2C->SCLDir) 52 53#define ATII2CSDADirOff \ 54 if (pATII2C->SDADir != 0) \ 55 (*pATII2C->I2CSetBits)(pATII2C, pATI, \ 56 pATII2C->I2CCur & ~pATII2C->SDADir) 57 58#define ATII2CSDADirOn \ 59 if (pATII2C->SDADir != 0) \ 60 (*pATII2C->I2CSetBits)(pATII2C, pATI, \ 61 pATII2C->I2CCur | pATII2C->SDADir) 62 63 64#define ATII2CSCLBitGet \ 65 ((*pATII2C->I2CGetBits)(pATI) & pATII2C->SCLGet) 66 67#define ATII2CSCLBitOff \ 68 do \ 69 { \ 70 (*pATII2C->I2CSetBits)(pATII2C, pATI, \ 71 pATII2C->I2CCur & ~pATII2C->SCLSet); \ 72 ATII2CDelay; \ 73 } while (0) 74 75#define ATII2CSCLBitOn \ 76 do \ 77 { \ 78 (*pATII2C->I2CSetBits)(pATII2C, pATI, \ 79 pATII2C->I2CCur | pATII2C->SCLSet); \ 80 do /* Wait until all devices have released SCL */ \ 81 { \ 82 ATII2CDelay; \ 83 } while (ATII2CSCLBitGet == 0); \ 84 } while (0) 85 86 87#define ATII2CSDABitGet \ 88 ((*pATII2C->I2CGetBits)(pATI) & pATII2C->SDAGet) 89 90#define ATII2CSDABitOff \ 91 do \ 92 { \ 93 (*pATII2C->I2CSetBits)(pATII2C, pATI, \ 94 pATII2C->I2CCur & ~pATII2C->SDASet); \ 95 ATII2CDelay; \ 96 } while (0) 97 98#define ATII2CSDABitOn \ 99 do \ 100 { \ 101 (*pATII2C->I2CSetBits)(pATII2C, pATI, \ 102 pATII2C->I2CCur | pATII2C->SDASet); \ 103 ATII2CDelay; \ 104 } while (0) 105 106#define ATII2CSDABitSet(_flag) \ 107 do \ 108 { \ 109 if (_flag) \ 110 ATII2CSDABitOn; \ 111 else \ 112 ATII2CSDABitOff; \ 113 } while (0) 114 115 116/* 117 * ATII2CStart -- 118 * 119 * This function puts a start signal on the I2C bus. 120 */ 121static Bool 122ATII2CStart 123( 124 I2CBusPtr pI2CBus, 125 int timeout 126) 127{ 128 ATII2CPtr pATII2C = pI2CBus->DriverPrivate.ptr; 129 ATIPtr pATI = pATII2C->pATI; 130 131 (void)timeout; 132 133 /* 134 * Set I2C line directions to out-bound. SCL will remain out-bound until 135 * next I2C Stop. 136 */ 137 ATII2CSCLDirOn; 138 ATII2CSDADirOn; 139 140 /* 141 * Send Start bit. This is a pull-down of the data line while the clock 142 * line is pulled up. 143 */ 144 ATII2CSDABitOn; 145 ATII2CSCLBitOn; 146 ATII2CSDABitOff; 147 ATII2CSCLBitOff; 148 149 return TRUE; 150} 151 152/* 153 * ATII2CAddress -- 154 * 155 * This function puts an 8-bit address on the I2C bus. 156 */ 157static Bool 158ATII2CAddress 159( 160 I2CDevPtr pI2CDev, 161 I2CSlaveAddr Address 162) 163{ 164 I2CBusPtr pI2CBus = pI2CDev->pI2CBus; 165 166 /* Send low byte of device address */ 167 if ((*pI2CBus->I2CPutByte)(pI2CDev, (I2CByte)Address)) 168 { 169 /* Send top byte of address, if appropriate */ 170 if (((Address & 0x00F8U) != 0x00F0U) && 171 ((Address & 0x00FEU) != 0x0000U)) 172 return TRUE; 173 174 if ((*pI2CBus->I2CPutByte)(pI2CDev, (I2CByte)(Address >> 8))) 175 return TRUE; 176 } 177 178 /* Kill I2C transaction on failure */ 179 (*pI2CBus->I2CStop)(pI2CDev); 180 return FALSE; 181} 182 183/* 184 * ATII2CStop -- 185 * 186 * This function puts a stop signal on the I2C bus. 187 */ 188static void 189ATII2CStop 190( 191 I2CDevPtr pI2CDev 192) 193{ 194 I2CBusPtr pI2CBus = pI2CDev->pI2CBus; 195 ATII2CPtr pATII2C = pI2CBus->DriverPrivate.ptr; 196 ATIPtr pATI = pATII2C->pATI; 197 198 ATII2CSDADirOn; /* Set data line direction to out-bound */ 199 200 /* 201 * Send Stop bit. This is a pull-up of the data line while the clock line 202 * is pulled up. 203 */ 204 ATII2CSDABitOff; 205 ATII2CSCLBitOn; 206 ATII2CSDABitOn; 207 ATII2CSCLBitOff; 208 209 /* Reset I2C line directions to in-bound */ 210 ATII2CSCLDirOff; 211 ATII2CSDADirOff; 212} 213 214/* 215 * ATII2CPutByte -- 216 * 217 * This function puts an 8-bit value on the I2C bus, starting with its MSB. 218 */ 219static Bool 220ATII2CPutByte 221( 222 I2CDevPtr pI2CDev, 223 I2CByte Data 224) 225{ 226 I2CBusPtr pI2CBus = pI2CDev->pI2CBus; 227 ATII2CPtr pATII2C = pI2CBus->DriverPrivate.ptr; 228 ATIPtr pATI = pATII2C->pATI; 229 int i; 230 Bool Result; 231 232 ATII2CSDADirOn; /* Set data line direction to out-bound */ 233 234 /* Send data byte */ 235 for (i = 0; i < 8; i++) 236 { 237 ATII2CSDABitSet(Data & 0x80U); 238 ATII2CSCLBitOn; 239 ATII2CSCLBitOff; 240 241 Data <<= 1; 242 } 243 244 ATII2CSDABitOn; /* Release data line */ 245 246 ATII2CSDADirOff; /* Set data line direction to in-bound */ 247 248 ATII2CSCLBitOn; /* Start bit-read clock pulse */ 249 250 /* Get [N]ACK bit */ 251 if (ATII2CSDABitGet) 252 Result = FALSE; 253 else 254 Result = TRUE; 255 256 ATII2CSCLBitOff; /* End clock pulse */ 257 258 return Result; 259} 260 261/* 262 * ATII2CGetByte -- 263 * 264 * This function retrieves an 8-bit value from the I2C bus. 265 */ 266static Bool 267ATII2CGetByte 268( 269 I2CDevPtr pI2CDev, 270 I2CByte *pData, 271 Bool Last 272) 273{ 274 I2CBusPtr pI2CBus = pI2CDev->pI2CBus; 275 ATII2CPtr pATII2C = pI2CBus->DriverPrivate.ptr; 276 ATIPtr pATI = pATII2C->pATI; 277 unsigned long Value = 1; 278 279 do 280 { 281 ATII2CSCLBitOn; /* Start bit-read clock pulse */ 282 283 /* Accumulate bit into byte value */ 284 Value <<= 1; 285 if (ATII2CSDABitGet) 286 Value++; 287 288 ATII2CSCLBitOff; /* End clock pulse */ 289 } while (Value <= (unsigned long)((I2CByte)(-1))); 290 291 *pData = (I2CByte)Value; 292 293 ATII2CSDADirOn; /* Set data line direction to out-bound */ 294 295 /* Send [N]ACK bit */ 296 ATII2CSDABitSet(Last); 297 ATII2CSCLBitOn; 298 ATII2CSCLBitOff; 299 300 if (!Last) 301 ATII2CSDABitOn; /* Release data line */ 302 303 ATII2CSDADirOff; /* Set data line direction to in-bound */ 304 305 return TRUE; 306} 307 308/* 309 * ATICreateI2CBusRec -- 310 * 311 * This function is called to initialise an I2CBusRec. 312 */ 313I2CBusPtr 314ATICreateI2CBusRec 315( 316 int iScreen, 317 ATIPtr pATI, 318 char *BusName 319) 320{ 321 I2CBusPtr pI2CBus; 322 ATII2CPtr pATII2C = xnfcalloc(1, SizeOf(ATII2CRec)); 323 324 if (!(pI2CBus = xf86CreateI2CBusRec())) 325 { 326 xf86DrvMsg(iScreen, X_WARNING, "Unable to allocate I2C Bus record.\n"); 327 xfree(pATII2C); 328 return NULL; 329 } 330 331 /* Fill in generic structure fields */ 332 pI2CBus->BusName = BusName; 333 pI2CBus->scrnIndex = iScreen; 334 335 pI2CBus->I2CAddress = ATII2CAddress; 336 pI2CBus->I2CStart = ATII2CStart; 337 pI2CBus->I2CStop = ATII2CStop; 338 pI2CBus->I2CPutByte = ATII2CPutByte; 339 pI2CBus->I2CGetByte = ATII2CGetByte; 340 341 pI2CBus->DriverPrivate.ptr = pATII2C; 342 343 pATII2C->pATI = pATI; 344 345 if (xf86I2CBusInit(pI2CBus)) 346 return pI2CBus; 347 348 xf86DrvMsg(iScreen, X_WARNING, 349 "I2C bus %s initialisation failure.\n", BusName); 350 xf86DestroyI2CBusRec(pI2CBus, TRUE, TRUE); 351 xfree(pATII2C); 352 return NULL; 353} 354 355/* 356 * ATII2CPreInit -- 357 * 358 * This is called by ATIPreInit() to create I2C bus record(s) for the adapter. 359 */ 360void 361ATII2CPreInit 362( 363 ScrnInfoPtr pScreenInfo, 364 ATIPtr pATI 365) 366{ 367 if (!xf86LoadSubModule(pScreenInfo, "i2c")) 368 return; 369 370 ATIMach64I2CPreInit(pScreenInfo, pATI); 371} 372 373/* 374 * ATII2CFreeScreen -- 375 * 376 * This is called by ATIFreeScreen() to remove the driver's I2C interface. 377 */ 378void 379ATII2CFreeScreen 380( 381 int iScreen 382) 383{ 384 I2CBusPtr pI2CBus, *ppI2CBus; 385 ATII2CPtr pATII2C; 386 int nI2CBus; 387 388 nI2CBus = xf86I2CGetScreenBuses(iScreen, &ppI2CBus); 389 while (--nI2CBus >= 0) 390 { 391 pI2CBus = ppI2CBus[nI2CBus]; 392 pATII2C = pI2CBus->DriverPrivate.ptr; 393 394 xf86DestroyI2CBusRec(pI2CBus, TRUE, TRUE); 395 xfree(pATII2C); 396 } 397 398 xfree(ppI2CBus); 399} 400