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

Revision 457, 9.3 KB (checked in by phil, 14 years ago)

(Phil) More fixes and changes.

  • 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 "smbus.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/* 3DFX defines */
48#ifndef PCI_VENDOR_ID_3DFX
49#define PCI_VENDOR_ID_3DFX 0x121a
50#endif
51#ifndef PCI_DEVICE_ID_3DFX_VOODOO3
52#define PCI_DEVICE_ID_3DFX_VOODOO3 0x05
53#endif
54
55/* insmod parameters */
56
57static int voodoo3_init(void);
58static int voodoo3_cleanup(void);
59static int voodoo3_setup(void);
60static s32 voodoo3_access(u8 addr, char read_write,
61                        u8 command, int size, union smbus_data * data);
62
63#ifdef MODULE
64extern int init_module(void);
65extern int cleanup_module(void);
66#endif /* MODULE */
67
68static struct smbus_adapter voodoo3_adapter;
69static int voodoo3_initialized;
70static unsigned short voodoo3_smba = 0;
71static unsigned int state=0xcf980020;
72static unsigned char *mem;
73static int v3_num;
74
75inline outlong(int off,unsigned int dat)
76{
77        *((unsigned int*)(mem+off))=dat;
78}
79
80
81inline unsigned int readlong(int off)
82{
83        return *((unsigned int*)(mem+off));
84}
85
86inline out(void)
87{
88        outlong(0x78,state);
89        udelay(10);
90}
91
92inline void dat(int data)
93{
94  state&=~(1<<25);
95  if (data)
96    state|=(1<<25);
97}
98
99inline void clkon(void)
100{
101  state|=(1<<24);
102}
103
104inline void clkoff(void)
105{
106  state&=~(1<<24);
107}
108
109inline int rdat(void)
110{
111        dat(1);
112        out();
113        return((readlong(0x78)&(1<<27) )!=0 );
114}
115
116/* Changing the Data line while clock is 'on' (high) is a
117   no-no, except for a start or stop.  */
118   
119void Voodoo3_I2CStart(void)
120{
121  clkon(); /* in theory, clk is already on */
122  out();
123  dat(0);
124  out();
125  clkoff();
126  out();
127  clkon();
128}
129
130void Voodoo3_I2CStop(void)
131{
132  dat(0);
133  out();
134  clkon();
135  out();
136  dat(1);
137  out();
138  clkoff();
139  out();
140  clkon();
141  out();
142}
143
144int Voodoo3_I2CAck()
145{
146int ack;
147
148  dat(1); /* put data line in tristate/hi */
149  clkon();
150  out();
151  ack=rdat();
152  clkoff();
153  out();
154  return ack;
155
156}
157
158int Voodoo3_I2CReadByte()
159{
160  int i,temp;
161  unsigned char data=0;
162
163  clkoff();
164  out();
165  for (i=7; i>=0; i--) {
166    out();
167    clkon();
168    data|=(rdat()<<i);
169    out();
170    clkoff();
171  }
172  temp=Voodoo3_I2CAck();
173#ifdef DEBUG
174  printk("i2c-voodoo3: Readbyte ack -->0x%X, read result -->0x%X\n",temp,data);
175#endif
176  if (! temp)
177   return data;
178  else {
179   return -1;
180  }
181}
182
183int Voodoo3_I2CSendByte(unsigned char data)
184{
185  int i,temp;
186
187  clkoff();
188  out();
189  for (i=7; i>=0; i--) {
190    dat(temp=data&(1<<i));
191    out();
192    clkon();
193    out();
194    dat(temp);
195    clkoff();
196    out();
197  }
198  temp=Voodoo3_I2CAck();
199#ifdef DEBUG
200  printk("i2c-voodoo3: Writebyte ack -->0x%X\n",temp);
201#endif
202  return temp;
203}
204
205
206int Voodoo3_BusCheck(void) {
207/* Check the bus to see if it is in use */
208
209  if (! rdat()) {
210    printk("i2c-voodoo3: I2C bus in use or hung!  Try again later.\n");
211    return 1;
212  }
213  return 0;
214}
215
216/* Note, this is actually a byte read and not a byte_data read */
217/* I.e., the command value is dumped.                          */
218int Voodoo3_I2CRead(int addr,int command)
219{
220        int dat=0;
221
222        Voodoo3_I2CStart();
223        if (Voodoo3_I2CSendByte(addr)) { 
224#ifdef DEBUG
225          printk("i2c-voodoo3: No Ack on addr WriteByte to addr 0x%X\n",addr);
226#endif
227          dat=-1;
228          goto ENDREAD; }
229        dat=Voodoo3_I2CReadByte();
230ENDREAD: Voodoo3_I2CStop();
231#ifdef DEBUG
232        printk("i2c-voodoo3: Byte read at addr:0x%X (command:0x%X) result:0x%X\n",addr,command,dat);
233#endif
234        return dat;
235}
236
237int Voodoo3_I2CWrite(int addr,int command,int data)
238{
239int result=0;
240
241        Voodoo3_I2CStart();
242        if (Voodoo3_I2CSendByte(addr)) { 
243#ifdef DEBUG
244          printk("i2c-voodoo3: No Ack on addr WriteByte to addr 0x%X\n",addr);
245#endif
246          result=-1;
247          goto ENDWRITE; }
248        if (Voodoo3_I2CSendByte(command)) {
249#ifdef DEBUG
250          printk("i2c-voodoo3: No Ack on command WriteByte to addr 0x%X\n",addr);
251#endif
252          result=-1;
253          goto ENDWRITE; }
254        if (Voodoo3_I2CSendByte(data)) {
255#ifdef DEBUG
256          printk("i2c-voodoo3: No Ack on data WriteByte to addr 0x%X\n",addr);
257#endif
258        }
259        result=-1;
260ENDWRITE: Voodoo3_I2CStop();
261#ifdef DEBUG
262        printk("i2c-voodoo3: Byte write at addr:0x%X command:0x%X data:0x%X\n",addr,command,data);
263#endif
264        return result;
265}
266
267void config_v3(struct pci_dev *dev, int num)
268{
269        unsigned int cadr;
270
271        /* map Voodoo3 memory */
272        cadr=dev->base_address[0];
273        cadr&=PCI_BASE_ADDRESS_MEM_MASK;
274        mem=ioremap(cadr, 0x1000);
275       
276        /* Enable TV out mode, Voodoo3_I2C bus, etc. */
277        *((unsigned int *)(mem+0x70))=0x8160;
278        *((unsigned int *)(mem+0x78))=0xcf980020;
279        printk("i2c-voodoo3: Using Banshee/Voodoo3 at 0x%X\n",mem);
280}
281
282
283
284/* Detect whether a Voodoo3 can be found, and initialize it, where necessary.
285   Note the differences between kernels with the old PCI BIOS interface and
286   newer kernels with the real PCI interface. In compat.h some things are
287   defined to make the transition easier. */
288static int voodoo3_setup(void)
289{
290        struct pci_dev *dev = pci_devices;
291        int result=0;
292        int flag=0;
293
294        v3_num=0;
295
296        while (dev)
297        {
298                if (dev->vendor == PCI_VENDOR_ID_3DFX)
299                        if (dev->device == PCI_DEVICE_ID_3DFX_VOODOO3) {
300                          if (!flag) {
301                                config_v3(dev,v3_num++);
302                          } else { v3_num++; }
303                          flag=1;
304                        }
305                if (result)
306                        return result;
307                dev = dev->next;
308        }
309        if(v3_num > 0) {
310                printk(KERN_INFO "i2c-voodoo3: %d Banshee/Voodoo3(s) found.\n", v3_num);
311                return 0;
312        } else {
313                printk(KERN_INFO "v3tv: No Voodoo3 found.\n");
314                return -ENODEV;
315        }
316}
317
318
319/* Return -1 on error. See smbus.h for more information */
320s32 voodoo3_access(u8 addr, char read_write,
321                 u8 command, int size, union smbus_data * data)
322{
323int temp;
324
325  if (Voodoo3_BusCheck()) return -1;
326
327  if ((size == SMBUS_BYTE_DATA) || (size == SMBUS_BYTE)) {
328        addr=((addr & 0x7f) << 1) | (read_write & 0x01);
329        if (read_write == SMBUS_READ) {
330          temp=Voodoo3_I2CRead(addr,command);
331          if (temp < 0) {
332#ifdef DEBUG
333                printk("i2c-voodoo3: no device at 0x%X\n",addr);
334#endif
335                return -1;
336          }
337          data->byte=temp; 
338#ifdef DEBUG
339                printk("i2c-voodoo3: access returning 0x%X\n",data->byte);
340#endif
341        } else {
342          return Voodoo3_I2CWrite(addr,command,data->byte);
343        }
344        return 0;
345  } else { return -1; } /* The rest isn't implemented yet */
346}
347
348
349int voodoo3_init(void)
350{
351  int res;
352  printk("i2c-voodoo3.o version %s (%s)\n",LM_VERSION,LM_DATE);
353#ifdef DEBUG
354/* PE- It might be good to make this a permanent part of the code! */
355  if (voodoo3_initialized) {
356    printk("i2c-voodoo3.o: Oops, voodoo3_init called a second time!\n");
357    return -EBUSY;
358  }
359#endif
360  voodoo3_initialized = 0;
361  if ((res = voodoo3_setup())) {
362    printk("i2c-voodoo3.o: Voodoo3 not detected, module not inserted.\n");
363    voodoo3_cleanup();
364    return res;
365  }
366  voodoo3_initialized ++;
367  sprintf(voodoo3_adapter.name,"SMBus Voodoo3 adapter at %04x",voodoo3_smba);
368  voodoo3_adapter.id = ALGO_SMBUS | SMBUS_VOODOO3;
369  voodoo3_adapter.algo = &smbus_algorithm;
370  voodoo3_adapter.smbus_access = &voodoo3_access;
371  if ((res = smbus_add_adapter(&voodoo3_adapter))) {
372    printk("i2c-voodoo3.o: Adapter registration failed, module not inserted.\n");
373    voodoo3_cleanup();
374    return res;
375  }
376  voodoo3_initialized++;
377  printk("i2c-voodoo3.o: Voodoo3 I2C bus detected and initialized\n");
378  return 0;
379}
380
381int voodoo3_cleanup(void)
382{
383  int res;
384 
385  iounmap(mem);
386  if (voodoo3_initialized >= 2)
387  {
388    if ((res = smbus_del_adapter(&voodoo3_adapter))) {
389      printk("i2c-voodoo3.o: smbus_del_adapter failed, module not removed\n");
390      return res;
391    } else
392      voodoo3_initialized--;
393  }
394  if (voodoo3_initialized >= 1) {
395    voodoo3_initialized--;
396  }
397  return 0;
398}
399
400#ifdef MODULE
401
402MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl> and Philip Edelbrock <phil@netroedge.com> and Ralph Metzler <rjkm@thp.uni-koeln.de>");
403MODULE_DESCRIPTION("Voodoo3 I2C/SMBus driver");
404
405int init_module(void)
406{
407  return voodoo3_init();
408}
409
410int cleanup_module(void)
411{
412  return voodoo3_cleanup();
413}
414
415#endif /* MODULE */
416
Note: See TracBrowser for help on using the browser.