root/lm-sensors/trunk/kernel/busses/i2c-amd756.c @ 1562

Revision 1562, 13.8 KB (checked in by mds, 12 years ago)

for nForce, change from static base address to reading base

from 0x14 (contributed by

Michael Steil <mist@…> from the xbox team and
yrsh2scp@… (Yoshifumi R. Shimizu);

also check for uninitialized base address.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1/*
2    amd756.c - Part of lm_sensors, Linux kernel modules for hardware
3              monitoring
4
5    Copyright (c) 1999-2002 Merlin Hughes <merlin@merlin.org>
6
7    Shamelessly ripped from i2c-piix4.c:
8
9    Copyright (c) 1998, 1999  Frodo Looijaard <frodol@dds.nl> and
10    Philip Edelbrock <phil@netroedge.com>
11
12    This program is free software; you can redistribute it and/or modify
13    it under the terms of the GNU General Public License as published by
14    the Free Software Foundation; either version 2 of the License, or
15    (at your option) any later version.
16
17    This program is distributed in the hope that it will be useful,
18    but WITHOUT ANY WARRANTY; without even the implied warranty of
19    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20    GNU General Public License for more details.
21
22    You should have received a copy of the GNU General Public License
23    along with this program; if not, write to the Free Software
24    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25*/
26
27/*
28    2002-04-08: Added nForce support. (Csaba Halasz)
29    2002-10-03: Fixed nForce PnP I/O port. (Michael Steil)
30*/
31
32/*
33   Supports AMD756, AMD766, AMD768 and nVidia nForce
34   Note: we assume there can only be one device, 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#ifndef PCI_DEVICE_ID_AMD_756
50#define PCI_DEVICE_ID_AMD_756 0x740B
51#endif
52#ifndef PCI_DEVICE_ID_AMD_766
53#define PCI_DEVICE_ID_AMD_766 0x7413
54#endif
55#ifndef PCI_DEVICE_ID_NVIDIA_NFORCE_SMBUS
56#define PCI_DEVICE_ID_NVIDIA_NFORCE_SMBUS 0x01B4
57#endif
58
59struct sd {
60    const unsigned short vendor;
61    const unsigned short device;
62    const unsigned short function;
63    const char* name;
64    int amdsetup:1;
65};
66
67static struct sd supported[] = {
68    {PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_756, 3, "AMD756", 1},
69    {PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_766, 3, "AMD766", 1},
70    {PCI_VENDOR_ID_AMD, 0x7443, 3, "AMD768", 1},
71    {PCI_VENDOR_ID_NVIDIA, 0x01B4, 1, "nVidia nForce", 0},
72    {0, 0, 0}
73};
74
75/* AMD756 SMBus address offsets */
76#define SMB_ADDR_OFFSET        0xE0
77#define SMB_IOSIZE             16
78#define SMB_GLOBAL_STATUS      (0x0 + amd756_smba)
79#define SMB_GLOBAL_ENABLE      (0x2 + amd756_smba)
80#define SMB_HOST_ADDRESS       (0x4 + amd756_smba)
81#define SMB_HOST_DATA          (0x6 + amd756_smba)
82#define SMB_HOST_COMMAND       (0x8 + amd756_smba)
83#define SMB_HOST_BLOCK_DATA    (0x9 + amd756_smba)
84#define SMB_HAS_DATA           (0xA + amd756_smba)
85#define SMB_HAS_DEVICE_ADDRESS (0xC + amd756_smba)
86#define SMB_HAS_HOST_ADDRESS   (0xE + amd756_smba)
87#define SMB_SNOOP_ADDRESS      (0xF + amd756_smba)
88
89/* PCI Address Constants */
90
91/* address of I/O space */
92#define SMBBA     0x058         /* mh */
93#define SMBBANFORCE     0x014
94
95/* general configuration */
96#define SMBGCFG   0x041         /* mh */
97
98/* silicon revision code */
99#define SMBREV    0x008
100
101/* Other settings */
102#define MAX_TIMEOUT 500
103
104/* AMD756 constants */
105#define AMD756_QUICK        0x00
106#define AMD756_BYTE         0x01
107#define AMD756_BYTE_DATA    0x02
108#define AMD756_WORD_DATA    0x03
109#define AMD756_PROCESS_CALL 0x04
110#define AMD756_BLOCK_DATA   0x05
111
112/* insmod parameters */
113
114#ifdef MODULE
115static
116#else
117extern
118#endif
119int __init i2c_amd756_init(void);
120static int __init amd756_cleanup(void);
121static int amd756_setup(void);
122static s32 amd756_access(struct i2c_adapter *adap, u16 addr,
123                         unsigned short flags, char read_write,
124                         u8 command, int size, union i2c_smbus_data *data);
125static void amd756_do_pause(unsigned int amount);
126static void amd756_abort(void);
127static int amd756_transaction(void);
128static void amd756_inc(struct i2c_adapter *adapter);
129static void amd756_dec(struct i2c_adapter *adapter);
130static u32 amd756_func(struct i2c_adapter *adapter);
131
132#ifdef MODULE
133extern int init_module(void);
134extern int cleanup_module(void);
135#endif                          /* MODULE */
136
137static struct i2c_algorithm smbus_algorithm = {
138        /* name */ "Non-I2C SMBus adapter",
139        /* id */ I2C_ALGO_SMBUS,
140        /* master_xfer */ NULL,
141        /* smbus_access */ amd756_access,
142        /* slave;_send */ NULL,
143        /* slave_rcv */ NULL,
144        /* algo_control */ NULL,
145        /* functionality */ amd756_func,
146};
147
148static struct i2c_adapter amd756_adapter = {
149        "unset",
150        I2C_ALGO_SMBUS | I2C_HW_SMBUS_AMD756,
151        &smbus_algorithm,
152        NULL,
153        amd756_inc,
154        amd756_dec,
155        NULL,
156        NULL,
157};
158
159static int __initdata amd756_initialized;
160static struct sd *amd756_sd = NULL;
161static unsigned short amd756_smba = 0;
162
163int amd756_setup(void)
164{
165        unsigned char temp;
166        struct sd *currdev;
167        struct pci_dev *AMD756_dev = NULL;
168
169        if (pci_present() == 0) {
170                return -ENODEV;
171        }
172
173        /* Look for a supported chip */
174        for(currdev = supported; currdev->vendor; ) {
175                AMD756_dev = pci_find_device(currdev->vendor,
176                                                currdev->device, AMD756_dev);
177                if (AMD756_dev != NULL) {
178                        if (PCI_FUNC(AMD756_dev->devfn) == currdev->function)
179                                break;
180                } else {
181                    currdev++;
182                }
183        }
184
185        if (AMD756_dev == NULL) {
186                printk
187                    ("i2c-amd756.o: Error: No AMD756 or compatible device detected!\n");
188                return -ENODEV;
189        }
190        printk(KERN_INFO "i2c-amd756.o: Found %s SMBus controller.\n", currdev->name);
191
192        if (currdev->amdsetup)
193        {
194                pci_read_config_byte(AMD756_dev, SMBGCFG, &temp);
195                if ((temp & 128) == 0) {
196                        printk("i2c-amd756.o: Error: SMBus controller I/O not enabled!\n");
197                        return -ENODEV;
198                }
199
200                /* Determine the address of the SMBus areas */
201                /* Technically it is a dword but... */
202                pci_read_config_word(AMD756_dev, SMBBA, &amd756_smba);
203                amd756_smba &= 0xff00;
204                amd756_smba += SMB_ADDR_OFFSET;
205        } else {
206                pci_read_config_word(AMD756_dev, SMBBANFORCE, &amd756_smba);
207                amd756_smba &= 0xfffc;
208        }
209        if(amd756_smba == 0) {
210                printk(KERN_ERR "i2c-amd756.o: Error: SMB base address uninitialized\n");
211                return -ENODEV;
212        }
213        if (check_region(amd756_smba, SMB_IOSIZE)) {
214                printk
215                    ("i2c-amd756.o: SMB region 0x%x already in use!\n",
216                     amd756_smba);
217                return -ENODEV;
218        }
219
220        /* Everything is happy, let's grab the memory and set things up. */
221        request_region(amd756_smba, SMB_IOSIZE, "amd756-smbus");
222
223#ifdef DEBUG
224        pci_read_config_byte(AMD756_dev, SMBREV, &temp);
225        printk("i2c-amd756.o: SMBREV = 0x%X\n", temp);
226        printk("i2c-amd756.o: AMD756_smba = 0x%X\n", amd756_smba);
227#endif                          /* DEBUG */
228
229        /* store struct sd * for future reference */
230        amd756_sd = currdev;
231
232        return 0;
233}
234
235/*
236  SMBUS event = I/O 28-29 bit 11
237     see E0 for the status bits and enabled in E2
238     
239*/
240
241/* Internally used pause function */
242void amd756_do_pause(unsigned int amount)
243{
244        current->state = TASK_INTERRUPTIBLE;
245        schedule_timeout(amount);
246}
247
248#define GS_ABRT_STS (1 << 0)
249#define GS_COL_STS (1 << 1)
250#define GS_PRERR_STS (1 << 2)
251#define GS_HST_STS (1 << 3)
252#define GS_HCYC_STS (1 << 4)
253#define GS_TO_STS (1 << 5)
254#define GS_SMB_STS (1 << 11)
255
256#define GS_CLEAR_STS (GS_ABRT_STS | GS_COL_STS | GS_PRERR_STS | \
257  GS_HCYC_STS | GS_TO_STS )
258
259#define GE_CYC_TYPE_MASK (7)
260#define GE_HOST_STC (1 << 3)
261#define GE_ABORT (1 << 5)
262
263void amd756_abort(void)
264{
265        printk("i2c-amd756.o: Sending abort.\n");
266        outw_p(inw(SMB_GLOBAL_ENABLE) | GE_ABORT, SMB_GLOBAL_ENABLE);
267        amd756_do_pause(100);
268        outw_p(GS_CLEAR_STS, SMB_GLOBAL_STATUS);
269}
270
271int amd756_transaction(void)
272{
273        int temp;
274        int result = 0;
275        int timeout = 0;
276
277#ifdef DEBUG
278        printk
279            ("i2c-amd756.o: Transaction (pre): GS=%04x, GE=%04x, ADD=%04x, DAT=%04x\n",
280             inw_p(SMB_GLOBAL_STATUS), inw_p(SMB_GLOBAL_ENABLE),
281             inw_p(SMB_HOST_ADDRESS), inb_p(SMB_HOST_DATA));
282#endif
283
284        /* Make sure the SMBus host is ready to start transmitting */
285        if ((temp = inw_p(SMB_GLOBAL_STATUS)) & (GS_HST_STS | GS_SMB_STS)) {
286#ifdef DEBUG
287                printk
288                    ("i2c-amd756.o: SMBus busy (%04x). Waiting... \n", temp);
289#endif
290                do {
291                        amd756_do_pause(1);
292                        temp = inw_p(SMB_GLOBAL_STATUS);
293                } while ((temp & (GS_HST_STS | GS_SMB_STS)) &&
294                         (timeout++ < MAX_TIMEOUT));
295                /* If the SMBus is still busy, we give up */
296                if (timeout >= MAX_TIMEOUT) {
297                        printk("i2c-amd756.o: Busy wait timeout! (%04x)\n", temp);
298                        amd756_abort();
299                        return -1;
300                }
301                timeout = 0;
302        }
303
304        /* start the transaction by setting the start bit */
305        outw_p(inw(SMB_GLOBAL_ENABLE) | GE_HOST_STC, SMB_GLOBAL_ENABLE);
306
307        /* We will always wait for a fraction of a second! */
308        do {
309                amd756_do_pause(1);
310                temp = inw_p(SMB_GLOBAL_STATUS);
311        } while ((temp & GS_HST_STS) && (timeout++ < MAX_TIMEOUT));
312
313        /* If the SMBus is still busy, we give up */
314        if (timeout >= MAX_TIMEOUT) {
315                printk("i2c-amd756.o: Completion timeout!\n");
316                amd756_abort ();
317                return -1;
318        }
319
320        if (temp & GS_PRERR_STS) {
321                result = -1;
322#ifdef DEBUG
323                printk("i2c-amd756.o: SMBus Protocol error (no response)!\n");
324#endif
325        }
326
327        if (temp & GS_COL_STS) {
328                result = -1;
329                printk("i2c-amd756.o: SMBus collision!\n");
330        }
331
332        if (temp & GS_TO_STS) {
333                result = -1;
334#ifdef DEBUG
335                printk("i2c-amd756.o: SMBus protocol timeout!\n");
336#endif
337        }
338#ifdef DEBUG
339        if (temp & GS_HCYC_STS) {
340                printk("i2c-amd756.o: SMBus protocol success!\n");
341        }
342#endif
343
344        outw_p(GS_CLEAR_STS, SMB_GLOBAL_STATUS);
345
346#ifdef DEBUG
347        if (((temp = inw_p(SMB_GLOBAL_STATUS)) & GS_CLEAR_STS) != 0x00) {
348                printk
349                    ("i2c-amd756.o: Failed reset at end of transaction (%04x)\n",
350                     temp);
351        }
352        printk
353            ("i2c-amd756.o: Transaction (post): GS=%04x, GE=%04x, ADD=%04x, DAT=%04x\n",
354             inw_p(SMB_GLOBAL_STATUS), inw_p(SMB_GLOBAL_ENABLE),
355             inw_p(SMB_HOST_ADDRESS), inb_p(SMB_HOST_DATA));
356#endif
357
358        return result;
359}
360
361/* Return -1 on error. */
362s32 amd756_access(struct i2c_adapter * adap, u16 addr,
363                  unsigned short flags, char read_write,
364                  u8 command, int size, union i2c_smbus_data * data)
365{
366        int i, len;
367
368  /** TODO: Should I supporte the 10-bit transfers? */
369        switch (size) {
370        case I2C_SMBUS_PROC_CALL:
371                printk
372                    ("i2c-amd756.o: I2C_SMBUS_PROC_CALL not supported!\n");
373                /* TODO: Well... It is supported, I'm just not sure what to do here... */
374                return -1;
375        case I2C_SMBUS_QUICK:
376                outw_p(((addr & 0x7f) << 1) | (read_write & 0x01),
377                       SMB_HOST_ADDRESS);
378                size = AMD756_QUICK;
379                break;
380        case I2C_SMBUS_BYTE:
381                outw_p(((addr & 0x7f) << 1) | (read_write & 0x01),
382                       SMB_HOST_ADDRESS);
383                /* TODO: Why only during write? */
384                if (read_write == I2C_SMBUS_WRITE)
385                        outb_p(command, SMB_HOST_COMMAND);
386                size = AMD756_BYTE;
387                break;
388        case I2C_SMBUS_BYTE_DATA:
389                outw_p(((addr & 0x7f) << 1) | (read_write & 0x01),
390                       SMB_HOST_ADDRESS);
391                outb_p(command, SMB_HOST_COMMAND);
392                if (read_write == I2C_SMBUS_WRITE)
393                        outw_p(data->byte, SMB_HOST_DATA);
394                size = AMD756_BYTE_DATA;
395                break;
396        case I2C_SMBUS_WORD_DATA:
397                outw_p(((addr & 0x7f) << 1) | (read_write & 0x01),
398                       SMB_HOST_ADDRESS);
399                outb_p(command, SMB_HOST_COMMAND);
400                if (read_write == I2C_SMBUS_WRITE)
401                        outw_p(data->word, SMB_HOST_DATA);      /* TODO: endian???? */
402                size = AMD756_WORD_DATA;
403                break;
404        case I2C_SMBUS_BLOCK_DATA:
405                outw_p(((addr & 0x7f) << 1) | (read_write & 0x01),
406                       SMB_HOST_ADDRESS);
407                outb_p(command, SMB_HOST_COMMAND);
408                if (read_write == I2C_SMBUS_WRITE) {
409                        len = data->block[0];
410                        if (len < 0)
411                                len = 0;
412                        if (len > 32)
413                                len = 32;
414                        outw_p(len, SMB_HOST_DATA);
415                        /* i = inw_p(SMBHSTCNT); Reset SMBBLKDAT */
416                        for (i = 1; i <= len; i++)
417                                outb_p(data->block[i],
418                                       SMB_HOST_BLOCK_DATA);
419                }
420                size = AMD756_BLOCK_DATA;
421                break;
422        }
423
424        /* How about enabling interrupts... */
425        outw_p(size & GE_CYC_TYPE_MASK, SMB_GLOBAL_ENABLE);
426
427        if (amd756_transaction())       /* Error in transaction */
428                return -1;
429
430        if ((read_write == I2C_SMBUS_WRITE) || (size == AMD756_QUICK))
431                return 0;
432
433
434        switch (size) {
435        case AMD756_BYTE:
436                data->byte = inw_p(SMB_HOST_DATA);
437                break;
438        case AMD756_BYTE_DATA:
439                data->byte = inw_p(SMB_HOST_DATA);
440                break;
441        case AMD756_WORD_DATA:
442                data->word = inw_p(SMB_HOST_DATA);      /* TODO: endian???? */
443                break;
444        case AMD756_BLOCK_DATA:
445                data->block[0] = inw_p(SMB_HOST_DATA) & 0x3f;
446                if(data->block[0] > 32)
447                        data->block[0] = 32;
448                /* i = inw_p(SMBHSTCNT); Reset SMBBLKDAT */
449                for (i = 1; i <= data->block[0]; i++)
450                        data->block[i] = inb_p(SMB_HOST_BLOCK_DATA);
451                break;
452        }
453
454        return 0;
455}
456
457void amd756_inc(struct i2c_adapter *adapter)
458{
459        MOD_INC_USE_COUNT;
460}
461
462void amd756_dec(struct i2c_adapter *adapter)
463{
464
465        MOD_DEC_USE_COUNT;
466}
467
468u32 amd756_func(struct i2c_adapter *adapter)
469{
470        return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
471            I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
472            I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_PROC_CALL;
473}
474
475int __init i2c_amd756_init(void)
476{
477        int res;
478        printk("i2c-amd756.o version %s (%s)\n", LM_VERSION, LM_DATE);
479#ifdef DEBUG
480/* PE- It might be good to make this a permanent part of the code! */
481        if (amd756_initialized) {
482                printk
483                    ("i2c-amd756.o: Oops, amd756_init called a second time!\n");
484                return -EBUSY;
485        }
486#endif
487        amd756_initialized = 0;
488        if ((res = amd756_setup())) {
489                printk
490                    ("i2c-amd756.o: AMD756 or compatible device not detected, module not inserted.\n");
491                amd756_cleanup();
492                return res;
493        }
494        amd756_initialized++;
495        sprintf(amd756_adapter.name, "SMBus %s adapter at %04x",
496                amd756_sd->name, amd756_smba);
497        if ((res = i2c_add_adapter(&amd756_adapter))) {
498                printk
499                    ("i2c-amd756.o: Adapter registration failed, module not inserted.\n");
500                amd756_cleanup();
501                return res;
502        }
503        amd756_initialized++;
504        printk("i2c-amd756.o: %s bus detected and initialized\n",
505               amd756_sd->name);
506        return 0;
507}
508
509int __init amd756_cleanup(void)
510{
511        int res;
512        if (amd756_initialized >= 2) {
513                if ((res = i2c_del_adapter(&amd756_adapter))) {
514                        printk
515                            ("i2c-amd756.o: i2c_del_adapter failed, module not removed\n");
516                        return res;
517                } else
518                        amd756_initialized--;
519        }
520        if (amd756_initialized >= 1) {
521                release_region(amd756_smba, SMB_IOSIZE);
522                amd756_initialized--;
523        }
524        return 0;
525}
526
527EXPORT_NO_SYMBOLS;
528
529#ifdef MODULE
530
531MODULE_AUTHOR("Merlin Hughes <merlin@merlin.org>");
532MODULE_DESCRIPTION("AMD756/766/768/nVidia nForce SMBus driver");
533
534#ifdef MODULE_LICENSE
535MODULE_LICENSE("GPL");
536#endif
537
538int init_module(void)
539{
540        return i2c_amd756_init();
541}
542
543int cleanup_module(void)
544{
545        return amd756_cleanup();
546}
547
548#endif                          /* MODULE */
Note: See TracBrowser for help on using the browser.