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

Revision 651, 14.4 KB (checked in by frodo, 14 years ago)

Moved AMD756 ID to the main i2c-id.h file

  • 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 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/* Note: we assume there can only be one AMD756, with one SMBus interface */
28
29#include <linux/version.h>
30#include <linux/module.h>
31#include <linux/pci.h>
32#include <asm/io.h>
33#include <linux/kernel.h>
34#include <linux/stddef.h>
35#include <linux/sched.h>
36#include <linux/ioport.h>
37#include <linux/i2c.h>
38#include "version.h"
39#include "compat.h"
40
41#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,54))
42#include <linux/bios32.h>
43#endif
44
45#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,53)
46#include <linux/init.h>
47#else
48#define __init
49#define __initdata
50#endif
51
52/* AMD756 SMBus address offsets */
53#define SMB_GLOBAL_STATUS      (0xE0 + amd756_smba)
54#define SMB_GLOBAL_ENABLE      (0xE2 + amd756_smba)
55#define SMB_HOST_ADDRESS       (0xE4 + amd756_smba)
56#define SMB_HOST_DATA          (0xE6 + amd756_smba)
57#define SMB_HOST_COMMAND       (0xE8 + amd756_smba)
58#define SMB_HOST_BLOCK_DATA    (0xE9 + amd756_smba)
59#define SMB_HAS_DATA           (0xEA + amd756_smba)
60#define SMB_HAS_DEVICE_ADDRESS (0xEC + amd756_smba)
61#define SMB_HAS_HOST_ADDRESS   (0xEE + amd756_smba)
62#define SMB_SNOOP_ADDRESS      (0xEF + amd756_smba)
63
64/* PCI Address Constants */
65
66/* address of I/O space */
67#define SMBBA     0x058 /* mh */
68
69/* general configuration */
70#define SMBGCFG   0x041 /* mh */
71
72/* silicon revision code */
73#define SMBREV    0x008
74
75#define SMBSLVC   0x0D3
76#define SMBSHDW1  0x0D4
77#define SMBSHDW2  0x0D5
78
79/* Other settings */
80#define MAX_TIMEOUT 500
81#define  ENABLE_INT9 0
82
83/* AMD756 constants */
84#define AMD756_QUICK        0x00
85#define AMD756_BYTE         0x01
86#define AMD756_BYTE_DATA    0x02
87#define AMD756_WORD_DATA    0x03
88#define AMD756_PROCESS_CALL 0x04
89#define AMD756_BLOCK_DATA   0x05
90
91/* insmod parameters */
92
93#ifdef MODULE
94static
95#else
96extern
97#endif
98       int __init i2c_amd756_init(void);
99static int __init amd756_cleanup(void);
100static int amd756_setup(void);
101static s32 amd756_access(struct i2c_adapter *adap, u16 addr, 
102                        unsigned short flags, char read_write,
103                        u8 command, int size, union i2c_smbus_data * data);
104static void amd756_do_pause( unsigned int amount );
105static int amd756_transaction(void);
106static void amd756_inc(struct i2c_adapter *adapter);
107static void amd756_dec(struct i2c_adapter *adapter);
108static u32 amd756_func(struct i2c_adapter *adapter);
109
110#ifdef MODULE
111extern int init_module(void);
112extern int cleanup_module(void);
113#endif /* MODULE */
114
115static struct i2c_algorithm smbus_algorithm = {
116  /* name */            "Non-I2C SMBus adapter",
117  /* id */              I2C_ALGO_SMBUS,
118  /* master_xfer */     NULL,
119  /* smbus_access */    amd756_access,
120  /* slave_send */      NULL,
121  /* slave_rcv */       NULL,
122  /* algo_control */    NULL,
123  /* functionality */   amd756_func,
124};
125
126static struct i2c_adapter amd756_adapter = {
127  "unset",
128  I2C_ALGO_SMBUS | I2C_HW_SMBUS_AMD756,
129  &smbus_algorithm,
130  NULL,
131  amd756_inc,
132  amd756_dec,
133  NULL,
134  NULL,
135};
136
137static int __initdata amd756_initialized;
138static unsigned short amd756_smba = 0;
139
140/* externalize it */
141#define PCI_DEVICE_ID_AMD_756 0x740B
142
143/* Detect whether a AMD756 can be found, and initialize it, where necessary.
144   Note the differences between kernels with the old PCI BIOS interface and
145   newer kernels with the real PCI interface. In compat.h some things are
146   defined to make the transition easier. */
147int amd756_setup(void)
148{
149  int error_return=0;
150  unsigned char temp;
151
152#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,54))
153  struct pci_dev *AMD756_dev;
154#else
155  unsigned char AMD756_bus, AMD756_devfn;
156  int i,res;
157#endif
158
159  /* First check whether we can access PCI at all */
160  if (pci_present() == 0) {
161    printk("i2c-amd756.o: Error: No PCI-bus found!\n");
162    error_return=-ENODEV;
163    goto END;
164  }
165
166  /* Look for the AMD756, function 3 */
167#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,54))
168  /* Note: we keep on searching until we have found 'function 3' */
169  AMD756_dev = NULL;
170  do
171    AMD756_dev = pci_find_device(PCI_VENDOR_ID_AMD, 
172                                PCI_DEVICE_ID_AMD_756, AMD756_dev);
173  while (AMD756_dev && (PCI_FUNC(AMD756_dev->devfn) != 3));
174  if(AMD756_dev == NULL) {
175#else /* LINUX_VERSION_CODE < KERNEL_VERSION(2,1,54) */
176  for (i = 0; 
177       ! (res = pcibios_find_device(PCI_VENDOR_ID_INTEL,
178                                    PCI_DEVICE_ID_INTEL_82371AB_3,
179                                    i,&AMD756_bus, &AMD756_devfn)) && 
180         PCI_FUNC(AMD756_devfn) != 3; 
181       i++);
182     
183  if (res) {
184#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,54) */
185    printk("i2c-amd756.o: Error: Can't detect AMD756, function 3!\n");
186    error_return=-ENODEV;
187    goto END;
188  } 
189
190/* Determine the address of the SMBus areas */
191
192  /* Technically it is a dword but... */
193  pci_read_config_word_united(AMD756_dev, AMD756_bus, AMD756_devfn,
194                              SMBBA, &amd756_smba);
195  amd756_smba &= 0xfff0;
196
197  if (check_region(amd756_smba, 8)) {
198    printk("i2c-amd756.o: AMD756_smb region 0x%x already in use!\n", amd756_smba);
199    error_return=-ENODEV;
200    goto END;
201  }
202
203  pci_read_config_byte_united(AMD756_dev, AMD756_bus, AMD756_devfn,
204                              SMBGCFG, &temp);
205
206  if ((temp & 128) == 0) {
207    printk("SMBUS: Error: Host SMBus controller I/O not enabled!\n");     
208    error_return=-ENODEV;
209    goto END;
210  }
211
212  /* Everything is happy, let's grab the memory and set things up. */
213  request_region(amd756_smba, 8, "amd756-smbus");       
214
215#ifdef DEBUG
216  /*
217  if ((temp & 0x0E) == 8)
218     printk("i2c-amd756.o: AMD756 using Interrupt 9 for SMBus.\n");
219  else if ((temp & 0x0E) == 0)
220     printk("i2c-amd756.o: AMD756 using Interrupt SMI# for SMBus.\n");
221  else
222     printk("i2c-amd756.o: AMD756: Illegal Interrupt configuration (or code out "
223            "of date)!\n");
224  */
225
226  pci_read_config_byte_united(AMD756_dev, AMD756_bus, AMD756_devfn, SMBREV, 
227                              &temp);
228  printk("i2c-amd756.o: SMBREV = 0x%X\n",temp);
229  printk("i2c-amd756.o: AMD756_smba = 0x%X\n",amd756_smba);
230#endif /* DEBUG */
231
232END:
233  return error_return;
234}
235
236/*
237  SMBUS event = I/O 28-29 bit 11
238     see E0 for the status bits and enabled in E2
239     
240*/
241
242/* Internally used pause function */
243void amd756_do_pause( unsigned int amount )
244{
245      current->state = TASK_INTERRUPTIBLE;
246      schedule_timeout(amount);
247}
248
249#define GS_ABRT_STS (1 << 0)
250#define GS_COL_STS (1 << 1)
251#define GS_PRERR_STS (1 << 2)
252#define GS_HST_STS (1 << 3)
253#define GS_HCYC_STS (1 << 4)
254#define GS_TO_STS (1 << 5)
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 
262/* Another internally used function */
263int amd756_transaction(void) 
264{
265  int temp;
266  int result=0;
267  int timeout=0;
268
269#ifdef DEBUG
270  printk("i2c-amd756.o: Transaction (pre): GS=%04x, GE=%04x, ADD=%04x, DAT=%04x\n",
271         inw_p(SMB_GLOBAL_STATUS),inw_p(SMB_GLOBAL_ENABLE),inw_p(SMB_HOST_ADDRESS),inb_p(SMB_HOST_DATA));
272#endif
273
274  /* Make sure the SMBus host is ready to start transmitting */
275  /* TODO: What about SM_BSY? */
276  if (((temp = inw_p(SMB_GLOBAL_STATUS)) & GS_HST_STS) != 0x00) {
277#ifdef DEBUG
278    printk("i2c-amd756.o: SMBus busy (%04x). Resetting (NOT)... \n",temp);
279#endif
280    /* TODO: How to reset the AMD?
281    outb_p(temp, SMBHSTSTS);
282    */
283    if (((temp = inw_p(SMB_GLOBAL_STATUS)) & GS_HST_STS) != 0x00) {
284#ifdef DEBUG
285      printk("i2c-amd756.o: Failed! (%04x)\n",temp);
286#endif
287      return -1;
288    } else {
289#ifdef DEBUG
290      printk("i2c-amd756.o: Successfull!\n");
291#endif
292    }
293  }
294
295  /* start the transaction by setting bit 6 */
296  outw_p(inw(SMB_GLOBAL_ENABLE) | GE_HOST_STC, SMB_GLOBAL_ENABLE); 
297
298  /* We will always wait for a fraction of a second! (See AMD756 docs errata) */
299  do {
300    amd756_do_pause(1);
301    temp=inw_p(SMB_GLOBAL_STATUS);
302  } while ((temp & GS_HST_STS) && (timeout++ < MAX_TIMEOUT));
303
304  /* If the SMBus is still busy, we give up */
305  if (timeout >= MAX_TIMEOUT) {
306#ifdef DEBUG
307    printk("i2c-amd756.o: SMBus explicit timeout!\n"); 
308    result = -1;
309#endif
310  }
311
312  if (temp & GS_PRERR_STS) {
313    result = -1;
314#ifdef DEBUG
315    printk("i2c-amd756.o: SMBus Protocol error!\n");
316#endif
317  }
318
319  if (temp & GS_COL_STS) {
320    result = -1;
321    printk("i2c-amd756.o: SMBus collision!\n");
322    /* TODO: Clear Collision Status with a 1 */
323  }
324
325  if (temp & GS_TO_STS) {
326    result = -1;
327#ifdef DEBUG
328    printk("i2c-amd756.o: SMBus protocol timeout!\n");
329#endif
330  }
331
332#ifdef DEBUG
333  if (temp & GS_HCYC_STS) {
334    printk("i2c-amd756.o: SMBus protocol success!\n");
335  }
336#endif
337
338  outw_p (GS_CLEAR_STS, SMB_GLOBAL_STATUS);
339
340  if (((temp = inw_p (SMB_GLOBAL_STATUS)) & GS_CLEAR_STS) != 0x00) {
341#ifdef DEBUG
342    printk("i2c-amd756.o: Failed reset at end of transaction (%04x)\n",temp);
343#endif
344  }
345 
346#ifdef DEBUG
347  printk("i2c-amd756.o: Transaction (post): GS=%04x, GE=%04x, ADD=%04x, DAT=%04x\n",
348         inw_p(SMB_GLOBAL_STATUS),inw_p(SMB_GLOBAL_ENABLE),inw_p(SMB_HOST_ADDRESS),inb_p(SMB_HOST_DATA));
349#endif
350 
351  return result;
352}
353
354/* Return -1 on error. See smbus.h for more information */
355s32 amd756_access(struct i2c_adapter *adap, u16 addr, 
356                 unsigned short flags, char read_write,
357                 u8 command, int size, union i2c_smbus_data * data)
358{
359  int i,len;
360   
361  /** TODO: Should I supporte the 10-bit transfers? */
362  switch(size) {
363    case I2C_SMBUS_PROC_CALL:
364      printk("i2c-amd756.o: I2C_SMBUS_PROC_CALL not supported!\n");
365      /* TODO: Well... It is supported, I'm just not sure what to do here... */
366      return -1;
367    case I2C_SMBUS_QUICK:
368      outw_p (((addr & 0x7f) << 1) | (read_write & 0x01), SMB_HOST_ADDRESS);
369      size = AMD756_QUICK;
370      break;
371    case I2C_SMBUS_BYTE:
372      outw_p (((addr & 0x7f) << 1) | (read_write & 0x01), SMB_HOST_ADDRESS);
373      /* TODO: Why only during write? */
374      if (read_write == I2C_SMBUS_WRITE)
375        outb_p (command, SMB_HOST_COMMAND);
376      size = AMD756_BYTE;
377      break;
378    case I2C_SMBUS_BYTE_DATA:
379      outw_p (((addr & 0x7f) << 1) | (read_write & 0x01), SMB_HOST_ADDRESS);
380      outb_p (command, SMB_HOST_COMMAND);
381      if (read_write == I2C_SMBUS_WRITE)
382        outw_p(data->byte, SMB_HOST_DATA);
383      size = AMD756_BYTE_DATA;
384      break;
385    case I2C_SMBUS_WORD_DATA:
386      outw_p (((addr & 0x7f) << 1) | (read_write & 0x01), SMB_HOST_ADDRESS);
387      outb_p (command, SMB_HOST_COMMAND);
388      if (read_write == I2C_SMBUS_WRITE)
389        outw_p (data->word, SMB_HOST_DATA); /* TODO: endian???? */
390      size = AMD756_WORD_DATA;
391      break;
392    case I2C_SMBUS_BLOCK_DATA:
393      outw_p (((addr & 0x7f) << 1) | (read_write & 0x01), SMB_HOST_ADDRESS);
394      outb_p (command, SMB_HOST_COMMAND);
395      if (read_write == I2C_SMBUS_WRITE) {
396        len = data->block[0];
397        if (len < 0) 
398          len = 0;
399        if (len > 32)
400          len = 32;
401        outw_p (len, SMB_HOST_DATA);
402        /* i = inw_p(SMBHSTCNT); Reset SMBBLKDAT */
403        for (i = 1; i <= len; i ++)
404          outb_p (data->block[i], SMB_HOST_BLOCK_DATA);
405      }
406      size = AMD756_BLOCK_DATA;
407      break;
408  }
409
410  /* How about enabling interrupts... */
411  outw_p (size & GE_CYC_TYPE_MASK, SMB_GLOBAL_ENABLE);
412
413  if (amd756_transaction()) /* Error in transaction */ 
414    return -1; 
415 
416  if ((read_write == I2C_SMBUS_WRITE) || (size == AMD756_QUICK))
417    return 0;
418 
419
420  switch(size) {
421    case AMD756_BYTE:
422      data->byte = inw_p (SMB_HOST_DATA);
423      break;
424    case AMD756_BYTE_DATA:
425      data->byte = inw_p (SMB_HOST_DATA);
426      break;
427    case AMD756_WORD_DATA:
428      data->word = inw_p (SMB_HOST_DATA); /* TODO: endian???? */
429      break;
430    case AMD756_BLOCK_DATA:
431      data->block[0] = inw_p (SMB_HOST_DATA & 63);
432      /* i = inw_p(SMBHSTCNT); Reset SMBBLKDAT */
433      for (i = 1; i <= data->block[0]; i++)
434        data->block[i] = inb_p (SMB_HOST_BLOCK_DATA);
435      break;
436  }
437 
438  return 0;
439}
440
441void amd756_inc(struct i2c_adapter *adapter)
442{
443        MOD_INC_USE_COUNT;
444}
445
446void amd756_dec(struct i2c_adapter *adapter)
447{
448
449        MOD_DEC_USE_COUNT;
450}
451
452u32 amd756_func(struct i2c_adapter *adapter)
453{
454  return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | 
455         I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | 
456         I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_PROC_CALL;
457}
458
459int __init i2c_amd756_init(void)
460{
461  int res;
462  printk("amd756.o version %s (%s)\n",LM_VERSION,LM_DATE);
463#ifdef DEBUG
464/* PE- It might be good to make this a permanent part of the code! */
465  if (amd756_initialized) {
466    printk("i2c-amd756.o: Oops, amd756_init called a second time!\n");
467    return -EBUSY;
468  }
469#endif
470  amd756_initialized = 0;
471  if ((res = amd756_setup())) {
472    printk("i2c-amd756.o: AMD756 not detected, module not inserted.\n");
473    amd756_cleanup();
474    return res;
475  }
476  amd756_initialized ++;
477  sprintf(amd756_adapter.name,"SMBus AMD756 adapter at %04x",amd756_smba);
478  if ((res = i2c_add_adapter(&amd756_adapter))) {
479    printk("i2c-amd756.o: Adapter registration failed, module not inserted.\n");
480    amd756_cleanup();
481    return res;
482  }
483  amd756_initialized++;
484  printk("i2c-amd756.o: AMD756 bus detected and initialized\n");
485  return 0;
486}
487
488int __init amd756_cleanup(void)
489{
490  int res;
491  if (amd756_initialized >= 2)
492  {
493    if ((res = i2c_del_adapter(&amd756_adapter))) {
494      printk("i2c-amd756.o: i2c_del_adapter failed, module not removed\n");
495      return res;
496    } else
497      amd756_initialized--;
498  }
499  if (amd756_initialized >= 1) {
500    release_region(amd756_smba, 8);
501    amd756_initialized--;
502  }
503  return 0;
504}
505
506EXPORT_NO_SYMBOLS;
507
508#ifdef MODULE
509
510MODULE_AUTHOR("Merlni Hughes <merlin@merlin.org>");
511MODULE_DESCRIPTION("AMD756 SMBus driver");
512
513int init_module(void)
514{
515  return i2c_amd756_init();
516}
517
518int cleanup_module(void)
519{
520  return amd756_cleanup();
521}
522
523#endif /* MODULE */
524
Note: See TracBrowser for help on using the browser.