root/lm-sensors/trunk/kernel/busses/i2c-sis645.c @ 1700

Revision 1700, 13.9 KB (checked in by mmh, 10 years ago)

Added support for SiS648/961 and SiS645DX/962.

Added IDs to PCI driver id table.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1/*
2    sis645.c - Part of lm_sensors, Linux kernel modules for hardware
3              monitoring
4
5    Copyright (c) 2002 Mark M. Hoffman <mhoffman@lightlink.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/*
23    This module must be considered BETA unless and until
24    the chipset manufacturer releases a datasheet.
25
26    The register definitions are based on the SiS630.
27    The method for *finding* the registers is based on trial and error.
28
29    A history of changes to this file is available by anonymous CVS:
30    http://www2.lm-sensors.nu/~lm78/download.html
31*/
32
33/*
34    Note: we assume there can only be one SiS645 with one SMBus interface
35*/
36
37#include <linux/version.h>
38#include <linux/module.h>
39#include <linux/pci.h>
40#include <asm/io.h>
41#include <linux/kernel.h>
42#include <linux/stddef.h>
43#include <linux/sched.h>
44#include <linux/ioport.h>
45#include <linux/i2c.h>
46#include "version.h"
47#include <linux/init.h>
48
49MODULE_LICENSE("GPL");
50
51/* PCI identifiers */
52
53/* SiS645 north bridge */
54#ifndef PCI_DEVICE_ID_SI_645
55#define PCI_DEVICE_ID_SI_645 0x0645
56#endif
57
58/* SiS645DX north bridge */
59#ifndef PCI_DEVICE_ID_SI_646
60#define PCI_DEVICE_ID_SI_646 0x0646
61#endif
62
63/* SiS648 north bridge */
64#ifndef PCI_DEVICE_ID_SI_648
65#define PCI_DEVICE_ID_SI_648 0x0648
66#endif
67
68/* SiS650 north bridge */
69#ifndef PCI_DEVICE_ID_SI_650
70#define PCI_DEVICE_ID_SI_650 0x0650
71#endif
72
73/* SiS735 combo chipset */
74#ifndef PCI_DEVICE_ID_SI_735
75#define PCI_DEVICE_ID_SI_735 0x0735
76#endif
77
78/* SiS961 south bridge */
79#ifndef PCI_DEVICE_ID_SI_961
80#define PCI_DEVICE_ID_SI_961 0x0961
81#endif
82
83/* SiS962 south bridge */
84#ifndef PCI_DEVICE_ID_SI_962
85#define PCI_DEVICE_ID_SI_962 0x0962
86#endif
87
88
89#define PCI_DEVICE_ID_SI_SMBUS 0x16
90
91/* base address register in PCI config space */
92#define BASE_IO_REG 0x04
93
94/* SiS645 SMBus registers */
95#define SMB_STS      0x00
96#define SMB_EN       0x01
97#define SMB_CNT      0x02
98#define SMB_HOST_CNT 0x03
99#define SMB_ADDR     0x04
100#define SMB_CMD      0x05
101#define SMB_PCOUNT   0x06
102#define SMB_COUNT    0x07
103#define SMB_BYTE     0x08
104#define SMB_DEV_ADDR 0x10
105#define SMB_DB0      0x11
106#define SMB_DB1      0x12
107#define SMB_SAA      0x13
108
109/* register count for request_region */
110#define SIS645_SMB_IOREGION 0x20
111
112/* Other settings */
113#define MAX_TIMEOUT 500
114
115/* SiS645 SMBus constants */
116#define SIS645_QUICK      0x00
117#define SIS645_BYTE       0x01
118#define SIS645_BYTE_DATA  0x02
119#define SIS645_WORD_DATA  0x03
120#define SIS645_PROC_CALL  0x04
121#define SIS645_BLOCK_DATA 0x05
122
123static int sis645_enable_smbus(struct pci_dev *dev);
124static int sis645_build_dev(struct pci_dev **smbus_dev,
125                            struct pci_dev *bridge_dev);
126
127static void sis645_do_pause(unsigned int amount);
128static int sis645_transaction(int size);
129
130
131
132static unsigned short sis645_smbus_base = 0;
133
134static u8 sis645_read(u8 reg)
135{
136        return inb(sis645_smbus_base + reg) ;
137}
138
139static void sis645_write(u8 reg, u8 data)
140{
141        outb(data, sis645_smbus_base + reg) ;
142}
143
144#ifdef CONFIG_HOTPLUG
145
146/* Turns on SMBus device if it is not; return 0 iff successful
147 */
148static int sis645_enable_smbus(struct pci_dev *dev)
149{
150        u8 val = 0;
151
152        pci_read_config_byte(dev, 0x77, &val);
153
154#ifdef DEBUG
155        printk("i2c-sis645.o: Config byte was 0x%02x.\n", val);
156#endif
157
158        pci_write_config_byte(dev, 0x77, val & ~0x10);
159
160        pci_read_config_byte(dev, 0x77, &val);
161
162        if (val & 0x10) {
163#ifdef DEBUG
164                printk("i2c-sis645.o: Error: Config byte stuck!\n");
165#endif
166                return -1;
167        }
168
169        return 0;
170}
171
172/* Builds the basic pci_dev for SiS645 SMBus
173 */
174static int sis645_build_dev(struct pci_dev **smbus_dev,
175                            struct pci_dev *bridge_dev)
176{
177        struct pci_dev temp_dev;
178        u16 vid = 0, did = 0;
179        int ret;
180
181        /* fill in the device structure for search */
182        memset(&temp_dev, 0, sizeof(temp_dev));
183        temp_dev.bus = bridge_dev->bus;
184        temp_dev.sysdata = bridge_dev->bus->sysdata;
185        temp_dev.hdr_type = PCI_HEADER_TYPE_NORMAL;
186
187        /* the SMBus device is function 1 on the same unit as the ISA bridge */
188        temp_dev.devfn = bridge_dev->devfn + 1;
189
190        /* query to make sure */
191        ret = pci_read_config_word(&temp_dev, PCI_VENDOR_ID, &vid);
192        if (ret || PCI_VENDOR_ID_SI != vid) {
193                printk("i2c-sis645.o: Couldn't find SMBus device!\n");
194                return ret;
195        }
196        temp_dev.vendor = vid;
197
198        ret = pci_read_config_word(&temp_dev, PCI_DEVICE_ID, &did);
199        if (ret || PCI_DEVICE_ID_SI_SMBUS != did) {
200                printk("i2c-sis645.o: Couldn't find SMBus device!\n");
201                return ret;
202        }
203        temp_dev.device = did;
204
205        /* ok, we've got it... request some memory and finish it off */
206        *smbus_dev = kmalloc(sizeof(**smbus_dev), GFP_ATOMIC);
207        if (NULL == *smbus_dev) {
208                printk("i2c-sis645.o: Error: Out of memory!\n");
209                return -ENOMEM;
210        }
211
212        **smbus_dev = temp_dev;
213       
214        ret = pci_setup_device(*smbus_dev);
215        if (ret) {
216                printk("i2c-sis645.o: pci_setup_device failed (0x%08x)\n",ret);
217        }
218        return ret;
219}
220
221#endif /* CONFIG_HOTPLUG */
222
223/* Detect whether a SiS645 can be found, and initialize it, where necessary.
224 */
225static int sis645_setup(void)
226{
227        struct pci_dev *SIS645_ISA_dev;
228        struct pci_dev *SIS645_SMBUS_dev;
229        int ret;
230        u16 ww = 0;
231
232        if (pci_present() == 0) {
233                printk("i2c-sis645.o: Error: No PCI-bus found!\n");
234                return -ENODEV;
235        }
236
237        if (SIS645_ISA_dev = pci_find_device(PCI_VENDOR_ID_SI,
238                        PCI_DEVICE_ID_SI_961, NULL)) {
239                printk("i2c-sis645.o: Found SiS961 south bridge.\n");
240        }
241
242        else if (SIS645_ISA_dev = pci_find_device(PCI_VENDOR_ID_SI,
243                        PCI_DEVICE_ID_SI_962, NULL)) {
244                printk("i2c-sis645.o: Found SiS962 [MuTIOL Media IO].\n");
245        }
246
247        else if (SIS645_ISA_dev = pci_find_device(PCI_VENDOR_ID_SI,
248                        PCI_DEVICE_ID_SI_503, NULL)) {
249                printk("i2c-sis645.o: Found SiS south bridge in compatability mode(?)\n");
250
251                /* look for known compatible north bridges */
252                if ((NULL == pci_find_device(PCI_VENDOR_ID_SI, 
253                                PCI_DEVICE_ID_SI_645, NULL))
254                        && (NULL == pci_find_device(PCI_VENDOR_ID_SI,
255                                PCI_DEVICE_ID_SI_646, NULL))
256                        && (NULL == pci_find_device(PCI_VENDOR_ID_SI,
257                                PCI_DEVICE_ID_SI_648, NULL))
258                        && (NULL == pci_find_device(PCI_VENDOR_ID_SI,
259                                PCI_DEVICE_ID_SI_650, NULL))
260                        && (NULL == pci_find_device(PCI_VENDOR_ID_SI,
261                                PCI_DEVICE_ID_SI_735, NULL))) {
262                        printk("i2c-sis645.o: Error: Can't find suitable host bridge!\n");
263                        return -ENODEV;
264                }
265        }
266
267        else {
268                printk("i2c-sis645.o: Error: Can't find suitable south bridge!\n");
269                return -ENODEV;
270        }
271
272        if (!(SIS645_SMBUS_dev = pci_find_device(PCI_VENDOR_ID_SI,
273                        PCI_DEVICE_ID_SI_SMBUS, NULL))) {
274
275                printk("i2c-sis645.o: "
276                        "Attempting to enable SiS645 SMBus device\n");
277
278#ifndef CONFIG_HOTPLUG
279                printk("i2c-sis645.o: "
280                        "Requires kernel >= 2.4 with CONFIG_HOTPLUG, sorry!\n");
281                return -ENODEV;
282
283#else /* CONFIG_HOTPLUG */
284                if (ret = sis645_enable_smbus(SIS645_ISA_dev)) {
285                        return ret;
286                }
287
288                if (ret = sis645_build_dev(&SIS645_SMBUS_dev, SIS645_ISA_dev)) {
289                        return ret;
290                }
291
292                if (ret = pci_enable_device(SIS645_SMBUS_dev)) {
293                        printk("i2c-sis645.o: Can't pci_enable SMBus device!"
294                                " (0x%08x)\n", ret);
295                        return ret;
296                }
297
298                pci_insert_device(SIS645_SMBUS_dev, SIS645_SMBUS_dev->bus);
299       
300#endif /* CONFIG_HOTPLUG */
301
302        }
303
304        pci_read_config_word(SIS645_SMBUS_dev, PCI_CLASS_DEVICE, &ww);
305        if (PCI_CLASS_SERIAL_SMBUS != ww) {
306                printk("i2c-sis645.o: Error: Unsupported device class 0x%04x!\n", ww);
307                return -ENODEV;
308        }
309
310        /* get the IO base address */
311        sis645_smbus_base = SIS645_SMBUS_dev->resource[BASE_IO_REG].start;
312        if (!sis645_smbus_base) {
313                printk("i2c-sis645.o: SiS645 SMBus base address not initialized!\n");
314                return -EINVAL;
315        }
316        printk("i2c-sis645.o: SiS645 SMBus base address: 0x%04x\n", sis645_smbus_base);
317
318        /* Everything is happy, let's grab the memory and set things up. */
319        if (!request_region(sis645_smbus_base, SIS645_SMB_IOREGION, "sis645-smbus")) {
320                printk
321                    ("i2c-sis645.o: SMBus registers 0x%04x-0x%04x already in use!\n",
322                     sis645_smbus_base, sis645_smbus_base + SIS645_SMB_IOREGION - 1);
323                return -EINVAL;
324        }
325
326        return(0);
327}
328
329
330/* Internally used pause function */
331static void sis645_do_pause(unsigned int amount)
332{
333        current->state = TASK_INTERRUPTIBLE;
334        schedule_timeout(amount);
335}
336
337/* Execute a SMBus transaction.
338   int size is from SIS645_QUICK to SIS645_BLOCK_DATA
339 */
340static int sis645_transaction(int size)
341{
342        int temp;
343        int result = 0;
344        int timeout = 0;
345
346        /* Make sure the SMBus host is ready to start transmitting */
347        if (((temp = sis645_read(SMB_CNT)) & 0x03) != 0x00) {
348#ifdef DEBUG
349                printk("i2c-sis645.o: SMBus busy (0x%02x). Resetting...\n",
350                                temp);
351#endif
352
353                /* kill the transaction */
354                sis645_write(SMB_HOST_CNT, 0x20);
355
356                /* check it again */
357                if (((temp = sis645_read(SMB_CNT)) & 0x03) != 0x00) {
358#ifdef DEBUG
359                        printk("i2c-sis645.o: Failed! (0x%02x)\n", temp);
360#endif
361                        return -1;
362                } else {
363#ifdef DEBUG
364                        printk("i2c-sis645.o: Successful!\n");
365#endif
366                }
367        }
368
369        /* Turn off timeout interrupts, set fast host clock */
370        sis645_write(SMB_CNT, 0x20);
371
372        /* clear all (sticky) status flags */
373        temp = sis645_read(SMB_STS);
374        sis645_write(SMB_STS, temp & 0x1e);
375
376        /* start the transaction by setting bit 4 and size bits */
377        sis645_write(SMB_HOST_CNT, 0x10 | (size & 0x07));
378
379        /* We will always wait for a fraction of a second! */
380        do {
381                sis645_do_pause(1);
382                temp = sis645_read(SMB_STS);
383        } while (!(temp & 0x0e) && (timeout++ < MAX_TIMEOUT));
384
385        /* If the SMBus is still busy, we give up */
386        if (timeout >= MAX_TIMEOUT) {
387                printk("i2c-sis645.o: SMBus Timeout! (0x%02x)\n",temp);
388                result = -1;
389        }
390
391        /* device error - probably missing ACK */
392        if (temp & 0x02) {
393#ifdef DEBUG
394                printk("i2c-sis645.o: Error: Failed bus transaction!\n");
395#endif
396                result = -1;
397        }
398
399        /* bus collision */
400        if (temp & 0x04) {
401#ifdef DEBUG
402                printk("i2c-sis645.o: Error: Bus collision!\n");
403#endif
404                result = -1;
405        }
406
407        /* Finish up by resetting the bus */
408        sis645_write(SMB_STS, temp);
409        if (temp = sis645_read(SMB_STS)) {
410#ifdef DEBUG
411                printk("i2c-sis645.o: Failed reset at end of transaction!"
412                                " (0x%02x)\n", temp);
413#endif
414        }
415
416        return result;
417}
418
419/* Return -1 on error. */
420s32 sis645_access(struct i2c_adapter * adap, u16 addr,
421                   unsigned short flags, char read_write,
422                   u8 command, int size, union i2c_smbus_data * data)
423{
424
425        switch (size) {
426        case I2C_SMBUS_QUICK:
427                sis645_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01));
428                size = SIS645_QUICK;
429                break;
430
431        case I2C_SMBUS_BYTE:
432                sis645_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01));
433                if (read_write == I2C_SMBUS_WRITE)
434                        sis645_write(SMB_CMD, command);
435                size = SIS645_BYTE;
436                break;
437
438        case I2C_SMBUS_BYTE_DATA:
439                sis645_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01));
440                sis645_write(SMB_CMD, command);
441                if (read_write == I2C_SMBUS_WRITE)
442                        sis645_write(SMB_BYTE, data->byte);
443                size = SIS645_BYTE_DATA;
444                break;
445
446        case I2C_SMBUS_PROC_CALL:
447        case I2C_SMBUS_WORD_DATA:
448                sis645_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01));
449                sis645_write(SMB_CMD, command);
450                if (read_write == I2C_SMBUS_WRITE) {
451                        sis645_write(SMB_BYTE, data->word & 0xff);
452                        sis645_write(SMB_BYTE + 1, (data->word & 0xff00) >> 8);
453                }
454                size = (size == I2C_SMBUS_PROC_CALL ? SIS645_PROC_CALL : SIS645_WORD_DATA);
455                break;
456
457        case I2C_SMBUS_BLOCK_DATA:
458                /* TO DO: */
459                printk("sis645.o: SMBus block not implemented!\n");
460                return -1;
461                break;
462
463        default:
464                printk("sis645.o: unsupported I2C size\n");
465                return -1;
466                break;
467        }
468
469        if (sis645_transaction(size))
470                return -1;
471
472        if ((size != SIS645_PROC_CALL) &&
473                        ((read_write == I2C_SMBUS_WRITE) || (size == SIS645_QUICK)))
474                return 0;
475
476        switch (size) {
477        case SIS645_BYTE:
478        case SIS645_BYTE_DATA:
479                data->byte = sis645_read(SMB_BYTE);
480                break;
481
482        case SIS645_WORD_DATA:
483        case SIS645_PROC_CALL:
484                data->word = sis645_read(SMB_BYTE) +
485                                (sis645_read(SMB_BYTE + 1) << 8);
486                break;
487        }
488        return 0;
489}
490
491
492static u32 sis645_func(struct i2c_adapter *adapter)
493{
494        return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
495            I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
496            I2C_FUNC_SMBUS_PROC_CALL;
497}
498
499
500static struct i2c_algorithm smbus_algorithm = {
501        .name           = "Non-I2C SMBus adapter",
502        .id             = I2C_ALGO_SMBUS,
503        .smbus_xfer     = sis645_access,
504        .functionality  = sis645_func,
505};
506
507static struct i2c_adapter sis645_adapter = {
508        .owner          = THIS_MODULE,
509        .name           = "unset",
510        .id             = I2C_ALGO_SMBUS | I2C_HW_SMBUS_SIS645,
511        .algo           = &smbus_algorithm,
512};
513
514
515static struct pci_device_id sis645_ids[] __devinitdata = {
516
517        /* look for these south bridges */
518        { PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_503, PCI_ANY_ID, PCI_ANY_ID, },
519        { PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_961, PCI_ANY_ID, PCI_ANY_ID, },
520        { PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_962, PCI_ANY_ID, PCI_ANY_ID, },
521
522        { 0, }
523};
524
525static int __devinit sis645_probe(struct pci_dev *dev, const struct pci_device_id *id)
526{
527
528        if (sis645_setup()) {
529                printk ("i2c-sis645.o: SiS645 not detected, module not inserted.\n");
530
531                return -ENODEV;
532        }
533       
534        sprintf(sis645_adapter.name, "SMBus SiS645 adapter at 0x%04x", sis645_smbus_base);
535        i2c_add_adapter(&sis645_adapter);
536}
537
538static void __devexit sis645_remove(struct pci_dev *dev)
539{
540        i2c_del_adapter(&sis645_adapter);
541}
542
543
544static struct pci_driver sis645_driver = {
545        .name           = "sis645 smbus",
546        .id_table       = sis645_ids,
547        .probe          = sis645_probe,
548        .remove         = __devexit_p(sis645_remove),
549};
550
551static int __init i2c_sis645_init(void)
552{
553        printk("i2c-sis645.o: version %s (%s)\n", LM_VERSION, LM_DATE);
554        return pci_module_init(&sis645_driver);
555}
556
557
558static void __exit i2c_sis645_exit(void)
559{
560        pci_unregister_driver(&sis645_driver);
561        release_region(sis645_smbus_base, SIS645_SMB_IOREGION);
562}
563
564
565
566MODULE_AUTHOR("Mark M. Hoffman <mhoffman@lightlink.com>");
567MODULE_DESCRIPTION("SiS645 SMBus driver");
568
569/* Register initialization functions using helper macros */
570module_init(i2c_sis645_init);
571module_exit(i2c_sis645_exit);
Note: See TracBrowser for help on using the browser.