2
0
mirror of https://github.com/lm-sensors/lm-sensors synced 2025-10-15 14:35:54 +00:00
Files
lm-sensors/prog/hotplug/p4b_smbus.c
Mark D. Studebaker 4b316e9f0d add support for ICH4; patch from
Klaus Woltereck <kw42@gmx.de>


git-svn-id: http://lm-sensors.org/svn/lm-sensors/trunk@1404 7894878c-1315-0410-8ee3-d5d059ff63e0
2002-06-28 02:49:46 +00:00

313 lines
7.4 KiB
C

/*
* p4b_smbus.c
*
* Initialize the I801SMBus device on ASUS P4B Bords
*/
/*
Copyright (c) 2002 Ilja Rauhut <IljaRauhut@web.de> and
Klaus Woltereck <kw42@gmx.net>,
Based on the m7101.c hotplug example by:
Copyright (c) 2000 Burkhard Kohl <buk@buks.ipn.de>,
Frank Bauer <frank.bauer@nikocity.de>, and
Mark D. Studebaker <mdsxyz123@yahoo.com>
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; either version 2 of the License, or
(at your option) any later version.
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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/* CHANGES:
* February 13,2002 IR First Version
*
* This code is absolutely experimental - use it at your own
* risk.
*
* Warning, this module will work only with 2.4.x kernels.
* If it works with earlier kernels then it is by luck.
*
* Warning, this module does not work with egcs < 2.95.2, use
* gcc > 2.7.2 or egcs >= 2.95.2.
*
*
* June 21, 2002, added support for the ICH4, code clean up -- Klaus
*/
#ifdef P4Bsmbus_DEBUG
#define DBG(x...) printk(x)
#else
#define DBG(x...)
#endif
#ifndef TRUE
#define TRUE 1
#define FALSE !TRUE
#endif
#include <linux/config.h>
#ifndef CONFIG_HOTPLUG
#error ERROR - You must have 'Support for hot-pluggable devices' enabled in your kernel (under 'general setup')!!
#endif
/* Deal with CONFIG_MODVERSIONS */
#ifdef CONFIG_MODVERSIONS
#define MODVERSIONS
#include <linux/modversions.h>
#endif
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/stddef.h>
#include <linux/ioport.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <asm/types.h>
#include <linux/sched.h>
#include <linux/signal.h>
#include <asm/irq.h>
/*
rmm from lm_sensors-2.3.3:
*/
#define SMB_IO_SIZE 0xF
#define SMB_BASE 0x20
/*
* some shorter definitions for the ICHx PCI device IDs
*/
#define ICH2 PCI_DEVICE_ID_INTEL_82801BA_0
#define ICH2_SMBUS PCI_DEVICE_ID_INTEL_82801BA_3
#ifndef PCI_DEVICE_ID_INTEL_82801DB_0
#define PCI_DEVICE_ID_INTEL_82801DB_0 0x24c0
#define PCI_DEVICE_ID_INTEL_82801DB_3 0x24c3
#endif
#define ICH4 PCI_DEVICE_ID_INTEL_82801DB_0
#define ICH4_SMBUS PCI_DEVICE_ID_INTEL_82801DB_3
/* status, used to indicate that io space needs to be freed */
static struct pci_dev *i801smbus = NULL;
static int i801smbus_inserted = FALSE;
extern void cleanup_module(void);
static rwlock_t i801smbus_lock = RW_LOCK_UNLOCKED;
static unsigned long i801smbus_lock_flags = 0;
/*
* Checks whether SMBus is enabled and turns it on in case they are not.
* It's done by clearing Bit 8 and 4 in i801 config space F2h, PCI-Device 0x8086:0x2440(ICH2)/0x24c0(ICH4)
*/
static int
i801smbus_enable(struct pci_dev *dev){
u16 val = 0;
pci_read_config_word(dev, 0xF2, &val);
DBG("i801smbus: i801smbus config byte reading 0x%X.\n", val);
if (val & 0x008) {
pci_write_config_word(dev, 0xF2, val & 0x77);
pci_read_config_word(dev, 0xF2, &val);
if(val & 0x008)
{
DBG("i801smbus: i801smbus config byte locked:-(\n");
return -EIO;
}
else
printk("SMBus activated in LPC!\n");
}
return 0;
}
/*
* Builds the basic pci_dev for the i801smbus
*/
static int i801smbus_build(struct pci_dev **i801smbus, struct pci_bus *bus)
{
u32 devfn;
u16 id = 0;
u16 vid = 0;
int ret;
DBG("i801smbus: requesting kernel space for the i801smbus entry.\n");
*i801smbus = kmalloc(sizeof(**i801smbus), GFP_ATOMIC);
if(NULL == *i801smbus) {
printk("i801smbus: out of memory.\n");
return -ENOMEM;
}
/* minimally fill in structure for search */
/* The device should be on the same bus as the i801. */
memset(*i801smbus, 0, sizeof(**i801smbus));
(*i801smbus)->bus = bus;
(*i801smbus)->sysdata = bus->sysdata;
(*i801smbus)->hdr_type = PCI_HEADER_TYPE_NORMAL;
DBG("i801smbus: now looking for i801smbus.\n");
for (id = 0, devfn = 0; devfn < 0xFF; devfn++) {
(*i801smbus)->devfn = devfn;
ret = pci_read_config_word(*i801smbus, PCI_DEVICE_ID, &id);
if (ret == 0 && (ICH2_SMBUS == id || ICH4_SMBUS == id)) {
pci_read_config_word(*i801smbus, PCI_VENDOR_ID, &vid);
if(vid == 0x8086)
break;
}
}
if (!(ICH2_SMBUS == id || ICH4_SMBUS == id)) {
DBG("i801smbus: i801smbus not found although i801 present - strange.\n");
return -EACCES;
} else {
DBG("i801smbus: i801smbus found and enabled. Devfn: 0x%X.\n", devfn);
}
/* We now have the devfn and bus of the i801smbus device.
* let's put the rest of the device data together.
*/
(*i801smbus)->vendor = 0x8086;
(*i801smbus)->hdr_type = PCI_HEADER_TYPE_NORMAL;
(*i801smbus)->device = id;
return(pci_setup_device(*i801smbus));
}
/* Initialize the module */
int init_module(void)
{
struct pci_bus *bus = NULL;
struct pci_dev *dev = NULL;
int ret = 0;
DBG("i801smbus: init_module().\n");
/* Are we on a PCI-Board? */
if (!pci_present()) {
printk("i801smbus: No PCI bus found - sorry.\n");
return -ENODEV;
}
/* We want to be sure that the i801smbus is not present yet. */
dev = pci_find_device(0x8086, ICH2_SMBUS, NULL);
if (dev)
{
printk("i801smbus: SMBus already active\n");
return -EPERM;
}
dev = pci_find_device(0x8086, ICH4_SMBUS, NULL);
if (dev)
{
printk("i801smbus: SMBus already active\n");
return -EPERM;
}
/* Are we operating a i801 chipset */
dev = pci_find_device(0x8086, ICH2, NULL);
if (NULL == dev)
{
dev = pci_find_device(0x8086, ICH4, NULL);
if (NULL == dev)
{
printk("INTEL ICH2/4 (82801AB/DB) not found.\n");
return -ENODEV ;
}
else
{
printk("found Intel ICH4 (82801DB).\n");
}
}
else
{
printk("found Intel ICH2 (82801AB).\n");
}
/* we need the bus pointer later */
bus = dev->bus;
if ( (ret = i801smbus_enable(dev)) )
{
printk("i801smbus: Unable to turn on i801smbus device - sorry!\n");
return ret;
}
if ( (ret = i801smbus_build(&i801smbus, bus)) )
return ret;
if ( (ret = pci_enable_device(i801smbus)) ) {
printk("i801smbus: Unable to pci_enable i801smbus device!\n");
return ret;
}
DBG("i801smbus: now inserting.\n");
pci_insert_device(i801smbus, i801smbus->bus);
printk("i801smbus: Enabled\n");
i801smbus_inserted = TRUE;
return 0;
}
void cleanup_module(void)
{
write_lock_irqsave(i801smbus_lock, i801smbus_lock_flags);
if (i801smbus_inserted) {
pci_remove_device(i801smbus);
i801smbus_inserted = FALSE;
}
write_unlock_irqrestore(i801smbus_lock, i801smbus_lock_flags);
if (NULL != i801smbus)
{
kfree(i801smbus);
}
printk("i801smbus: SMBus device removed\n");
}
EXPORT_NO_SYMBOLS;
#ifdef MODULE
#ifdef MODULE_LICENSE
MODULE_LICENSE("GPL");
#endif
MODULE_AUTHOR("Ilja Rauhut <IljaRauhut@web.de>, "
"Burkhard Kohl <bku@buks.ipn.de>, "
"Frank Bauer <frank.bauer@nikocity.de>, "
"Mark Studebaker <mdsxyz123@yahoo.com>,"
"and Klaus Woltereck <kw42@gmx.net>");
MODULE_DESCRIPTION("i801smbus PCI Inserter");
#endif /* MODULE */