aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/i2c/busses/i2c-sis630.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/i2c/busses/i2c-sis630.c')
-rw-r--r--drivers/i2c/busses/i2c-sis630.c523
1 files changed, 523 insertions, 0 deletions
diff --git a/drivers/i2c/busses/i2c-sis630.c b/drivers/i2c/busses/i2c-sis630.c
new file mode 100644
index 000000000000..58df63df1540
--- /dev/null
+++ b/drivers/i2c/busses/i2c-sis630.c
@@ -0,0 +1,523 @@
1/*
2 i2c-sis630.c - Part of lm_sensors, Linux kernel modules for hardware
3 monitoring
4
5 Copyright (c) 2002,2003 Alexander Malysh <amalysh@web.de>
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/*
23 Changes:
24 24.08.2002
25 Fixed the typo in sis630_access (Thanks to Mark M. Hoffman)
26 Changed sis630_transaction.(Thanks to Mark M. Hoffman)
27 18.09.2002
28 Added SIS730 as supported.
29 21.09.2002
30 Added high_clock module option.If this option is set
31 used Host Master Clock 56KHz (default 14KHz).For now we save old Host
32 Master Clock and after transaction completed restore (otherwise
33 it's confuse BIOS and hung Machine).
34 24.09.2002
35 Fixed typo in sis630_access
36 Fixed logical error by restoring of Host Master Clock
37 31.07.2003
38 Added block data read/write support.
39*/
40
41/*
42 Status: beta
43
44 Supports:
45 SIS 630
46 SIS 730
47
48 Note: we assume there can only be one device, with one SMBus interface.
49*/
50
51#include <linux/config.h>
52#include <linux/kernel.h>
53#include <linux/module.h>
54#include <linux/delay.h>
55#include <linux/pci.h>
56#include <linux/ioport.h>
57#include <linux/init.h>
58#include <linux/i2c.h>
59#include <asm/io.h>
60
61/* SIS630 SMBus registers */
62#define SMB_STS 0x80 /* status */
63#define SMB_EN 0x81 /* status enable */
64#define SMB_CNT 0x82
65#define SMBHOST_CNT 0x83
66#define SMB_ADDR 0x84
67#define SMB_CMD 0x85
68#define SMB_PCOUNT 0x86 /* processed count */
69#define SMB_COUNT 0x87
70#define SMB_BYTE 0x88 /* ~0x8F data byte field */
71#define SMBDEV_ADDR 0x90
72#define SMB_DB0 0x91
73#define SMB_DB1 0x92
74#define SMB_SAA 0x93
75
76/* register count for request_region */
77#define SIS630_SMB_IOREGION 20
78
79/* PCI address constants */
80/* acpi base address register */
81#define SIS630_ACPI_BASE_REG 0x74
82/* bios control register */
83#define SIS630_BIOS_CTL_REG 0x40
84
85/* Other settings */
86#define MAX_TIMEOUT 500
87
88/* SIS630 constants */
89#define SIS630_QUICK 0x00
90#define SIS630_BYTE 0x01
91#define SIS630_BYTE_DATA 0x02
92#define SIS630_WORD_DATA 0x03
93#define SIS630_PCALL 0x04
94#define SIS630_BLOCK_DATA 0x05
95
96/* insmod parameters */
97static int high_clock;
98static int force;
99module_param(high_clock, bool, 0);
100MODULE_PARM_DESC(high_clock, "Set Host Master Clock to 56KHz (default 14KHz).");
101module_param(force, bool, 0);
102MODULE_PARM_DESC(force, "Forcibly enable the SIS630. DANGEROUS!");
103
104/* acpi base address */
105static unsigned short acpi_base = 0;
106
107/* supported chips */
108static int supported[] = {
109 PCI_DEVICE_ID_SI_630,
110 PCI_DEVICE_ID_SI_730,
111 0 /* terminates the list */
112};
113
114static inline u8 sis630_read(u8 reg)
115{
116 return inb(acpi_base + reg);
117}
118
119static inline void sis630_write(u8 reg, u8 data)
120{
121 outb(data, acpi_base + reg);
122}
123
124static int sis630_transaction_start(struct i2c_adapter *adap, int size, u8 *oldclock)
125{
126 int temp;
127
128 /* Make sure the SMBus host is ready to start transmitting. */
129 if ((temp = sis630_read(SMB_CNT) & 0x03) != 0x00) {
130 dev_dbg(&adap->dev, "SMBus busy (%02x).Resetting...\n",temp);
131 /* kill smbus transaction */
132 sis630_write(SMBHOST_CNT, 0x20);
133
134 if ((temp = sis630_read(SMB_CNT) & 0x03) != 0x00) {
135 dev_dbg(&adap->dev, "Failed! (%02x)\n", temp);
136 return -1;
137 } else {
138 dev_dbg(&adap->dev, "Successfull!\n");
139 }
140 }
141
142 /* save old clock, so we can prevent machine for hung */
143 *oldclock = sis630_read(SMB_CNT);
144
145 dev_dbg(&adap->dev, "saved clock 0x%02x\n", *oldclock);
146
147 /* disable timeout interrupt , set Host Master Clock to 56KHz if requested */
148 if (high_clock)
149 sis630_write(SMB_CNT, 0x20);
150 else
151 sis630_write(SMB_CNT, (*oldclock & ~0x40));
152
153 /* clear all sticky bits */
154 temp = sis630_read(SMB_STS);
155 sis630_write(SMB_STS, temp & 0x1e);
156
157 /* start the transaction by setting bit 4 and size */
158 sis630_write(SMBHOST_CNT,0x10 | (size & 0x07));
159
160 return 0;
161}
162
163static int sis630_transaction_wait(struct i2c_adapter *adap, int size)
164{
165 int temp, result = 0, timeout = 0;
166
167 /* We will always wait for a fraction of a second! */
168 do {
169 msleep(1);
170 temp = sis630_read(SMB_STS);
171 /* check if block transmitted */
172 if (size == SIS630_BLOCK_DATA && (temp & 0x10))
173 break;
174 } while (!(temp & 0x0e) && (timeout++ < MAX_TIMEOUT));
175
176 /* If the SMBus is still busy, we give up */
177 if (timeout >= MAX_TIMEOUT) {
178 dev_dbg(&adap->dev, "SMBus Timeout!\n");
179 result = -1;
180 }
181
182 if (temp & 0x02) {
183 dev_dbg(&adap->dev, "Error: Failed bus transaction\n");
184 result = -1;
185 }
186
187 if (temp & 0x04) {
188 dev_err(&adap->dev, "Bus collision!\n");
189 result = -1;
190 /*
191 TBD: Datasheet say:
192 the software should clear this bit and restart SMBUS operation.
193 Should we do it or user start request again?
194 */
195 }
196
197 return result;
198}
199
200static void sis630_transaction_end(struct i2c_adapter *adap, u8 oldclock)
201{
202 int temp = 0;
203
204 /* clear all status "sticky" bits */
205 sis630_write(SMB_STS, temp);
206
207 dev_dbg(&adap->dev, "SMB_CNT before clock restore 0x%02x\n", sis630_read(SMB_CNT));
208
209 /*
210 * restore old Host Master Clock if high_clock is set
211 * and oldclock was not 56KHz
212 */
213 if (high_clock && !(oldclock & 0x20))
214 sis630_write(SMB_CNT,(sis630_read(SMB_CNT) & ~0x20));
215
216 dev_dbg(&adap->dev, "SMB_CNT after clock restore 0x%02x\n", sis630_read(SMB_CNT));
217}
218
219static int sis630_transaction(struct i2c_adapter *adap, int size)
220{
221 int result = 0;
222 u8 oldclock = 0;
223
224 result = sis630_transaction_start(adap, size, &oldclock);
225 if (!result) {
226 result = sis630_transaction_wait(adap, size);
227 sis630_transaction_end(adap, oldclock);
228 }
229
230 return result;
231}
232
233static int sis630_block_data(struct i2c_adapter *adap, union i2c_smbus_data *data, int read_write)
234{
235 int i, len = 0, rc = 0;
236 u8 oldclock = 0;
237
238 if (read_write == I2C_SMBUS_WRITE) {
239 len = data->block[0];
240 if (len < 0)
241 len = 0;
242 else if (len > 32)
243 len = 32;
244 sis630_write(SMB_COUNT, len);
245 for (i=1; i <= len; i++) {
246 dev_dbg(&adap->dev, "set data 0x%02x\n", data->block[i]);
247 /* set data */
248 sis630_write(SMB_BYTE+(i-1)%8, data->block[i]);
249 if (i==8 || (len<8 && i==len)) {
250 dev_dbg(&adap->dev, "start trans len=%d i=%d\n",len ,i);
251 /* first transaction */
252 if (sis630_transaction_start(adap, SIS630_BLOCK_DATA, &oldclock))
253 return -1;
254 }
255 else if ((i-1)%8 == 7 || i==len) {
256 dev_dbg(&adap->dev, "trans_wait len=%d i=%d\n",len,i);
257 if (i>8) {
258 dev_dbg(&adap->dev, "clear smbary_sts len=%d i=%d\n",len,i);
259 /*
260 If this is not first transaction,
261 we must clear sticky bit.
262 clear SMBARY_STS
263 */
264 sis630_write(SMB_STS,0x10);
265 }
266 if (sis630_transaction_wait(adap, SIS630_BLOCK_DATA)) {
267 dev_dbg(&adap->dev, "trans_wait failed\n");
268 rc = -1;
269 break;
270 }
271 }
272 }
273 }
274 else {
275 /* read request */
276 data->block[0] = len = 0;
277 if (sis630_transaction_start(adap, SIS630_BLOCK_DATA, &oldclock)) {
278 return -1;
279 }
280 do {
281 if (sis630_transaction_wait(adap, SIS630_BLOCK_DATA)) {
282 dev_dbg(&adap->dev, "trans_wait failed\n");
283 rc = -1;
284 break;
285 }
286 /* if this first transaction then read byte count */
287 if (len == 0)
288 data->block[0] = sis630_read(SMB_COUNT);
289
290 /* just to be sure */
291 if (data->block[0] > 32)
292 data->block[0] = 32;
293
294 dev_dbg(&adap->dev, "block data read len=0x%x\n", data->block[0]);
295
296 for (i=0; i < 8 && len < data->block[0]; i++,len++) {
297 dev_dbg(&adap->dev, "read i=%d len=%d\n", i, len);
298 data->block[len+1] = sis630_read(SMB_BYTE+i);
299 }
300
301 dev_dbg(&adap->dev, "clear smbary_sts len=%d i=%d\n",len,i);
302
303 /* clear SMBARY_STS */
304 sis630_write(SMB_STS,0x10);
305 } while(len < data->block[0]);
306 }
307
308 sis630_transaction_end(adap, oldclock);
309
310 return rc;
311}
312
313/* Return -1 on error. */
314static s32 sis630_access(struct i2c_adapter *adap, u16 addr,
315 unsigned short flags, char read_write,
316 u8 command, int size, union i2c_smbus_data *data)
317{
318 switch (size) {
319 case I2C_SMBUS_QUICK:
320 sis630_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01));
321 size = SIS630_QUICK;
322 break;
323 case I2C_SMBUS_BYTE:
324 sis630_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01));
325 if (read_write == I2C_SMBUS_WRITE)
326 sis630_write(SMB_CMD, command);
327 size = SIS630_BYTE;
328 break;
329 case I2C_SMBUS_BYTE_DATA:
330 sis630_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01));
331 sis630_write(SMB_CMD, command);
332 if (read_write == I2C_SMBUS_WRITE)
333 sis630_write(SMB_BYTE, data->byte);
334 size = SIS630_BYTE_DATA;
335 break;
336 case I2C_SMBUS_PROC_CALL:
337 case I2C_SMBUS_WORD_DATA:
338 sis630_write(SMB_ADDR,((addr & 0x7f) << 1) | (read_write & 0x01));
339 sis630_write(SMB_CMD, command);
340 if (read_write == I2C_SMBUS_WRITE) {
341 sis630_write(SMB_BYTE, data->word & 0xff);
342 sis630_write(SMB_BYTE + 1,(data->word & 0xff00) >> 8);
343 }
344 size = (size == I2C_SMBUS_PROC_CALL ? SIS630_PCALL : SIS630_WORD_DATA);
345 break;
346 case I2C_SMBUS_BLOCK_DATA:
347 sis630_write(SMB_ADDR,((addr & 0x7f) << 1) | (read_write & 0x01));
348 sis630_write(SMB_CMD, command);
349 size = SIS630_BLOCK_DATA;
350 return sis630_block_data(adap, data, read_write);
351 default:
352 printk("Unsupported I2C size\n");
353 return -1;
354 break;
355 }
356
357 if (sis630_transaction(adap, size))
358 return -1;
359
360 if ((size != SIS630_PCALL) &&
361 ((read_write == I2C_SMBUS_WRITE) || (size == SIS630_QUICK))) {
362 return 0;
363 }
364
365 switch(size) {
366 case SIS630_BYTE:
367 case SIS630_BYTE_DATA:
368 data->byte = sis630_read(SMB_BYTE);
369 break;
370 case SIS630_PCALL:
371 case SIS630_WORD_DATA:
372 data->word = sis630_read(SMB_BYTE) + (sis630_read(SMB_BYTE + 1) << 8);
373 break;
374 default:
375 return -1;
376 break;
377 }
378
379 return 0;
380}
381
382static u32 sis630_func(struct i2c_adapter *adapter)
383{
384 return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA |
385 I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_PROC_CALL |
386 I2C_FUNC_SMBUS_BLOCK_DATA;
387}
388
389static int sis630_setup(struct pci_dev *sis630_dev)
390{
391 unsigned char b;
392 struct pci_dev *dummy = NULL;
393 int retval = -ENODEV, i;
394
395 /* check for supported SiS devices */
396 for (i=0; supported[i] > 0 ; i++) {
397 if ((dummy = pci_get_device(PCI_VENDOR_ID_SI, supported[i], dummy)))
398 break; /* found */
399 }
400
401 if (dummy) {
402 pci_dev_put(dummy);
403 }
404 else if (force) {
405 dev_err(&sis630_dev->dev, "WARNING: Can't detect SIS630 compatible device, but "
406 "loading because of force option enabled\n");
407 }
408 else {
409 return -ENODEV;
410 }
411
412 /*
413 Enable ACPI first , so we can accsess reg 74-75
414 in acpi io space and read acpi base addr
415 */
416 if (pci_read_config_byte(sis630_dev, SIS630_BIOS_CTL_REG,&b)) {
417 dev_err(&sis630_dev->dev, "Error: Can't read bios ctl reg\n");
418 goto exit;
419 }
420 /* if ACPI already enabled , do nothing */
421 if (!(b & 0x80) &&
422 pci_write_config_byte(sis630_dev, SIS630_BIOS_CTL_REG, b | 0x80)) {
423 dev_err(&sis630_dev->dev, "Error: Can't enable ACPI\n");
424 goto exit;
425 }
426
427 /* Determine the ACPI base address */
428 if (pci_read_config_word(sis630_dev,SIS630_ACPI_BASE_REG,&acpi_base)) {
429 dev_err(&sis630_dev->dev, "Error: Can't determine ACPI base address\n");
430 goto exit;
431 }
432
433 dev_dbg(&sis630_dev->dev, "ACPI base at 0x%04x\n", acpi_base);
434
435 /* Everything is happy, let's grab the memory and set things up. */
436 if (!request_region(acpi_base + SMB_STS, SIS630_SMB_IOREGION, "sis630-smbus")) {
437 dev_err(&sis630_dev->dev, "SMBus registers 0x%04x-0x%04x already "
438 "in use!\n", acpi_base + SMB_STS, acpi_base + SMB_SAA);
439 goto exit;
440 }
441
442 retval = 0;
443
444exit:
445 if (retval)
446 acpi_base = 0;
447 return retval;
448}
449
450
451static struct i2c_algorithm smbus_algorithm = {
452 .name = "Non-I2C SMBus adapter",
453 .id = I2C_ALGO_SMBUS,
454 .smbus_xfer = sis630_access,
455 .functionality = sis630_func,
456};
457
458static struct i2c_adapter sis630_adapter = {
459 .owner = THIS_MODULE,
460 .class = I2C_CLASS_HWMON,
461 .name = "unset",
462 .algo = &smbus_algorithm,
463};
464
465static struct pci_device_id sis630_ids[] __devinitdata = {
466 { PCI_DEVICE(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_503) },
467 { PCI_DEVICE(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_LPC) },
468 { 0, }
469};
470
471MODULE_DEVICE_TABLE (pci, sis630_ids);
472
473static int __devinit sis630_probe(struct pci_dev *dev, const struct pci_device_id *id)
474{
475 if (sis630_setup(dev)) {
476 dev_err(&dev->dev, "SIS630 comp. bus not detected, module not inserted.\n");
477 return -ENODEV;
478 }
479
480 /* set up the driverfs linkage to our parent device */
481 sis630_adapter.dev.parent = &dev->dev;
482
483 sprintf(sis630_adapter.name, "SMBus SIS630 adapter at %04x",
484 acpi_base + SMB_STS);
485
486 return i2c_add_adapter(&sis630_adapter);
487}
488
489static void __devexit sis630_remove(struct pci_dev *dev)
490{
491 if (acpi_base) {
492 i2c_del_adapter(&sis630_adapter);
493 release_region(acpi_base + SMB_STS, SIS630_SMB_IOREGION);
494 acpi_base = 0;
495 }
496}
497
498
499static struct pci_driver sis630_driver = {
500 .name = "sis630_smbus",
501 .id_table = sis630_ids,
502 .probe = sis630_probe,
503 .remove = __devexit_p(sis630_remove),
504};
505
506static int __init i2c_sis630_init(void)
507{
508 return pci_register_driver(&sis630_driver);
509}
510
511
512static void __exit i2c_sis630_exit(void)
513{
514 pci_unregister_driver(&sis630_driver);
515}
516
517
518MODULE_LICENSE("GPL");
519MODULE_AUTHOR("Alexander Malysh <amalysh@web.de>");
520MODULE_DESCRIPTION("SIS630 SMBus driver");
521
522module_init(i2c_sis630_init);
523module_exit(i2c_sis630_exit);