root/lm-sensors/trunk/kernel/busses/i2c-osb4.c @ 949

Revision 949, 13.1 KB (checked in by mds, 12 years ago)

(mds) add i2c- prefix to module name in version/date printk for those

that were missing it.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1/*
2    i2c-osb4.c - Part of lm_sensors, Linux kernel modules for hardware monitoring
3
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 2 of the License, or
7    (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program; if not, write to the Free Software
16    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18    This driver was partly written by Steffen Persvold (sp@scali.com)
19    for ServerWorks OSB4 southbridge.
20
21*/
22
23/* Note: we assume there can only be one OSB4, with one SMBus interface */
24
25#include <linux/version.h>
26#include <linux/module.h>
27#include <linux/pci.h>
28#include <asm/io.h>
29#include <linux/kernel.h>
30#include <linux/stddef.h>
31#include <linux/sched.h>
32#include <linux/ioport.h>
33#include <linux/i2c.h>
34#include <linux/init.h>
35#include "version.h"
36
37#ifndef PCI_DEVICE_ID_SERVERWORKS_OSB4
38#define PCI_DEVICE_ID_SERVERWORKS_OSB4 0x0200
39#endif
40
41/* OSB4 SMBus address offsets */
42#define SMBHSTSTS (0 + osb4_smba)
43#define SMBHSLVSTS (1 + osb4_smba)
44#define SMBHSTCNT (2 + osb4_smba)
45#define SMBHSTCMD (3 + osb4_smba)
46#define SMBHSTADD (4 + osb4_smba)
47#define SMBHSTDAT0 (5 + osb4_smba)
48#define SMBHSTDAT1 (6 + osb4_smba)
49#define SMBBLKDAT (7 + osb4_smba)
50#define SMBSLVCNT (8 + osb4_smba)
51#define SMBSHDWCMD (9 + osb4_smba)
52#define SMBSLVEVT (0xA + osb4_smba)
53#define SMBSLVDAT (0xC + osb4_smba)
54
55/* PCI Address Constants */
56#define SMBBA     0x090
57#define SMBHSTCFG 0x0D2
58#define SMBSLVC   0x0D3
59#define SMBSHDW1  0x0D4
60#define SMBSHDW2  0x0D5
61#define SMBREV    0x0D6
62
63/* Other settings */
64#define MAX_TIMEOUT 500
65#define ENABLE_INT9 0
66
67/* OSB4 constants */
68#define OSB4_QUICK      0x00
69#define OSB4_BYTE       0x04
70#define OSB4_BYTE_DATA  0x08
71#define OSB4_WORD_DATA  0x0C
72#define OSB4_BLOCK_DATA 0x14
73
74/* insmod parameters */
75
76/* If force is set to anything different from 0, we forcibly enable the
77   OSB4. DANGEROUS! */
78static int force = 0;
79MODULE_PARM(force, "i");
80MODULE_PARM_DESC(force, "Forcibly enable the OSB4. DANGEROUS!");
81
82/* If force_addr is set to anything different from 0, we forcibly enable
83   the OSB4 at the given address. VERY DANGEROUS! */
84static int force_addr = 0;
85MODULE_PARM(force_addr, "i");
86MODULE_PARM_DESC(force_addr,
87                 "Forcibly enable the OSB4 at the given address. "
88                 "EXTREMELY DANGEROUS!");
89
90#ifdef MODULE
91static
92#else
93extern
94#endif
95int __init i2c_osb4_init(void);
96static int __init osb4_cleanup(void);
97static int osb4_setup(void);
98static s32 osb4_access(struct i2c_adapter *adap, u16 addr,
99                        unsigned short flags, char read_write,
100                        u8 command, int size, union i2c_smbus_data *data);
101static void osb4_do_pause(unsigned int amount);
102static int osb4_transaction(void);
103static void osb4_inc(struct i2c_adapter *adapter);
104static void osb4_dec(struct i2c_adapter *adapter);
105static u32 osb4_func(struct i2c_adapter *adapter);
106
107#ifdef MODULE
108extern int init_module(void);
109extern int cleanup_module(void);
110#endif                          /* MODULE */
111
112static struct i2c_algorithm smbus_algorithm = {
113        /* name */ "Non-I2C SMBus adapter",
114        /* id */ I2C_ALGO_SMBUS,
115        /* master_xfer */ NULL,
116        /* smbus_access */ osb4_access,
117        /* slave_send */ NULL,
118        /* slave_rcv */ NULL,
119        /* algo_control */ NULL,
120        /* functionality */ osb4_func,
121};
122
123static struct i2c_adapter osb4_adapter = {
124        "unset",
125        I2C_ALGO_SMBUS | I2C_HW_SMBUS_OSB4,
126        &smbus_algorithm,
127        NULL,
128        osb4_inc,
129        osb4_dec,
130        NULL,
131        NULL,
132};
133
134static int __initdata osb4_initialized;
135static unsigned short osb4_smba = 0;
136
137
138/* Detect whether a OSB4 can be found, and initialize it, where necessary.
139   Note the differences between kernels with the old PCI BIOS interface and
140   newer kernels with the real PCI interface. In compat.h some things are
141   defined to make the transition easier. */
142int osb4_setup(void)
143{
144        int error_return = 0;
145        unsigned char temp;
146
147        struct pci_dev *OSB4_dev;
148
149        /* First check whether we can access PCI at all */
150        if (pci_present() == 0) {
151                printk("i2c-osb4.o: Error: No PCI-bus found!\n");
152                error_return = -ENODEV;
153                goto END;
154        }
155
156        /* Look for the OSB4, function 0 */
157        /* Note: we keep on searching until we have found 'function 0' */
158        OSB4_dev = NULL;
159        do
160                OSB4_dev = pci_find_device(PCI_VENDOR_ID_SERVERWORKS,
161                                            PCI_DEVICE_ID_SERVERWORKS_OSB4,
162                                            OSB4_dev);
163        while (OSB4_dev && (PCI_FUNC(OSB4_dev->devfn) != 0));
164        if (OSB4_dev == NULL) {
165                printk
166                    ("i2c-osb4.o: Error: Can't detect OSB4, function 0!\n");
167                error_return = -ENODEV;
168                goto END;
169        }
170
171        /* Determine the address of the SMBus areas */
172        if (force_addr) {
173                osb4_smba = force_addr & 0xfff0;
174                force = 0;
175        } else {
176
177                pci_read_config_word(OSB4_dev, SMBBA, &osb4_smba);
178                osb4_smba &= 0xfff0;
179        }
180
181        if (check_region(osb4_smba, 8)) {
182                printk
183                    ("i2c-osb4.o: OSB4_smb region 0x%x already in use!\n",
184                     osb4_smba);
185                error_return = -ENODEV;
186                goto END;
187        }
188
189        pci_read_config_byte(OSB4_dev, SMBHSTCFG, &temp);
190        /* If force_addr is set, we program the new address here. Just to make
191           sure, we disable the OSB4 first. */
192        if (force_addr) {
193                pci_write_config_byte(OSB4_dev, SMBHSTCFG, temp & 0xfe);
194                pci_write_config_word(OSB4_dev, SMBBA, osb4_smba);
195                pci_write_config_byte(OSB4_dev, SMBHSTCFG, temp | 0x01);
196                printk
197                    ("i2c-osb4.o: WARNING: OSB4 SMBus interface set to new "
198                     "address %04x!\n", osb4_smba);
199        } else if ((temp & 1) == 0) {
200                if (force) {
201                        /* NOTE: This assumes I/O space and other allocations WERE
202                           done by the Bios!  Don't complain if your hardware does weird
203                           things after enabling this. :') Check for Bios updates before
204                           resorting to this.  */
205                        pci_write_config_byte(OSB4_dev, SMBHSTCFG,
206                                              temp | 1);
207                        printk
208                            ("i2c-osb4.o: WARNING: OSB4 SMBus interface has been FORCEFULLY "
209                             "ENABLED!\n");
210                } else {
211                        printk
212                            ("SMBUS: Error: Host SMBus controller not enabled!\n");
213                        error_return = -ENODEV;
214                        goto END;
215                }
216        }
217
218        /* Everything is happy, let's grab the memory and set things up. */
219        request_region(osb4_smba, 8, "osb4-smbus");
220
221#ifdef DEBUG
222        if ((temp & 0x0E) == 8)
223                printk
224                    ("i2c-osb4.o: OSB4 using Interrupt 9 for SMBus.\n");
225        else if ((temp & 0x0E) == 0)
226                printk
227                    ("i2c-osb4.o: OSB4 using Interrupt SMI# for SMBus.\n");
228        else
229                printk
230                    ("i2c-osb4.o: OSB4: Illegal Interrupt configuration (or code out "
231                     "of date)!\n");
232
233        pci_read_config_byte(OSB4_dev, SMBREV, &temp);
234        printk("i2c-osb4.o: SMBREV = 0x%X\n", temp);
235        printk("i2c-osb4.o: OSB4_smba = 0x%X\n", osb4_smba);
236#endif                          /* DEBUG */
237
238      END:
239        return error_return;
240}
241
242
243/* Internally used pause function */
244void osb4_do_pause(unsigned int amount)
245{
246        current->state = TASK_INTERRUPTIBLE;
247        schedule_timeout(amount);
248}
249
250/* Another internally used function */
251int osb4_transaction(void)
252{
253        int temp;
254        int result = 0;
255        int timeout = 0;
256
257#ifdef DEBUG
258        printk
259            ("i2c-osb4.o: Transaction (pre): CNT=%02x, CMD=%02x, ADD=%02x, DAT0=%02x, "
260             "DAT1=%02x\n", inb_p(SMBHSTCNT), inb_p(SMBHSTCMD),
261             inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), inb_p(SMBHSTDAT1));
262#endif
263
264        /* Make sure the SMBus host is ready to start transmitting */
265        if ((temp = inb_p(SMBHSTSTS)) != 0x00) {
266#ifdef DEBUG
267                printk("i2c-osb4.o: SMBus busy (%02x). Resetting... \n",
268                       temp);
269#endif
270                outb_p(temp, SMBHSTSTS);
271                if ((temp = inb_p(SMBHSTSTS)) != 0x00) {
272#ifdef DEBUG
273                        printk("i2c-osb4.o: Failed! (%02x)\n", temp);
274#endif
275                        return -1;
276                } else {
277#ifdef DEBUG
278                        printk("i2c-osb4.o: Successfull!\n");
279#endif
280                }
281        }
282
283        /* start the transaction by setting bit 6 */
284        outb_p(inb(SMBHSTCNT) | 0x040, SMBHSTCNT);
285
286        /* We will always wait for a fraction of a second! (See OSB4 docs errata) */
287        do {
288                osb4_do_pause(1);
289                temp = inb_p(SMBHSTSTS);
290        } while ((temp & 0x01) && (timeout++ < MAX_TIMEOUT));
291
292        /* If the SMBus is still busy, we give up */
293        if (timeout >= MAX_TIMEOUT) {
294#ifdef DEBUG
295                printk("i2c-osb4.o: SMBus Timeout!\n");
296                result = -1;
297#endif
298        }
299
300        if (temp & 0x10) {
301                result = -1;
302#ifdef DEBUG
303                printk("i2c-osb4.o: Error: Failed bus transaction\n");
304#endif
305        }
306
307        if (temp & 0x08) {
308                result = -1;
309                printk
310                    ("i2c-osb4.o: Bus collision! SMBus may be locked until next hard\n"
311                     "reset. (sorry!)\n");
312                /* Clock stops and slave is stuck in mid-transmission */
313        }
314
315        if (temp & 0x04) {
316                result = -1;
317#ifdef DEBUG
318                printk("i2c-osb4.o: Error: no response!\n");
319#endif
320        }
321
322        if (inb_p(SMBHSTSTS) != 0x00)
323                outb_p(inb(SMBHSTSTS), SMBHSTSTS);
324
325        if ((temp = inb_p(SMBHSTSTS)) != 0x00) {
326#ifdef DEBUG
327                printk
328                    ("i2c-osb4.o: Failed reset at end of transaction (%02x)\n",
329                     temp);
330#endif
331        }
332#ifdef DEBUG
333        printk
334            ("i2c-osb4.o: Transaction (post): CNT=%02x, CMD=%02x, ADD=%02x, "
335             "DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTCNT), inb_p(SMBHSTCMD),
336             inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), inb_p(SMBHSTDAT1));
337#endif
338        return result;
339}
340
341/* Return -1 on error. See smbus.h for more information */
342s32 osb4_access(struct i2c_adapter * adap, u16 addr,
343                 unsigned short flags, char read_write,
344                 u8 command, int size, union i2c_smbus_data * data)
345{
346        int i, len;
347
348        switch (size) {
349        case I2C_SMBUS_PROC_CALL:
350                printk
351                    ("i2c-osb4.o: I2C_SMBUS_PROC_CALL not supported!\n");
352                return -1;
353        case I2C_SMBUS_QUICK:
354                outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
355                       SMBHSTADD);
356                size = OSB4_QUICK;
357                break;
358        case I2C_SMBUS_BYTE:
359                outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
360                       SMBHSTADD);
361                if (read_write == I2C_SMBUS_WRITE)
362                        outb_p(command, SMBHSTCMD);
363                size = OSB4_BYTE;
364                break;
365        case I2C_SMBUS_BYTE_DATA:
366                outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
367                       SMBHSTADD);
368                outb_p(command, SMBHSTCMD);
369                if (read_write == I2C_SMBUS_WRITE)
370                        outb_p(data->byte, SMBHSTDAT0);
371                size = OSB4_BYTE_DATA;
372                break;
373        case I2C_SMBUS_WORD_DATA:
374                outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
375                       SMBHSTADD);
376                outb_p(command, SMBHSTCMD);
377                if (read_write == I2C_SMBUS_WRITE) {
378                        outb_p(data->word & 0xff, SMBHSTDAT0);
379                        outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1);
380                }
381                size = OSB4_WORD_DATA;
382                break;
383        case I2C_SMBUS_BLOCK_DATA:
384                outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
385                       SMBHSTADD);
386                outb_p(command, SMBHSTCMD);
387                if (read_write == I2C_SMBUS_WRITE) {
388                        len = data->block[0];
389                        if (len < 0)
390                                len = 0;
391                        if (len > 32)
392                                len = 32;
393                        outb_p(len, SMBHSTDAT0);
394                        i = inb_p(SMBHSTCNT);   /* Reset SMBBLKDAT */
395                        for (i = 1; i <= len; i++)
396                                outb_p(data->block[i], SMBBLKDAT);
397                }
398                size = OSB4_BLOCK_DATA;
399                break;
400        }
401
402        outb_p((size & 0x1C) + (ENABLE_INT9 & 1), SMBHSTCNT);
403
404        if (osb4_transaction()) /* Error in transaction */
405                return -1;
406
407        if ((read_write == I2C_SMBUS_WRITE) || (size == OSB4_QUICK))
408                return 0;
409
410
411        switch (size) {
412        case OSB4_BYTE: /* Where is the result put? I assume here it is in
413                                   SMBHSTDAT0 but it might just as well be in the
414                                   SMBHSTCMD. No clue in the docs */
415
416                data->byte = inb_p(SMBHSTDAT0);
417                break;
418        case OSB4_BYTE_DATA:
419                data->byte = inb_p(SMBHSTDAT0);
420                break;
421        case OSB4_WORD_DATA:
422                data->word = inb_p(SMBHSTDAT0) + (inb_p(SMBHSTDAT1) << 8);
423                break;
424        case OSB4_BLOCK_DATA:
425                data->block[0] = inb_p(SMBHSTDAT0);
426                i = inb_p(SMBHSTCNT);   /* Reset SMBBLKDAT */
427                for (i = 1; i <= data->block[0]; i++)
428                        data->block[i] = inb_p(SMBBLKDAT);
429                break;
430        }
431        return 0;
432}
433
434void osb4_inc(struct i2c_adapter *adapter)
435{
436        MOD_INC_USE_COUNT;
437}
438
439void osb4_dec(struct i2c_adapter *adapter)
440{
441
442        MOD_DEC_USE_COUNT;
443}
444
445u32 osb4_func(struct i2c_adapter *adapter)
446{
447        return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
448            I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
449            I2C_FUNC_SMBUS_BLOCK_DATA;
450}
451
452int __init i2c_osb4_init(void)
453{
454        int res;
455        printk("i2c-osb4.o version %s (%s)\n", LM_VERSION, LM_DATE);
456        if (osb4_initialized) {
457                printk
458                    ("i2c-osb4.o: Oops, osb4_init called a second time!\n");
459                return -EBUSY;
460        }
461        osb4_initialized = 0;
462        if ((res = osb4_setup())) {
463                printk
464                    ("i2c-osb4.o: OSB4 not detected, module not inserted.\n");
465                osb4_cleanup();
466                return res;
467        }
468        osb4_initialized++;
469        sprintf(osb4_adapter.name, "SMBus OSB4 adapter at %04x",
470                osb4_smba);
471        if ((res = i2c_add_adapter(&osb4_adapter))) {
472                printk
473                    ("i2c-osb4.o: Adapter registration failed, module not inserted.\n");
474                osb4_cleanup();
475                return res;
476        }
477        osb4_initialized++;
478        printk("i2c-osb4.o: OSB4 bus detected and initialized\n");
479        return 0;
480}
481
482int __init osb4_cleanup(void)
483{
484        int res;
485        if (osb4_initialized >= 2) {
486                if ((res = i2c_del_adapter(&osb4_adapter))) {
487                        printk
488                            ("i2c-osb4.o: i2c_del_adapter failed, module not removed\n");
489                        return res;
490                } else
491                        osb4_initialized--;
492        }
493        if (osb4_initialized >= 1) {
494                release_region(osb4_smba, 8);
495                osb4_initialized--;
496        }
497        return 0;
498}
499
500EXPORT_NO_SYMBOLS;
501
502#ifdef MODULE
503
504MODULE_AUTHOR("Steffen Persvold <sp@scali.no>, Philip Edelbrock <phil@netroedge.com>, and Frodo Looijaard <frodol@dds.nl>");
505MODULE_DESCRIPTION("ServerWorks OSB4 SMBus driver");
506
507int init_module(void)
508{
509        return i2c_osb4_init();
510}
511
512int cleanup_module(void)
513{
514        return osb4_cleanup();
515}
516
517#endif                          /* MODULE */
Note: See TracBrowser for help on using the browser.