[LinuxBIOS] r344 - LinuxBIOSv3/southbridge/amd/cs5536

svn at openbios.org svn at openbios.org
Tue Jun 5 19:04:59 CEST 2007


Author: rminnich
Date: 2007-06-05 19:04:59 +0200 (Tue, 05 Jun 2007)
New Revision: 344

Added:
   LinuxBIOSv3/southbridge/amd/cs5536/smbus_initram.c
Log:
SMBus support for the AMD Geode CS5536. This code can initialize
the SMBus and basically provides one public funtion which can be used
by other code (e.g. in the northbridge): smbus_read_byte().

Signed-off-by: Ronald G. Minnich <rminnich at gmail.com>
Signed-off-by: Uwe Hermann <uwe at hermann-uwe.de>
Acked-by: Ronald G. Minnich <rminnich at gmail.com>



Added: LinuxBIOSv3/southbridge/amd/cs5536/smbus_initram.c
===================================================================
--- LinuxBIOSv3/southbridge/amd/cs5536/smbus_initram.c	                        (rev 0)
+++ LinuxBIOSv3/southbridge/amd/cs5536/smbus_initram.c	2007-06-05 17:04:59 UTC (rev 344)
@@ -0,0 +1,331 @@
+/*
+ * This file is part of the LinuxBIOS project.
+ *
+ * Copyright (C) 2007 Advanced Micro Devices, Inc.
+ *
+ * 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 <console.h>
+#include <io.h>
+#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
+
+/**
+ * Enable the SMBus.
+ *
+ * Basically, set the enable bit in the controller. This can be (and is)
+ * called multiple times.
+ */
+static void smbus_enable(void)
+{
+	/* Set the Serial Clock Line (SCL) frequency and enable the SMBus
+	 * controller. 0x20 is one possible frequency. For DRAM we use 0x7f,
+	 * probably because it is a slower and presumably more reliable clock.
+	 */
+	/* outb((0x20 << 1) | SMB_CTRL2_ENABLE, smbus_io_base + SMB_CTRL2); */
+	outb((0x7F << 1) | SMB_CTRL2_ENABLE, SMBUS_IO_BASE + SMB_CTRL2);
+}
+
+/**
+ * Initialize the SMBus controller.
+ *
+ * Basically, set the enable bit in the controller and set the host
+ * controller address. Code can call this more than once, but the effect of
+ * doing so is uncertain due to SMBus address set.
+ */
+static void smbus_init(void)
+{
+	smbus_enable();
+
+	/* Setup SMBus host controller address to 0xEF. This is not an
+	 * I/O port address but an SMBus address.
+	 */
+	outb((0xEF | SMB_ADD_SAEN), SMBUS_IO_BASE + SMB_ADD);
+}
+
+/**
+ * Fixed delay for the SMBus controller.
+ *
+ * Currently, this is a no-op. No delay. We are going to leave it here as it
+ * indicates where a delay might be needed, for future chipset issues that
+ * might happen. And if such issues might happen, they usually do.
+ */
+static void smbus_delay(void)
+{
+	/* inb(0x80); */
+}
+
+/**
+ * Wait for the SMBus controller to become ready.
+ *
+ * There are three ways this can happen. Either the controller becomes
+ * ready (good); or we get an error (bad), in which case we return the
+ * error; or, we time out and give up, in which case we return
+ * SMBUS_WAIT_UNTIL_READY_TIMEOUT (very bad).
+ *
+ * @param smbus_io_base The SMBus I/O base.
+ * @return The error code, or 0 on success.
+ */
+static int smbus_wait(u16 smbus_io_base)
+{
+	u8 val;
+	unsigned long loops = SMBUS_TIMEOUT;
+
+	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(BIOS_DEBUG, "SMBus WAIT ERROR %x\n", val);
+			return SMBUS_ERROR;
+		}
+	} while (--loops);
+
+	return loops ? 0 : SMBUS_WAIT_UNTIL_READY_TIMEOUT;
+}
+
+/**
+ * Generate an SMBus start condition.
+ *
+ * Kick off the SMBus. At this point we will own the bus and can issue an
+ * address or other part of the transaction. This code can either exit
+ * immediately due to a bus conflict, returning SMBUS_ERROR, or it will,
+ * once the bus is started, return the error code (or success!) from
+ * smbus_wait().
+ *
+ * @param smbus_io_base The SMBus I/O base.
+ * @return The error code, or 0 on success.
+ */
+static int smbus_start_condition(u16 smbus_io_base)
+{
+	u8 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);
+}
+
+/**
+ * Check the SMBus stop condition.
+ *
+ * Wait until the SDA status is set, indicating that data has been returned.
+ *
+ * @param smbus_io_base The SMBus I/O base.
+ * @return The error code, or 0 on success.
+ */
+static int smbus_check_stop_condition(u16 smbus_io_base)
+{
+	u8 val;
+	unsigned long loops = SMBUS_TIMEOUT;
+
+	/* Check for SDA status. */
+	do {
+		smbus_delay();
+		val = inb(smbus_io_base + SMB_CTRL1);
+		if ((val & SMB_CTRL1_STOP) == 0) {
+			break;
+		}
+		smbus_enable();
+	} while (--loops);
+
+	return loops ? 0 : SMBUS_WAIT_UNTIL_READY_TIMEOUT;
+}
+
+/**
+ * Stop the SMBus and wait for the stop to be acknowledged by the
+ * hardware, using smbus_wait() to wait.
+ *
+ * @param smbus_io_base The SMBus I/O base.
+ * @return The error code, or 0 on success.
+ */
+static int smbus_stop_condition(u16 smbus_io_base)
+{
+	outb(SMB_CTRL1_STOP, smbus_io_base + SMB_CTRL1);
+	return smbus_wait(smbus_io_base);
+}
+
+/**
+ * Acknowledge the SMBus.
+ *
+ * Always succeeds. Always sends the ack.
+ *
+ * @param smbus_io_base The SMBus I/O base.
+ */
+static void smbus_ack(u16 smbus_io_base)
+{
+	u8 val = inb(smbus_io_base + SMB_CTRL1);
+	outb(val | SMB_CTRL1_ACK, smbus_io_base + SMB_CTRL1);
+}
+
+/**
+ * Set the slave address (e.g. 0x50 for DRAM socket 0) into the SMBus packet.
+ * 
+ * @param smbus_io_base The SMBus I/O base.
+ * @param device The device.
+ * @return The error code, or 0 on success.
+ */
+static int smbus_send_slave_address(u16 smbus_io_base, u8 device)
+{
+	u8 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(BIOS_DEBUG, "SEND SLAVE ERROR (%x)\n", val);
+		return SMBUS_ERROR;
+	}
+
+	return smbus_wait(smbus_io_base);
+}
+
+/**
+ * Issue an SMBus command.
+ *
+ * This can fail if there is a bus conflict. The chipset will indicate
+ * an error.
+ *
+ * @param smbus_io_base The SMBus I/O base.
+ * @param command The command.
+ * @return The error code, or 0 on success.
+ */
+static int smbus_send_command(u16 smbus_io_base, u8 command)
+{
+	u8 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);
+}
+
+/**
+ * Get the data from the returned SMBus packet.
+ *
+ * @param smbus_io_base The SMBus I/O base.
+ * @return The data from the SMBus packet area.
+ */
+static u8 smbus_get_result(u16 smbus_io_base)
+{
+	return inb(smbus_io_base + SMB_SDA);
+}
+
+/**
+ * Read a byte from the SMBus.
+ *
+ * All the previous functions exist to support this one. Read a byte from
+ * device 'device' at address 'address'.
+ *
+ * @param smbus_io_base The SMBus I/O base.
+ * @param device The device.
+ * @param command The command.
+ * @return The data from the SMBus packet area.
+ */
+static u8 do_smbus_read_byte(u16 smbus_io_base, u8 device, u8 address)
+{
+	char *error = NULL;
+	int errno;
+
+	if (smbus_check_stop_condition(smbus_io_base)) {
+		error = "smbus_check_stop_condition timed out";
+		goto err
+	}
+
+	if (errno = smbus_start_condition(smbus_io_base)) {
+		error = "smbus_start_condition";
+		goto err;
+	}
+
+	if (errno = smbus_send_slave_address(smbus_io_base, device)) {
+		error = "smbus_send_slave_address";
+		goto err;
+	}
+
+	smbus_ack(smbus_io_base);
+
+	if (errno = smbus_send_command(smbus_io_base, address)) {
+		error = "smbus_send_command";
+		goto err;
+	}
+
+	if (errno = smbus_start_condition(smbus_io_base)) {
+		error = "smbus_start_condition";
+		goto err;
+	}
+
+	if (errno = smbus_send_slave_address(smbus_io_base, device | 0x01)) {
+		error = "smbus_send_slave_address";
+		goto err;
+	}
+
+	if (errno = smbus_stop_condition(smbus_io_base)) {
+		error = "second smbus_stop_condition";
+		goto err;
+	}
+
+	return smbus_get_result(smbus_io_base);
+
+err:
+	printk(BIOS_ERR, "SMBus READ ERROR: %s; device %02x", error, device);
+	/* 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;
+}
+
+/**
+ * Read a byte from the SMBus.
+ *
+ * All other functions in this file are static and support this one function.
+ * Since we are using gcc now, we use a static variable to tell us whether
+ * to enable the SMBus. Thus the northbridge code need only worry about
+ * one thing -- calling this function to read DRAM parameters.
+ *
+ * @param device The device.
+ * @param command The command.
+ * @return The data from the SMBus packet area or an error of 0xff (i.e. -1).
+ */
+int smbus_read_byte(u16 device, u8 address)
+{
+	static int first_time = 1;
+
+	if (first_time) {
+		smbus_init();
+		first_time = 0;
+	}
+
+	return do_smbus_read_byte(SMBUS_IO_BASE, device, address);
+}





More information about the coreboot mailing list