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

Revision 1704, 13.7 KB (checked in by mmh, 10 years ago)

Removed some redundant pci_find_device calls.

Cleaned up kernel log messages.

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