root/lm-sensors/trunk/prog/hotplug/p4b_smbus.c

Revision 4282, 8.7 kB (checked in by khali, 2 years ago)

p4b_smbus: Add support for the ICH5. The Asus P4P800-X motherboard that
was given to our project by Winbond has its SMBus hidden and needs this.
Also update the documentation a bit.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1 /*
2  * p4b_smbus.c
3  *
4  * Initialize the SMBus device on ICH2/2-M/4/4-M/5 (82801BA/BAM/DB/DBM/EB)
5  */
6 /*
7     Copyright (c) 2002 Ilja Rauhut <IljaRauhut@web.de> and
8     Klaus Woltereck <kw42@gmx.net>,
9
10     Based on the m7101.c hotplug example by:
11
12     Copyright (c) 2000 Burkhard Kohl <buk@buks.ipn.de>,
13     Frank Bauer <frank.bauer@nikocity.de>, and
14     Mark D. Studebaker <mdsxyz123@yahoo.com>
15
16     This program is free software; you can redistribute it and/or modify
17     it under the terms of the GNU General Public License as published by
18     the Free Software Foundation; either version 2 of the License, or
19     (at your option) any later version.
20
21     This program is distributed in the hope that it will be useful,
22     but WITHOUT ANY WARRANTY; without even the implied warranty of
23     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24     GNU General Public License for more details.
25
26     You should have received a copy of the GNU General Public License
27     along with this program; if not, write to the Free Software
28     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29  */
30 /* CHANGES:
31  * February 13,2002 IR First Version
32  *
33  * This code is absolutely experimental - use it at your own
34  * risk.
35  *
36  * Warning, this module will work only with 2.4.x kernels.
37  * If it works with earlier kernels then it is by luck.
38  *
39  * Warning, this module does not work with egcs < 2.95.2, use
40  * gcc > 2.7.2 or egcs >= 2.95.2.
41  *
42  *
43  * June 21, 2002, added support for the ICH4, code clean up -- Klaus
44  *
45  * Apr 13, 2004, modified for ICH4-M (82801DBM) -- Axel Thimm <Axel.Thimm@ATrpms.net>
46  *               bugfix: register F2/bit 8 on ICH2* is bit 0 on ICH4*
47  *
48  * Jan 6, 2007, added support for the ICH5 -- Jean Delvare
49  */
50
51
52 #ifdef P4Bsmbus_DEBUG
53 #define DBG(x...) printk(x)
54 #else
55 #define DBG(x...)
56 #endif
57
58 #ifndef TRUE
59 #define TRUE 1
60 #define FALSE !TRUE
61 #endif
62
63 #include <linux/config.h>
64 #ifndef CONFIG_HOTPLUG
65 #error ERROR - You must have 'Support for hot-pluggable devices' enabled in your kernel (under 'general setup')!!
66 #endif
67
68 #include <linux/module.h>
69 #include <linux/kernel.h>
70 #include <linux/pci.h>
71 #include <linux/stddef.h>
72 #include <linux/ioport.h>
73 #include <linux/errno.h>
74 #include <linux/slab.h>
75 #include <linux/interrupt.h>
76 #include <asm/types.h>
77
78 #include <linux/sched.h>
79 #include <linux/signal.h>
80 #include <asm/irq.h>
81
82 /*
83 rmm    from lm_sensors-2.3.3:
84  */
85
86 #define SMB_IO_SIZE  0xF
87 #define SMB_BASE 0x20
88
89
90 /*
91  * some shorter definitions for the ICHx PCI device IDs
92  */
93
94 #define ICH2 0x2440
95 #define ICH2_M 0x244c
96 #define ICH2_SMBUS 0x2443
97
98 #define ICH4 0x24c0
99 #define ICH4_M 0x24cc
100 #define ICH4_SMBUS 0x24c3
101
102 #define ICH5 0x24d0
103 #define ICH5_SMBUS 0x24d3
104
105 #define is_supported_smbus(id) \
106         ((id) == ICH2_SMBUS || \
107          (id) == ICH4_SMBUS || \
108          (id) == ICH5_SMBUS)
109
110 /* status, used to indicate that io space needs to be freed */
111 static struct pci_dev *i801smbus = NULL;
112 static int i801smbus_inserted = FALSE;
113 extern void cleanup_module(void);
114  
115 static rwlock_t i801smbus_lock = RW_LOCK_UNLOCKED;
116 static unsigned long i801smbus_lock_flags = 0;
117
118 /*
119  * Checks whether SMBus is enabled and turns it on in case they are not.
120  * It's done by modifying the i801 function disable register, F2h.
121  * ICH2(-M): PCI-Device 0x8086:0x2440(0x244c)
122  *           Bit 3: Disables SMBus Host Controller function.
123  *           Bit 8: allows SMBus I/O space to be accessible when Bit 3 is set.
124  * ICH4(-M): PCI-Device 0x8086:0x24c0(0x24cc)
125  *           Bit 3: Disables SMBus Host Controller function.
126  *           Bit 0: allows SMBus I/O space to be accessible when Bit 3 is set.
127  * ICH5:     PCI-Device 0x8086:0x24d0
128  *           Same as ICH4.
129  */
130 static int
131 i801smbus_enable(struct pci_dev *dev, u16 testmask, u16 mask){
132         u16  val   = 0;
133
134         pci_read_config_word(dev, 0xF2, &val);
135         DBG("i801smbus: i801smbus config byte reading 0x%X.\n", val);
136         if (val & testmask) {
137                 pci_write_config_word(dev, 0xF2, val & mask);
138                 pci_read_config_word(dev, 0xF2, &val);
139                 if(val & testmask)
140                   {
141                     DBG("i801smbus: i801smbus config byte locked:-(\n");
142                     return -EIO;
143                   }
144                 else
145                   printk("i801smbus: SMBus activated in LPC!\n");
146         }
147         return 0;
148
149 }
150
151 /*
152  * Builds the basic pci_dev for the i801smbus
153  */
154 static int i801smbus_build(struct pci_dev **i801smbus, struct pci_bus *bus)
155 {
156         u32 devfn;
157         u16 id = 0;
158         u16 vid = 0;
159         int ret;
160
161         DBG("i801smbus: requesting kernel space for the i801smbus entry.\n");
162         *i801smbus = kmalloc(sizeof(**i801smbus), GFP_ATOMIC);
163         if(NULL == *i801smbus) {
164                 printk("i801smbus: out of memory.\n");
165                 return -ENOMEM;
166         }
167
168         /* minimally fill in structure for search */
169         /* The device should be on the same bus as the i801. */
170         memset(*i801smbus, 0, sizeof(**i801smbus));
171         (*i801smbus)->bus    = bus;
172         (*i801smbus)->sysdata = bus->sysdata;
173         (*i801smbus)->hdr_type = PCI_HEADER_TYPE_NORMAL;
174
175         DBG("i801smbus: now looking for i801smbus.\n");
176         for  (id = 0, devfn = 0; devfn < 0xFF; devfn++) {
177                 (*i801smbus)->devfn = devfn;
178                 ret = pci_read_config_word(*i801smbus, PCI_DEVICE_ID, &id);
179                 if (ret == 0 && is_supported_smbus(id)) {
180                         pci_read_config_word(*i801smbus, PCI_VENDOR_ID, &vid);
181                         if(vid == 0x8086)
182                                 break;
183                 }
184         }
185         if (!is_supported_smbus(id)) { 
186                 DBG("i801smbus: i801smbus not found although i801 present - strange.\n");
187                 return -EACCES;
188         } else {
189                 DBG("i801smbus: i801smbus found and enabled. Devfn: 0x%X.\n", devfn);
190         }
191         /* We now have the devfn and bus of the i801smbus device.
192          * let's put the rest of the device data together.
193          */
194
195         (*i801smbus)->vendor = 0x8086;
196         (*i801smbus)->hdr_type = PCI_HEADER_TYPE_NORMAL;
197         (*i801smbus)->device = id;
198  
199         return(pci_setup_device(*i801smbus));
200 }
201
202
203
204
205 /* Initialize the module */
206 int init_module(void)
207 {
208         struct pci_bus *bus = NULL;
209         struct pci_dev *dev = NULL;
210         int ret = 0;
211         u16 testmask = 0, mask = 0;
212
213         DBG("i801smbus: init_module().\n");
214
215         /* Are we on a PCI-Board? */
216         if (!pci_present()) {
217                 printk("i801smbus: No PCI bus found - sorry.\n");
218                 return -ENODEV;
219         }
220
221         /* We want to be sure that the i801smbus is not present yet. */
222         dev = pci_find_device(0x8086, ICH2_SMBUS, NULL);
223
224         if (dev)
225           {
226             printk("i801smbus: SMBus already active\n");
227             return -EPERM;
228           }
229        
230         dev = pci_find_device(0x8086, ICH4_SMBUS, NULL);
231
232         if (dev)
233           {
234             printk("i801smbus: SMBus already active\n");
235             return -EPERM;
236           }
237        
238         dev = pci_find_device(0x8086, ICH5_SMBUS, NULL);
239
240         if (dev)
241           {
242             printk("i801smbus: SMBus already active\n");
243             return -EPERM;
244           }
245        
246         /* Are we operating a i801 chipset */
247         if ((dev = pci_find_device(0x8086, ICH2, NULL)) != 0)
248           {
249             printk("i801smbus: found Intel ICH2 (82801BA).\n");
250             testmask = 0x008;
251             mask = 0xfef7;
252           }
253         else if ((dev = pci_find_device(0x8086, ICH2_M, NULL)) != 0)
254           {
255             printk("i801smbus: found Intel ICH2-M (82801BAM).\n");
256             testmask = 0x008;
257             mask = 0xfef7;
258           }
259         else if ((dev = pci_find_device(0x8086, ICH4, NULL)) != 0)
260           {
261             printk("i801smbus: found Intel ICH4 (82801DB).\n");
262             testmask = 0x008;
263             mask = 0xfff6;
264           }
265         else if ((dev = pci_find_device(0x8086, ICH4_M, NULL)) != 0)
266           {
267             printk("i801smbus: found Intel ICH4-M (82801DBM).\n");
268             testmask = 0x008;
269             mask = 0xfff6;
270           }
271         else if ((dev = pci_find_device(0x8086, ICH5, NULL)) != 0)
272           {
273             printk(KERN_INFO "i801smbus: Found Intel ICH5 (82801EB)\n");
274             testmask = 0x008;
275             mask = 0xfff6;
276           }
277         else
278           {
279             printk(KERN_NOTICE "i801smbus: No supported Intel ICH (82801) "
280                    "chip found\n");
281             return -ENODEV ;
282           }
283
284         /* we need the bus pointer later */
285         bus = dev->bus;
286
287
288         if ( (ret = i801smbus_enable(dev, testmask, mask)) )
289           {
290             printk("i801smbus: Unable to turn on i801smbus device - sorry!\n");
291             return ret;
292           }
293      
294         if ( (ret = i801smbus_build(&i801smbus, bus)) )
295                 return ret;
296
297
298         if ( (ret = pci_enable_device(i801smbus)) ) {
299                 printk("i801smbus: Unable to pci_enable i801smbus device!\n");
300                 return ret;
301         }
302    
303         DBG("i801smbus: now inserting.\n");
304         pci_insert_device(i801smbus, i801smbus->bus);
305         printk("i801smbus: Enabled\n");
306         i801smbus_inserted = TRUE;
307         return 0;
308 }
309
310
311 void cleanup_module(void)
312 {
313         write_lock_irqsave(&i801smbus_lock, i801smbus_lock_flags);
314         if (i801smbus_inserted) {
315                 pci_remove_device(i801smbus);
316                 i801smbus_inserted = FALSE;
317         }
318         write_unlock_irqrestore(&i801smbus_lock, i801smbus_lock_flags);
319
320         if (NULL != i801smbus)
321           {
322             kfree(i801smbus);
323           }
324         printk("i801smbus: SMBus device removed\n");
325 }
326
327 EXPORT_NO_SYMBOLS;
328
329 #ifdef MODULE
330 #ifdef MODULE_LICENSE
331 MODULE_LICENSE("GPL");
332 #endif
333 MODULE_AUTHOR("Ilja Rauhut <IljaRauhut@web.de>, "
334               "Burkhard Kohl <bku@buks.ipn.de>, "
335               "Frank Bauer <frank.bauer@nikocity.de>, "
336               "Mark Studebaker <mdsxyz123@yahoo.com>,"
337               "and Klaus Woltereck <kw42@gmx.net>");
338 MODULE_DESCRIPTION("i801smbus PCI Inserter");
339
340 #endif                          /* MODULE */
Note: See TracBrowser for help on using the browser.