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

Revision 583, 14.5 KB (checked in by frodo, 14 years ago)

`functionality' support for all bus drivers

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