]>
Commit | Line | Data |
---|---|---|
1da177e4 | 1 | /* |
1da177e4 LT |
2 | Copyright (c) 1998 - 2002 Frodo Looijaard <frodol@dds.nl> and |
3 | Philip Edelbrock <phil@netroedge.com> | |
4 | ||
5 | This program is free software; you can redistribute it and/or modify | |
6 | it under the terms of the GNU General Public License as published by | |
7 | the Free Software Foundation; either version 2 of the License, or | |
8 | (at your option) any later version. | |
9 | ||
10 | This program is distributed in the hope that it will be useful, | |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | GNU General Public License for more details. | |
1da177e4 LT |
14 | */ |
15 | ||
16 | /* | |
17 | Supports: | |
18 | Intel PIIX4, 440MX | |
506a8b6c | 19 | Serverworks OSB4, CSB5, CSB6, HT-1000, HT-1100 |
2a2f7404 | 20 | ATI IXP200, IXP300, IXP400, SB600, SB700/SP5100, SB800 |
032f708b | 21 | AMD Hudson-2, ML, CZ |
1da177e4 LT |
22 | SMSC Victory66 |
23 | ||
2a2f7404 AA |
24 | Note: we assume there can only be one device, with one or more |
25 | SMBus interfaces. | |
1da177e4 LT |
26 | */ |
27 | ||
1da177e4 LT |
28 | #include <linux/module.h> |
29 | #include <linux/moduleparam.h> | |
30 | #include <linux/pci.h> | |
31 | #include <linux/kernel.h> | |
32 | #include <linux/delay.h> | |
33 | #include <linux/stddef.h> | |
1da177e4 LT |
34 | #include <linux/ioport.h> |
35 | #include <linux/i2c.h> | |
c415b303 | 36 | #include <linux/slab.h> |
1da177e4 | 37 | #include <linux/dmi.h> |
54fb4a05 | 38 | #include <linux/acpi.h> |
21782180 | 39 | #include <linux/io.h> |
1da177e4 LT |
40 | |
41 | ||
1da177e4 LT |
42 | /* PIIX4 SMBus address offsets */ |
43 | #define SMBHSTSTS (0 + piix4_smba) | |
44 | #define SMBHSLVSTS (1 + piix4_smba) | |
45 | #define SMBHSTCNT (2 + piix4_smba) | |
46 | #define SMBHSTCMD (3 + piix4_smba) | |
47 | #define SMBHSTADD (4 + piix4_smba) | |
48 | #define SMBHSTDAT0 (5 + piix4_smba) | |
49 | #define SMBHSTDAT1 (6 + piix4_smba) | |
50 | #define SMBBLKDAT (7 + piix4_smba) | |
51 | #define SMBSLVCNT (8 + piix4_smba) | |
52 | #define SMBSHDWCMD (9 + piix4_smba) | |
53 | #define SMBSLVEVT (0xA + piix4_smba) | |
54 | #define SMBSLVDAT (0xC + piix4_smba) | |
55 | ||
56 | /* count for request_region */ | |
57 | #define SMBIOSIZE 8 | |
58 | ||
59 | /* PCI Address Constants */ | |
60 | #define SMBBA 0x090 | |
61 | #define SMBHSTCFG 0x0D2 | |
62 | #define SMBSLVC 0x0D3 | |
63 | #define SMBSHDW1 0x0D4 | |
64 | #define SMBSHDW2 0x0D5 | |
65 | #define SMBREV 0x0D6 | |
66 | ||
67 | /* Other settings */ | |
68 | #define MAX_TIMEOUT 500 | |
69 | #define ENABLE_INT9 0 | |
70 | ||
71 | /* PIIX4 constants */ | |
72 | #define PIIX4_QUICK 0x00 | |
73 | #define PIIX4_BYTE 0x04 | |
74 | #define PIIX4_BYTE_DATA 0x08 | |
75 | #define PIIX4_WORD_DATA 0x0C | |
76 | #define PIIX4_BLOCK_DATA 0x14 | |
77 | ||
78 | /* insmod parameters */ | |
79 | ||
80 | /* If force is set to anything different from 0, we forcibly enable the | |
81 | PIIX4. DANGEROUS! */ | |
60507095 | 82 | static int force; |
1da177e4 LT |
83 | module_param (force, int, 0); |
84 | MODULE_PARM_DESC(force, "Forcibly enable the PIIX4. DANGEROUS!"); | |
85 | ||
86 | /* If force_addr is set to anything different from 0, we forcibly enable | |
87 | the PIIX4 at the given address. VERY DANGEROUS! */ | |
60507095 | 88 | static int force_addr; |
1da177e4 LT |
89 | module_param (force_addr, int, 0); |
90 | MODULE_PARM_DESC(force_addr, | |
91 | "Forcibly enable the PIIX4 at the given address. " | |
92 | "EXTREMELY DANGEROUS!"); | |
93 | ||
b1c1759c | 94 | static int srvrworks_csb5_delay; |
d6072f84 | 95 | static struct pci_driver piix4_driver; |
1da177e4 | 96 | |
0b255e92 | 97 | static const struct dmi_system_id piix4_dmi_blacklist[] = { |
c2fc54fc JD |
98 | { |
99 | .ident = "Sapphire AM2RD790", | |
100 | .matches = { | |
101 | DMI_MATCH(DMI_BOARD_VENDOR, "SAPPHIRE Inc."), | |
102 | DMI_MATCH(DMI_BOARD_NAME, "PC-AM2RD790"), | |
103 | }, | |
104 | }, | |
105 | { | |
106 | .ident = "DFI Lanparty UT 790FX", | |
107 | .matches = { | |
108 | DMI_MATCH(DMI_BOARD_VENDOR, "DFI Inc."), | |
109 | DMI_MATCH(DMI_BOARD_NAME, "LP UT 790FX"), | |
110 | }, | |
111 | }, | |
112 | { } | |
113 | }; | |
114 | ||
115 | /* The IBM entry is in a separate table because we only check it | |
116 | on Intel-based systems */ | |
0b255e92 | 117 | static const struct dmi_system_id piix4_dmi_ibm[] = { |
1da177e4 LT |
118 | { |
119 | .ident = "IBM", | |
120 | .matches = { DMI_MATCH(DMI_SYS_VENDOR, "IBM"), }, | |
121 | }, | |
122 | { }, | |
123 | }; | |
124 | ||
14a8086d AA |
125 | struct i2c_piix4_adapdata { |
126 | unsigned short smba; | |
127 | }; | |
128 | ||
0b255e92 BP |
129 | static int piix4_setup(struct pci_dev *PIIX4_dev, |
130 | const struct pci_device_id *id) | |
1da177e4 LT |
131 | { |
132 | unsigned char temp; | |
14a8086d | 133 | unsigned short piix4_smba; |
1da177e4 | 134 | |
b1c1759c DM |
135 | if ((PIIX4_dev->vendor == PCI_VENDOR_ID_SERVERWORKS) && |
136 | (PIIX4_dev->device == PCI_DEVICE_ID_SERVERWORKS_CSB5)) | |
137 | srvrworks_csb5_delay = 1; | |
138 | ||
c2fc54fc JD |
139 | /* On some motherboards, it was reported that accessing the SMBus |
140 | caused severe hardware problems */ | |
141 | if (dmi_check_system(piix4_dmi_blacklist)) { | |
142 | dev_err(&PIIX4_dev->dev, | |
143 | "Accessing the SMBus on this system is unsafe!\n"); | |
144 | return -EPERM; | |
145 | } | |
146 | ||
1da177e4 | 147 | /* Don't access SMBus on IBM systems which get corrupted eeproms */ |
c2fc54fc | 148 | if (dmi_check_system(piix4_dmi_ibm) && |
1da177e4 | 149 | PIIX4_dev->vendor == PCI_VENDOR_ID_INTEL) { |
f9ba6c04 | 150 | dev_err(&PIIX4_dev->dev, "IBM system detected; this module " |
1da177e4 LT |
151 | "may corrupt your serial eeprom! Refusing to load " |
152 | "module!\n"); | |
153 | return -EPERM; | |
154 | } | |
155 | ||
156 | /* Determine the address of the SMBus areas */ | |
157 | if (force_addr) { | |
158 | piix4_smba = force_addr & 0xfff0; | |
159 | force = 0; | |
160 | } else { | |
161 | pci_read_config_word(PIIX4_dev, SMBBA, &piix4_smba); | |
162 | piix4_smba &= 0xfff0; | |
163 | if(piix4_smba == 0) { | |
fa63cd56 | 164 | dev_err(&PIIX4_dev->dev, "SMBus base address " |
1da177e4 LT |
165 | "uninitialized - upgrade BIOS or use " |
166 | "force_addr=0xaddr\n"); | |
167 | return -ENODEV; | |
168 | } | |
169 | } | |
170 | ||
54fb4a05 | 171 | if (acpi_check_region(piix4_smba, SMBIOSIZE, piix4_driver.name)) |
18669eab | 172 | return -ENODEV; |
54fb4a05 | 173 | |
d6072f84 | 174 | if (!request_region(piix4_smba, SMBIOSIZE, piix4_driver.name)) { |
fa63cd56 | 175 | dev_err(&PIIX4_dev->dev, "SMBus region 0x%x already in use!\n", |
1da177e4 | 176 | piix4_smba); |
fa63cd56 | 177 | return -EBUSY; |
1da177e4 LT |
178 | } |
179 | ||
180 | pci_read_config_byte(PIIX4_dev, SMBHSTCFG, &temp); | |
181 | ||
1da177e4 LT |
182 | /* If force_addr is set, we program the new address here. Just to make |
183 | sure, we disable the PIIX4 first. */ | |
184 | if (force_addr) { | |
185 | pci_write_config_byte(PIIX4_dev, SMBHSTCFG, temp & 0xfe); | |
186 | pci_write_config_word(PIIX4_dev, SMBBA, piix4_smba); | |
187 | pci_write_config_byte(PIIX4_dev, SMBHSTCFG, temp | 0x01); | |
188 | dev_info(&PIIX4_dev->dev, "WARNING: SMBus interface set to " | |
189 | "new address %04x!\n", piix4_smba); | |
190 | } else if ((temp & 1) == 0) { | |
191 | if (force) { | |
192 | /* This should never need to be done, but has been | |
193 | * noted that many Dell machines have the SMBus | |
194 | * interface on the PIIX4 disabled!? NOTE: This assumes | |
195 | * I/O space and other allocations WERE done by the | |
196 | * Bios! Don't complain if your hardware does weird | |
197 | * things after enabling this. :') Check for Bios | |
198 | * updates before resorting to this. | |
199 | */ | |
200 | pci_write_config_byte(PIIX4_dev, SMBHSTCFG, | |
201 | temp | 1); | |
8117e41e JP |
202 | dev_notice(&PIIX4_dev->dev, |
203 | "WARNING: SMBus interface has been FORCEFULLY ENABLED!\n"); | |
1da177e4 LT |
204 | } else { |
205 | dev_err(&PIIX4_dev->dev, | |
66f8a8ff | 206 | "SMBus Host Controller not enabled!\n"); |
1da177e4 | 207 | release_region(piix4_smba, SMBIOSIZE); |
1da177e4 LT |
208 | return -ENODEV; |
209 | } | |
210 | } | |
211 | ||
54aaa1ca | 212 | if (((temp & 0x0E) == 8) || ((temp & 0x0E) == 2)) |
66f8a8ff | 213 | dev_dbg(&PIIX4_dev->dev, "Using IRQ for SMBus\n"); |
1da177e4 | 214 | else if ((temp & 0x0E) == 0) |
66f8a8ff | 215 | dev_dbg(&PIIX4_dev->dev, "Using SMI# for SMBus\n"); |
1da177e4 LT |
216 | else |
217 | dev_err(&PIIX4_dev->dev, "Illegal Interrupt configuration " | |
218 | "(or code out of date)!\n"); | |
219 | ||
220 | pci_read_config_byte(PIIX4_dev, SMBREV, &temp); | |
fa63cd56 JD |
221 | dev_info(&PIIX4_dev->dev, |
222 | "SMBus Host Controller at 0x%x, revision %d\n", | |
223 | piix4_smba, temp); | |
1da177e4 | 224 | |
14a8086d | 225 | return piix4_smba; |
1da177e4 LT |
226 | } |
227 | ||
0b255e92 | 228 | static int piix4_setup_sb800(struct pci_dev *PIIX4_dev, |
a94dd00f | 229 | const struct pci_device_id *id, u8 aux) |
87e1960e | 230 | { |
14a8086d | 231 | unsigned short piix4_smba; |
87e1960e | 232 | unsigned short smba_idx = 0xcd6; |
032f708b SH |
233 | u8 smba_en_lo, smba_en_hi, smb_en, smb_en_status; |
234 | u8 i2ccfg, i2ccfg_offset = 0x10; | |
87e1960e | 235 | |
3806e94b | 236 | /* SB800 and later SMBus does not support forcing address */ |
87e1960e | 237 | if (force || force_addr) { |
3806e94b | 238 | dev_err(&PIIX4_dev->dev, "SMBus does not support " |
87e1960e SH |
239 | "forcing address!\n"); |
240 | return -EINVAL; | |
241 | } | |
242 | ||
243 | /* Determine the address of the SMBus areas */ | |
032f708b SH |
244 | if ((PIIX4_dev->vendor == PCI_VENDOR_ID_AMD && |
245 | PIIX4_dev->device == PCI_DEVICE_ID_AMD_HUDSON2_SMBUS && | |
246 | PIIX4_dev->revision >= 0x41) || | |
247 | (PIIX4_dev->vendor == PCI_VENDOR_ID_AMD && | |
248 | PIIX4_dev->device == 0x790b && | |
249 | PIIX4_dev->revision >= 0x49)) | |
250 | smb_en = 0x00; | |
251 | else | |
252 | smb_en = (aux) ? 0x28 : 0x2c; | |
a94dd00f | 253 | |
87e1960e SH |
254 | if (!request_region(smba_idx, 2, "smba_idx")) { |
255 | dev_err(&PIIX4_dev->dev, "SMBus base address index region " | |
256 | "0x%x already in use!\n", smba_idx); | |
257 | return -EBUSY; | |
258 | } | |
259 | outb_p(smb_en, smba_idx); | |
260 | smba_en_lo = inb_p(smba_idx + 1); | |
261 | outb_p(smb_en + 1, smba_idx); | |
262 | smba_en_hi = inb_p(smba_idx + 1); | |
263 | release_region(smba_idx, 2); | |
264 | ||
032f708b SH |
265 | if (!smb_en) { |
266 | smb_en_status = smba_en_lo & 0x10; | |
267 | piix4_smba = smba_en_hi << 8; | |
268 | if (aux) | |
269 | piix4_smba |= 0x20; | |
270 | } else { | |
271 | smb_en_status = smba_en_lo & 0x01; | |
272 | piix4_smba = ((smba_en_hi << 8) | smba_en_lo) & 0xffe0; | |
273 | } | |
274 | ||
275 | if (!smb_en_status) { | |
87e1960e | 276 | dev_err(&PIIX4_dev->dev, |
66f8a8ff | 277 | "SMBus Host Controller not enabled!\n"); |
87e1960e SH |
278 | return -ENODEV; |
279 | } | |
280 | ||
87e1960e | 281 | if (acpi_check_region(piix4_smba, SMBIOSIZE, piix4_driver.name)) |
18669eab | 282 | return -ENODEV; |
87e1960e SH |
283 | |
284 | if (!request_region(piix4_smba, SMBIOSIZE, piix4_driver.name)) { | |
285 | dev_err(&PIIX4_dev->dev, "SMBus region 0x%x already in use!\n", | |
286 | piix4_smba); | |
287 | return -EBUSY; | |
288 | } | |
289 | ||
a94dd00f RM |
290 | /* Aux SMBus does not support IRQ information */ |
291 | if (aux) { | |
292 | dev_info(&PIIX4_dev->dev, | |
85fd0fe6 SH |
293 | "Auxiliary SMBus Host Controller at 0x%x\n", |
294 | piix4_smba); | |
a94dd00f RM |
295 | return piix4_smba; |
296 | } | |
297 | ||
87e1960e SH |
298 | /* Request the SMBus I2C bus config region */ |
299 | if (!request_region(piix4_smba + i2ccfg_offset, 1, "i2ccfg")) { | |
300 | dev_err(&PIIX4_dev->dev, "SMBus I2C bus config region " | |
301 | "0x%x already in use!\n", piix4_smba + i2ccfg_offset); | |
302 | release_region(piix4_smba, SMBIOSIZE); | |
87e1960e SH |
303 | return -EBUSY; |
304 | } | |
305 | i2ccfg = inb_p(piix4_smba + i2ccfg_offset); | |
306 | release_region(piix4_smba + i2ccfg_offset, 1); | |
307 | ||
308 | if (i2ccfg & 1) | |
66f8a8ff | 309 | dev_dbg(&PIIX4_dev->dev, "Using IRQ for SMBus\n"); |
87e1960e | 310 | else |
66f8a8ff | 311 | dev_dbg(&PIIX4_dev->dev, "Using SMI# for SMBus\n"); |
87e1960e SH |
312 | |
313 | dev_info(&PIIX4_dev->dev, | |
314 | "SMBus Host Controller at 0x%x, revision %d\n", | |
315 | piix4_smba, i2ccfg >> 4); | |
316 | ||
14a8086d | 317 | return piix4_smba; |
87e1960e SH |
318 | } |
319 | ||
0b255e92 BP |
320 | static int piix4_setup_aux(struct pci_dev *PIIX4_dev, |
321 | const struct pci_device_id *id, | |
322 | unsigned short base_reg_addr) | |
2a2f7404 AA |
323 | { |
324 | /* Set up auxiliary SMBus controllers found on some | |
325 | * AMD chipsets e.g. SP5100 (SB700 derivative) */ | |
326 | ||
327 | unsigned short piix4_smba; | |
328 | ||
329 | /* Read address of auxiliary SMBus controller */ | |
330 | pci_read_config_word(PIIX4_dev, base_reg_addr, &piix4_smba); | |
331 | if ((piix4_smba & 1) == 0) { | |
332 | dev_dbg(&PIIX4_dev->dev, | |
333 | "Auxiliary SMBus controller not enabled\n"); | |
334 | return -ENODEV; | |
335 | } | |
336 | ||
337 | piix4_smba &= 0xfff0; | |
338 | if (piix4_smba == 0) { | |
339 | dev_dbg(&PIIX4_dev->dev, | |
340 | "Auxiliary SMBus base address uninitialized\n"); | |
341 | return -ENODEV; | |
342 | } | |
343 | ||
344 | if (acpi_check_region(piix4_smba, SMBIOSIZE, piix4_driver.name)) | |
345 | return -ENODEV; | |
346 | ||
347 | if (!request_region(piix4_smba, SMBIOSIZE, piix4_driver.name)) { | |
348 | dev_err(&PIIX4_dev->dev, "Auxiliary SMBus region 0x%x " | |
349 | "already in use!\n", piix4_smba); | |
350 | return -EBUSY; | |
351 | } | |
352 | ||
353 | dev_info(&PIIX4_dev->dev, | |
354 | "Auxiliary SMBus Host Controller at 0x%x\n", | |
355 | piix4_smba); | |
356 | ||
357 | return piix4_smba; | |
358 | } | |
359 | ||
e154bf6f | 360 | static int piix4_transaction(struct i2c_adapter *piix4_adapter) |
1da177e4 | 361 | { |
e154bf6f AA |
362 | struct i2c_piix4_adapdata *adapdata = i2c_get_adapdata(piix4_adapter); |
363 | unsigned short piix4_smba = adapdata->smba; | |
1da177e4 LT |
364 | int temp; |
365 | int result = 0; | |
366 | int timeout = 0; | |
367 | ||
e154bf6f | 368 | dev_dbg(&piix4_adapter->dev, "Transaction (pre): CNT=%02x, CMD=%02x, " |
1da177e4 LT |
369 | "ADD=%02x, DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTCNT), |
370 | inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), | |
371 | inb_p(SMBHSTDAT1)); | |
372 | ||
373 | /* Make sure the SMBus host is ready to start transmitting */ | |
374 | if ((temp = inb_p(SMBHSTSTS)) != 0x00) { | |
e154bf6f | 375 | dev_dbg(&piix4_adapter->dev, "SMBus busy (%02x). " |
541e6a02 | 376 | "Resetting...\n", temp); |
1da177e4 LT |
377 | outb_p(temp, SMBHSTSTS); |
378 | if ((temp = inb_p(SMBHSTSTS)) != 0x00) { | |
e154bf6f | 379 | dev_err(&piix4_adapter->dev, "Failed! (%02x)\n", temp); |
97140342 | 380 | return -EBUSY; |
1da177e4 | 381 | } else { |
e154bf6f | 382 | dev_dbg(&piix4_adapter->dev, "Successful!\n"); |
1da177e4 LT |
383 | } |
384 | } | |
385 | ||
386 | /* start the transaction by setting bit 6 */ | |
387 | outb_p(inb(SMBHSTCNT) | 0x040, SMBHSTCNT); | |
388 | ||
389 | /* We will always wait for a fraction of a second! (See PIIX4 docs errata) */ | |
b1c1759c DM |
390 | if (srvrworks_csb5_delay) /* Extra delay for SERVERWORKS_CSB5 */ |
391 | msleep(2); | |
392 | else | |
393 | msleep(1); | |
394 | ||
b6a31950 | 395 | while ((++timeout < MAX_TIMEOUT) && |
b1c1759c | 396 | ((temp = inb_p(SMBHSTSTS)) & 0x01)) |
1da177e4 | 397 | msleep(1); |
1da177e4 LT |
398 | |
399 | /* If the SMBus is still busy, we give up */ | |
b6a31950 | 400 | if (timeout == MAX_TIMEOUT) { |
e154bf6f | 401 | dev_err(&piix4_adapter->dev, "SMBus Timeout!\n"); |
97140342 | 402 | result = -ETIMEDOUT; |
1da177e4 LT |
403 | } |
404 | ||
405 | if (temp & 0x10) { | |
97140342 | 406 | result = -EIO; |
e154bf6f | 407 | dev_err(&piix4_adapter->dev, "Error: Failed bus transaction\n"); |
1da177e4 LT |
408 | } |
409 | ||
410 | if (temp & 0x08) { | |
97140342 | 411 | result = -EIO; |
e154bf6f | 412 | dev_dbg(&piix4_adapter->dev, "Bus collision! SMBus may be " |
1da177e4 LT |
413 | "locked until next hard reset. (sorry!)\n"); |
414 | /* Clock stops and slave is stuck in mid-transmission */ | |
415 | } | |
416 | ||
417 | if (temp & 0x04) { | |
97140342 | 418 | result = -ENXIO; |
e154bf6f | 419 | dev_dbg(&piix4_adapter->dev, "Error: no response!\n"); |
1da177e4 LT |
420 | } |
421 | ||
422 | if (inb_p(SMBHSTSTS) != 0x00) | |
423 | outb_p(inb(SMBHSTSTS), SMBHSTSTS); | |
424 | ||
425 | if ((temp = inb_p(SMBHSTSTS)) != 0x00) { | |
e154bf6f | 426 | dev_err(&piix4_adapter->dev, "Failed reset at end of " |
1da177e4 LT |
427 | "transaction (%02x)\n", temp); |
428 | } | |
e154bf6f | 429 | dev_dbg(&piix4_adapter->dev, "Transaction (post): CNT=%02x, CMD=%02x, " |
1da177e4 LT |
430 | "ADD=%02x, DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTCNT), |
431 | inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), | |
432 | inb_p(SMBHSTDAT1)); | |
433 | return result; | |
434 | } | |
435 | ||
97140342 | 436 | /* Return negative errno on error. */ |
1da177e4 LT |
437 | static s32 piix4_access(struct i2c_adapter * adap, u16 addr, |
438 | unsigned short flags, char read_write, | |
439 | u8 command, int size, union i2c_smbus_data * data) | |
440 | { | |
14a8086d AA |
441 | struct i2c_piix4_adapdata *adapdata = i2c_get_adapdata(adap); |
442 | unsigned short piix4_smba = adapdata->smba; | |
1da177e4 | 443 | int i, len; |
97140342 | 444 | int status; |
1da177e4 LT |
445 | |
446 | switch (size) { | |
1da177e4 | 447 | case I2C_SMBUS_QUICK: |
fa63cd56 | 448 | outb_p((addr << 1) | read_write, |
1da177e4 LT |
449 | SMBHSTADD); |
450 | size = PIIX4_QUICK; | |
451 | break; | |
452 | case I2C_SMBUS_BYTE: | |
fa63cd56 | 453 | outb_p((addr << 1) | read_write, |
1da177e4 LT |
454 | SMBHSTADD); |
455 | if (read_write == I2C_SMBUS_WRITE) | |
456 | outb_p(command, SMBHSTCMD); | |
457 | size = PIIX4_BYTE; | |
458 | break; | |
459 | case I2C_SMBUS_BYTE_DATA: | |
fa63cd56 | 460 | outb_p((addr << 1) | read_write, |
1da177e4 LT |
461 | SMBHSTADD); |
462 | outb_p(command, SMBHSTCMD); | |
463 | if (read_write == I2C_SMBUS_WRITE) | |
464 | outb_p(data->byte, SMBHSTDAT0); | |
465 | size = PIIX4_BYTE_DATA; | |
466 | break; | |
467 | case I2C_SMBUS_WORD_DATA: | |
fa63cd56 | 468 | outb_p((addr << 1) | read_write, |
1da177e4 LT |
469 | SMBHSTADD); |
470 | outb_p(command, SMBHSTCMD); | |
471 | if (read_write == I2C_SMBUS_WRITE) { | |
472 | outb_p(data->word & 0xff, SMBHSTDAT0); | |
473 | outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1); | |
474 | } | |
475 | size = PIIX4_WORD_DATA; | |
476 | break; | |
477 | case I2C_SMBUS_BLOCK_DATA: | |
fa63cd56 | 478 | outb_p((addr << 1) | read_write, |
1da177e4 LT |
479 | SMBHSTADD); |
480 | outb_p(command, SMBHSTCMD); | |
481 | if (read_write == I2C_SMBUS_WRITE) { | |
482 | len = data->block[0]; | |
fa63cd56 JD |
483 | if (len == 0 || len > I2C_SMBUS_BLOCK_MAX) |
484 | return -EINVAL; | |
1da177e4 LT |
485 | outb_p(len, SMBHSTDAT0); |
486 | i = inb_p(SMBHSTCNT); /* Reset SMBBLKDAT */ | |
487 | for (i = 1; i <= len; i++) | |
488 | outb_p(data->block[i], SMBBLKDAT); | |
489 | } | |
490 | size = PIIX4_BLOCK_DATA; | |
491 | break; | |
ac7fc4fb JD |
492 | default: |
493 | dev_warn(&adap->dev, "Unsupported transaction %d\n", size); | |
494 | return -EOPNOTSUPP; | |
1da177e4 LT |
495 | } |
496 | ||
497 | outb_p((size & 0x1C) + (ENABLE_INT9 & 1), SMBHSTCNT); | |
498 | ||
e154bf6f | 499 | status = piix4_transaction(adap); |
97140342 DB |
500 | if (status) |
501 | return status; | |
1da177e4 LT |
502 | |
503 | if ((read_write == I2C_SMBUS_WRITE) || (size == PIIX4_QUICK)) | |
504 | return 0; | |
505 | ||
506 | ||
507 | switch (size) { | |
3578a075 | 508 | case PIIX4_BYTE: |
1da177e4 LT |
509 | case PIIX4_BYTE_DATA: |
510 | data->byte = inb_p(SMBHSTDAT0); | |
511 | break; | |
512 | case PIIX4_WORD_DATA: | |
513 | data->word = inb_p(SMBHSTDAT0) + (inb_p(SMBHSTDAT1) << 8); | |
514 | break; | |
515 | case PIIX4_BLOCK_DATA: | |
516 | data->block[0] = inb_p(SMBHSTDAT0); | |
fa63cd56 JD |
517 | if (data->block[0] == 0 || data->block[0] > I2C_SMBUS_BLOCK_MAX) |
518 | return -EPROTO; | |
1da177e4 LT |
519 | i = inb_p(SMBHSTCNT); /* Reset SMBBLKDAT */ |
520 | for (i = 1; i <= data->block[0]; i++) | |
521 | data->block[i] = inb_p(SMBBLKDAT); | |
522 | break; | |
523 | } | |
524 | return 0; | |
525 | } | |
526 | ||
527 | static u32 piix4_func(struct i2c_adapter *adapter) | |
528 | { | |
529 | return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | | |
530 | I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | | |
531 | I2C_FUNC_SMBUS_BLOCK_DATA; | |
532 | } | |
533 | ||
8f9082c5 | 534 | static const struct i2c_algorithm smbus_algorithm = { |
1da177e4 LT |
535 | .smbus_xfer = piix4_access, |
536 | .functionality = piix4_func, | |
537 | }; | |
538 | ||
392debf1 | 539 | static const struct pci_device_id piix4_ids[] = { |
9b7389c0 JD |
540 | { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3) }, |
541 | { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443MX_3) }, | |
542 | { PCI_DEVICE(PCI_VENDOR_ID_EFAR, PCI_DEVICE_ID_EFAR_SLC90E66_3) }, | |
543 | { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP200_SMBUS) }, | |
544 | { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP300_SMBUS) }, | |
545 | { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP400_SMBUS) }, | |
546 | { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_SBX00_SMBUS) }, | |
3806e94b | 547 | { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_HUDSON2_SMBUS) }, |
b996ac90 | 548 | { PCI_DEVICE(PCI_VENDOR_ID_AMD, 0x790b) }, |
9b7389c0 JD |
549 | { PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, |
550 | PCI_DEVICE_ID_SERVERWORKS_OSB4) }, | |
551 | { PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, | |
552 | PCI_DEVICE_ID_SERVERWORKS_CSB5) }, | |
553 | { PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, | |
554 | PCI_DEVICE_ID_SERVERWORKS_CSB6) }, | |
555 | { PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, | |
556 | PCI_DEVICE_ID_SERVERWORKS_HT1000SB) }, | |
506a8b6c FL |
557 | { PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, |
558 | PCI_DEVICE_ID_SERVERWORKS_HT1100LD) }, | |
1da177e4 LT |
559 | { 0, } |
560 | }; | |
561 | ||
562 | MODULE_DEVICE_TABLE (pci, piix4_ids); | |
563 | ||
e154bf6f | 564 | static struct i2c_adapter *piix4_main_adapter; |
2a2f7404 | 565 | static struct i2c_adapter *piix4_aux_adapter; |
e154bf6f | 566 | |
0b255e92 BP |
567 | static int piix4_add_adapter(struct pci_dev *dev, unsigned short smba, |
568 | struct i2c_adapter **padap) | |
e154bf6f AA |
569 | { |
570 | struct i2c_adapter *adap; | |
571 | struct i2c_piix4_adapdata *adapdata; | |
572 | int retval; | |
573 | ||
574 | adap = kzalloc(sizeof(*adap), GFP_KERNEL); | |
575 | if (adap == NULL) { | |
576 | release_region(smba, SMBIOSIZE); | |
577 | return -ENOMEM; | |
578 | } | |
579 | ||
580 | adap->owner = THIS_MODULE; | |
581 | adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD; | |
582 | adap->algo = &smbus_algorithm; | |
583 | ||
584 | adapdata = kzalloc(sizeof(*adapdata), GFP_KERNEL); | |
585 | if (adapdata == NULL) { | |
586 | kfree(adap); | |
587 | release_region(smba, SMBIOSIZE); | |
588 | return -ENOMEM; | |
589 | } | |
590 | ||
591 | adapdata->smba = smba; | |
592 | ||
593 | /* set up the sysfs linkage to our parent device */ | |
594 | adap->dev.parent = &dev->dev; | |
595 | ||
596 | snprintf(adap->name, sizeof(adap->name), | |
597 | "SMBus PIIX4 adapter at %04x", smba); | |
598 | ||
599 | i2c_set_adapdata(adap, adapdata); | |
600 | ||
601 | retval = i2c_add_adapter(adap); | |
602 | if (retval) { | |
603 | dev_err(&dev->dev, "Couldn't register adapter!\n"); | |
604 | kfree(adapdata); | |
605 | kfree(adap); | |
606 | release_region(smba, SMBIOSIZE); | |
607 | return retval; | |
608 | } | |
609 | ||
610 | *padap = adap; | |
611 | return 0; | |
612 | } | |
613 | ||
0b255e92 | 614 | static int piix4_probe(struct pci_dev *dev, const struct pci_device_id *id) |
1da177e4 LT |
615 | { |
616 | int retval; | |
617 | ||
76b3e28f CC |
618 | if ((dev->vendor == PCI_VENDOR_ID_ATI && |
619 | dev->device == PCI_DEVICE_ID_ATI_SBX00_SMBUS && | |
620 | dev->revision >= 0x40) || | |
621 | dev->vendor == PCI_VENDOR_ID_AMD) | |
87e1960e | 622 | /* base address location etc changed in SB800 */ |
a94dd00f | 623 | retval = piix4_setup_sb800(dev, id, 0); |
87e1960e SH |
624 | else |
625 | retval = piix4_setup(dev, id); | |
626 | ||
2a2f7404 | 627 | /* If no main SMBus found, give up */ |
14a8086d | 628 | if (retval < 0) |
1da177e4 LT |
629 | return retval; |
630 | ||
2a2f7404 AA |
631 | /* Try to register main SMBus adapter, give up if we can't */ |
632 | retval = piix4_add_adapter(dev, retval, &piix4_main_adapter); | |
633 | if (retval < 0) | |
634 | return retval; | |
635 | ||
636 | /* Check for auxiliary SMBus on some AMD chipsets */ | |
a94dd00f RM |
637 | retval = -ENODEV; |
638 | ||
2a2f7404 | 639 | if (dev->vendor == PCI_VENDOR_ID_ATI && |
a94dd00f RM |
640 | dev->device == PCI_DEVICE_ID_ATI_SBX00_SMBUS) { |
641 | if (dev->revision < 0x40) { | |
642 | retval = piix4_setup_aux(dev, id, 0x58); | |
643 | } else { | |
644 | /* SB800 added aux bus too */ | |
645 | retval = piix4_setup_sb800(dev, id, 1); | |
2a2f7404 AA |
646 | } |
647 | } | |
648 | ||
a94dd00f RM |
649 | if (dev->vendor == PCI_VENDOR_ID_AMD && |
650 | dev->device == PCI_DEVICE_ID_AMD_HUDSON2_SMBUS) { | |
651 | retval = piix4_setup_sb800(dev, id, 1); | |
652 | } | |
653 | ||
654 | if (retval > 0) { | |
655 | /* Try to add the aux adapter if it exists, | |
656 | * piix4_add_adapter will clean up if this fails */ | |
657 | piix4_add_adapter(dev, retval, &piix4_aux_adapter); | |
658 | } | |
659 | ||
2a2f7404 | 660 | return 0; |
1da177e4 LT |
661 | } |
662 | ||
0b255e92 | 663 | static void piix4_adap_remove(struct i2c_adapter *adap) |
1da177e4 | 664 | { |
14a8086d AA |
665 | struct i2c_piix4_adapdata *adapdata = i2c_get_adapdata(adap); |
666 | ||
667 | if (adapdata->smba) { | |
668 | i2c_del_adapter(adap); | |
669 | release_region(adapdata->smba, SMBIOSIZE); | |
e154bf6f AA |
670 | kfree(adapdata); |
671 | kfree(adap); | |
1da177e4 LT |
672 | } |
673 | } | |
674 | ||
0b255e92 | 675 | static void piix4_remove(struct pci_dev *dev) |
14a8086d | 676 | { |
e154bf6f AA |
677 | if (piix4_main_adapter) { |
678 | piix4_adap_remove(piix4_main_adapter); | |
679 | piix4_main_adapter = NULL; | |
680 | } | |
2a2f7404 AA |
681 | |
682 | if (piix4_aux_adapter) { | |
683 | piix4_adap_remove(piix4_aux_adapter); | |
684 | piix4_aux_adapter = NULL; | |
685 | } | |
14a8086d AA |
686 | } |
687 | ||
1da177e4 LT |
688 | static struct pci_driver piix4_driver = { |
689 | .name = "piix4_smbus", | |
690 | .id_table = piix4_ids, | |
691 | .probe = piix4_probe, | |
0b255e92 | 692 | .remove = piix4_remove, |
1da177e4 LT |
693 | }; |
694 | ||
56f21788 | 695 | module_pci_driver(piix4_driver); |
1da177e4 LT |
696 | |
697 | MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl> and " | |
698 | "Philip Edelbrock <phil@netroedge.com>"); | |
699 | MODULE_DESCRIPTION("PIIX4 SMBus driver"); | |
700 | MODULE_LICENSE("GPL"); |