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

Revision 1793, 14.2 KB (checked in by mmh, 10 years ago)

Added recognition of SiS746 host bridge.
(mmh) Addressed some trivial compiler warnings.
(mmh) Updated documentation.

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