/* * p4b_smbus.c * * Initialize the I801SMBus device on ASUS P4B Bords */ /* Copyright (c) 2002 Ilja Rauhut , Based on the m7101.c hotplug example by: Copyright (c) 2000 Burkhard Kohl , Frank Bauer , and Mark D. Studebaker 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. * */ #ifdef P4Bsmbus_DEBUG #define DBG(x...) printk(x) #else #define DBG(x...) #endif #ifndef TRUE #define TRUE 1 #define FALSE !TRUE #endif #include #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 #endif #include #include #include #include #include #include #include #include #include #include #include #include /* rmm from lm_sensors-2.3.3: */ #define SMB_IO_SIZE 0xF #define SMB_BASE 0x20 /* 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(); 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 */ 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 && 0x2443 == id) { pci_read_config_word(*i801smbus, PCI_VENDOR_ID, &vid); if(vid == 0x8086) break; } } if (0x2443 != 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 = 0x2443; 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,0x2443, NULL); if (dev) { printk("i801smbus: SMBus already actve\n"); return -EPERM; } /* Are we operating a i801 chipset */ dev = pci_find_device(0x8086,0x2440, NULL); if (NULL == dev) { printk("INTEL ICH2 type 82801BA not found.\n"); return -ENODEV ; } /* 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() { 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 , " "Burkhard Kohl , " "Frank Bauer , " "and Mark Studebaker "); MODULE_DESCRIPTION("i801smbus PCI Inserter"); #endif /* MODULE */