Rev 420 | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
420 | giacomo | 1 | /* |
2 | i2c-viapro.c - Part of lm_sensors, Linux kernel modules for hardware |
||
3 | monitoring |
||
4 | Copyright (c) 1998 - 2002 Frodo Looijaard <frodol@dds.nl>, |
||
5 | Philip Edelbrock <phil@netroedge.com>, Kyösti Mälkki <kmalkki@cc.hut.fi>, |
||
6 | Mark D. Studebaker <mdsxyz123@yahoo.com> |
||
7 | |||
8 | This program is free software; you can redistribute it and/or modify |
||
9 | it under the terms of the GNU General Public License as published by |
||
10 | the Free Software Foundation; either version 2 of the License, or |
||
11 | (at your option) any later version. |
||
12 | |||
13 | This program is distributed in the hope that it will be useful, |
||
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
16 | GNU General Public License for more details. |
||
17 | |||
18 | You should have received a copy of the GNU General Public License |
||
19 | along with this program; if not, write to the Free Software |
||
20 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
||
21 | */ |
||
22 | |||
23 | /* |
||
24 | Supports Via devices: |
||
25 | 82C596A/B (0x3050) |
||
26 | 82C596B (0x3051) |
||
27 | 82C686A/B |
||
28 | 8231 |
||
29 | 8233 |
||
30 | 8233A (0x3147 and 0x3177) |
||
31 | 8235 |
||
32 | Note: we assume there can only be one device, with one SMBus interface. |
||
33 | */ |
||
34 | |||
35 | #include <linux/module.h> |
||
36 | #include <linux/pci.h> |
||
37 | #include <linux/kernel.h> |
||
38 | #include <linux/stddef.h> |
||
39 | #include <linux/sched.h> |
||
40 | #include <linux/ioport.h> |
||
41 | #include <linux/i2c.h> |
||
42 | #include <linux/init.h> |
||
43 | #include <asm/io.h> |
||
44 | |||
45 | #define SMBBA1 0x90 |
||
46 | #define SMBBA2 0x80 |
||
47 | #define SMBBA3 0xD0 |
||
48 | |||
49 | /* SMBus address offsets */ |
||
50 | static unsigned short vt596_smba; |
||
51 | #define SMBHSTSTS (vt596_smba + 0) |
||
52 | #define SMBHSLVSTS (vt596_smba + 1) |
||
53 | #define SMBHSTCNT (vt596_smba + 2) |
||
54 | #define SMBHSTCMD (vt596_smba + 3) |
||
55 | #define SMBHSTADD (vt596_smba + 4) |
||
56 | #define SMBHSTDAT0 (vt596_smba + 5) |
||
57 | #define SMBHSTDAT1 (vt596_smba + 6) |
||
58 | #define SMBBLKDAT (vt596_smba + 7) |
||
59 | #define SMBSLVCNT (vt596_smba + 8) |
||
60 | #define SMBSHDWCMD (vt596_smba + 9) |
||
61 | #define SMBSLVEVT (vt596_smba + 0xA) |
||
62 | #define SMBSLVDAT (vt596_smba + 0xC) |
||
63 | |||
64 | /* PCI Address Constants */ |
||
65 | |||
66 | /* SMBus data in configuration space can be found in two places, |
||
67 | We try to select the better one*/ |
||
68 | |||
69 | static unsigned short smb_cf_hstcfg = 0xD2; |
||
70 | |||
71 | #define SMBHSTCFG (smb_cf_hstcfg) |
||
72 | #define SMBSLVC (smb_cf_hstcfg + 1) |
||
73 | #define SMBSHDW1 (smb_cf_hstcfg + 2) |
||
74 | #define SMBSHDW2 (smb_cf_hstcfg + 3) |
||
75 | #define SMBREV (smb_cf_hstcfg + 4) |
||
76 | |||
77 | /* Other settings */ |
||
78 | #define MAX_TIMEOUT 500 |
||
79 | #define ENABLE_INT9 0 |
||
80 | |||
81 | /* VT82C596 constants */ |
||
82 | #define VT596_QUICK 0x00 |
||
83 | #define VT596_BYTE 0x04 |
||
84 | #define VT596_BYTE_DATA 0x08 |
||
85 | #define VT596_WORD_DATA 0x0C |
||
86 | #define VT596_BLOCK_DATA 0x14 |
||
87 | |||
88 | |||
89 | /* If force is set to anything different from 0, we forcibly enable the |
||
90 | VT596. DANGEROUS! */ |
||
91 | static int force; |
||
92 | MODULE_PARM(force, "i"); |
||
93 | MODULE_PARM_DESC(force, "Forcibly enable the SMBus. DANGEROUS!"); |
||
94 | |||
95 | /* If force_addr is set to anything different from 0, we forcibly enable |
||
96 | the VT596 at the given address. VERY DANGEROUS! */ |
||
97 | static int force_addr; |
||
98 | MODULE_PARM(force_addr, "i"); |
||
99 | MODULE_PARM_DESC(force_addr, |
||
100 | "Forcibly enable the SMBus at the given address. " |
||
101 | "EXTREMELY DANGEROUS!"); |
||
102 | |||
103 | |||
104 | static struct i2c_adapter vt596_adapter; |
||
105 | |||
106 | /* Another internally used function */ |
||
107 | static int vt596_transaction(void) |
||
108 | { |
||
109 | int temp; |
||
110 | int result = 0; |
||
111 | int timeout = 0; |
||
112 | |||
113 | dev_dbg(&vt596_adapter.dev, "Transaction (pre): CNT=%02x, CMD=%02x, " |
||
114 | "ADD=%02x, DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTCNT), |
||
115 | inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), |
||
116 | inb_p(SMBHSTDAT1)); |
||
117 | |||
118 | /* Make sure the SMBus host is ready to start transmitting */ |
||
119 | if ((temp = inb_p(SMBHSTSTS)) != 0x00) { |
||
120 | dev_dbg(&vt596_adapter.dev, "SMBus busy (0x%02x). " |
||
121 | "Resetting...\n", temp); |
||
122 | |||
123 | outb_p(temp, SMBHSTSTS); |
||
124 | if ((temp = inb_p(SMBHSTSTS)) != 0x00) { |
||
125 | dev_dbg(&vt596_adapter.dev, "Failed! (0x%02x)\n", temp); |
||
126 | |||
127 | return -1; |
||
128 | } else { |
||
129 | dev_dbg(&vt596_adapter.dev, "Successfull!\n"); |
||
130 | } |
||
131 | } |
||
132 | |||
133 | /* start the transaction by setting bit 6 */ |
||
134 | outb_p(inb(SMBHSTCNT) | 0x040, SMBHSTCNT); |
||
135 | |||
136 | /* We will always wait for a fraction of a second! |
||
137 | I don't know if VIA needs this, Intel did */ |
||
138 | do { |
||
139 | i2c_delay(1); |
||
140 | temp = inb_p(SMBHSTSTS); |
||
141 | } while ((temp & 0x01) && (timeout++ < MAX_TIMEOUT)); |
||
142 | |||
143 | /* If the SMBus is still busy, we give up */ |
||
144 | if (timeout >= MAX_TIMEOUT) { |
||
145 | result = -1; |
||
146 | dev_dbg(&vt596_adapter.dev, "SMBus Timeout!\n"); |
||
147 | } |
||
148 | |||
149 | if (temp & 0x10) { |
||
150 | result = -1; |
||
151 | dev_dbg(&vt596_adapter.dev, "Error: Failed bus transaction\n"); |
||
152 | } |
||
153 | |||
154 | if (temp & 0x08) { |
||
155 | result = -1; |
||
156 | dev_info(&vt596_adapter.dev, "Bus collision! SMBus may be " |
||
157 | "locked until next hard\nreset. (sorry!)\n"); |
||
158 | /* Clock stops and slave is stuck in mid-transmission */ |
||
159 | } |
||
160 | |||
161 | if (temp & 0x04) { |
||
162 | result = -1; |
||
163 | dev_dbg(&vt596_adapter.dev, "Error: no response!\n"); |
||
164 | } |
||
165 | |||
166 | if (inb_p(SMBHSTSTS) != 0x00) |
||
167 | outb_p(inb(SMBHSTSTS), SMBHSTSTS); |
||
168 | |||
169 | if ((temp = inb_p(SMBHSTSTS)) != 0x00) { |
||
170 | dev_dbg(&vt596_adapter.dev, "Failed reset at end of " |
||
171 | "transaction (%02x)\n", temp); |
||
172 | } |
||
173 | dev_dbg(&vt596_adapter.dev, "Transaction (post): CNT=%02x, CMD=%02x, " |
||
174 | "ADD=%02x, DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTCNT), |
||
175 | inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), |
||
176 | inb_p(SMBHSTDAT1)); |
||
177 | |||
178 | return result; |
||
179 | } |
||
180 | |||
181 | /* Return -1 on error. */ |
||
182 | static s32 vt596_access(struct i2c_adapter *adap, u16 addr, |
||
183 | unsigned short flags, char read_write, u8 command, |
||
184 | int size, union i2c_smbus_data *data) |
||
185 | { |
||
186 | int i, len; |
||
187 | |||
188 | switch (size) { |
||
189 | case I2C_SMBUS_PROC_CALL: |
||
190 | dev_info(&vt596_adapter.dev, |
||
191 | "I2C_SMBUS_PROC_CALL not supported!\n"); |
||
192 | return -1; |
||
193 | case I2C_SMBUS_QUICK: |
||
194 | outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), |
||
195 | SMBHSTADD); |
||
196 | size = VT596_QUICK; |
||
197 | break; |
||
198 | case I2C_SMBUS_BYTE: |
||
199 | outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), |
||
200 | SMBHSTADD); |
||
201 | if (read_write == I2C_SMBUS_WRITE) |
||
202 | outb_p(command, SMBHSTCMD); |
||
203 | size = VT596_BYTE; |
||
204 | break; |
||
205 | case I2C_SMBUS_BYTE_DATA: |
||
206 | outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), |
||
207 | SMBHSTADD); |
||
208 | outb_p(command, SMBHSTCMD); |
||
209 | if (read_write == I2C_SMBUS_WRITE) |
||
210 | outb_p(data->byte, SMBHSTDAT0); |
||
211 | size = VT596_BYTE_DATA; |
||
212 | break; |
||
213 | case I2C_SMBUS_WORD_DATA: |
||
214 | outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), |
||
215 | SMBHSTADD); |
||
216 | outb_p(command, SMBHSTCMD); |
||
217 | if (read_write == I2C_SMBUS_WRITE) { |
||
218 | outb_p(data->word & 0xff, SMBHSTDAT0); |
||
219 | outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1); |
||
220 | } |
||
221 | size = VT596_WORD_DATA; |
||
222 | break; |
||
223 | case I2C_SMBUS_BLOCK_DATA: |
||
224 | outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), |
||
225 | SMBHSTADD); |
||
226 | outb_p(command, SMBHSTCMD); |
||
227 | if (read_write == I2C_SMBUS_WRITE) { |
||
228 | len = data->block[0]; |
||
229 | if (len < 0) |
||
230 | len = 0; |
||
231 | if (len > 32) |
||
232 | len = 32; |
||
233 | outb_p(len, SMBHSTDAT0); |
||
234 | i = inb_p(SMBHSTCNT); /* Reset SMBBLKDAT */ |
||
235 | for (i = 1; i <= len; i++) |
||
236 | outb_p(data->block[i], SMBBLKDAT); |
||
237 | } |
||
238 | size = VT596_BLOCK_DATA; |
||
239 | break; |
||
240 | } |
||
241 | |||
242 | outb_p((size & 0x1C) + (ENABLE_INT9 & 1), SMBHSTCNT); |
||
243 | |||
244 | if (vt596_transaction()) /* Error in transaction */ |
||
245 | return -1; |
||
246 | |||
247 | if ((read_write == I2C_SMBUS_WRITE) || (size == VT596_QUICK)) |
||
248 | return 0; |
||
249 | |||
250 | switch (size) { |
||
251 | case VT596_BYTE: |
||
252 | /* Where is the result put? I assume here it is in |
||
253 | * SMBHSTDAT0 but it might just as well be in the |
||
254 | * SMBHSTCMD. No clue in the docs |
||
255 | */ |
||
256 | data->byte = inb_p(SMBHSTDAT0); |
||
257 | break; |
||
258 | case VT596_BYTE_DATA: |
||
259 | data->byte = inb_p(SMBHSTDAT0); |
||
260 | break; |
||
261 | case VT596_WORD_DATA: |
||
262 | data->word = inb_p(SMBHSTDAT0) + (inb_p(SMBHSTDAT1) << 8); |
||
263 | break; |
||
264 | case VT596_BLOCK_DATA: |
||
265 | data->block[0] = inb_p(SMBHSTDAT0); |
||
266 | i = inb_p(SMBHSTCNT); /* Reset SMBBLKDAT */ |
||
267 | for (i = 1; i <= data->block[0]; i++) |
||
268 | data->block[i] = inb_p(SMBBLKDAT); |
||
269 | break; |
||
270 | } |
||
271 | return 0; |
||
272 | } |
||
273 | |||
274 | static u32 vt596_func(struct i2c_adapter *adapter) |
||
275 | { |
||
276 | return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | |
||
277 | I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | |
||
278 | I2C_FUNC_SMBUS_BLOCK_DATA; |
||
279 | } |
||
280 | |||
281 | static struct i2c_algorithm smbus_algorithm = { |
||
282 | .name = "Non-I2C SMBus adapter", |
||
283 | .id = I2C_ALGO_SMBUS, |
||
284 | .smbus_xfer = vt596_access, |
||
285 | .functionality = vt596_func, |
||
286 | }; |
||
287 | |||
288 | static struct i2c_adapter vt596_adapter = { |
||
289 | .owner = THIS_MODULE, |
||
290 | .class = I2C_ADAP_CLASS_SMBUS, |
||
291 | .algo = &smbus_algorithm, |
||
292 | .name = "unset", |
||
293 | }; |
||
294 | |||
295 | static int __devinit vt596_probe(struct pci_dev *pdev, |
||
296 | const struct pci_device_id *id) |
||
297 | { |
||
298 | unsigned char temp; |
||
299 | int error = -ENODEV; |
||
300 | |||
301 | /* Determine the address of the SMBus areas */ |
||
302 | if (force_addr) { |
||
303 | vt596_smba = force_addr & 0xfff0; |
||
304 | force = 0; |
||
305 | goto found; |
||
306 | } |
||
307 | |||
308 | if ((pci_read_config_word(pdev, id->driver_data, &vt596_smba)) || |
||
309 | !(vt596_smba & 0x1)) { |
||
310 | /* try 2nd address and config reg. for 596 */ |
||
311 | if (id->device == PCI_DEVICE_ID_VIA_82C596_3 && |
||
312 | !pci_read_config_word(pdev, SMBBA2, &vt596_smba) && |
||
313 | (vt596_smba & 0x1)) { |
||
314 | smb_cf_hstcfg = 0x84; |
||
315 | } else { |
||
316 | /* no matches at all */ |
||
317 | dev_err(&pdev->dev, "Cannot configure " |
||
318 | "SMBus I/O Base address\n"); |
||
319 | return -ENODEV; |
||
320 | } |
||
321 | } |
||
322 | |||
323 | vt596_smba &= 0xfff0; |
||
324 | if (vt596_smba == 0) { |
||
325 | dev_err(&pdev->dev, "SMBus base address " |
||
326 | "uninitialized - upgrade BIOS or use " |
||
327 | "force_addr=0xaddr\n"); |
||
328 | return -ENODEV; |
||
329 | } |
||
330 | |||
331 | found: |
||
332 | if (!request_region(vt596_smba, 8, "viapro-smbus")) { |
||
333 | dev_err(&pdev->dev, "SMBus region 0x%x already in use!\n", |
||
334 | vt596_smba); |
||
335 | return -ENODEV; |
||
336 | } |
||
337 | |||
338 | pci_read_config_byte(pdev, SMBHSTCFG, &temp); |
||
339 | /* If force_addr is set, we program the new address here. Just to make |
||
340 | sure, we disable the VT596 first. */ |
||
341 | if (force_addr) { |
||
342 | pci_write_config_byte(pdev, SMBHSTCFG, temp & 0xfe); |
||
343 | pci_write_config_word(pdev, id->driver_data, vt596_smba); |
||
344 | pci_write_config_byte(pdev, SMBHSTCFG, temp | 0x01); |
||
345 | dev_warn(&pdev->dev, "WARNING: SMBus interface set to new " |
||
346 | "address 0x%04x!\n", vt596_smba); |
||
347 | } else if ((temp & 1) == 0) { |
||
348 | if (force) { |
||
349 | /* NOTE: This assumes I/O space and other allocations |
||
350 | * WERE done by the Bios! Don't complain if your |
||
351 | * hardware does weird things after enabling this. |
||
352 | * :') Check for Bios updates before resorting to |
||
353 | * this. |
||
354 | */ |
||
355 | pci_write_config_byte(pdev, SMBHSTCFG, temp | 1); |
||
356 | dev_info(&pdev->dev, "Enabling SMBus device\n"); |
||
357 | } else { |
||
358 | dev_err(&pdev->dev, "SMBUS: Error: Host SMBus " |
||
359 | "controller not enabled! - upgrade BIOS or " |
||
360 | "use force=1\n"); |
||
361 | goto release_region; |
||
362 | } |
||
363 | } |
||
364 | |||
365 | if ((temp & 0x0E) == 8) |
||
366 | dev_dbg(&pdev->dev, "using Interrupt 9 for SMBus.\n"); |
||
367 | else if ((temp & 0x0E) == 0) |
||
368 | dev_dbg(&pdev->dev, "using Interrupt SMI# for SMBus.\n"); |
||
369 | else |
||
370 | dev_dbg(&pdev->dev, "Illegal Interrupt configuration " |
||
371 | "(or code out of date)!\n"); |
||
372 | |||
373 | pci_read_config_byte(pdev, SMBREV, &temp); |
||
374 | dev_dbg(&pdev->dev, "SMBREV = 0x%X\n", temp); |
||
375 | dev_dbg(&pdev->dev, "VT596_smba = 0x%X\n", vt596_smba); |
||
376 | |||
377 | vt596_adapter.dev.parent = &pdev->dev; |
||
378 | snprintf(vt596_adapter.name, I2C_NAME_SIZE, |
||
379 | "SMBus Via Pro adapter at %04x", vt596_smba); |
||
380 | |||
381 | return i2c_add_adapter(&vt596_adapter); |
||
382 | |||
383 | release_region: |
||
384 | release_region(vt596_smba, 8); |
||
385 | return error; |
||
386 | } |
||
387 | |||
388 | static void __devexit vt596_remove(struct pci_dev *pdev) |
||
389 | { |
||
390 | i2c_del_adapter(&vt596_adapter); |
||
391 | release_region(vt596_smba, 8); |
||
392 | } |
||
393 | |||
394 | static struct pci_device_id vt596_ids[] = { |
||
395 | { |
||
396 | .vendor = PCI_VENDOR_ID_VIA, |
||
397 | .device = PCI_DEVICE_ID_VIA_82C596_3, |
||
398 | .subvendor = PCI_ANY_ID, |
||
399 | .subdevice = PCI_ANY_ID, |
||
400 | .driver_data = SMBBA1, |
||
401 | }, |
||
402 | { |
||
403 | .vendor = PCI_VENDOR_ID_VIA, |
||
404 | .device = PCI_DEVICE_ID_VIA_82C596B_3, |
||
405 | .subvendor = PCI_ANY_ID, |
||
406 | .subdevice = PCI_ANY_ID, |
||
407 | .driver_data = SMBBA1, |
||
408 | }, |
||
409 | { |
||
410 | .vendor = PCI_VENDOR_ID_VIA, |
||
411 | .device = PCI_DEVICE_ID_VIA_82C686_4, |
||
412 | .subvendor = PCI_ANY_ID, |
||
413 | .subdevice = PCI_ANY_ID, |
||
414 | .driver_data = SMBBA1, |
||
415 | }, |
||
416 | { |
||
417 | .vendor = PCI_VENDOR_ID_VIA, |
||
418 | .device = PCI_DEVICE_ID_VIA_8233_0, |
||
419 | .subvendor = PCI_ANY_ID, |
||
420 | .subdevice = PCI_ANY_ID, |
||
421 | .driver_data = SMBBA3 |
||
422 | }, |
||
423 | { |
||
424 | .vendor = PCI_VENDOR_ID_VIA, |
||
425 | .device = PCI_DEVICE_ID_VIA_8233A, |
||
426 | .subvendor = PCI_ANY_ID, |
||
427 | .subdevice = PCI_ANY_ID, |
||
428 | .driver_data = SMBBA3, |
||
429 | }, |
||
430 | { |
||
431 | .vendor = PCI_VENDOR_ID_VIA, |
||
432 | .device = PCI_DEVICE_ID_VIA_8235, |
||
433 | .subvendor = PCI_ANY_ID, |
||
434 | .subdevice = PCI_ANY_ID, |
||
435 | .driver_data = SMBBA3 |
||
436 | }, |
||
437 | { |
||
438 | .vendor = PCI_VENDOR_ID_VIA, |
||
439 | .device = PCI_DEVICE_ID_VIA_8231_4, |
||
440 | .subvendor = PCI_ANY_ID, |
||
441 | .subdevice = PCI_ANY_ID, |
||
442 | .driver_data = SMBBA1, |
||
443 | }, |
||
444 | { 0, } |
||
445 | }; |
||
446 | |||
447 | static struct pci_driver vt596_driver = { |
||
448 | .name = "vt596 smbus", |
||
449 | .id_table = vt596_ids, |
||
450 | .probe = vt596_probe, |
||
451 | .remove = __devexit_p(vt596_remove), |
||
452 | }; |
||
453 | |||
454 | static int __init i2c_vt596_init(void) |
||
455 | { |
||
456 | return pci_module_init(&vt596_driver); |
||
457 | } |
||
458 | |||
459 | |||
460 | static void __exit i2c_vt596_exit(void) |
||
461 | { |
||
462 | pci_unregister_driver(&vt596_driver); |
||
463 | } |
||
464 | |||
465 | MODULE_AUTHOR( |
||
466 | "Frodo Looijaard <frodol@dds.nl> and " |
||
467 | "Philip Edelbrock <phil@netroedge.com>"); |
||
468 | MODULE_DESCRIPTION("vt82c596 SMBus driver"); |
||
469 | MODULE_LICENSE("GPL"); |
||
470 | |||
471 | module_init(i2c_vt596_init); |
||
472 | module_exit(i2c_vt596_exit); |