summaryrefslogtreecommitdiff
path: root/src/gfx/i2c_acc.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/gfx/i2c_acc.c')
-rw-r--r--src/gfx/i2c_acc.c876
1 files changed, 876 insertions, 0 deletions
diff --git a/src/gfx/i2c_acc.c b/src/gfx/i2c_acc.c
new file mode 100644
index 0000000..a45a2c3
--- /dev/null
+++ b/src/gfx/i2c_acc.c
@@ -0,0 +1,876 @@
+/* Copyright (c) 2005 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ * Neither the name of the Advanced Micro Devices, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ * */
+
+/*
+ * This file contains routines to write to and read from the I2C bus using
+ * the ACCESS.bus hardware in the SC1200.
+ * */
+
+/* SUPER IO DEFINITIONS */
+
+#define INDEX_1 0x15C /* base address 1 selected */
+#define DATA_1 0x15D
+#define INDEX_2 0x2E /* base address 2 selected */
+#define DATA_2 0x2F
+#define PCI_INDEX 0xCF8 /* PCI configuration space INDEX */
+#define PCI_DATA 0xCFC /* PCI configuration space DATA */
+#define BASE_ADR_MSB_REG 0x60 /* base address MSB register */
+#define BASE_ADR_LSB_REG 0x61 /* base address LSB register */
+
+#define SIO_BASE_ADR_15C_15D 0x6000000
+#define SIO_BASE_ADR_2E_2F 0x4000000
+
+/* SUPER IO GLOBALS */
+
+unsigned short index_reg, data_reg;
+
+/* ACCESS BUS DEFINITIONS */
+
+#define ACC_I2C_TIMEOUT 1000000 /* Number of reads before timing out */
+#define ACB1_BASE 0x810 /* ACCESS.bus base addresses */
+#define ACB2_BASE 0x820
+#define ACBSDA 0 /* ACB serial data */
+#define ACBST 1 /* ACB status */
+#define ACBCST 2 /* ACB control status */
+#define ACBCTL1 3 /* ACB control 1 */
+#define ACBADDR 4 /* ACB own address */
+#define ACBCTL2 5 /* ACB control 2 */
+#define LDN 0x7 /* Logical Device Numbers */
+#define ACB1_LDN 0x5
+#define ACB2_LDN 0x6
+
+/* INITIAL ACCESS.bus BASE ADDRESS VALUES */
+
+unsigned short base_address_array[3] = { 0, ACB1_BASE, ACB2_BASE };
+char Freq = 0x71;
+
+/* LOCAL ACCESS.bus FUNCTION DECLARATIONS */
+
+void acc_i2c_start(unsigned char busnum);
+void acc_i2c_stop(unsigned char busnum);
+void acc_i2c_abort_data(unsigned char busnum);
+void acc_i2c_bus_recovery(unsigned char busnum);
+void acc_i2c_stall_after_start(unsigned char busnum, int state);
+void acc_i2c_send_address(unsigned char busnum, unsigned char cData);
+int acc_i2c_ack(unsigned char busnum, int fPut, int negAck);
+void acc_i2c_stop_clock(unsigned char busnum);
+void acc_i2c_activate_clock(unsigned char busnum);
+void acc_i2c_write_byte(unsigned char busnum, unsigned char cData);
+unsigned char acc_i2c_read_byte(unsigned char busnum, int last_byte);
+void acc_i2c_reset_bus(unsigned char busnum);
+int acc_i2c_request_master(unsigned char busnum);
+void acc_i2c_config(unsigned char busnum, short adr, char freq);
+char acc_i2c_set_freq(unsigned char busnum, char freq);
+unsigned short acc_i2c_set_base_address(unsigned char busnum, short adr);
+
+/* LOCAL HELPER ROUTINES */
+
+void OsPciReadDWord(int bus, int dev, int func, int address,
+ unsigned long *data);
+int sio_set_index_data_reg(void);
+void sio_write_reg(unsigned char reg, unsigned char data);
+unsigned char sio_read_reg(unsigned char reg);
+
+/*---------------------------------------------------------------------------
+ * OsPciReadDWord
+ *
+ * This routine reads one double word from the PCI configuration header
+ * defined by 'bus', 'dev', 'func' and 'address' to the double word
+ * pointed to by 'data'.
+ *
+ * Returns : None.
+ *---------------------------------------------------------------------------
+ */
+void
+OsPciReadDWord(int bus, int dev, int func, int address, unsigned long *data)
+{
+ /*
+ * The address of a double word in the Configuration Header is built in
+ * the following way :
+ * {10000000,bus[23:16],device[15:11],function[10:8],address[7:2],00}
+ */
+ long addr = (0x80000000 |
+ ((bus & 0xff) << 16) |
+ ((dev & 0x1f) << 11) | ((func & 0x7) << 8) | (address & 0xff));
+ OUTD(PCI_INDEX, addr);
+ *data = IND(PCI_DATA);
+}
+
+/*---------------------------------------------------------------------------
+ * sio_set_index_data_reg
+ *
+ * This routine checks which index and data registers to use
+ * in order to access the Super I/O registers
+ *
+ * Returns : 1 - OK
+ * 0 - Super I/O disabled or configuration access disabled
+ *---------------------------------------------------------------------------
+ */
+int
+sio_set_index_data_reg(void)
+{
+ unsigned long xbus_expention_bar, io_control_reg1;
+
+ OsPciReadDWord(0, 0x12, 5, 0x10, &xbus_expention_bar);
+ xbus_expention_bar = xbus_expention_bar & 0xfffffffe;
+ io_control_reg1 = IND((unsigned short)xbus_expention_bar);
+
+ if ((io_control_reg1) & (SIO_BASE_ADR_15C_15D)) {
+ index_reg = INDEX_1;
+ data_reg = DATA_1;
+ return (1);
+ }
+
+ if ((io_control_reg1) & (SIO_BASE_ADR_2E_2F)) {
+ index_reg = INDEX_2;
+ data_reg = DATA_2;
+ return (1);
+ }
+
+ return (0);
+}
+
+/*---------------------------------------------------------------------------
+ * sio_write_reg
+ *
+ * This routine writes 'data' to 'reg' Super I/O register
+ *
+ * Returns : None
+ *---------------------------------------------------------------------------
+ */
+void
+sio_write_reg(unsigned char reg, unsigned char data)
+{
+ OUTB(index_reg, reg);
+ OUTB(data_reg, data);
+}
+
+/*---------------------------------------------------------------------------
+ * sio_read_reg
+ *
+ * This routine reads data from 'reg' Super I/O register
+ *
+ * Returns : The data read from the requested register
+ *---------------------------------------------------------------------------
+ */
+unsigned char
+sio_read_reg(unsigned char reg)
+{
+ OUTB(index_reg, reg);
+ return INB(data_reg);
+}
+
+/*---------------------------------------------------------------------------
+ * gfx_i2c_reset
+ *
+ * This routine resets the I2C bus as follows :
+ * · Sets the base address of the ACCESS.bus
+ * · Sets the frequency of the ACCESS.bus
+ * · Resets the ACCESS.bus
+ *
+ * If 'adr' is -1 the address is read from the hardware.
+ * If 'freq' is -1 the frequency is set to 56 clock cycles.
+ *---------------------------------------------------------------------------
+ */
+#if GFX_I2C_DYNAMIC
+int
+acc_i2c_reset(unsigned char busnum, short adr, char freq)
+#else
+int
+gfx_i2c_reset(unsigned char busnum, short adr, char freq)
+#endif
+{
+ if ((busnum != 1) && (busnum != 2))
+ return GFX_STATUS_BAD_PARAMETER;
+ acc_i2c_config(busnum, adr, freq);
+ if (base_address_array[busnum] == 0)
+ return GFX_STATUS_ERROR;
+ acc_i2c_reset_bus(busnum);
+ return GFX_STATUS_OK;
+}
+
+/*---------------------------------------------------------------------------
+ * gfx_i2c_select_gpio
+ *
+ * This routine selects which GPIO pins to use.
+ *---------------------------------------------------------------------------
+ */
+#if GFX_I2C_DYNAMIC
+int
+acc_i2c_select_gpio(int clock, int data)
+#else
+int
+gfx_i2c_select_gpio(int clock, int data)
+#endif
+{
+ /* THIS ROUTINE DOES NOT APPLY TO THE ACCESS.bus IMPLEMENTATION. */
+
+ return (GFX_STATUS_OK);
+}
+
+/*---------------------------------------------------------------------------
+ * gfx_i2c_write
+ *
+ * This routine writes data to the specified I2C address.
+ * busnum - ACCESS.bus number (1 or 2).
+ *---------------------------------------------------------------------------
+ */
+#if GFX_I2C_DYNAMIC
+int
+acc_i2c_write(unsigned char busnum, unsigned char chipadr,
+ unsigned char subadr, unsigned char bytes, unsigned char *data)
+#else
+int
+gfx_i2c_write(unsigned char busnum, unsigned char chipadr,
+ unsigned char subadr, unsigned char bytes, unsigned char *data)
+#endif
+{
+ int loop = 0;
+
+ if ((busnum != 1) && (busnum != 2))
+ return GFX_STATUS_BAD_PARAMETER;
+
+ /* REQUEST MASTER */
+
+ if (!acc_i2c_request_master(busnum))
+ return (GFX_STATUS_ERROR);
+
+ /* WRITE ADDRESS COMMAND */
+
+ acc_i2c_ack(busnum, 1, 0);
+ acc_i2c_stall_after_start(busnum, 1);
+ acc_i2c_send_address(busnum, (unsigned char)(chipadr & 0xFE));
+ acc_i2c_stall_after_start(busnum, 0);
+ if (!acc_i2c_ack(busnum, 0, 0))
+ return (GFX_STATUS_ERROR);
+
+ /* WRITE COMMAND */
+
+ acc_i2c_write_byte(busnum, subadr);
+ if (!acc_i2c_ack(busnum, 0, 0))
+ return (GFX_STATUS_ERROR);
+
+ /* WRITE DATA */
+
+ for (loop = 0; loop < bytes; loop++) {
+ acc_i2c_write_byte(busnum, *data);
+ if (loop < (bytes - 1))
+ data += sizeof(unsigned char);
+ if (!acc_i2c_ack(busnum, 0, 0))
+ return (GFX_STATUS_ERROR);
+ }
+ data -= (bytes - 1);
+ acc_i2c_stop(busnum);
+
+ return GFX_STATUS_OK;
+}
+
+/*---------------------------------------------------------------------------
+ * gfx_i2c_read
+ *
+ * This routine reads data from the specified I2C address.
+ * busnum - ACCESS.bus number (1 or 2).
+ *---------------------------------------------------------------------------
+ */
+#if GFX_I2C_DYNAMIC
+int
+acc_i2c_read(unsigned char busnum, unsigned char chipadr,
+ unsigned char subadr, unsigned char bytes, unsigned char *data)
+#else
+int
+gfx_i2c_read(unsigned char busnum, unsigned char chipadr,
+ unsigned char subadr, unsigned char bytes, unsigned char *data)
+#endif
+{
+ unsigned char bytesRead;
+
+ if ((busnum != 1) && (busnum != 2))
+ return GFX_STATUS_BAD_PARAMETER;
+
+ if (bytes == 0)
+ return GFX_STATUS_OK;
+
+ /* REQUEST MASTER */
+
+ if (!acc_i2c_request_master(busnum))
+ return (GFX_STATUS_ERROR);
+
+ /* WRITE ADDRESS COMMAND */
+
+ acc_i2c_ack(busnum, 1, 0);
+ acc_i2c_stall_after_start(busnum, 1);
+ acc_i2c_send_address(busnum, (unsigned char)(chipadr & 0xFE));
+ acc_i2c_stall_after_start(busnum, 0);
+ if (!acc_i2c_ack(busnum, 0, 0))
+ return (GFX_STATUS_ERROR);
+
+ /* WRITE COMMAND */
+
+ acc_i2c_write_byte(busnum, subadr);
+ if (!acc_i2c_ack(busnum, 0, 0))
+ return (GFX_STATUS_ERROR);
+
+ /* START THE READ */
+
+ acc_i2c_start(busnum);
+
+ /* WRITE ADDRESS COMMAND */
+
+ acc_i2c_ack(busnum, 1, 1);
+ acc_i2c_stall_after_start(busnum, 1);
+ acc_i2c_send_address(busnum, (unsigned char)(chipadr | 0x01));
+
+ /* IF LAST BYTE */
+
+ if (bytes == 1)
+ acc_i2c_ack(busnum, 1, 1);
+ else
+ acc_i2c_ack(busnum, 1, 0);
+
+ acc_i2c_stall_after_start(busnum, 0);
+
+ if (!acc_i2c_ack(busnum, 0, 0))
+ return (GFX_STATUS_ERROR);
+
+ /* READ COMMAND */
+
+ for (bytesRead = 0; bytesRead < bytes; bytesRead += 1) {
+ if (bytesRead < (bytes - 2)) {
+ data[bytesRead] = acc_i2c_read_byte(busnum, 0);
+ acc_i2c_ack(busnum, 1, 0);
+ } else if (bytesRead == (bytes - 2)) { /* TWO BYTES LEFT */
+ acc_i2c_ack(busnum, 1, 1);
+ data[bytesRead] = acc_i2c_read_byte(busnum, 0);
+ acc_i2c_ack(busnum, 1, 1);
+ } else { /* LAST BYTE */
+
+ data[bytesRead] = acc_i2c_read_byte(busnum, 1);
+ acc_i2c_stop(busnum);
+ }
+
+ /* WHILE NOT LAST BYTE */
+
+ if ((!(bytesRead == (bytes - 1))) && (!acc_i2c_ack(busnum, 0, 0)))
+ return (bytesRead);
+ }
+
+ return GFX_STATUS_OK;
+}
+
+/*---------------------------------------------------------------------------
+ * gfx_i2c_init
+ *
+ * This routine initializes the use of the ACCESS.BUS.
+ *---------------------------------------------------------------------------
+ */
+#if GFX_I2C_DYNAMIC
+int
+acc_i2c_init(void)
+#else
+int
+gfx_i2c_init(void)
+#endif
+{
+ /* ### ADD ### THIS ROUTINE IS NOT YET IMPLEMENTED FOR ACCESS.bus */
+ return (GFX_STATUS_OK);
+}
+
+/*---------------------------------------------------------------------------
+ * gfx_i2c_cleanup
+ *
+ * This routine ends the use of the ACCESS.BUS.
+ *---------------------------------------------------------------------------
+ */
+#if GFX_I2C_DYNAMIC
+void
+acc_i2c_cleanup(void)
+#else
+void
+gfx_i2c_cleanup(void)
+#endif
+{
+ /* ### ADD ### THIS ROUTINE IS NOT YET IMPLEMENTED FOR ACCESS.bus */
+}
+
+/*--------------------------------------------------------*/
+/* LOCAL ROUTINES SPECIFIC TO ACCESS.bus IMPLEMENTATION */
+/*--------------------------------------------------------*/
+
+/*---------------------------------------------------------------------------
+ * acc_i2c_reset_bus
+ *
+ * This routine resets the I2C bus.
+ *---------------------------------------------------------------------------
+ */
+void
+acc_i2c_reset_bus(unsigned char busnum)
+{
+ unsigned char reg;
+ unsigned short bus_base_address = base_address_array[busnum];
+
+ /* Disable the ACCESS.bus device and */
+ /* Configure the SCL frequency */
+ OUTB((unsigned short)(bus_base_address + ACBCTL2),
+ (unsigned char)(Freq & 0xFE));
+
+ /* Configure no interrupt mode (polling) and */
+ /* Disable global call address */
+ OUTB((unsigned short)(bus_base_address + ACBCTL1), 0x0);
+
+ /* Disable slave address */
+ OUTB((unsigned short)(bus_base_address + ACBADDR), 0x0);
+
+ /* Enable the ACCESS.bus device */
+ reg = INB((unsigned short)(bus_base_address + ACBCTL2));
+ reg |= 0x01;
+ OUTB((unsigned short)(bus_base_address + ACBCTL2), reg);
+
+ /* Issue STOP event */
+
+ acc_i2c_stop(busnum);
+
+ /* Clear NEGACK, STASTR and BER bits */
+ OUTB((unsigned short)(bus_base_address + ACBST), 0x38);
+
+ /* Clear BB (BUS BUSY) bit */
+ reg = INB((unsigned short)(bus_base_address + ACBCST));
+ reg |= 0x02;
+ OUTB((unsigned short)(bus_base_address + ACBCST), reg);
+}
+
+/*---------------------------------------------------------------------------
+ * acc_i2c_start
+ *
+ * This routine starts a transfer on the I2C bus.
+ *---------------------------------------------------------------------------
+ */
+void
+acc_i2c_start(unsigned char busnum)
+{
+ unsigned char reg;
+ unsigned short bus_base_address = base_address_array[busnum];
+
+ reg = INB((unsigned short)(bus_base_address + ACBCTL1));
+ reg |= 0x01;
+ OUTB((unsigned short)(bus_base_address + ACBCTL1), reg);
+}
+
+/*---------------------------------------------------------------------------
+ * acc_i2c_stop
+ *
+ * This routine stops a transfer on the I2C bus.
+ *---------------------------------------------------------------------------
+ */
+void
+acc_i2c_stop(unsigned char busnum)
+{
+ unsigned char reg;
+ unsigned short bus_base_address = base_address_array[busnum];
+
+ reg = INB((unsigned short)(bus_base_address + ACBCTL1));
+ reg |= 0x02;
+ OUTB((unsigned short)(bus_base_address + ACBCTL1), reg);
+}
+
+/*---------------------------------------------------------------------------
+ * acc_i2c_abort_data
+ *---------------------------------------------------------------------------
+ */
+void
+acc_i2c_abort_data(unsigned char busnum)
+{
+ unsigned char reg;
+ unsigned short bus_base_address = base_address_array[busnum];
+
+ acc_i2c_stop(busnum);
+ reg = INB((unsigned short)(bus_base_address + ACBCTL1));
+ reg |= 0x10;
+ OUTB((unsigned short)(bus_base_address + ACBCTL1), reg);
+}
+
+/*---------------------------------------------------------------------------
+ * acc_i2c_bus_recovery
+ *---------------------------------------------------------------------------
+ */
+void
+acc_i2c_bus_recovery(unsigned char busnum)
+{
+ acc_i2c_abort_data(busnum);
+ acc_i2c_reset_bus(busnum);
+}
+
+/*---------------------------------------------------------------------------
+ * acc_i2c_stall_after_start
+ *---------------------------------------------------------------------------
+ */
+void
+acc_i2c_stall_after_start(unsigned char busnum, int state)
+{
+ unsigned char reg;
+ unsigned short bus_base_address = base_address_array[busnum];
+
+ reg = INB((unsigned short)(bus_base_address + ACBCTL1));
+ if (state)
+ reg |= 0x80;
+ else
+ reg &= 0x7F;
+ OUTB((unsigned short)(bus_base_address + ACBCTL1), reg);
+
+ if (!state) {
+ reg = INB((unsigned short)(bus_base_address + ACBST));
+ reg |= 0x08;
+ OUTB((unsigned short)(bus_base_address + ACBST), reg);
+ }
+}
+
+/*---------------------------------------------------------------------------
+ * acc_i2c_send_address
+ *---------------------------------------------------------------------------
+ */
+void
+acc_i2c_send_address(unsigned char busnum, unsigned char cData)
+{
+ unsigned char reg;
+ unsigned short bus_base_address = base_address_array[busnum];
+ unsigned long timeout = 0;
+
+ /* WRITE THE DATA */
+
+ OUTB((unsigned short)(bus_base_address + ACBSDA), cData);
+ while (1) {
+ reg = INB((unsigned short)(bus_base_address + ACBST));
+ if ((reg & 0x38) != 0) /* check STASTR, BER and NEGACK */
+ break;
+ if (timeout++ == ACC_I2C_TIMEOUT) {
+ acc_i2c_bus_recovery(busnum);
+ return;
+ }
+ }
+
+ /* CHECK FOR BUS ERROR */
+
+ if (reg & 0x20) {
+ acc_i2c_bus_recovery(busnum);
+ return;
+ }
+
+ /* CHECK NEGATIVE ACKNOWLEDGE */
+
+ if (reg & 0x10) {
+ acc_i2c_abort_data(busnum);
+ return;
+ }
+
+}
+
+/*---------------------------------------------------------------------------
+ * acc_i2c_ack
+ *
+ * This routine looks for acknowledge on the I2C bus.
+ *---------------------------------------------------------------------------
+ */
+int
+acc_i2c_ack(unsigned char busnum, int fPut, int negAck)
+{
+ unsigned char reg;
+ unsigned short bus_base_address = base_address_array[busnum];
+ unsigned long timeout = 0;
+
+ if (fPut) { /* read operation */
+ if (!negAck) {
+ /* Push Ack onto I2C bus */
+ reg = INB((unsigned short)(bus_base_address + ACBCTL1));
+ reg &= 0xE7;
+ OUTB((unsigned short)(bus_base_address + ACBCTL1), reg);
+ } else {
+ /* Push negAck onto I2C bus */
+ reg = INB((unsigned short)(bus_base_address + ACBCTL1));
+ reg |= 0x10;
+ OUTB((unsigned short)(bus_base_address + ACBCTL1), reg);
+ }
+ } else { /* write operation */
+ /* Receive Ack from I2C bus */
+ while (1) {
+ reg = INB((unsigned short)(bus_base_address + ACBST));
+ if ((reg & 0x70) != 0) /* check SDAST, BER and NEGACK */
+ break;
+ if (timeout++ == ACC_I2C_TIMEOUT) {
+ acc_i2c_bus_recovery(busnum);
+ return (0);
+ }
+ }
+
+ /* CHECK FOR BUS ERROR */
+
+ if (reg & 0x20) {
+ acc_i2c_bus_recovery(busnum);
+ return (0);
+ }
+
+ /* CHECK NEGATIVE ACKNOWLEDGE */
+
+ if (reg & 0x10) {
+ acc_i2c_abort_data(busnum);
+ return (0);
+ }
+ }
+ return (1);
+}
+
+/*---------------------------------------------------------------------------
+ * acc_i2c_stop_clock
+ *
+ * This routine stops the ACCESS.bus clock.
+ *---------------------------------------------------------------------------
+ */
+void
+acc_i2c_stop_clock(unsigned char busnum)
+{
+ unsigned char reg;
+ unsigned short bus_base_address = base_address_array[busnum];
+
+ reg = INB((unsigned short)(bus_base_address + ACBCTL2));
+ reg &= ~0x01;
+ OUTB((unsigned short)(bus_base_address + ACBCTL2), reg);
+}
+
+/*---------------------------------------------------------------------------
+ * acc_i2c_activate_clock
+ *
+ * This routine activates the ACCESS.bus clock.
+ *---------------------------------------------------------------------------
+ */
+void
+acc_i2c_activate_clock(unsigned char busnum)
+{
+ unsigned char reg;
+ unsigned short bus_base_address = base_address_array[busnum];
+
+ reg = INB((unsigned short)(bus_base_address + ACBCTL2));
+ reg |= 0x01;
+ OUTB((unsigned short)(bus_base_address + ACBCTL2), reg);
+}
+
+/*---------------------------------------------------------------------------
+ * acc_i2c_write_byte
+ *
+ * This routine writes a byte to the I2C bus
+ *---------------------------------------------------------------------------
+ */
+void
+acc_i2c_write_byte(unsigned char busnum, unsigned char cData)
+{
+ unsigned char reg;
+ unsigned short bus_base_address = base_address_array[busnum];
+ unsigned long timeout = 0;
+
+ while (1) {
+ reg = INB((unsigned short)(bus_base_address + ACBST));
+ if (reg & 0x70)
+ break;
+ if (timeout++ == ACC_I2C_TIMEOUT) {
+ acc_i2c_bus_recovery(busnum);
+ return;
+ }
+ }
+
+ /* CHECK FOR BUS ERROR */
+
+ if (reg & 0x20) {
+ acc_i2c_bus_recovery(busnum);
+ return;
+ }
+
+ /* CHECK NEGATIVE ACKNOWLEDGE */
+
+ if (reg & 0x10) {
+ acc_i2c_abort_data(busnum);
+ return;
+ }
+
+ /* WRITE THE DATA */
+
+ OUTB((unsigned short)(bus_base_address + ACBSDA), cData);
+}
+
+/*---------------------------------------------------------------------------
+ * acc_i2c_read_byte
+ *
+ * This routine reads a byte from the I2C bus
+ *---------------------------------------------------------------------------
+ */
+unsigned char
+acc_i2c_read_byte(unsigned char busnum, int last_byte)
+{
+ unsigned char cData, reg;
+ unsigned short bus_base_address = base_address_array[busnum];
+ unsigned long timeout = 0;
+
+ while (1) {
+ reg = INB((unsigned short)(bus_base_address + ACBST));
+ if (reg & 0x60)
+ break;
+ if (timeout++ == ACC_I2C_TIMEOUT) {
+ acc_i2c_bus_recovery(busnum);
+ return (0xEF);
+ }
+ }
+
+ /* CHECK FOR BUS ERROR */
+
+ if (reg & 0x20) {
+ acc_i2c_bus_recovery(busnum);
+ return (0xEE);
+ }
+
+ /* READ DATA */
+ if (last_byte)
+ acc_i2c_stop_clock(busnum);
+ cData = INB((unsigned short)(bus_base_address + ACBSDA));
+ if (last_byte)
+ acc_i2c_activate_clock(busnum);
+
+ return (cData);
+}
+
+/*---------------------------------------------------------------------------
+ * acc_i2c_request_master
+ *---------------------------------------------------------------------------
+ */
+int
+acc_i2c_request_master(unsigned char busnum)
+{
+ unsigned char reg;
+ unsigned short bus_base_address = base_address_array[busnum];
+ unsigned long timeout = 0;
+
+ acc_i2c_start(busnum);
+ while (1) {
+ reg = INB((unsigned short)(bus_base_address + ACBST));
+ if (reg & 0x60)
+ break;
+ if (timeout++ == ACC_I2C_TIMEOUT) {
+ acc_i2c_bus_recovery(busnum);
+ return (0);
+ }
+ }
+
+ /* CHECK FOR BUS ERROR */
+
+ if (reg & 0x20) {
+ acc_i2c_abort_data(busnum);
+ return (0);
+ }
+
+ /* CHECK NEGATIVE ACKNOWLEDGE */
+
+ if (reg & 0x10) {
+ acc_i2c_abort_data(busnum);
+ return (0);
+ }
+ return (1);
+}
+
+/*--------------------------------------------------------*/
+/* LOCAL ROUTINES SPECIFIC TO ACCESS.bus INITIALIZATION */
+/*--------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------
+ * acc_i2c_config
+ *
+ * This routine configures the I2C bus
+ *----------------------------------------------------------------------------
+ */
+void
+acc_i2c_config(unsigned char busnum, short adr, char freq)
+{
+ base_address_array[busnum] = acc_i2c_set_base_address(busnum, adr);
+ Freq = acc_i2c_set_freq(busnum, freq);
+}
+
+/*----------------------------------------------------------------------------
+ * acc_i2c_set_freq
+ *
+ * This routine sets the frequency of the I2C bus
+ *----------------------------------------------------------------------------
+ */
+char
+acc_i2c_set_freq(unsigned char busnum, char freq)
+{
+ unsigned short bus_base_address = base_address_array[busnum];
+
+ OUTB((unsigned short)(bus_base_address + ACBCTL2), 0x0);
+
+ if (freq == -1)
+ freq = 0x71;
+ else {
+ freq = freq << 1;
+ freq |= 0x01;
+ }
+
+ OUTB((unsigned short)(bus_base_address + ACBCTL2), freq);
+ return (freq);
+}
+
+/*---------------------------------------------------------------------------
+ * acc_i2c_set_base_address
+ *
+ * This routine sets the base address of the I2C bus
+ *---------------------------------------------------------------------------
+ */
+unsigned short
+acc_i2c_set_base_address(unsigned char busnum, short adr)
+{
+ unsigned short ab_base_addr;
+
+ /* Get Super I/O Index and Data registers */
+ if (!sio_set_index_data_reg())
+ return (0);
+
+ /* Configure LDN to current ACB */
+ if (busnum == 1)
+ sio_write_reg(LDN, ACB1_LDN);
+ if (busnum == 2)
+ sio_write_reg(LDN, ACB2_LDN);
+
+ if (adr == -1) {
+ /* Get ACCESS.bus base address */
+ ab_base_addr = sio_read_reg(BASE_ADR_MSB_REG);
+ ab_base_addr = ab_base_addr << 8;
+ ab_base_addr |= sio_read_reg(BASE_ADR_LSB_REG);
+ if (ab_base_addr != 0)
+ return ab_base_addr;
+ else
+ adr = (busnum == 1 ? ACB1_BASE : ACB2_BASE);
+ }
+
+ /* Set ACCESS.bus base address */
+ sio_write_reg(BASE_ADR_LSB_REG, (unsigned char)(adr & 0xFF));
+ sio_write_reg(BASE_ADR_MSB_REG, (unsigned char)(adr >> 8));
+
+ return adr;
+}
+
+/* END OF FILE */