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