Subversion Repositories shark

Rev

Rev 420 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
420 giacomo 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
/* #define DEBUG 1 */
52
 
53
#include <linux/kernel.h>
54
#include <linux/module.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 */
97
static int high_clock = 0;
98
static int force = 0;
99
MODULE_PARM(high_clock, "i");
100
MODULE_PARM_DESC(high_clock, "Set Host Master Clock to 56KHz (default 14KHz).");
101
MODULE_PARM(force, "i");
102
MODULE_PARM_DESC(force, "Forcibly enable the SIS630. DANGEROUS!");
103
 
104
/* acpi base address */
105
static unsigned short acpi_base = 0;
106
 
107
/* supported chips */
108
static int supported[] = {
109
        PCI_DEVICE_ID_SI_630,
110
        PCI_DEVICE_ID_SI_730,
111
 
112
};
113
 
114
static inline u8 sis630_read(u8 reg)
115
{
116
        return inb(acpi_base + reg);
117
}
118
 
119
static inline void sis630_write(u8 reg, u8 data)
120
{
121
        outb(data, acpi_base + reg);
122
}
123
 
124
static 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 > 0)
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
 
163
static 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
                i2c_delay(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
 
200
static 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 > 0 && !(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
 
219
static 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
 
233
static 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. */
314
static 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
 
382
static 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
 
389
static 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 > 0) {
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
 
444
exit:
445
        if (retval)
446
                acpi_base = 0;
447
        return retval;
448
}
449
 
450
 
451
static 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
 
458
static struct i2c_adapter sis630_adapter = {
459
        .owner          = THIS_MODULE,
460
        .class          = I2C_ADAP_CLASS_SMBUS,
461
        .name           = "unset",
462
        .algo           = &smbus_algorithm,
463
};
464
 
465
static struct pci_device_id sis630_ids[] __devinitdata = {
466
        { PCI_DEVICE(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_503) },
467
        { 0, }
468
};
469
 
470
static int __devinit sis630_probe(struct pci_dev *dev, const struct pci_device_id *id)
471
{
472
        if (sis630_setup(dev)) {
473
                dev_err(&dev->dev, "SIS630 comp. bus not detected, module not inserted.\n");
474
                return -ENODEV;
475
        }
476
 
477
        /* set up the driverfs linkage to our parent device */
478
        sis630_adapter.dev.parent = &dev->dev;
479
 
480
        sprintf(sis630_adapter.name, "SMBus SIS630 adapter at %04x",
481
                acpi_base + SMB_STS);
482
 
483
        return i2c_add_adapter(&sis630_adapter);
484
}
485
 
486
static void __devexit sis630_remove(struct pci_dev *dev)
487
{
488
        if (acpi_base) {
489
                i2c_del_adapter(&sis630_adapter);
490
                release_region(acpi_base + SMB_STS, SIS630_SMB_IOREGION);
491
                acpi_base = 0;
492
        }
493
}
494
 
495
 
496
static struct pci_driver sis630_driver = {
497
        .name           = "sis630 smbus",
498
        .id_table       = sis630_ids,
499
        .probe          = sis630_probe,
500
        .remove         = __devexit_p(sis630_remove),
501
};
502
 
503
static int __init i2c_sis630_init(void)
504
{
505
        return pci_module_init(&sis630_driver);
506
}
507
 
508
 
509
static void __exit i2c_sis630_exit(void)
510
{
511
        pci_unregister_driver(&sis630_driver);
512
}
513
 
514
 
515
MODULE_LICENSE("GPL");
516
MODULE_AUTHOR("Alexander Malysh <amalysh@web.de>");
517
MODULE_DESCRIPTION("SIS630 SMBus driver");
518
 
519
module_init(i2c_sis630_init);
520
module_exit(i2c_sis630_exit);