File: i2c-pcf-epp.c

package info (click to toggle)
i2c 2.6.3-5
  • links: PTS
  • area: main
  • in suites: woody
  • size: 592 kB
  • ctags: 1,051
  • sloc: ansic: 6,419; perl: 637; makefile: 186
file content (315 lines) | stat: -rw-r--r-- 7,405 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
/* ------------------------------------------------------------------------- */
/* i2c-pcf-epp.c i2c-hw access for PCF8584 style EPP parallel port adapters  */
/* ------------------------------------------------------------------------- */

#include <linux/kernel.h>
#include <linux/ioport.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/version.h>
#include <linux/init.h>
#include <linux/parport.h>
#include <asm/irq.h>
#include <asm/io.h>

#include "i2c.h"
#include "i2c-algo-pcf.h"
#include "i2c-pcf8584.h"

struct  i2c_pcf_epp {
  int pe_base;
  int pe_irq;
  int pe_clock;
  int pe_own;
} ;

#define DEFAULT_BASE 0x378
#define DEFAULT_IRQ      7
#define DEFAULT_CLOCK 0x1c
#define DEFAULT_OWN   0x55

static int base  = 0;
static int irq   = 0;
static int clock = 0;
static int own   = 0;
static int i2c_debug=0;
static struct i2c_pcf_epp gpe;
#if (LINUX_VERSION_CODE < 0x020301)
static struct wait_queue *pcf_wait = NULL;
#else
static wait_queue_head_t pcf_wait;
#endif
static int pcf_pending;

/* ----- global defines -----------------------------------------------	*/
#define DEB(x)	if (i2c_debug>=1) x
#define DEB2(x) if (i2c_debug>=2) x
#define DEB3(x) if (i2c_debug>=3) x
#define DEBE(x)	x	/* error messages 				*/

/* --- Convenience defines for the EPP/SPP port:			*/
#define BASE	((struct i2c_pcf_epp *)(data))->pe_base
#define DATA	BASE			/* SPP data port */
#define STAT	(BASE+1)		/* SPP status port */
#define CTRL	(BASE+2)		/* SPP control port */
#define EADD	(BASE+3)		/* EPP address port */
#define EDAT	(BASE+4)		/* EPP data port */

/* ----- local functions ----------------------------------------------	*/

static void pcf_epp_setbyte(void *data, int ctl, int val)
{
  if (ctl) {
    if (gpe.pe_irq > 0) {
      DEB3(printk(KERN_DEBUG "i2c-pcf-epp.o: Write control 0x%x\n",
		  val|I2C_PCF_ENI));
      // set A0 pin HIGH
      outb(inb(CTRL) | PARPORT_CONTROL_INIT, CTRL);
      // DEB3(printk(KERN_DEBUG "i2c-pcf-epp.o: CTRL port = 0x%x\n", inb(CTRL)));
      // DEB3(printk(KERN_DEBUG "i2c-pcf-epp.o: STAT port = 0x%x\n", inb(STAT)));
      
      // EPP write data cycle
      outb(val | I2C_PCF_ENI, EDAT);
    } else {
      DEB3(printk(KERN_DEBUG "i2c-pcf-epp.o: Write control 0x%x\n", val));
      // set A0 pin HIGH
      outb(inb(CTRL) | PARPORT_CONTROL_INIT, CTRL);
      outb(val, CTRL);
    }
  } else {
    DEB3(printk(KERN_DEBUG "i2c-pcf-epp.o: Write data 0x%x\n", val));
    // set A0 pin LO
    outb(inb(CTRL) & ~PARPORT_CONTROL_INIT, CTRL);
    // DEB3(printk(KERN_DEBUG "i2c-pcf-epp.o: CTRL port = 0x%x\n", inb(CTRL)));
    // DEB3(printk(KERN_DEBUG "i2c-pcf-epp.o: STAT port = 0x%x\n", inb(STAT)));
    outb(val, EDAT);
  }
}

static int pcf_epp_getbyte(void *data, int ctl)
{
  int val;

  if (ctl) {
    // set A0 pin HIGH
    outb(inb(CTRL) | PARPORT_CONTROL_INIT, CTRL);
    val = inb(EDAT);
    DEB3(printk(KERN_DEBUG "i2c-pcf-epp.o: Read control 0x%x\n", val));
  } else {
    // set A0 pin LOW
    outb(inb(CTRL) & ~PARPORT_CONTROL_INIT, CTRL);
    val = inb(EDAT);
    DEB3(printk(KERN_DEBUG "i2c-pcf-epp.o: Read data 0x%x\n", val));
  }
  return (val);
}

static int pcf_epp_getown(void *data)
{
  return (gpe.pe_own);
}


static int pcf_epp_getclock(void *data)
{
  return (gpe.pe_clock);
}

#if 0
static void pcf_epp_sleep(unsigned long timeout)
{
  schedule_timeout( timeout * HZ);
}
#endif

static void pcf_epp_waitforpin(void) {
  int timeout = 10;

  if (gpe.pe_irq > 0) {
    cli();
    if (pcf_pending == 0) {
      interruptible_sleep_on_timeout(&pcf_wait, timeout*HZ);
      //udelay(100);
    } else {
      pcf_pending = 0;
    }
    sti();
  } else {
    udelay(100);
  }
}

static void pcf_epp_handler(int this_irq, void *dev_id, struct pt_regs *regs) {
  pcf_pending = 1;
  wake_up_interruptible(&pcf_wait);
  DEB3(printk(KERN_DEBUG "i2c-pcf-epp.o: in interrupt handler.\n"));
}


static int pcf_epp_init(void *data)
{
  if (check_region(gpe.pe_base, 5) < 0 ) {
    
    printk(KERN_WARNING "Could not request port region with base 0x%x\n", gpe.pe_base);
    return -ENODEV;
  } else {
    request_region(gpe.pe_base, 5, "i2c (EPP parallel port adapter)");
  }

  DEB3(printk(KERN_DEBUG "i2c-pcf-epp.o: init status port = 0x%x\n", inb(0x379)));
  
  if (gpe.pe_irq > 0) {
    if (request_irq(gpe.pe_irq, pcf_epp_handler, 0, "PCF8584", 0) < 0) {
      printk(KERN_NOTICE "i2c-pcf-epp.o: Request irq%d failed\n", gpe.pe_irq);
      gpe.pe_irq = 0;
    } else
      disable_irq(gpe.pe_irq);
      enable_irq(gpe.pe_irq);
  }
  // EPP mode initialize
  // enable interrupt from nINTR pin
  outb(inb(CTRL)|0x14, CTRL);
  // clear ERROR bit of STAT
  outb(inb(STAT)|0x01, STAT);
  outb(inb(STAT)&~0x01,STAT);
  
  return 0;
}


static void pcf_epp_exit(void)
{
  if (gpe.pe_irq > 0) {
    disable_irq(gpe.pe_irq);
    free_irq(gpe.pe_irq, 0);
  }
  release_region(gpe.pe_base , 5);
}


static int pcf_epp_reg(struct i2c_client *client)
{
  return 0;
}


static int pcf_epp_unreg(struct i2c_client *client)
{
  return 0;
}

static void pcf_epp_inc_use(struct i2c_adapter *adap)
{
#ifdef MODULE
  MOD_INC_USE_COUNT;
#endif
}

static void pcf_epp_dec_use(struct i2c_adapter *adap)
{
#ifdef MODULE
  MOD_DEC_USE_COUNT;
#endif
}


/* ------------------------------------------------------------------------
 * Encapsulate the above functions in the correct operations structure.
 * This is only done when more than one hardware adapter is supported.
 */
static struct i2c_algo_pcf_data pcf_epp_data = {
  NULL,
  pcf_epp_setbyte,
  pcf_epp_getbyte,
  pcf_epp_getown,
  pcf_epp_getclock,
  pcf_epp_waitforpin,
  80, 80, 100,		/*	waits, timeout */
};

static struct i2c_adapter pcf_epp_ops = {
  "PCF8584 EPP adapter",
  I2C_HW_P_LP,
  NULL,
  &pcf_epp_data,
  pcf_epp_inc_use,
  pcf_epp_dec_use,
  pcf_epp_reg,
  pcf_epp_unreg,
};

int __init i2c_pcfepp_init(void) 
{
  struct i2c_pcf_epp *pepp = &gpe;

  printk(KERN_DEBUG "i2c-pcf-epp.o: i2c pcf8584-epp adapter module version %s (%s)\n", I2C_VERSION, I2C_DATE);
  if (base == 0)
    pepp->pe_base = DEFAULT_BASE;
  else
    pepp->pe_base = base;

  if (irq == 0)
    pepp->pe_irq = DEFAULT_IRQ;
  else if (irq<0) {
    // switch off irq
    pepp->pe_irq=0;
  } else {
    pepp->pe_irq = irq;
  }
  if (clock == 0)
    pepp->pe_clock = DEFAULT_CLOCK;
  else
    pepp->pe_clock = clock;

  if (own == 0)
    pepp->pe_own = DEFAULT_OWN;
  else
    pepp->pe_own = own;

  pcf_epp_data.data = (void *)pepp;
#if (LINUX_VERSION_CODE >= 0x020301)
  init_waitqueue_head(&pcf_wait);
#endif
  if (pcf_epp_init(pepp) == 0) {
    int ret;
    if ( (ret = i2c_pcf_add_bus(&pcf_epp_ops)) < 0) {
      printk(KERN_WARNING "i2c_pcf_add_bus caused an error: %d\n",ret);
      release_region(pepp->pe_base , 5);
      return ret;
    }
  } else {
    
    return -ENODEV;
  }
  printk(KERN_DEBUG "i2c-pcf-epp.o: found device at %#x.\n", pepp->pe_base);
  return 0;
}


EXPORT_NO_SYMBOLS;

#ifdef MODULE
MODULE_AUTHOR("Hans Berglund <hb@spacetec.no> \n modified by Ryosuke Tajima <rosk@jsk.t.u-tokyo.ac.jp>");
MODULE_DESCRIPTION("I2C-Bus adapter routines for PCF8584 EPP parallel port adapter");

MODULE_PARM(base, "i");
MODULE_PARM(irq, "i");
MODULE_PARM(clock, "i");
MODULE_PARM(own, "i");
MODULE_PARM(i2c_debug, "i");

int init_module(void) 
{
  return i2c_pcfepp_init();
}

void cleanup_module(void) 
{
  i2c_pcf_del_bus(&pcf_epp_ops);
  pcf_epp_exit();
}

#endif