2010-03-14 18:01:08 +01:00
|
|
|
/*
|
|
|
|
* This file is part of the coreboot project.
|
|
|
|
*
|
|
|
|
* Copyright (C) 2004 Ronald G. Minnich
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; version 2 of the License.
|
|
|
|
*
|
|
|
|
* 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
|
|
|
|
*/
|
|
|
|
|
2010-02-27 02:50:21 +01:00
|
|
|
#include "i82801dx.h"
|
2004-08-24 18:20:46 +02:00
|
|
|
#include <smbus.h>
|
|
|
|
#include <pci.h>
|
|
|
|
#include <arch/io.h>
|
|
|
|
|
|
|
|
#define PM_BUS 0
|
|
|
|
#define PM_DEVFN PCI_DEVFN(0x1f,3)
|
|
|
|
|
|
|
|
void smbus_enable(void)
|
|
|
|
{
|
|
|
|
unsigned char byte;
|
|
|
|
/* iobase addr */
|
|
|
|
pcibios_write_config_dword(PM_BUS, PM_DEVFN, 0x20, SMBUS_IO_BASE | 1);
|
|
|
|
/* smbus enable */
|
2010-03-14 18:01:08 +01:00
|
|
|
pcibios_write_config_byte(PM_BUS, PM_DEVFN, 0x40, 1);
|
2004-08-24 18:20:46 +02:00
|
|
|
/* iospace enable */
|
|
|
|
pcibios_write_config_word(PM_BUS, PM_DEVFN, 0x4, 1);
|
|
|
|
|
2010-03-14 18:01:08 +01:00
|
|
|
/* Disable interrupt generation */
|
|
|
|
outb(0, SMBUS_IO_BASE + SMBHSTCTL);
|
2004-08-24 18:20:46 +02:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void smbus_setup(void)
|
|
|
|
{
|
|
|
|
outb(0, SMBUS_IO_BASE + SMBHSTSTAT);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void smbus_wait_until_ready(void)
|
|
|
|
{
|
2010-03-14 18:01:08 +01:00
|
|
|
while ((inb(SMBUS_IO_BASE + SMBHSTSTAT) & 1) == 1) {
|
2004-08-24 18:20:46 +02:00
|
|
|
/* nop */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void smbus_wait_until_done(void)
|
|
|
|
{
|
|
|
|
unsigned char byte;
|
|
|
|
do {
|
|
|
|
byte = inb(SMBUS_IO_BASE + SMBHSTSTAT);
|
|
|
|
}
|
2010-03-14 18:01:08 +01:00
|
|
|
while ((byte & 1) == 1);
|
|
|
|
while ((byte & ~1) == 0) {
|
2004-08-24 18:20:46 +02:00
|
|
|
byte = inb(SMBUS_IO_BASE + SMBHSTSTAT);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int smbus_read_byte(unsigned device, unsigned address, unsigned char *result)
|
|
|
|
{
|
|
|
|
unsigned char host_status_register;
|
|
|
|
unsigned char byte;
|
|
|
|
|
|
|
|
smbus_wait_until_ready();
|
|
|
|
|
|
|
|
/* setup transaction */
|
|
|
|
/* disable interrupts */
|
|
|
|
outb(inb(SMBUS_IO_BASE + SMBHSTCTL) & (~1), SMBUS_IO_BASE + SMBHSTCTL);
|
|
|
|
/* set the device I'm talking too */
|
|
|
|
outb(((device & 0x7f) << 1) | 1, SMBUS_IO_BASE + SMBHSTADD);
|
|
|
|
/* set the command/address... */
|
|
|
|
outb(address & 0xFF, SMBUS_IO_BASE + SMBHSTCMD);
|
|
|
|
/* set up for a byte data read */
|
2010-03-14 18:01:08 +01:00
|
|
|
outb((inb(SMBUS_IO_BASE + SMBHSTCTL) & 0xE3) | (0x2 << 2),
|
|
|
|
SMBUS_IO_BASE + SMBHSTCTL);
|
2004-08-24 18:20:46 +02:00
|
|
|
|
|
|
|
/* clear any lingering errors, so the transaction will run */
|
|
|
|
outb(inb(SMBUS_IO_BASE + SMBHSTSTAT), SMBUS_IO_BASE + SMBHSTSTAT);
|
|
|
|
|
2010-03-14 18:01:08 +01:00
|
|
|
/* clear the data byte... */
|
2004-08-24 18:20:46 +02:00
|
|
|
outb(0, SMBUS_IO_BASE + SMBHSTDAT0);
|
|
|
|
|
|
|
|
/* start the command */
|
2010-03-14 18:01:08 +01:00
|
|
|
outb((inb(SMBUS_IO_BASE + SMBHSTCTL) | 0x40),
|
|
|
|
SMBUS_IO_BASE + SMBHSTCTL);
|
2004-08-24 18:20:46 +02:00
|
|
|
|
|
|
|
/* poll for transaction completion */
|
|
|
|
smbus_wait_until_done();
|
|
|
|
|
|
|
|
host_status_register = inb(SMBUS_IO_BASE + SMBHSTSTAT);
|
|
|
|
|
|
|
|
/* read results of transaction */
|
|
|
|
byte = inb(SMBUS_IO_BASE + SMBHSTDAT0);
|
|
|
|
|
|
|
|
*result = byte;
|
|
|
|
return host_status_register != 0x02;
|
|
|
|
}
|