diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/i2c/busses/i2c-sis5595.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/i2c/busses/i2c-sis5595.c')
-rw-r--r-- | drivers/i2c/busses/i2c-sis5595.c | 424 |
1 files changed, 424 insertions, 0 deletions
diff --git a/drivers/i2c/busses/i2c-sis5595.c b/drivers/i2c/busses/i2c-sis5595.c new file mode 100644 index 000000000000..425733b019b6 --- /dev/null +++ b/drivers/i2c/busses/i2c-sis5595.c | |||
@@ -0,0 +1,424 @@ | |||
1 | /* | ||
2 | sis5595.c - Part of lm_sensors, Linux kernel modules for hardware | ||
3 | monitoring | ||
4 | Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl> and | ||
5 | Philip Edelbrock <phil@netroedge.com> | ||
6 | |||
7 | This program is free software; you can redistribute it and/or modify | ||
8 | it under the terms of the GNU General Public License as published by | ||
9 | the Free Software Foundation; either version 2 of the License, or | ||
10 | (at your option) any later version. | ||
11 | |||
12 | This program is distributed in the hope that it will be useful, | ||
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | GNU General Public License for more details. | ||
16 | |||
17 | You should have received a copy of the GNU General Public License | ||
18 | along with this program; if not, write to the Free Software | ||
19 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
20 | */ | ||
21 | |||
22 | /* Note: we assume there can only be one SIS5595 with one SMBus interface */ | ||
23 | |||
24 | /* | ||
25 | Note: all have mfr. ID 0x1039. | ||
26 | SUPPORTED PCI ID | ||
27 | 5595 0008 | ||
28 | |||
29 | Note: these chips contain a 0008 device which is incompatible with the | ||
30 | 5595. We recognize these by the presence of the listed | ||
31 | "blacklist" PCI ID and refuse to load. | ||
32 | |||
33 | NOT SUPPORTED PCI ID BLACKLIST PCI ID | ||
34 | 540 0008 0540 | ||
35 | 550 0008 0550 | ||
36 | 5513 0008 5511 | ||
37 | 5581 0008 5597 | ||
38 | 5582 0008 5597 | ||
39 | 5597 0008 5597 | ||
40 | 5598 0008 5597/5598 | ||
41 | 630 0008 0630 | ||
42 | 645 0008 0645 | ||
43 | 646 0008 0646 | ||
44 | 648 0008 0648 | ||
45 | 650 0008 0650 | ||
46 | 651 0008 0651 | ||
47 | 730 0008 0730 | ||
48 | 735 0008 0735 | ||
49 | 745 0008 0745 | ||
50 | 746 0008 0746 | ||
51 | */ | ||
52 | |||
53 | /* TO DO: | ||
54 | * Add Block Transfers (ugly, but supported by the adapter) | ||
55 | * Add adapter resets | ||
56 | */ | ||
57 | |||
58 | #include <linux/config.h> | ||
59 | #include <linux/kernel.h> | ||
60 | #include <linux/module.h> | ||
61 | #include <linux/delay.h> | ||
62 | #include <linux/pci.h> | ||
63 | #include <linux/ioport.h> | ||
64 | #include <linux/init.h> | ||
65 | #include <linux/i2c.h> | ||
66 | #include <asm/io.h> | ||
67 | |||
68 | static int blacklist[] = { | ||
69 | PCI_DEVICE_ID_SI_540, | ||
70 | PCI_DEVICE_ID_SI_550, | ||
71 | PCI_DEVICE_ID_SI_630, | ||
72 | PCI_DEVICE_ID_SI_645, | ||
73 | PCI_DEVICE_ID_SI_646, | ||
74 | PCI_DEVICE_ID_SI_648, | ||
75 | PCI_DEVICE_ID_SI_650, | ||
76 | PCI_DEVICE_ID_SI_651, | ||
77 | PCI_DEVICE_ID_SI_730, | ||
78 | PCI_DEVICE_ID_SI_735, | ||
79 | PCI_DEVICE_ID_SI_745, | ||
80 | PCI_DEVICE_ID_SI_746, | ||
81 | PCI_DEVICE_ID_SI_5511, /* 5513 chip has the 0008 device but that ID | ||
82 | shows up in other chips so we use the 5511 | ||
83 | ID for recognition */ | ||
84 | PCI_DEVICE_ID_SI_5597, | ||
85 | PCI_DEVICE_ID_SI_5598, | ||
86 | 0, /* terminates the list */ | ||
87 | }; | ||
88 | |||
89 | /* Length of ISA address segment */ | ||
90 | #define SIS5595_EXTENT 8 | ||
91 | /* SIS5595 SMBus registers */ | ||
92 | #define SMB_STS_LO 0x00 | ||
93 | #define SMB_STS_HI 0x01 | ||
94 | #define SMB_CTL_LO 0x02 | ||
95 | #define SMB_CTL_HI 0x03 | ||
96 | #define SMB_ADDR 0x04 | ||
97 | #define SMB_CMD 0x05 | ||
98 | #define SMB_PCNT 0x06 | ||
99 | #define SMB_CNT 0x07 | ||
100 | #define SMB_BYTE 0x08 | ||
101 | #define SMB_DEV 0x10 | ||
102 | #define SMB_DB0 0x11 | ||
103 | #define SMB_DB1 0x12 | ||
104 | #define SMB_HAA 0x13 | ||
105 | |||
106 | /* PCI Address Constants */ | ||
107 | #define SMB_INDEX 0x38 | ||
108 | #define SMB_DAT 0x39 | ||
109 | #define SIS5595_ENABLE_REG 0x40 | ||
110 | #define ACPI_BASE 0x90 | ||
111 | |||
112 | /* Other settings */ | ||
113 | #define MAX_TIMEOUT 500 | ||
114 | |||
115 | /* SIS5595 constants */ | ||
116 | #define SIS5595_QUICK 0x00 | ||
117 | #define SIS5595_BYTE 0x02 | ||
118 | #define SIS5595_BYTE_DATA 0x04 | ||
119 | #define SIS5595_WORD_DATA 0x06 | ||
120 | #define SIS5595_PROC_CALL 0x08 | ||
121 | #define SIS5595_BLOCK_DATA 0x0A | ||
122 | |||
123 | /* insmod parameters */ | ||
124 | |||
125 | /* If force_addr is set to anything different from 0, we forcibly enable | ||
126 | the device at the given address. */ | ||
127 | static u16 force_addr = 0; | ||
128 | module_param(force_addr, ushort, 0); | ||
129 | MODULE_PARM_DESC(force_addr, "Initialize the base address of the i2c controller"); | ||
130 | |||
131 | static unsigned short sis5595_base = 0; | ||
132 | |||
133 | static u8 sis5595_read(u8 reg) | ||
134 | { | ||
135 | outb(reg, sis5595_base + SMB_INDEX); | ||
136 | return inb(sis5595_base + SMB_DAT); | ||
137 | } | ||
138 | |||
139 | static void sis5595_write(u8 reg, u8 data) | ||
140 | { | ||
141 | outb(reg, sis5595_base + SMB_INDEX); | ||
142 | outb(data, sis5595_base + SMB_DAT); | ||
143 | } | ||
144 | |||
145 | static int sis5595_setup(struct pci_dev *SIS5595_dev) | ||
146 | { | ||
147 | u16 a; | ||
148 | u8 val; | ||
149 | int *i; | ||
150 | int retval = -ENODEV; | ||
151 | |||
152 | /* Look for imposters */ | ||
153 | for (i = blacklist; *i != 0; i++) { | ||
154 | struct pci_dev *dev; | ||
155 | dev = pci_get_device(PCI_VENDOR_ID_SI, *i, NULL); | ||
156 | if (dev) { | ||
157 | dev_err(&SIS5595_dev->dev, "Looked for SIS5595 but found unsupported device %.4x\n", *i); | ||
158 | pci_dev_put(dev); | ||
159 | return -ENODEV; | ||
160 | } | ||
161 | } | ||
162 | |||
163 | /* Determine the address of the SMBus areas */ | ||
164 | pci_read_config_word(SIS5595_dev, ACPI_BASE, &sis5595_base); | ||
165 | if (sis5595_base == 0 && force_addr == 0) { | ||
166 | dev_err(&SIS5595_dev->dev, "ACPI base address uninitialized - upgrade BIOS or use force_addr=0xaddr\n"); | ||
167 | return -ENODEV; | ||
168 | } | ||
169 | |||
170 | if (force_addr) | ||
171 | sis5595_base = force_addr & ~(SIS5595_EXTENT - 1); | ||
172 | dev_dbg(&SIS5595_dev->dev, "ACPI Base address: %04x\n", sis5595_base); | ||
173 | |||
174 | /* NB: We grab just the two SMBus registers here, but this may still | ||
175 | * interfere with ACPI :-( */ | ||
176 | if (!request_region(sis5595_base + SMB_INDEX, 2, "sis5595-smbus")) { | ||
177 | dev_err(&SIS5595_dev->dev, "SMBus registers 0x%04x-0x%04x already in use!\n", | ||
178 | sis5595_base + SMB_INDEX, sis5595_base + SMB_INDEX + 1); | ||
179 | return -ENODEV; | ||
180 | } | ||
181 | |||
182 | if (force_addr) { | ||
183 | dev_info(&SIS5595_dev->dev, "forcing ISA address 0x%04X\n", sis5595_base); | ||
184 | if (pci_write_config_word(SIS5595_dev, ACPI_BASE, sis5595_base) | ||
185 | != PCIBIOS_SUCCESSFUL) | ||
186 | goto error; | ||
187 | if (pci_read_config_word(SIS5595_dev, ACPI_BASE, &a) | ||
188 | != PCIBIOS_SUCCESSFUL) | ||
189 | goto error; | ||
190 | if ((a & ~(SIS5595_EXTENT - 1)) != sis5595_base) { | ||
191 | /* doesn't work for some chips! */ | ||
192 | dev_err(&SIS5595_dev->dev, "force address failed - not supported?\n"); | ||
193 | goto error; | ||
194 | } | ||
195 | } | ||
196 | |||
197 | if (pci_read_config_byte(SIS5595_dev, SIS5595_ENABLE_REG, &val) | ||
198 | != PCIBIOS_SUCCESSFUL) | ||
199 | goto error; | ||
200 | if ((val & 0x80) == 0) { | ||
201 | dev_info(&SIS5595_dev->dev, "enabling ACPI\n"); | ||
202 | if (pci_write_config_byte(SIS5595_dev, SIS5595_ENABLE_REG, val | 0x80) | ||
203 | != PCIBIOS_SUCCESSFUL) | ||
204 | goto error; | ||
205 | if (pci_read_config_byte(SIS5595_dev, SIS5595_ENABLE_REG, &val) | ||
206 | != PCIBIOS_SUCCESSFUL) | ||
207 | goto error; | ||
208 | if ((val & 0x80) == 0) { | ||
209 | /* doesn't work for some chips? */ | ||
210 | dev_err(&SIS5595_dev->dev, "ACPI enable failed - not supported?\n"); | ||
211 | goto error; | ||
212 | } | ||
213 | } | ||
214 | |||
215 | /* Everything is happy */ | ||
216 | return 0; | ||
217 | |||
218 | error: | ||
219 | release_region(sis5595_base + SMB_INDEX, 2); | ||
220 | return retval; | ||
221 | } | ||
222 | |||
223 | static int sis5595_transaction(struct i2c_adapter *adap) | ||
224 | { | ||
225 | int temp; | ||
226 | int result = 0; | ||
227 | int timeout = 0; | ||
228 | |||
229 | /* Make sure the SMBus host is ready to start transmitting */ | ||
230 | temp = sis5595_read(SMB_STS_LO) + (sis5595_read(SMB_STS_HI) << 8); | ||
231 | if (temp != 0x00) { | ||
232 | dev_dbg(&adap->dev, "SMBus busy (%04x). Resetting... \n", temp); | ||
233 | sis5595_write(SMB_STS_LO, temp & 0xff); | ||
234 | sis5595_write(SMB_STS_HI, temp >> 8); | ||
235 | if ((temp = sis5595_read(SMB_STS_LO) + (sis5595_read(SMB_STS_HI) << 8)) != 0x00) { | ||
236 | dev_dbg(&adap->dev, "Failed! (%02x)\n", temp); | ||
237 | return -1; | ||
238 | } else { | ||
239 | dev_dbg(&adap->dev, "Successfull!\n"); | ||
240 | } | ||
241 | } | ||
242 | |||
243 | /* start the transaction by setting bit 4 */ | ||
244 | sis5595_write(SMB_CTL_LO, sis5595_read(SMB_CTL_LO) | 0x10); | ||
245 | |||
246 | /* We will always wait for a fraction of a second! */ | ||
247 | do { | ||
248 | msleep(1); | ||
249 | temp = sis5595_read(SMB_STS_LO); | ||
250 | } while (!(temp & 0x40) && (timeout++ < MAX_TIMEOUT)); | ||
251 | |||
252 | /* If the SMBus is still busy, we give up */ | ||
253 | if (timeout >= MAX_TIMEOUT) { | ||
254 | dev_dbg(&adap->dev, "SMBus Timeout!\n"); | ||
255 | result = -1; | ||
256 | } | ||
257 | |||
258 | if (temp & 0x10) { | ||
259 | dev_dbg(&adap->dev, "Error: Failed bus transaction\n"); | ||
260 | result = -1; | ||
261 | } | ||
262 | |||
263 | if (temp & 0x20) { | ||
264 | dev_err(&adap->dev, "Bus collision! SMBus may be locked until " | ||
265 | "next hard reset (or not...)\n"); | ||
266 | /* Clock stops and slave is stuck in mid-transmission */ | ||
267 | result = -1; | ||
268 | } | ||
269 | |||
270 | temp = sis5595_read(SMB_STS_LO) + (sis5595_read(SMB_STS_HI) << 8); | ||
271 | if (temp != 0x00) { | ||
272 | sis5595_write(SMB_STS_LO, temp & 0xff); | ||
273 | sis5595_write(SMB_STS_HI, temp >> 8); | ||
274 | } | ||
275 | |||
276 | temp = sis5595_read(SMB_STS_LO) + (sis5595_read(SMB_STS_HI) << 8); | ||
277 | if (temp != 0x00) | ||
278 | dev_dbg(&adap->dev, "Failed reset at end of transaction (%02x)\n", temp); | ||
279 | |||
280 | return result; | ||
281 | } | ||
282 | |||
283 | /* Return -1 on error. */ | ||
284 | static s32 sis5595_access(struct i2c_adapter *adap, u16 addr, | ||
285 | unsigned short flags, char read_write, | ||
286 | u8 command, int size, union i2c_smbus_data *data) | ||
287 | { | ||
288 | switch (size) { | ||
289 | case I2C_SMBUS_QUICK: | ||
290 | sis5595_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01)); | ||
291 | size = SIS5595_QUICK; | ||
292 | break; | ||
293 | case I2C_SMBUS_BYTE: | ||
294 | sis5595_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01)); | ||
295 | if (read_write == I2C_SMBUS_WRITE) | ||
296 | sis5595_write(SMB_CMD, command); | ||
297 | size = SIS5595_BYTE; | ||
298 | break; | ||
299 | case I2C_SMBUS_BYTE_DATA: | ||
300 | sis5595_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01)); | ||
301 | sis5595_write(SMB_CMD, command); | ||
302 | if (read_write == I2C_SMBUS_WRITE) | ||
303 | sis5595_write(SMB_BYTE, data->byte); | ||
304 | size = SIS5595_BYTE_DATA; | ||
305 | break; | ||
306 | case I2C_SMBUS_PROC_CALL: | ||
307 | case I2C_SMBUS_WORD_DATA: | ||
308 | sis5595_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01)); | ||
309 | sis5595_write(SMB_CMD, command); | ||
310 | if (read_write == I2C_SMBUS_WRITE) { | ||
311 | sis5595_write(SMB_BYTE, data->word & 0xff); | ||
312 | sis5595_write(SMB_BYTE + 1, | ||
313 | (data->word & 0xff00) >> 8); | ||
314 | } | ||
315 | size = (size == I2C_SMBUS_PROC_CALL) ? SIS5595_PROC_CALL : SIS5595_WORD_DATA; | ||
316 | break; | ||
317 | /* | ||
318 | case I2C_SMBUS_BLOCK_DATA: | ||
319 | printk(KERN_WARNING "sis5595.o: Block data not yet implemented!\n"); | ||
320 | return -1; | ||
321 | break; | ||
322 | */ | ||
323 | default: | ||
324 | printk(KERN_WARNING "sis5595.o: Unsupported transaction %d\n", size); | ||
325 | return -1; | ||
326 | } | ||
327 | |||
328 | sis5595_write(SMB_CTL_LO, ((size & 0x0E))); | ||
329 | |||
330 | if (sis5595_transaction(adap)) | ||
331 | return -1; | ||
332 | |||
333 | if ((size != SIS5595_PROC_CALL) && | ||
334 | ((read_write == I2C_SMBUS_WRITE) || (size == SIS5595_QUICK))) | ||
335 | return 0; | ||
336 | |||
337 | |||
338 | switch (size) { | ||
339 | case SIS5595_BYTE: /* Where is the result put? I assume here it is in | ||
340 | SMB_DATA but it might just as well be in the | ||
341 | SMB_CMD. No clue in the docs */ | ||
342 | case SIS5595_BYTE_DATA: | ||
343 | data->byte = sis5595_read(SMB_BYTE); | ||
344 | break; | ||
345 | case SIS5595_WORD_DATA: | ||
346 | case SIS5595_PROC_CALL: | ||
347 | data->word = sis5595_read(SMB_BYTE) + (sis5595_read(SMB_BYTE + 1) << 8); | ||
348 | break; | ||
349 | } | ||
350 | return 0; | ||
351 | } | ||
352 | |||
353 | static u32 sis5595_func(struct i2c_adapter *adapter) | ||
354 | { | ||
355 | return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | | ||
356 | I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | | ||
357 | I2C_FUNC_SMBUS_PROC_CALL; | ||
358 | } | ||
359 | |||
360 | static struct i2c_algorithm smbus_algorithm = { | ||
361 | .name = "Non-I2C SMBus adapter", | ||
362 | .id = I2C_ALGO_SMBUS, | ||
363 | .smbus_xfer = sis5595_access, | ||
364 | .functionality = sis5595_func, | ||
365 | }; | ||
366 | |||
367 | static struct i2c_adapter sis5595_adapter = { | ||
368 | .owner = THIS_MODULE, | ||
369 | .class = I2C_CLASS_HWMON, | ||
370 | .name = "unset", | ||
371 | .algo = &smbus_algorithm, | ||
372 | }; | ||
373 | |||
374 | static struct pci_device_id sis5595_ids[] __devinitdata = { | ||
375 | { PCI_DEVICE(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_503) }, | ||
376 | { 0, } | ||
377 | }; | ||
378 | |||
379 | MODULE_DEVICE_TABLE (pci, sis5595_ids); | ||
380 | |||
381 | static int __devinit sis5595_probe(struct pci_dev *dev, const struct pci_device_id *id) | ||
382 | { | ||
383 | if (sis5595_setup(dev)) { | ||
384 | dev_err(&dev->dev, "SIS5595 not detected, module not inserted.\n"); | ||
385 | return -ENODEV; | ||
386 | } | ||
387 | |||
388 | /* set up the driverfs linkage to our parent device */ | ||
389 | sis5595_adapter.dev.parent = &dev->dev; | ||
390 | |||
391 | sprintf(sis5595_adapter.name, "SMBus SIS5595 adapter at %04x", | ||
392 | sis5595_base + SMB_INDEX); | ||
393 | return i2c_add_adapter(&sis5595_adapter); | ||
394 | } | ||
395 | |||
396 | static void __devexit sis5595_remove(struct pci_dev *dev) | ||
397 | { | ||
398 | i2c_del_adapter(&sis5595_adapter); | ||
399 | release_region(sis5595_base + SMB_INDEX, 2); | ||
400 | } | ||
401 | |||
402 | static struct pci_driver sis5595_driver = { | ||
403 | .name = "sis5595_smbus", | ||
404 | .id_table = sis5595_ids, | ||
405 | .probe = sis5595_probe, | ||
406 | .remove = __devexit_p(sis5595_remove), | ||
407 | }; | ||
408 | |||
409 | static int __init i2c_sis5595_init(void) | ||
410 | { | ||
411 | return pci_register_driver(&sis5595_driver); | ||
412 | } | ||
413 | |||
414 | static void __exit i2c_sis5595_exit(void) | ||
415 | { | ||
416 | pci_unregister_driver(&sis5595_driver); | ||
417 | } | ||
418 | |||
419 | MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>"); | ||
420 | MODULE_DESCRIPTION("SIS5595 SMBus driver"); | ||
421 | MODULE_LICENSE("GPL"); | ||
422 | |||
423 | module_init(i2c_sis5595_init); | ||
424 | module_exit(i2c_sis5595_exit); | ||