xenocara/driver/xf86-video-mach64/src/atii2c.c
2012-05-15 06:19:26 +00:00

400 lines
11 KiB
C

/*
* Copyright 2003 through 2004 by Marc Aurele La France (TSI @ UQV), tsi@xfree86.org
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice appear in all copies and that both that copyright
* notice and this permission notice appear in supporting documentation, and
* that the name of Marc Aurele La France not be used in advertising or
* publicity pertaining to distribution of the software without specific,
* written prior permission. Marc Aurele La France makes no representations
* about the suitability of this software for any purpose. It is provided
* "as-is" without express or implied warranty.
*
* MARC AURELE LA FRANCE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO
* EVENT SHALL MARC AURELE LA FRANCE BE LIABLE FOR ANY SPECIAL, INDIRECT OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "atii2c.h"
#include "atimach64i2c.h"
#include "atistruct.h"
#include "xf86.h"
/* This is derived from GATOS code, with a liberal sprinkling of bug fixes */
/*
* Some local macros for use by the mid-level I2C functions.
*/
#define ATII2CDelay \
(*pI2CBus->I2CUDelay)(pI2CBus, pI2CBus->HoldTime)
#define ATII2CSCLDirOff \
if (pATII2C->SCLDir != 0) \
(*pATII2C->I2CSetBits)(pATII2C, pATI, \
pATII2C->I2CCur & ~pATII2C->SCLDir)
#define ATII2CSCLDirOn \
if (pATII2C->SCLDir != 0) \
(*pATII2C->I2CSetBits)(pATII2C, pATI, \
pATII2C->I2CCur | pATII2C->SCLDir)
#define ATII2CSDADirOff \
if (pATII2C->SDADir != 0) \
(*pATII2C->I2CSetBits)(pATII2C, pATI, \
pATII2C->I2CCur & ~pATII2C->SDADir)
#define ATII2CSDADirOn \
if (pATII2C->SDADir != 0) \
(*pATII2C->I2CSetBits)(pATII2C, pATI, \
pATII2C->I2CCur | pATII2C->SDADir)
#define ATII2CSCLBitGet \
((*pATII2C->I2CGetBits)(pATI) & pATII2C->SCLGet)
#define ATII2CSCLBitOff \
do \
{ \
(*pATII2C->I2CSetBits)(pATII2C, pATI, \
pATII2C->I2CCur & ~pATII2C->SCLSet); \
ATII2CDelay; \
} while (0)
#define ATII2CSCLBitOn \
do \
{ \
(*pATII2C->I2CSetBits)(pATII2C, pATI, \
pATII2C->I2CCur | pATII2C->SCLSet); \
do /* Wait until all devices have released SCL */ \
{ \
ATII2CDelay; \
} while (ATII2CSCLBitGet == 0); \
} while (0)
#define ATII2CSDABitGet \
((*pATII2C->I2CGetBits)(pATI) & pATII2C->SDAGet)
#define ATII2CSDABitOff \
do \
{ \
(*pATII2C->I2CSetBits)(pATII2C, pATI, \
pATII2C->I2CCur & ~pATII2C->SDASet); \
ATII2CDelay; \
} while (0)
#define ATII2CSDABitOn \
do \
{ \
(*pATII2C->I2CSetBits)(pATII2C, pATI, \
pATII2C->I2CCur | pATII2C->SDASet); \
ATII2CDelay; \
} while (0)
#define ATII2CSDABitSet(_flag) \
do \
{ \
if (_flag) \
ATII2CSDABitOn; \
else \
ATII2CSDABitOff; \
} while (0)
/*
* ATII2CStart --
*
* This function puts a start signal on the I2C bus.
*/
static Bool
ATII2CStart
(
I2CBusPtr pI2CBus,
int timeout
)
{
ATII2CPtr pATII2C = pI2CBus->DriverPrivate.ptr;
ATIPtr pATI = pATII2C->pATI;
(void)timeout;
/*
* Set I2C line directions to out-bound. SCL will remain out-bound until
* next I2C Stop.
*/
ATII2CSCLDirOn;
ATII2CSDADirOn;
/*
* Send Start bit. This is a pull-down of the data line while the clock
* line is pulled up.
*/
ATII2CSDABitOn;
ATII2CSCLBitOn;
ATII2CSDABitOff;
ATII2CSCLBitOff;
return TRUE;
}
/*
* ATII2CAddress --
*
* This function puts an 8-bit address on the I2C bus.
*/
static Bool
ATII2CAddress
(
I2CDevPtr pI2CDev,
I2CSlaveAddr Address
)
{
I2CBusPtr pI2CBus = pI2CDev->pI2CBus;
/* Send low byte of device address */
if ((*pI2CBus->I2CPutByte)(pI2CDev, (I2CByte)Address))
{
/* Send top byte of address, if appropriate */
if (((Address & 0x00F8U) != 0x00F0U) &&
((Address & 0x00FEU) != 0x0000U))
return TRUE;
if ((*pI2CBus->I2CPutByte)(pI2CDev, (I2CByte)(Address >> 8)))
return TRUE;
}
/* Kill I2C transaction on failure */
(*pI2CBus->I2CStop)(pI2CDev);
return FALSE;
}
/*
* ATII2CStop --
*
* This function puts a stop signal on the I2C bus.
*/
static void
ATII2CStop
(
I2CDevPtr pI2CDev
)
{
I2CBusPtr pI2CBus = pI2CDev->pI2CBus;
ATII2CPtr pATII2C = pI2CBus->DriverPrivate.ptr;
ATIPtr pATI = pATII2C->pATI;
ATII2CSDADirOn; /* Set data line direction to out-bound */
/*
* Send Stop bit. This is a pull-up of the data line while the clock line
* is pulled up.
*/
ATII2CSDABitOff;
ATII2CSCLBitOn;
ATII2CSDABitOn;
ATII2CSCLBitOff;
/* Reset I2C line directions to in-bound */
ATII2CSCLDirOff;
ATII2CSDADirOff;
}
/*
* ATII2CPutByte --
*
* This function puts an 8-bit value on the I2C bus, starting with its MSB.
*/
static Bool
ATII2CPutByte
(
I2CDevPtr pI2CDev,
I2CByte Data
)
{
I2CBusPtr pI2CBus = pI2CDev->pI2CBus;
ATII2CPtr pATII2C = pI2CBus->DriverPrivate.ptr;
ATIPtr pATI = pATII2C->pATI;
int i;
Bool Result;
ATII2CSDADirOn; /* Set data line direction to out-bound */
/* Send data byte */
for (i = 0; i < 8; i++)
{
ATII2CSDABitSet(Data & 0x80U);
ATII2CSCLBitOn;
ATII2CSCLBitOff;
Data <<= 1;
}
ATII2CSDABitOn; /* Release data line */
ATII2CSDADirOff; /* Set data line direction to in-bound */
ATII2CSCLBitOn; /* Start bit-read clock pulse */
/* Get [N]ACK bit */
if (ATII2CSDABitGet)
Result = FALSE;
else
Result = TRUE;
ATII2CSCLBitOff; /* End clock pulse */
return Result;
}
/*
* ATII2CGetByte --
*
* This function retrieves an 8-bit value from the I2C bus.
*/
static Bool
ATII2CGetByte
(
I2CDevPtr pI2CDev,
I2CByte *pData,
Bool Last
)
{
I2CBusPtr pI2CBus = pI2CDev->pI2CBus;
ATII2CPtr pATII2C = pI2CBus->DriverPrivate.ptr;
ATIPtr pATI = pATII2C->pATI;
unsigned long Value = 1;
do
{
ATII2CSCLBitOn; /* Start bit-read clock pulse */
/* Accumulate bit into byte value */
Value <<= 1;
if (ATII2CSDABitGet)
Value++;
ATII2CSCLBitOff; /* End clock pulse */
} while (Value <= (unsigned long)((I2CByte)(-1)));
*pData = (I2CByte)Value;
ATII2CSDADirOn; /* Set data line direction to out-bound */
/* Send [N]ACK bit */
ATII2CSDABitSet(Last);
ATII2CSCLBitOn;
ATII2CSCLBitOff;
if (!Last)
ATII2CSDABitOn; /* Release data line */
ATII2CSDADirOff; /* Set data line direction to in-bound */
return TRUE;
}
/*
* ATICreateI2CBusRec --
*
* This function is called to initialise an I2CBusRec.
*/
I2CBusPtr
ATICreateI2CBusRec
(
int iScreen,
ATIPtr pATI,
char *BusName
)
{
I2CBusPtr pI2CBus;
ATII2CPtr pATII2C = xnfcalloc(1, SizeOf(ATII2CRec));
if (!(pI2CBus = xf86CreateI2CBusRec()))
{
xf86DrvMsg(iScreen, X_WARNING, "Unable to allocate I2C Bus record.\n");
free(pATII2C);
return NULL;
}
/* Fill in generic structure fields */
pI2CBus->BusName = BusName;
pI2CBus->scrnIndex = iScreen;
pI2CBus->I2CAddress = ATII2CAddress;
pI2CBus->I2CStart = ATII2CStart;
pI2CBus->I2CStop = ATII2CStop;
pI2CBus->I2CPutByte = ATII2CPutByte;
pI2CBus->I2CGetByte = ATII2CGetByte;
pI2CBus->DriverPrivate.ptr = pATII2C;
pATII2C->pATI = pATI;
if (xf86I2CBusInit(pI2CBus))
return pI2CBus;
xf86DrvMsg(iScreen, X_WARNING,
"I2C bus %s initialisation failure.\n", BusName);
xf86DestroyI2CBusRec(pI2CBus, TRUE, TRUE);
free(pATII2C);
return NULL;
}
/*
* ATII2CPreInit --
*
* This is called by ATIPreInit() to create I2C bus record(s) for the adapter.
*/
void
ATII2CPreInit
(
ScrnInfoPtr pScreenInfo,
ATIPtr pATI
)
{
if (!xf86LoadSubModule(pScreenInfo, "i2c"))
return;
ATIMach64I2CPreInit(pScreenInfo, pATI);
}
/*
* ATII2CFreeScreen --
*
* This is called by ATIFreeScreen() to remove the driver's I2C interface.
*/
void
ATII2CFreeScreen
(
int iScreen
)
{
I2CBusPtr pI2CBus, *ppI2CBus;
ATII2CPtr pATII2C;
int nI2CBus;
nI2CBus = xf86I2CGetScreenBuses(iScreen, &ppI2CBus);
while (--nI2CBus >= 0)
{
pI2CBus = ppI2CBus[nI2CBus];
pATII2C = pI2CBus->DriverPrivate.ptr;
xf86DestroyI2CBusRec(pI2CBus, TRUE, TRUE);
free(pATII2C);
}
free(ppI2CBus);
}