/****************************************************************************
 * I2CUTIL.C                                                                *
 *                                                                          *
 * Purpose:     To provide a skeleton I2C driver for implemetation on       *
 *              other I2C HW environments.                                  *
 *                                                                          *
 * Copyright (C) 1999 ATI Technologies Inc.  All rights reserved.           *
 ****************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include "main.h"
#include "defines.h"
#include "regdef.h"

_I2C_INFO I2C_INFO;

DWORD nacks = 0;
DWORD halts = 0;

/****************************************************************************
 *  I2C_Set (_I2C_PACKET *Packet)                                           *
 *  Function: Sets data at an I2C address                                   *
 *    Inputs: _I2C_PACKET *Packet - pointer to I2C Packet structre          *
 *   Outputs: I2C_DONE - success, the data was accepted by the slave        *
 *            I2C_NACK - the data was not accepted                          *
 *            I2C_HALT - a timeout condition has occured                    *
 ****************************************************************************/
BYTE I2C_Set (_I2C_PACKET *Packet)
{
    BYTE loop;
    DWORD i2c_cntl_0, i2c_cntl_1;

    R128_WaitForFifo (4 + Packet->number_of_bytes);

    // Clear the status bits of the I2C Controller
    regw (I2C_CNTL_0, I2C_DONE | I2C_NACK | I2C_HALT | I2C_SOFT_RST);

    // Write the address into the buffer first
    regw (I2C_DATA, (DWORD) Packet->address);

    // Write Value into the buffer
    for (loop = 0; loop < Packet->number_of_bytes; loop++)
    {
        regw8 (I2C_DATA, Packet->data[loop]);
    }

    i2c_cntl_1 = (I2C_INFO.TimeLimit << 24) | I2C_EN | I2C_SEL_HW |
                  Packet->number_of_bytes | 0x00000100;
    regw (I2C_CNTL_1, i2c_cntl_1);

    // set I2C_CNTL_0
    i2c_cntl_0 = (I2C_INFO.Nfactor << 24) | (I2C_INFO.Mfactor << 16) |
                  I2C_GO | (Packet->start << 8) | (Packet->stop << 9) | I2C_DRIVE_EN;
    regw (I2C_CNTL_0, i2c_cntl_0);

    // wait for finishing of I2C operation
    while (regr8 (I2C_CNTL_0 + 1) & (I2C_GO>>8));

    loop = I2C_WaitForAck ();

    return (loop);
} // I2C_Set ()...

/****************************************************************************
 *  I2C_Get (_I2C_PACKET *Packet)                                           *
 *                                                                          *
 *  Function: Gets data from an I2C address                                 *
 *    Inputs: _I2C_PACKET *Packet - pointer to I2C Packet structre          *
 *   Outputs: I2C_DONE - success, the data was received from the slave      *
 *            I2C_NACK - the data was not received                          *
 *            I2C_HALT - a timeout condition has occured                    *
 ****************************************************************************/
BYTE I2C_Get (_I2C_PACKET *Packet)
{
    BYTE loop, retval;
    DWORD i2c_cntl_0, i2c_cntl_1;

    R128_WaitForFifo (4 + Packet->number_of_bytes);

    regw (I2C_CNTL_0, 0x00000027);

    // set address byte
    regw (I2C_DATA, Packet->address);

    // receive num_data byte(s) and transmit 1 address byte
    i2c_cntl_1 = (I2C_INFO.TimeLimit << 24) | I2C_EN | I2C_SEL_HW |
                  Packet->number_of_bytes | 0x00000100;
    regw (I2C_CNTL_1, i2c_cntl_1);

    // set I2C_CNTL_0
    i2c_cntl_0 = (I2C_INFO.Nfactor << 24) | (I2C_INFO.Mfactor << 16) |
                  I2C_GO | (Packet->start << 8) | (Packet->stop << 9) | I2C_DRIVE_EN | I2C_RECEIVE;
    regw (I2C_CNTL_0, i2c_cntl_0);

    // wait for finishing of I2C operation
    while (regr8 (I2C_CNTL_0 + 1) & (I2C_GO>>8));

    retval = I2C_WaitForAck ();

    // Wait while the buffer registers are filled.
    R128_Delay (1);

    for (loop = 0; loop < Packet->number_of_bytes; loop++)
    {
        // read the data register
        R128_WaitForFifo (1);
        if ((retval == I2C_HALT) || (retval == I2C_NACK))
        {
            Packet->data[loop] = 0xFF;
        }
        else
        {
            Packet->data[loop] = regr8 (I2C_DATA);
        }
    }

    return (I2C_DONE);
} // I2C_Get

/****************************************************************************
 *  I2C_WaitForAck (void)                                                   *
 *                                                                          *
 *  Function: polls the I2C status bits, waiting for an acknowledge or      *
 *            an error condition.                                           *
 *    Inputs: NONE                                                          *
 *   Outputs: I2C_DONE - the I2C transfer was completed                     *
 *            I2C_NACK - an NACK was received from the slave                *
 *            I2C_HALT - a timeout condition has occured                    *
 ****************************************************************************/
BYTE I2C_WaitForAck (void)
{
    BYTE retval = 0;
    BYTE test;

    R128_Delay (1);
    do
    {
        retval = regr8 (I2C_CNTL_0);
        if (retval & I2C_HALT)
        {
            halts++;
            return (I2C_HALT);
        }
        if (retval & I2C_NACK)
        {
            nacks++;
            return (I2C_NACK);
        }
        test = retval && (BYTE)I2C_DONE;
    }
    //while ((retval & I2C_DONE) != 0);
    while (test == 0);

    return (I2C_DONE);
}


/****************************************************************************
 * R128_SetI2CInfo (DWORD clock_rate)                                       *
 *                                                                          *
 *  Function: determines the I2C clock rate settings (N and M factors),     *
 *            the appropriate time limit value, and sets the global         *
 *            structure with these values.                                  *
 *            an error condition.                                           *
 *    Inputs: DWORD clock_rate - the requested I2C clock rate, in Hz.       *
 *   Outputs: NONE                                                          *
 ****************************************************************************/
void R128_SetI2CInfo (DWORD clock_rate)
{
    double i2c_period, xclk_period, n;
    BYTE loop;

    // First, make sure that the passed clock rate is valid.
    if (clock_rate > 100000)
    {
        // maximum clock rate is 100 kHz
        clock_rate = 100000;
    }
    if (clock_rate <= 0)
    {
        // We'll set the minimum clock rate to 7.5 kHz
        clock_rate = 7500;
    }

    // Calculate appropriate N and M factors based on requested clock rate.
    // We'll use the formula:
    //      I2C Clock Period = 4 * N * M * XCLK Period
    // where N = M + 1;
    i2c_period = 1.0/(double)clock_rate;
    xclk_period = 1.0/((double)PLL_BLOCK.XCLK * 10000.0);
    n = i2c_period / (xclk_period * 4.0);

    // we actually want the square root of n, since:
    //  N * M = I2C Clock Period / (4 * XCLK Period)
    for (loop = 1; loop < 255; loop++)
    {
        if (n/(double)loop < (double)loop)
        {
            break;
        }
    }

    I2C_INFO.Nfactor = I2C_INFO.Mfactor = loop - 1;
    //I2C_INFO.Mfactor = loop - 2; // M = N - 1
    I2C_INFO.TimeLimit = I2C_INFO.Nfactor * 2;
    I2C_INFO.ClockRate = clock_rate;

} // R128_SetI2CInfo ()...


/****************************************************************************
 *  I2C_Stop (void)                                                         *
 *                                                                          *
 *  Function: Stops transactions on the I2C bus, effectively shutting down  *
 *            the bus.                                                      *
 *    Inputs: NONE                                                          *
 *   Outputs: NONE                                                          *
 ****************************************************************************/
void I2C_Stop (void)
{
    BYTE    reg;

    // reset status flags
    reg = regr8 (I2C_CNTL_0 + 0) & 0xF8;
    regw8 (I2C_CNTL_0 + 0, reg);

    // issue ABORT call
    reg = regr8 (I2C_CNTL_0 + 1) & 0xE7;
    regw8 (I2C_CNTL_0 + 1, (reg | 0x18));

    // wait for GO bit to go low
    while (regr8 (I2C_CNTL_0 + 0) & I2C_GO);

} // I2C_Stop ()...

/****************************************************************************
 *  I2C_Reset (void)                                                        *
 *                                                                          *
 *  Function: Resets the I2C bus.  Actually just calls I2C_Stop ()          *
 *    Inputs: NONE                                                          *
 *   Outputs: NONE                                                          *
 ****************************************************************************/
void I2C_Reset (void)
{
    I2C_Stop ();
} // I2C_Reset ()...

/****************************************************************************
 *  I2C_Enable (void)                                                       *
 *                                                                          *
 *  Function: Enables the I2C bus.                                          *
 *    Inputs: NONE                                                          *
 *   Outputs: NONE                                                          *
 ****************************************************************************/
void I2C_Enable (void)
{
    regw8 (I2C_CNTL_1 + 2, ((I2C_SEL_HW | I2C_EN) >> 16));
    regw8 (I2C_CNTL_0 + 0, (I2C_DONE | I2C_NACK | I2C_HALT |
                            I2C_SOFT_RST | I2C_DRIVE_EN |
                            I2C_DRIVE_SEL));

} // I2C_Enable ()...


/****************************************************************************
 *  I2C_Disable (void)                                                      *
 *                                                                          *
 *  Function: Disables the I2C bus.                                         *
 *    Inputs: NONE                                                          *
 *   Outputs: NONE                                                          *
 ****************************************************************************/
void I2C_Disable (void)
{
    regw (I2C_CNTL_1, 0x00000000);
} // I2C_Disable ()...




/****************************************************************************
 *  I2C_EnableAudio (void)                                                  *
 *                                                                          *
 *  Function: Enables audio for the TDA9850 chip                            *
 *    Inputs: NONE                                                          *
 *   Outputs: NONE                                                          *
 ****************************************************************************/
void I2C_EnableAudio (void)
{

}

