via_i2c.c revision 7e6fb56f
17e6fb56fSmrg/* 27e6fb56fSmrg * Copyright 2004 The Unichrome Project [unichrome.sf.net] 37e6fb56fSmrg * Copyright 1998-2003 VIA Technologies, Inc. All Rights Reserved. 47e6fb56fSmrg * Copyright 2001-2003 S3 Graphics, Inc. All Rights Reserved. 57e6fb56fSmrg * 67e6fb56fSmrg * Permission is hereby granted, free of charge, to any person obtaining a 77e6fb56fSmrg * copy of this software and associated documentation files (the "Software"), 87e6fb56fSmrg * to deal in the Software without restriction, including without limitation 97e6fb56fSmrg * the rights to use, copy, modify, merge, publish, distribute, sub license, 107e6fb56fSmrg * and/or sell copies of the Software, and to permit persons to whom the 117e6fb56fSmrg * Software is furnished to do so, subject to the following conditions: 127e6fb56fSmrg * 137e6fb56fSmrg * The above copyright notice and this permission notice (including the 147e6fb56fSmrg * next paragraph) shall be included in all copies or substantial portions 157e6fb56fSmrg * of the Software. 167e6fb56fSmrg * 177e6fb56fSmrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 187e6fb56fSmrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 197e6fb56fSmrg * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL 207e6fb56fSmrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 217e6fb56fSmrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 227e6fb56fSmrg * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 237e6fb56fSmrg * DEALINGS IN THE SOFTWARE. 247e6fb56fSmrg */ 257e6fb56fSmrg 267e6fb56fSmrg/* 277e6fb56fSmrg * Implements three i2c busses through registers SR26, SR2c, and SR31. 287e6fb56fSmrg */ 297e6fb56fSmrg 307e6fb56fSmrg#ifdef HAVE_CONFIG_H 317e6fb56fSmrg#include "config.h" 327e6fb56fSmrg#endif 337e6fb56fSmrg 347e6fb56fSmrg#include "via_driver.h" 357e6fb56fSmrg#include "via_vgahw.h" 367e6fb56fSmrg 377e6fb56fSmrg#define SDA_READ 0x04 387e6fb56fSmrg#define SCL_READ 0x08 397e6fb56fSmrg#define SDA_WRITE 0x10 407e6fb56fSmrg#define SCL_WRITE 0x20 417e6fb56fSmrg 427e6fb56fSmrg 437e6fb56fSmrg/* 447e6fb56fSmrg * CRT I2C 457e6fb56fSmrg */ 467e6fb56fSmrg 477e6fb56fSmrgstatic void 487e6fb56fSmrgViaI2C1PutBits(I2CBusPtr Bus, int clock, int data) 497e6fb56fSmrg{ 507e6fb56fSmrg vgaHWPtr hwp = VGAHWPTR(xf86Screens[Bus->scrnIndex]); 517e6fb56fSmrg CARD8 value = 0x01; /* Enable */ 527e6fb56fSmrg 537e6fb56fSmrg if (clock) 547e6fb56fSmrg value |= SCL_WRITE; 557e6fb56fSmrg 567e6fb56fSmrg if (data) 577e6fb56fSmrg value |= SDA_WRITE; 587e6fb56fSmrg 597e6fb56fSmrg ViaSeqMask(hwp, 0x26, value, 0x01 | SCL_WRITE | SDA_WRITE); 607e6fb56fSmrg} 617e6fb56fSmrg 627e6fb56fSmrgstatic void 637e6fb56fSmrgViaI2C1GetBits(I2CBusPtr Bus, int *clock, int *data) 647e6fb56fSmrg{ 657e6fb56fSmrg vgaHWPtr hwp = VGAHWPTR(xf86Screens[Bus->scrnIndex]); 667e6fb56fSmrg CARD8 value = hwp->readSeq(hwp, 0x26); 677e6fb56fSmrg 687e6fb56fSmrg *clock = (value & SCL_READ) != 0; 697e6fb56fSmrg *data = (value & SDA_READ) != 0; 707e6fb56fSmrg} 717e6fb56fSmrg 727e6fb56fSmrgstatic I2CBusPtr 737e6fb56fSmrgViaI2CBus1Init(int scrnIndex) 747e6fb56fSmrg{ 757e6fb56fSmrg I2CBusPtr pI2CBus = xf86CreateI2CBusRec(); 767e6fb56fSmrg 777e6fb56fSmrg DEBUG(xf86DrvMsg(scrnIndex, X_INFO, "ViaI2CBus1Init\n")); 787e6fb56fSmrg 797e6fb56fSmrg if (!pI2CBus) 807e6fb56fSmrg return NULL; 817e6fb56fSmrg 827e6fb56fSmrg pI2CBus->BusName = "I2C bus 1"; 837e6fb56fSmrg pI2CBus->scrnIndex = scrnIndex; 847e6fb56fSmrg pI2CBus->I2CPutBits = ViaI2C1PutBits; 857e6fb56fSmrg pI2CBus->I2CGetBits = ViaI2C1GetBits; 867e6fb56fSmrg 877e6fb56fSmrg if (!xf86I2CBusInit(pI2CBus)) { 887e6fb56fSmrg xf86DestroyI2CBusRec(pI2CBus, TRUE, FALSE); 897e6fb56fSmrg return NULL; 907e6fb56fSmrg } 917e6fb56fSmrg 927e6fb56fSmrg return pI2CBus; 937e6fb56fSmrg} 947e6fb56fSmrg 957e6fb56fSmrg 967e6fb56fSmrg/* 977e6fb56fSmrg * First data bus I2C: tends to have TV-encoders. 987e6fb56fSmrg */ 997e6fb56fSmrg 1007e6fb56fSmrgstatic void 1017e6fb56fSmrgViaI2C2PutBits(I2CBusPtr Bus, int clock, int data) 1027e6fb56fSmrg{ 1037e6fb56fSmrg vgaHWPtr hwp = VGAHWPTR(xf86Screens[Bus->scrnIndex]); 1047e6fb56fSmrg CARD8 value = 0x01; /* Enable */ 1057e6fb56fSmrg 1067e6fb56fSmrg if (clock) 1077e6fb56fSmrg value |= SCL_WRITE; 1087e6fb56fSmrg 1097e6fb56fSmrg if (data) 1107e6fb56fSmrg value |= SDA_WRITE; 1117e6fb56fSmrg 1127e6fb56fSmrg ViaSeqMask(hwp, 0x31, value, 0x01 | SCL_WRITE | SDA_WRITE); 1137e6fb56fSmrg} 1147e6fb56fSmrg 1157e6fb56fSmrgstatic void 1167e6fb56fSmrgViaI2C2GetBits(I2CBusPtr Bus, int *clock, int *data) 1177e6fb56fSmrg{ 1187e6fb56fSmrg vgaHWPtr hwp = VGAHWPTR(xf86Screens[Bus->scrnIndex]); 1197e6fb56fSmrg CARD8 value = hwp->readSeq(hwp, 0x31); 1207e6fb56fSmrg 1217e6fb56fSmrg *clock = (value & SCL_READ) != 0; 1227e6fb56fSmrg *data = (value & SDA_READ) != 0; 1237e6fb56fSmrg} 1247e6fb56fSmrg 1257e6fb56fSmrgstatic I2CBusPtr 1267e6fb56fSmrgViaI2CBus2Init(int scrnIndex) 1277e6fb56fSmrg{ 1287e6fb56fSmrg I2CBusPtr pI2CBus = xf86CreateI2CBusRec(); 1297e6fb56fSmrg 1307e6fb56fSmrg DEBUG(xf86DrvMsg(scrnIndex, X_INFO, "ViaI2cBus2Init\n")); 1317e6fb56fSmrg 1327e6fb56fSmrg if (!pI2CBus) 1337e6fb56fSmrg return NULL; 1347e6fb56fSmrg 1357e6fb56fSmrg pI2CBus->BusName = "I2C bus 2"; 1367e6fb56fSmrg pI2CBus->scrnIndex = scrnIndex; 1377e6fb56fSmrg pI2CBus->I2CPutBits = ViaI2C2PutBits; 1387e6fb56fSmrg pI2CBus->I2CGetBits = ViaI2C2GetBits; 1397e6fb56fSmrg 1407e6fb56fSmrg if (!xf86I2CBusInit(pI2CBus)) { 1417e6fb56fSmrg xf86DestroyI2CBusRec(pI2CBus, TRUE, FALSE); 1427e6fb56fSmrg return NULL; 1437e6fb56fSmrg } 1447e6fb56fSmrg 1457e6fb56fSmrg return pI2CBus; 1467e6fb56fSmrg} 1477e6fb56fSmrg 1487e6fb56fSmrg 1497e6fb56fSmrg/* 1507e6fb56fSmrg * A third I2C bus is implemented by a few IO pins. 1517e6fb56fSmrg * Requires higher level functions to be used properly. 1527e6fb56fSmrg */ 1537e6fb56fSmrg 1547e6fb56fSmrgstatic Bool 1557e6fb56fSmrgViaI2C3Start(I2CBusPtr b, int timeout) 1567e6fb56fSmrg{ 1577e6fb56fSmrg vgaHWPtr hwp = VGAHWPTR(xf86Screens[b->scrnIndex]); 1587e6fb56fSmrg 1597e6fb56fSmrg ViaSeqMask(hwp, 0x2C, 0xF0, 0xF0); 1607e6fb56fSmrg b->I2CUDelay(b, b->RiseFallTime); 1617e6fb56fSmrg 1627e6fb56fSmrg ViaSeqMask(hwp, 0x2C, 0x00, 0x10); 1637e6fb56fSmrg b->I2CUDelay(b, b->HoldTime); 1647e6fb56fSmrg ViaSeqMask(hwp, 0x2C, 0x00, 0x20); 1657e6fb56fSmrg b->I2CUDelay(b, b->HoldTime); 1667e6fb56fSmrg 1677e6fb56fSmrg return TRUE; 1687e6fb56fSmrg} 1697e6fb56fSmrg 1707e6fb56fSmrgstatic Bool 1717e6fb56fSmrgViaI2C3Address(I2CDevPtr d, I2CSlaveAddr addr) 1727e6fb56fSmrg{ 1737e6fb56fSmrg I2CBusPtr b = d->pI2CBus; 1747e6fb56fSmrg 1757e6fb56fSmrg#ifdef X_NEED_I2CSTART 1767e6fb56fSmrg if (b->I2CStart(d->pI2CBus, d->StartTimeout)) { 1777e6fb56fSmrg#else 1787e6fb56fSmrg if (ViaI2C3Start(d->pI2CBus, d->StartTimeout)) { 1797e6fb56fSmrg#endif 1807e6fb56fSmrg if (b->I2CPutByte(d, addr & 0xFF)) { 1817e6fb56fSmrg if ((addr & 0xF8) != 0xF0 && (addr & 0xFE) != 0x00) 1827e6fb56fSmrg return TRUE; 1837e6fb56fSmrg 1847e6fb56fSmrg if (b->I2CPutByte(d, (addr >> 8) & 0xFF)) 1857e6fb56fSmrg return TRUE; 1867e6fb56fSmrg } 1877e6fb56fSmrg 1887e6fb56fSmrg b->I2CStop(d); 1897e6fb56fSmrg } 1907e6fb56fSmrg 1917e6fb56fSmrg return FALSE; 1927e6fb56fSmrg} 1937e6fb56fSmrg 1947e6fb56fSmrgstatic void 1957e6fb56fSmrgViaI2C3Stop(I2CDevPtr d) 1967e6fb56fSmrg{ 1977e6fb56fSmrg I2CBusPtr b = d->pI2CBus; 1987e6fb56fSmrg vgaHWPtr hwp = VGAHWPTR(xf86Screens[b->scrnIndex]); 1997e6fb56fSmrg 2007e6fb56fSmrg ViaSeqMask(hwp, 0x2C, 0xC0, 0xF0); 2017e6fb56fSmrg b->I2CUDelay(b, b->RiseFallTime); 2027e6fb56fSmrg 2037e6fb56fSmrg ViaSeqMask(hwp, 0x2C, 0x20, 0x20); 2047e6fb56fSmrg b->I2CUDelay(b, b->HoldTime); 2057e6fb56fSmrg 2067e6fb56fSmrg ViaSeqMask(hwp, 0x2C, 0x10, 0x10); 2077e6fb56fSmrg b->I2CUDelay(b, b->HoldTime); 2087e6fb56fSmrg 2097e6fb56fSmrg ViaSeqMask(hwp, 0x2C, 0x00, 0x20); 2107e6fb56fSmrg b->I2CUDelay(b, b->HoldTime); 2117e6fb56fSmrg} 2127e6fb56fSmrg 2137e6fb56fSmrgstatic void 2147e6fb56fSmrgViaI2C3PutBit(I2CBusPtr b, Bool sda, int timeout) 2157e6fb56fSmrg{ 2167e6fb56fSmrg vgaHWPtr hwp = VGAHWPTR(xf86Screens[b->scrnIndex]); 2177e6fb56fSmrg 2187e6fb56fSmrg if (sda) 2197e6fb56fSmrg ViaSeqMask(hwp, 0x2C, 0x50, 0x50); 2207e6fb56fSmrg else 2217e6fb56fSmrg ViaSeqMask(hwp, 0x2C, 0x40, 0x50); 2227e6fb56fSmrg b->I2CUDelay(b, b->RiseFallTime / 5); 2237e6fb56fSmrg 2247e6fb56fSmrg ViaSeqMask(hwp, 0x2C, 0xA0, 0xA0); 2257e6fb56fSmrg b->I2CUDelay(b, b->HoldTime); 2267e6fb56fSmrg b->I2CUDelay(b, timeout); 2277e6fb56fSmrg 2287e6fb56fSmrg ViaSeqMask(hwp, 0x2C, 0x80, 0xA0); 2297e6fb56fSmrg b->I2CUDelay(b, b->RiseFallTime / 5); 2307e6fb56fSmrg} 2317e6fb56fSmrg 2327e6fb56fSmrgstatic Bool 2337e6fb56fSmrgViaI2C3PutByte(I2CDevPtr d, I2CByte data) 2347e6fb56fSmrg{ 2357e6fb56fSmrg I2CBusPtr b = d->pI2CBus; 2367e6fb56fSmrg vgaHWPtr hwp = VGAHWPTR(xf86Screens[b->scrnIndex]); 2377e6fb56fSmrg Bool ret; 2387e6fb56fSmrg int i; 2397e6fb56fSmrg 2407e6fb56fSmrg for (i = 7; i >= 0; i--) 2417e6fb56fSmrg ViaI2C3PutBit(b, (data >> i) & 0x01, b->BitTimeout); 2427e6fb56fSmrg 2437e6fb56fSmrg /* Raise first to avoid false positives. */ 2447e6fb56fSmrg ViaSeqMask(hwp, 0x2C, 0x50, 0x50); 2457e6fb56fSmrg ViaSeqMask(hwp, 0x2C, 0x00, 0x40); 2467e6fb56fSmrg b->I2CUDelay(b, b->RiseFallTime); 2477e6fb56fSmrg ViaSeqMask(hwp, 0x2C, 0xA0, 0xA0); 2487e6fb56fSmrg 2497e6fb56fSmrg if (hwp->readSeq(hwp, 0x2C) & 0x04) 2507e6fb56fSmrg ret = FALSE; 2517e6fb56fSmrg else 2527e6fb56fSmrg ret = TRUE; 2537e6fb56fSmrg 2547e6fb56fSmrg ViaSeqMask(hwp, 0x2C, 0x80, 0xA0); 2557e6fb56fSmrg b->I2CUDelay(b, b->RiseFallTime); 2567e6fb56fSmrg 2577e6fb56fSmrg return ret; 2587e6fb56fSmrg} 2597e6fb56fSmrg 2607e6fb56fSmrgstatic Bool 2617e6fb56fSmrgViaI2C3GetBit(I2CBusPtr b, int timeout) 2627e6fb56fSmrg{ 2637e6fb56fSmrg vgaHWPtr hwp = VGAHWPTR(xf86Screens[b->scrnIndex]); 2647e6fb56fSmrg Bool ret; 2657e6fb56fSmrg 2667e6fb56fSmrg ViaSeqMask(hwp, 0x2c, 0x80, 0xC0); 2677e6fb56fSmrg b->I2CUDelay(b, b->RiseFallTime / 5); 2687e6fb56fSmrg ViaSeqMask(hwp, 0x2c, 0xA0, 0xA0); 2697e6fb56fSmrg b->I2CUDelay(b, 3 * b->HoldTime); 2707e6fb56fSmrg b->I2CUDelay(b, timeout); 2717e6fb56fSmrg 2727e6fb56fSmrg if (hwp->readSeq(hwp, 0x2C) & 0x04) 2737e6fb56fSmrg ret = TRUE; 2747e6fb56fSmrg else 2757e6fb56fSmrg ret = FALSE; 2767e6fb56fSmrg 2777e6fb56fSmrg ViaSeqMask(hwp, 0x2C, 0x80, 0xA0); 2787e6fb56fSmrg b->I2CUDelay(b, b->HoldTime); 2797e6fb56fSmrg b->I2CUDelay(b, b->RiseFallTime / 5); 2807e6fb56fSmrg 2817e6fb56fSmrg return ret; 2827e6fb56fSmrg} 2837e6fb56fSmrg 2847e6fb56fSmrgstatic Bool 2857e6fb56fSmrgViaI2C3GetByte(I2CDevPtr d, I2CByte * data, Bool last) 2867e6fb56fSmrg{ 2877e6fb56fSmrg I2CBusPtr b = d->pI2CBus; 2887e6fb56fSmrg vgaHWPtr hwp = VGAHWPTR(xf86Screens[b->scrnIndex]); 2897e6fb56fSmrg int i; 2907e6fb56fSmrg 2917e6fb56fSmrg *data = 0x00; 2927e6fb56fSmrg 2937e6fb56fSmrg for (i = 7; i >= 0; i--) 2947e6fb56fSmrg if (ViaI2C3GetBit(b, b->BitTimeout)) 2957e6fb56fSmrg *data |= 0x01 << i; 2967e6fb56fSmrg 2977e6fb56fSmrg if (last) /* send NACK */ 2987e6fb56fSmrg ViaSeqMask(hwp, 0x2C, 0x50, 0x50); 2997e6fb56fSmrg else /* send ACK */ 3007e6fb56fSmrg ViaSeqMask(hwp, 0x2C, 0x40, 0x50); 3017e6fb56fSmrg 3027e6fb56fSmrg ViaSeqMask(hwp, 0x2C, 0xA0, 0xA0); 3037e6fb56fSmrg b->I2CUDelay(b, b->HoldTime); 3047e6fb56fSmrg 3057e6fb56fSmrg ViaSeqMask(hwp, 0x2C, 0x80, 0xA0); 3067e6fb56fSmrg 3077e6fb56fSmrg return TRUE; 3087e6fb56fSmrg} 3097e6fb56fSmrg 3107e6fb56fSmrgstatic I2CBusPtr 3117e6fb56fSmrgViaI2CBus3Init(int scrnIndex) 3127e6fb56fSmrg{ 3137e6fb56fSmrg I2CBusPtr pI2CBus = xf86CreateI2CBusRec(); 3147e6fb56fSmrg 3157e6fb56fSmrg DEBUG(xf86DrvMsg(scrnIndex, X_INFO, "ViaI2CBus3Init\n")); 3167e6fb56fSmrg 3177e6fb56fSmrg if (!pI2CBus) 3187e6fb56fSmrg return NULL; 3197e6fb56fSmrg 3207e6fb56fSmrg pI2CBus->BusName = "I2C bus 3"; 3217e6fb56fSmrg pI2CBus->scrnIndex = scrnIndex; 3227e6fb56fSmrg pI2CBus->I2CAddress = ViaI2C3Address; 3237e6fb56fSmrg#ifdef X_NEED_I2CSTART 3247e6fb56fSmrg pI2CBus->I2CStart = ViaI2C3Start; 3257e6fb56fSmrg#endif 3267e6fb56fSmrg pI2CBus->I2CStop = ViaI2C3Stop; 3277e6fb56fSmrg pI2CBus->I2CPutByte = ViaI2C3PutByte; 3287e6fb56fSmrg pI2CBus->I2CGetByte = ViaI2C3GetByte; 3297e6fb56fSmrg 3307e6fb56fSmrg pI2CBus->HoldTime = 10; 3317e6fb56fSmrg pI2CBus->BitTimeout = 10; 3327e6fb56fSmrg pI2CBus->ByteTimeout = 10; 3337e6fb56fSmrg pI2CBus->StartTimeout = 10; 3347e6fb56fSmrg 3357e6fb56fSmrg if (!xf86I2CBusInit(pI2CBus)) { 3367e6fb56fSmrg xf86DestroyI2CBusRec(pI2CBus, TRUE, FALSE); 3377e6fb56fSmrg return NULL; 3387e6fb56fSmrg } 3397e6fb56fSmrg 3407e6fb56fSmrg return pI2CBus; 3417e6fb56fSmrg} 3427e6fb56fSmrg 3437e6fb56fSmrg 3447e6fb56fSmrg#ifdef HAVE_DEBUG 3457e6fb56fSmrgstatic void 3467e6fb56fSmrgViaI2CScan(I2CBusPtr Bus) 3477e6fb56fSmrg{ 3487e6fb56fSmrg CARD8 i; 3497e6fb56fSmrg 3507e6fb56fSmrg xf86DrvMsg(Bus->scrnIndex, X_INFO, "ViaI2CScan: Scanning %s\n", 3517e6fb56fSmrg Bus->BusName); 3527e6fb56fSmrg 3537e6fb56fSmrg for (i = 0x10; i < 0xF0; i += 2) 3547e6fb56fSmrg if (xf86I2CProbeAddress(Bus, i)) 3557e6fb56fSmrg xf86DrvMsg(Bus->scrnIndex, X_PROBED, "Found slave on %s " 3567e6fb56fSmrg "- 0x%02X\n", Bus->BusName, i); 3577e6fb56fSmrg} 3587e6fb56fSmrg#endif /* HAVE_DEBUG */ 3597e6fb56fSmrg 3607e6fb56fSmrg 3617e6fb56fSmrgvoid 3627e6fb56fSmrgViaI2CInit(ScrnInfoPtr pScrn) 3637e6fb56fSmrg{ 3647e6fb56fSmrg VIAPtr pVia = VIAPTR(pScrn); 3657e6fb56fSmrg 3667e6fb56fSmrg DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, "ViaI2CInit\n")); 3677e6fb56fSmrg 3687e6fb56fSmrg pVia->pI2CBus1 = ViaI2CBus1Init(pScrn->scrnIndex); 3697e6fb56fSmrg pVia->pI2CBus2 = ViaI2CBus2Init(pScrn->scrnIndex); 3707e6fb56fSmrg pVia->pI2CBus3 = ViaI2CBus3Init(pScrn->scrnIndex); 3717e6fb56fSmrg 3727e6fb56fSmrg#ifdef HAVE_DEBUG 3737e6fb56fSmrg if (pVia->I2CScan) { 3747e6fb56fSmrg if (pVia->pI2CBus2) 3757e6fb56fSmrg ViaI2CScan(pVia->pI2CBus2); 3767e6fb56fSmrg if (pVia->pI2CBus3) 3777e6fb56fSmrg ViaI2CScan(pVia->pI2CBus3); 3787e6fb56fSmrg } 3797e6fb56fSmrg#endif 3807e6fb56fSmrg} 381