diff options
Diffstat (limited to 'src/southbridge/amd/cs5536/cs5536_early_smbus.c')
-rw-r--r-- | src/southbridge/amd/cs5536/cs5536_early_smbus.c | 216 |
1 files changed, 193 insertions, 23 deletions
diff --git a/src/southbridge/amd/cs5536/cs5536_early_smbus.c b/src/southbridge/amd/cs5536/cs5536_early_smbus.c index 0605d0cb82..a7617f8c43 100644 --- a/src/southbridge/amd/cs5536/cs5536_early_smbus.c +++ b/src/southbridge/amd/cs5536/cs5536_early_smbus.c @@ -1,45 +1,215 @@ -#include "cs5536_smbus.h" +/* +* This file is part of the LinuxBIOS project. +* +* Copyright (C) 2007 Advanced Micro Devices +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "cs5536.h" + +#define SMBUS_ERROR -1 +#define SMBUS_WAIT_UNTIL_READY_TIMEOUT -2 +#define SMBUS_WAIT_UNTIL_DONE_TIMEOUT -3 +#define SMBUS_TIMEOUT (1000) -#define SMBUS_IO_BASE 0x6000 /* initialization for SMBus Controller */ -static int cs5536_enable_smbus(void) +static void cs5536_enable_smbus(void) { - unsigned char val; - - /* reset SMBUS controller */ - outb(0, SMBUS_IO_BASE + SMB_CTRL2); /* Set SCL freq and enable SMB controller */ - val = inb(SMBUS_IO_BASE + SMB_CTRL2); - val |= ((0x20 << 1) | SMB_CTRL2_ENABLE); - outb(val, SMBUS_IO_BASE + SMB_CTRL2); + /*outb((0x20 << 1) | SMB_CTRL2_ENABLE, smbus_io_base + SMB_CTRL2);*/ + outb((0x7F << 1) | SMB_CTRL2_ENABLE, SMBUS_IO_BASE + SMB_CTRL2); /* Setup SMBus host controller address to 0xEF */ - val = inb(SMBUS_IO_BASE + SMB_ADD); - val |= (0xEF | SMB_ADD_SAEN); - outb(val, SMBUS_IO_BASE + SMB_ADD); + outb((0xEF | SMB_ADD_SAEN), SMBUS_IO_BASE + SMB_ADD); + +} + +static void smbus_delay(void) +{ + /* inb(0x80); */ +} + + +static int smbus_wait(unsigned smbus_io_base) { + unsigned long loops = SMBUS_TIMEOUT; + unsigned char val; + + do { + smbus_delay(); + val = inb(smbus_io_base + SMB_STS); + if ((val & SMB_STS_SDAST) != 0) + break; + if (val & (SMB_STS_BER | SMB_STS_NEGACK)) { + /*printk_debug("SMBUS WAIT ERROR %x\n", val);*/ + return SMBUS_ERROR; + } + } while(--loops); + return loops ? 0 : SMBUS_WAIT_UNTIL_READY_TIMEOUT; +} + +/* generate a smbus start condition */ +static int smbus_start_condition(unsigned smbus_io_base) +{ + unsigned char val; + + /* issue a START condition */ + val = inb(smbus_io_base + SMB_CTRL1); + outb(val | SMB_CTRL1_START, smbus_io_base + SMB_CTRL1); + + /* check for bus conflict */ + val = inb(smbus_io_base + SMB_STS); + if ((val & SMB_STS_BER) != 0) + return SMBUS_ERROR; + + return smbus_wait(smbus_io_base); +} + +static int smbus_check_stop_condition(unsigned smbus_io_base) +{ + unsigned char val; + unsigned long loops; + loops = SMBUS_TIMEOUT; + /* check for SDA status */ + do { + smbus_delay(); + val = inb(smbus_io_base + SMB_CTRL1); + if ((val & SMB_CTRL1_STOP) == 0) { + break; + } + outb((0x7F << 1) | SMB_CTRL2_ENABLE, smbus_io_base + SMB_CTRL2); + } while(--loops); + return loops?0:SMBUS_WAIT_UNTIL_READY_TIMEOUT; +} + +static int smbus_stop_condition(unsigned smbus_io_base) +{ + outb(SMB_CTRL1_STOP, smbus_io_base + SMB_CTRL1); + return smbus_wait(smbus_io_base); } -static int smbus_read_byte(unsigned device, unsigned address) +static int smbus_ack(unsigned smbus_io_base, int state) { - return do_smbus_read_byte(SMBUS_IO_BASE, device, address-1); + unsigned char val = inb(smbus_io_base + SMB_CTRL1); + +/* if (state) */ + outb(val | SMB_CTRL1_ACK, smbus_io_base + SMB_CTRL1); +/* else + outb(val & ~SMB_CTRL1_ACK, smbus_io_base + SMB_CTRL1); +*/ + return 0; } -#if 0 -static int smbus_recv_byte(unsigned device) +static int smbus_send_slave_address(unsigned smbus_io_base, unsigned char device) { - return do_smbus_recv_byte(SMBUS_IO_BASE, device); + unsigned char val; + + /* send the slave address */ + outb(device, smbus_io_base + SMB_SDA); + + /* check for bus conflict and NACK */ + val = inb(smbus_io_base + SMB_STS); + if (((val & SMB_STS_BER) != 0) || + ((val & SMB_STS_NEGACK) != 0)) { + /* printk_debug("SEND SLAVE ERROR (%x)\n", val);*/ + return SMBUS_ERROR; + } + return smbus_wait(smbus_io_base); } -static int smbus_send_byte(unsigned device, unsigned char val) +static int smbus_send_command(unsigned smbus_io_base, unsigned char command) { - return do_smbus_send_byte(SMBUS_IO_BASE, device, val); + unsigned char val; + + /* send the command */ + outb(command, smbus_io_base + SMB_SDA); + + /* check for bus conflict and NACK */ + val = inb(smbus_io_base + SMB_STS); + if (((val & SMB_STS_BER) != 0) || + ((val & SMB_STS_NEGACK) != 0)) + return SMBUS_ERROR; + + return smbus_wait(smbus_io_base); +} + +static unsigned char smbus_get_result(unsigned smbus_io_base) +{ + return inb(smbus_io_base + SMB_SDA); } +static unsigned char do_smbus_read_byte(unsigned smbus_io_base, unsigned char device, unsigned char address) +{ + unsigned char error = 0; + + if ((smbus_check_stop_condition(smbus_io_base))) { + error = 1; + goto err; + } + + if ((smbus_start_condition(smbus_io_base))) { + error = 2; + goto err; + } + + if ((smbus_send_slave_address(smbus_io_base, device))) { + error = 3; + goto err; + } + + smbus_ack(smbus_io_base, 1 ); + + if ((smbus_send_command(smbus_io_base, address))) { + error = 4; + goto err; + } + + if ((smbus_start_condition(smbus_io_base))) { + error = 5; + goto err; + } + + if ((smbus_send_slave_address(smbus_io_base, device | 0x01))) { + error = 6; + goto err; + } -static int smbus_write_byte(unsigned device, unsigned address, unsigned char val) + if ((smbus_stop_condition(smbus_io_base))) { + error = 7; + goto err; + } + + return smbus_get_result(smbus_io_base); + + +err: + print_debug("SMBUS READ ERROR:"); + print_debug_hex8(error); + print_debug(" device:"); + print_debug_hex8(device); + print_debug("\r\n"); + /* stop, clean up the error, and leave */ + smbus_stop_condition(smbus_io_base); + outb(inb(smbus_io_base + SMB_STS), smbus_io_base + SMB_STS); + outb(0x0, smbus_io_base + SMB_STS); + return 0xFF; +} + +static inline int smbus_read_byte(unsigned device, unsigned address) { - return do_smbus_write_byte(SMBUS_IO_BASE, device, address, val); + return do_smbus_read_byte(SMBUS_IO_BASE, device, address); } -#endif + |