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

Revision 645, 14.4 KB (checked in by frodo, 13 years ago)

Just to make sure, added `#include <linux/version.h>' at several

other places.

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