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

Revision 1804, 14.5 KB (checked in by mmh, 10 years ago)

Added missing PCI ID for 746. Also added comments noting in
(mmh) which release (> 2.4.9) the IDs were defined, if any.

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