root/lm-sensors/trunk/kernel/busses/i2c-voodoo3.c @ 578

Revision 578, 14.3 KB (checked in by phil, 14 years ago)

(Phil) More tweaking and testing.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1/*
2    voodoo3.c - Part of lm_sensors, Linux kernel modules for hardware
3              monitoring
4    Copyright (c) 1998, 1999  Frodo Looijaard <frodol@dds.nl>,
5    Philip Edelbrock <phil@netroedge.com> and
6    Ralph Metzler <rjkm@thp.uni-koeln.de>
7   
8    Based on code written by Ralph Metzler <rjkm@thp.uni-koeln.de> and
9    Simon Vogl
10
11    This program is free software; you can redistribute it and/or modify
12    it under the terms of the GNU General Public License as published by
13    the Free Software Foundation; either version 2 of the License, or
14    (at your option) any later version.
15
16    This program is distributed in the hope that it will be useful,
17    but WITHOUT ANY WARRANTY; without even the implied warranty of
18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19    GNU General Public License for more details.
20
21    You should have received a copy of the GNU General Public License
22    along with this program; if not, write to the Free Software
23    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24*/
25
26#define DEBUG 1
27
28/* This interfaces to the I2C bus of the Voodoo3 to gain access to
29    the BT869 and possibly other I2C devices. */
30
31#include <linux/module.h>
32#include <linux/pci.h>
33#include <asm/io.h>
34#include <linux/kernel.h>
35#include <linux/stddef.h>
36#include <linux/sched.h>
37#include <linux/ioport.h>
38#include <linux/delay.h>
39#include <linux/i2c.h>
40#include "version.h"
41#include "compat.h"
42
43#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,54))
44#include <linux/bios32.h>
45#endif
46
47#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,53)
48#include <linux/init.h>
49#else
50#define __init
51#define __initdata
52#endif
53
54/* 3DFX defines */
55#ifndef PCI_VENDOR_ID_3DFX
56#define PCI_VENDOR_ID_3DFX 0x121a
57#endif
58#ifndef PCI_DEVICE_ID_3DFX_VOODOO3
59#define PCI_DEVICE_ID_3DFX_VOODOO3 0x05
60#endif
61
62#ifdef MODULE
63static
64#else
65extern
66#endif
67       int __init i2c_voodoo3_init(void);
68static int __init voodoo3_cleanup(void);
69static int voodoo3_setup(void);
70static s32 voodoo3_access(struct i2c_adapter *adap, u8 addr, char read_write,
71                        u8 command, int size, union i2c_smbus_data * data);
72static void Voodoo3_I2CStart(void);
73static void Voodoo3_I2CStop(void);
74static int Voodoo3_I2CAck(int ackit);
75static int Voodoo3_I2CReadByte(int ackit);
76static int Voodoo3_I2CSendByte(unsigned char data);
77static int Voodoo3_BusCheck(void);
78static int Voodoo3_I2CRead_byte(int addr);
79static int Voodoo3_I2CRead_byte_data(int addr,int command);
80static int Voodoo3_I2CRead_word(int addr,int command);
81static int Voodoo3_I2CWrite_byte(int addr,int command);
82static int Voodoo3_I2CWrite_byte_data(int addr,int command,int data);
83static int Voodoo3_I2CWrite_word(int addr,int command,long data);
84static void config_v3(struct pci_dev *dev, int num);
85static void voodoo3_inc(struct i2c_adapter *adapter);
86static void voodoo3_dec(struct i2c_adapter *adapter);
87
88#ifdef MODULE
89extern int init_module(void);
90extern int cleanup_module(void);
91#endif /* MODULE */
92
93static struct i2c_algorithm smbus_algorithm = {
94  /* name */            "Non-I2C SMBus adapter",
95  /* id */              I2C_ALGO_SMBUS,
96  /* master_xfer */     NULL,
97  /* smbus_access */    &voodoo3_access,
98  /* slave_send */      NULL,
99  /* slave_rcv */       NULL,
100  /* algo_control */    NULL,
101};
102
103static struct i2c_adapter voodoo3_adapter = {
104 "unset",
105 I2C_ALGO_SMBUS | I2C_HW_SMBUS_VOODOO3,
106 &smbus_algorithm,
107 NULL,
108 voodoo3_inc,
109 voodoo3_dec,
110 NULL,
111 NULL,
112};
113
114static int __initdata voodoo3_initialized;
115static unsigned short voodoo3_smba = 0;
116static unsigned int state=0xcf980020;
117static unsigned char *mem;
118static int v3_num;
119
120extern inline void outlong(int off,unsigned int dat)
121{
122        *((unsigned int*)(mem+off))=dat;
123}
124
125
126extern inline unsigned int readlong(int off)
127{
128        return *((unsigned int*)(mem+off));
129}
130
131extern inline void out(void)
132{
133        outlong(0x78,state);
134        udelay(10);
135}
136
137extern inline void dat(int data)
138{
139  state&=~(1<<25);
140  if (data)
141    state|=(1<<25);
142}
143
144extern inline void clkon(void)
145{
146  state|=(1<<24);
147}
148
149extern inline void clkoff(void)
150{
151  state&=~(1<<24);
152}
153
154extern inline int rdat(void)
155{
156        dat(1);
157        out();
158        return((readlong(0x78)&(1<<27) )!=0 );
159}
160
161/* Changing the Data line while clock is 'on' (high) is a
162   no-no, except for a start or stop.  */
163   
164void Voodoo3_I2CStart(void)
165{
166  clkon(); /* in theory, clk is already on */
167  out();
168  out();
169  out();
170  out();
171  dat(0);
172  out();
173  clkoff();
174  out();
175}
176
177void Voodoo3_I2CStop(void)
178{
179  dat(0);
180  out();
181  clkon();
182  out();
183  dat(1);
184  out();
185  clkoff();
186  out();
187  out();
188  clkon();
189  out();
190}
191
192int Voodoo3_I2CAck(int ackit)
193{
194int ack;
195
196  out();
197  clkon();
198  if (!ackit) {
199   ack=rdat();
200  } else { dat(0); }
201  out();
202  clkoff();
203  out();
204  return ack;
205}
206
207int Voodoo3_I2CReadByte(int ackit)
208{
209  int i,temp;
210  unsigned char data=0;
211
212  for (i=7; i>=0; i--) {
213    out();
214    clkon();
215    data|=(rdat()<<i);
216    out();
217    clkoff();
218    out();
219  }
220  temp=Voodoo3_I2CAck(ackit);
221#ifdef DEBUG
222  printk("i2c-voodoo3: Readbyte ack -->0x%X, read result -->0x%X\n",temp,data);
223#endif
224  return data;
225}
226
227int Voodoo3_I2CSendByte(unsigned char data)
228{
229  int i,temp;
230
231  for (i=7; i>=0; i--) {
232    dat(temp=data&(1<<i));
233    out();
234    clkon();
235    out();
236//    dat(temp);
237    out();
238    clkoff();
239    out();
240  }
241  temp=Voodoo3_I2CAck(0);
242#ifdef DEBUG
243  printk("i2c-voodoo3: Writebyte ack -->0x%X\n",temp);
244#endif
245  return temp;
246}
247
248
249int Voodoo3_BusCheck(void) {
250/* Check the bus to see if it is in use */
251
252  if (! rdat()) {
253    printk("i2c-voodoo3: I2C bus in use or hung!  Try again later.\n");
254    return 1;
255  }
256  return 0;
257}
258
259int Voodoo3_I2CRead_byte(int addr)
260{
261        int this_dat=0;
262
263        Voodoo3_I2CStart();
264        if (Voodoo3_I2CSendByte(addr)) { 
265#ifdef DEBUG
266          printk("i2c-voodoo3: No Ack on addr WriteByte to addr 0x%X\n",addr);
267#endif
268          this_dat=-1;
269          goto ENDREAD1; }
270        this_dat=Voodoo3_I2CReadByte(0);
271ENDREAD1: Voodoo3_I2CStop();
272#ifdef DEBUG
273        printk("i2c-voodoo3: Byte read at addr:0x%X result:0x%X\n",addr,this_dat);
274#endif
275        return this_dat;
276}
277
278int Voodoo3_I2CRead_byte_data(int addr,int command)
279{
280        int this_dat=0;
281
282        Voodoo3_I2CStart();
283        if (Voodoo3_I2CSendByte(addr)) { 
284#ifdef DEBUG
285          printk("i2c-voodoo3: No Ack on addr WriteByte to addr 0x%X\n",addr);
286#endif
287          this_dat=-1;
288          goto ENDREAD2; }
289        if (!Voodoo3_I2CSendByte(command)) { 
290#ifdef DEBUG
291          printk("i2c-voodoo3: No Ack on cmd WriteByte to addr 0x%X\n",addr);
292#endif
293          this_dat=-1;
294          goto ENDREAD2; }
295        this_dat=Voodoo3_I2CReadByte(0);
296ENDREAD2: Voodoo3_I2CStop();
297#ifdef DEBUG
298        printk("i2c-voodoo3: Byte read at addr:0x%X (command:0x%X) result:0x%X\n",addr,command,this_dat);
299#endif
300        return this_dat;
301}
302
303int Voodoo3_I2CRead_word(int addr,int command)
304{
305        int this_dat=0;
306
307        Voodoo3_I2CStart();
308        if (Voodoo3_I2CSendByte(addr)) { 
309#ifdef DEBUG
310          printk("i2c-voodoo3: No Ack on addr WriteByte to addr 0x%X\n",addr);
311#endif
312          this_dat=-1;
313          goto ENDREAD3; }
314        this_dat=Voodoo3_I2CReadByte(1);
315        this_dat|=(Voodoo3_I2CReadByte(0)<<8);
316ENDREAD3: Voodoo3_I2CStop();
317#ifdef DEBUG
318        printk("i2c-voodoo3: Word read at addr:0x%X (command:0x%X) result:0x%X\n",addr,command,this_dat);
319#endif
320        return this_dat;
321}
322
323int Voodoo3_I2CWrite_byte(int addr,int command)
324{
325int result=0;
326
327        Voodoo3_I2CStart();
328        if (Voodoo3_I2CSendByte(addr)) { 
329#ifdef DEBUG
330          printk("i2c-voodoo3: No Ack on addr WriteByte to addr 0x%X\n",addr);
331#endif
332          result=-1;
333          goto ENDWRITE1; }
334        if (Voodoo3_I2CSendByte(command)) {
335#ifdef DEBUG
336          printk("i2c-voodoo3: No Ack on command WriteByte to addr 0x%X\n",addr);
337#endif
338          result=-1; }
339ENDWRITE1: Voodoo3_I2CStop();
340#ifdef DEBUG
341        printk("i2c-voodoo3: Byte write at addr:0x%X command:0x%X\n",addr,command);
342#endif
343        return result;
344}
345
346int Voodoo3_I2CWrite_byte_data(int addr,int command,int data)
347{
348int result=0;
349
350        Voodoo3_I2CStart();
351        if (Voodoo3_I2CSendByte(addr)) { 
352#ifdef DEBUG
353          printk("i2c-voodoo3: No Ack on addr WriteByte to addr 0x%X\n",addr);
354#endif
355          result=-1;
356          goto ENDWRITE2; }
357        if (Voodoo3_I2CSendByte(command)) {
358#ifdef DEBUG
359          printk("i2c-voodoo3: No Ack on command WriteByte to addr 0x%X\n",addr);
360#endif
361          result=-1;
362          goto ENDWRITE2; }
363        if (Voodoo3_I2CSendByte(data)) {
364#ifdef DEBUG
365          printk("i2c-voodoo3: No Ack on data WriteByte to addr 0x%X\n",addr);
366#endif
367        result=-1; }
368ENDWRITE2: Voodoo3_I2CStop();
369#ifdef DEBUG
370        printk("i2c-voodoo3: Byte write at addr:0x%X command:0x%X data:0x%X\n",addr,command,data);
371#endif
372        return result;
373}
374
375
376int Voodoo3_I2CWrite_word(int addr,int command,long data)
377{
378int result=0;
379
380        Voodoo3_I2CStart();
381        if (Voodoo3_I2CSendByte(addr)) { 
382#ifdef DEBUG
383          printk("i2c-voodoo3: No Ack on addr WriteByte to addr 0x%X\n",addr);
384#endif
385          result=-1;
386          goto ENDWRITE3; }
387        if (Voodoo3_I2CSendByte(command)) {
388#ifdef DEBUG
389          printk("i2c-voodoo3: No Ack on command WriteByte to addr 0x%X\n",addr);
390#endif
391          result=-1;
392          goto ENDWRITE3; }
393        if (Voodoo3_I2CSendByte(data & 0x0FF)) {
394#ifdef DEBUG
395          printk("i2c-voodoo3: No Ack on data WriteByte to addr 0x%X\n",addr);
396#endif
397        result=-1; 
398        goto ENDWRITE3; }
399        if (Voodoo3_I2CSendByte((data &0x0FF00)>>8)) {
400#ifdef DEBUG
401          printk("i2c-voodoo3: No Ack on data byte 2 WriteByte to addr 0x%X\n",addr);
402#endif
403        result=-1; }
404ENDWRITE3: Voodoo3_I2CStop();
405#ifdef DEBUG
406        printk("i2c-voodoo3: Word write at addr:0x%X command:0x%X data:0x%lX\n",addr,command,data);
407#endif
408        return result;
409}
410
411void config_v3(struct pci_dev *dev, int num)
412{
413        unsigned int cadr;
414
415        /* map Voodoo3 memory */
416#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,13)
417        cadr = dev->resource[0].start;
418#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,54)
419        cadr = dev->base_address[0];
420#else
421        pcibios_read_config_dword(dev->bus->number, dev->devfn,
422                                  PCI_BASE_ADDRESS_0,&cadr);
423#endif
424        cadr&=PCI_BASE_ADDRESS_MEM_MASK;
425        mem=ioremap(cadr, 0x1000);
426       
427        *((unsigned int *)(mem+0x70))=0x8160;
428        *((unsigned int *)(mem+0x78))=0xcf980020;
429        printk("i2c-voodoo3: Using Banshee/Voodoo3 at 0x%p\n",mem);
430}
431
432
433
434/* Detect whether a Voodoo3 can be found, and initialize it, where necessary.
435   Note the differences between kernels with the old PCI BIOS interface and
436   newer kernels with the real PCI interface. In compat.h some things are
437   defined to make the transition easier. */
438static int voodoo3_setup(void)
439{
440        struct pci_dev *dev = pci_devices;
441        int result=0;
442        int flag=0;
443
444        v3_num=0;
445
446        while (dev)
447        {
448                if (dev->vendor == PCI_VENDOR_ID_3DFX)
449                        if (dev->device == PCI_DEVICE_ID_3DFX_VOODOO3) {
450                          if (!flag) {
451                                config_v3(dev,v3_num++);
452                          } else { v3_num++; }
453                          flag=1;
454                        }
455                if (result)
456                        return result;
457                dev = dev->next;
458        }
459        if(v3_num > 0) {
460                printk(KERN_INFO "i2c-voodoo3: %d Banshee/Voodoo3(s) found.\n", v3_num);
461                return 0;
462        } else {
463                printk(KERN_INFO "v3tv: No Voodoo3 found.\n");
464                return -ENODEV;
465        }
466}
467
468
469/* Return -1 on error. See smbus.h for more information */
470s32 voodoo3_access(struct i2c_adapter *adap, u8 addr, char read_write,
471                 u8 command, int size, union i2c_smbus_data * data)
472{
473int temp=0;
474
475  if (Voodoo3_BusCheck()) return -1;
476
477  if ((read_write != I2C_SMBUS_READ) && (read_write != I2C_SMBUS_WRITE)) {
478        printk("i2c-voodoo3: Unknown read_write command! (0x%X)\n",read_write);
479        return -1;
480  }
481  addr=((addr & 0x7f) << 1) | (read_write & 0x01);
482  switch(size) {
483    case I2C_SMBUS_QUICK:
484        Voodoo3_I2CStart();
485        if (Voodoo3_I2CSendByte(addr)) { 
486#ifdef DEBUG
487          printk("i2c-voodoo3: No Ack on addr WriteByte to addr 0x%X\n",addr);
488#endif
489          return -1; }
490    case I2C_SMBUS_BYTE:
491        if (read_write == I2C_SMBUS_READ) { temp=Voodoo3_I2CRead_byte(addr); data->byte=temp;
492        } else { temp=Voodoo3_I2CWrite_byte(addr,command); }
493        goto TESTACK;
494    case I2C_SMBUS_BYTE_DATA:
495        if (read_write == I2C_SMBUS_READ) { temp=Voodoo3_I2CRead_byte_data(addr,command); data->byte=temp;
496        } else { temp=Voodoo3_I2CWrite_byte_data(addr,command,data->byte); }
497        goto TESTACK;
498    case I2C_SMBUS_WORD_DATA:
499        if (read_write == I2C_SMBUS_READ) { temp=Voodoo3_I2CRead_word(addr,command); data->word=temp;
500        } else { temp=Voodoo3_I2CWrite_word(addr,command,data->word); }
501        goto TESTACK;
502    case I2C_SMBUS_PROC_CALL:
503        printk("i2c-voodoo3: Proc call not supported.\n");
504        return -1;
505    case I2C_SMBUS_BLOCK_DATA:
506        printk("i2c-voodoo3: Block transfers not supported yet.\n");
507        return -1;
508  }
509
510TESTACK: if (temp < 0) {
511#ifdef DEBUG
512                printk("i2c-voodoo3: no device at 0x%X\n",addr);
513#endif
514                return -1;
515          }
516        return 0;
517}
518
519void voodoo3_inc(struct i2c_adapter *adapter)
520{
521        MOD_INC_USE_COUNT;
522}
523
524void voodoo3_dec(struct i2c_adapter *adapter)
525{
526
527        MOD_DEC_USE_COUNT;
528}
529
530int __init i2c_voodoo3_init(void)
531{
532  int res;
533  printk("i2c-voodoo3.o version %s (%s)\n",LM_VERSION,LM_DATE);
534#ifdef DEBUG
535/* PE- It might be good to make this a permanent part of the code! */
536  if (voodoo3_initialized) {
537    printk("i2c-voodoo3.o: Oops, voodoo3_init called a second time!\n");
538    return -EBUSY;
539  }
540#endif
541  voodoo3_initialized = 0;
542  if ((res = voodoo3_setup())) {
543    printk("i2c-voodoo3.o: Voodoo3 not detected, module not inserted.\n");
544    voodoo3_cleanup();
545    return res;
546  }
547  voodoo3_initialized ++;
548  sprintf(voodoo3_adapter.name,"SMBus Voodoo3 adapter at %04x",voodoo3_smba);
549  if ((res = i2c_add_adapter(&voodoo3_adapter))) {
550    printk("i2c-voodoo3.o: Adapter registration failed, module not inserted.\n");
551    voodoo3_cleanup();
552    return res;
553  }
554  voodoo3_initialized++;
555  printk("i2c-voodoo3.o: Voodoo3 I2C bus detected and initialized\n");
556  return 0;
557}
558
559int __init voodoo3_cleanup(void)
560{
561  int res;
562 
563  iounmap(mem);
564  if (voodoo3_initialized >= 2)
565  {
566    if ((res = i2c_del_adapter(&voodoo3_adapter))) {
567      printk("i2c-voodoo3.o: i2c_del_adapter failed, module not removed\n");
568      return res;
569    } else
570      voodoo3_initialized--;
571  }
572  if (voodoo3_initialized >= 1) {
573    voodoo3_initialized--;
574  }
575  return 0;
576}
577
578EXPORT_NO_SYMBOLS;
579
580#ifdef MODULE
581
582MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl> and Philip Edelbrock <phil@netroedge.com> and Ralph Metzler <rjkm@thp.uni-koeln.de>");
583MODULE_DESCRIPTION("Voodoo3 I2C/SMBus driver");
584
585
586int init_module(void)
587{
588  return i2c_voodoo3_init();
589}
590
591int cleanup_module(void)
592{
593  return voodoo3_cleanup();
594}
595
596#endif /* MODULE */
597
Note: See TracBrowser for help on using the browser.