summaryrefslogtreecommitdiffstats
path: root/drivers/char/i2c-parport.c
blob: cafe38f378d06c055802d91e4ac6df9ab7c66f04 (plain)
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
/*
 * I2C driver for parallel port
 *
 * Author: Phil Blundell <philb@gnu.org>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version
 * 2 of the License, or (at your option) any later version.
 *
 * This driver implements a simple I2C protocol by bit-twiddling some
 * signals on the parallel port.  Since the outputs on the parallel port
 * aren't open collector, three lines rather than two are used:
 *
 *	D0	clock out
 *	D1	data out
 *	BUSY	data in	
 */

#include <linux/parport.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <asm/spinlock.h>

#define I2C_DELAY   10

static int debug = 0;

struct parport_i2c_bus
{
  struct i2c_bus i2c;
  struct parport_i2c_bus *next;
};

static struct parport_i2c_bus *bus_list;

#ifdef __SMP__
static spinlock_t bus_list_lock = SPIN_LOCK_UNLOCKED;
#endif

/* software I2C functions */

static void i2c_setlines(struct i2c_bus *bus, int clk, int data)
{
  struct parport *p = bus->data;
  parport_write_data(p, (clk?1:0) | (data?2:0)); 
  udelay(I2C_DELAY);
}

static int i2c_getdataline(struct i2c_bus *bus)
{
  struct parport *p = bus->data;
  return (parport_read_status(p) & PARPORT_STATUS_BUSY) ? 0 : 1;
}

static struct i2c_bus parport_i2c_bus_template = 
{
  "...",
  I2C_BUSID_PARPORT,
  NULL,
  
  SPIN_LOCK_UNLOCKED,
  
  NULL,
  NULL,
	
  i2c_setlines,
  i2c_getdataline,
  NULL,
  NULL,
};

static void i2c_parport_attach(struct parport *port)
{
  struct parport_i2c_bus *b = kmalloc(sizeof(struct parport_i2c_bus), 
				      GFP_KERNEL);
  b->i2c = parport_i2c_bus_template;
  b->i2c.data = port;
  strncpy(b->i2c.name, port->name, 32);
  spin_lock(&bus_list_lock);
  b->next = bus_list;
  bus_list = b;
  spin_unlock(&bus_list_lock);
  i2c_register_bus(&b->i2c);
  if (debug)
    printk(KERN_DEBUG "i2c: attached to %s\n", port->name);
}

static void i2c_parport_detach(struct parport *port)
{
  struct parport_i2c_bus *b, *old_b = NULL;
  spin_lock(&bus_list_lock);
  b = bus_list;
  while (b)
  {
    if (b->i2c.data == port)
    {
      if (old_b)
	old_b->next = b->next;
      else
	bus_list = b->next;
      i2c_unregister_bus(&b->i2c);
      kfree(b);
      break;
    }
    old_b = b;
    b = b->next;
  }
  spin_unlock(&bus_list_lock);
  if (debug)
    printk(KERN_DEBUG "i2c: detached from %s\n", port->name);
}

static struct parport_driver parport_i2c_driver = 
{
  "i2c",
  i2c_parport_attach,
  i2c_parport_detach
};

#ifdef MODULE
int init_module(void)
#else
int __init i2c_parport_init(void)
#endif
{
  printk("I2C: driver for parallel port v0.1 philb@gnu.org\n");
  parport_register_driver(&parport_i2c_driver);
  return 0;
}

#ifdef MODULE
MODULE_PARM(debug, "i");

void cleanup_module(void)
{
  struct parport_i2c_bus *b = bus_list;
  while (b)
  {
    struct parport_i2c_bus *next = b->next;
    i2c_unregister_bus(&b->i2c);
    kfree(b);
    b = next;
  }
  parport_unregister_driver(&parport_i2c_driver);
}
#endif