i2c-parport.c 8.5 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3
/* ------------------------------------------------------------------------ *
 * i2c-parport.c I2C bus over parallel port                                 *
 * ------------------------------------------------------------------------ *
4
   Copyright (C) 2003-2011 Jean Delvare <khali@linux-fr.org>
J
Jean Delvare 已提交
5

L
Linus Torvalds 已提交
6 7 8 9
   Based on older i2c-philips-par.c driver
   Copyright (C) 1995-2000 Simon G. Vogl
   With some changes from:
   Frodo Looijaard <frodol@dds.nl>
10
   Kyösti Mälkki <kmalkki@cc.hut.fi>
J
Jean Delvare 已提交
11

L
Linus Torvalds 已提交
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
   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 program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 * ------------------------------------------------------------------------ */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
30
#include <linux/delay.h>
L
Linus Torvalds 已提交
31 32 33
#include <linux/parport.h>
#include <linux/i2c.h>
#include <linux/i2c-algo-bit.h>
34
#include <linux/i2c-smbus.h>
35
#include <linux/slab.h>
36 37
#include <linux/list.h>
#include <linux/mutex.h>
L
Linus Torvalds 已提交
38 39 40 41 42 43 44 45
#include "i2c-parport.h"

/* ----- Device list ------------------------------------------------------ */

struct i2c_par {
	struct pardevice *pdev;
	struct i2c_adapter adapter;
	struct i2c_algo_bit_data algo_data;
46 47
	struct i2c_smbus_alert_setup alert_data;
	struct i2c_client *ara;
48
	struct list_head node;
L
Linus Torvalds 已提交
49 50
};

51 52
static LIST_HEAD(adapter_list);
static DEFINE_MUTEX(adapter_list_lock);
L
Linus Torvalds 已提交
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

/* ----- Low-level parallel port access ----------------------------------- */

static void port_write_data(struct parport *p, unsigned char d)
{
	parport_write_data(p, d);
}

static void port_write_control(struct parport *p, unsigned char d)
{
	parport_write_control(p, d);
}

static unsigned char port_read_data(struct parport *p)
{
	return parport_read_data(p);
}

static unsigned char port_read_status(struct parport *p)
{
	return parport_read_status(p);
}

static unsigned char port_read_control(struct parport *p)
{
	return parport_read_control(p);
}

J
Jean Delvare 已提交
81
static void (* const port_write[])(struct parport *, unsigned char) = {
L
Linus Torvalds 已提交
82 83 84 85 86
	port_write_data,
	NULL,
	port_write_control,
};

J
Jean Delvare 已提交
87
static unsigned char (* const port_read[])(struct parport *) = {
L
Linus Torvalds 已提交
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
	port_read_data,
	port_read_status,
	port_read_control,
};

/* ----- Unified line operation functions --------------------------------- */

static inline void line_set(struct parport *data, int state,
	const struct lineop *op)
{
	u8 oldval = port_read[op->port](data);

	/* Touch only the bit(s) needed */
	if ((op->inverted && !state) || (!op->inverted && state))
		port_write[op->port](data, oldval | op->val);
	else
		port_write[op->port](data, oldval & ~op->val);
}

static inline int line_get(struct parport *data,
	const struct lineop *op)
{
	u8 oldval = port_read[op->port](data);

	return ((op->inverted && (oldval & op->val) != op->val)
	    || (!op->inverted && (oldval & op->val) == op->val));
}

/* ----- I2C algorithm call-back functions and structures ----------------- */

static void parport_setscl(void *data, int state)
{
	line_set((struct parport *) data, state, &adapter_parm[type].setscl);
}

static void parport_setsda(void *data, int state)
{
	line_set((struct parport *) data, state, &adapter_parm[type].setsda);
}

static int parport_getscl(void *data)
{
	return line_get((struct parport *) data, &adapter_parm[type].getscl);
}

static int parport_getsda(void *data)
{
	return line_get((struct parport *) data, &adapter_parm[type].getsda);
}

/* Encapsulate the functions above in the correct structure.
   Note that this is only a template, from which the real structures are
   copied. The attaching code will set getscl to NULL for adapters that
141
   cannot read SCL back, and will also make the data field point to
L
Linus Torvalds 已提交
142
   the parallel port structure. */
143
static const struct i2c_algo_bit_data parport_algo_data = {
L
Linus Torvalds 已提交
144 145 146 147
	.setsda		= parport_setsda,
	.setscl		= parport_setscl,
	.getsda		= parport_getsda,
	.getscl		= parport_getscl,
148
	.udelay		= 10, /* ~50 kbps */
L
Linus Torvalds 已提交
149
	.timeout	= HZ,
J
Jean Delvare 已提交
150
};
L
Linus Torvalds 已提交
151 152 153

/* ----- I2c and parallel port call-back functions and structures --------- */

154
static void i2c_parport_irq(void *data)
155 156 157 158 159 160 161 162 163 164 165 166
{
	struct i2c_par *adapter = data;
	struct i2c_client *ara = adapter->ara;

	if (ara) {
		dev_dbg(&ara->dev, "SMBus alert received\n");
		i2c_handle_smbus_alert(ara);
	} else
		dev_dbg(&adapter->adapter.dev,
			"SMBus alert received but no ARA client!\n");
}

J
Jean Delvare 已提交
167
static void i2c_parport_attach(struct parport *port)
L
Linus Torvalds 已提交
168 169
{
	struct i2c_par *adapter;
J
Jean Delvare 已提交
170

171
	adapter = kzalloc(sizeof(struct i2c_par), GFP_KERNEL);
L
Linus Torvalds 已提交
172
	if (adapter == NULL) {
173
		printk(KERN_ERR "i2c-parport: Failed to kzalloc\n");
L
Linus Torvalds 已提交
174 175 176 177
		return;
	}

	pr_debug("i2c-parport: attaching to %s\n", port->name);
178
	parport_disable_irq(port);
L
Linus Torvalds 已提交
179
	adapter->pdev = parport_register_device(port, "i2c-parport",
180
		NULL, NULL, i2c_parport_irq, PARPORT_FLAG_EXCL, adapter);
L
Linus Torvalds 已提交
181 182
	if (!adapter->pdev) {
		printk(KERN_ERR "i2c-parport: Unable to register with parport\n");
J
Jean Delvare 已提交
183
		goto err_free;
L
Linus Torvalds 已提交
184 185 186
	}

	/* Fill the rest of the structure */
J
Jean Delvare 已提交
187 188 189 190
	adapter->adapter.owner = THIS_MODULE;
	adapter->adapter.class = I2C_CLASS_HWMON;
	strlcpy(adapter->adapter.name, "Parallel port adapter",
		sizeof(adapter->adapter.name));
L
Linus Torvalds 已提交
191
	adapter->algo_data = parport_algo_data;
192 193
	/* Slow down if we can't sense SCL */
	if (!adapter_parm[type].getscl.val) {
L
Linus Torvalds 已提交
194
		adapter->algo_data.getscl = NULL;
195 196
		adapter->algo_data.udelay = 50; /* ~10 kbps */
	}
L
Linus Torvalds 已提交
197 198
	adapter->algo_data.data = port;
	adapter->adapter.algo_data = &adapter->algo_data;
199
	adapter->adapter.dev.parent = port->physport->dev;
L
Linus Torvalds 已提交
200 201 202

	if (parport_claim_or_block(adapter->pdev) < 0) {
		printk(KERN_ERR "i2c-parport: Could not claim parallel port\n");
J
Jean Delvare 已提交
203
		goto err_unregister;
L
Linus Torvalds 已提交
204 205 206 207 208 209
	}

	/* Reset hardware to a sane state (SCL and SDA high) */
	parport_setsda(port, 1);
	parport_setscl(port, 1);
	/* Other init if needed (power on...) */
210
	if (adapter_parm[type].init.val) {
L
Linus Torvalds 已提交
211
		line_set(port, 1, &adapter_parm[type].init);
212 213 214
		/* Give powered devices some time to settle */
		msleep(100);
	}
L
Linus Torvalds 已提交
215 216 217

	if (i2c_bit_add_bus(&adapter->adapter) < 0) {
		printk(KERN_ERR "i2c-parport: Unable to register with I2C\n");
J
Jean Delvare 已提交
218
		goto err_unregister;
L
Linus Torvalds 已提交
219 220
	}

221 222 223 224 225 226 227 228 229 230 231 232
	/* Setup SMBus alert if supported */
	if (adapter_parm[type].smbus_alert) {
		adapter->alert_data.alert_edge_triggered = 1;
		adapter->ara = i2c_setup_smbus_alert(&adapter->adapter,
						     &adapter->alert_data);
		if (adapter->ara)
			parport_enable_irq(port);
		else
			printk(KERN_WARNING "i2c-parport: Failed to register "
			       "ARA client\n");
	}

L
Linus Torvalds 已提交
233
	/* Add the new adapter to the list */
234 235 236
	mutex_lock(&adapter_list_lock);
	list_add_tail(&adapter->node, &adapter_list);
	mutex_unlock(&adapter_list_lock);
J
Jean Delvare 已提交
237
	return;
L
Linus Torvalds 已提交
238

J
Jean Delvare 已提交
239
 err_unregister:
240
	parport_release(adapter->pdev);
L
Linus Torvalds 已提交
241
	parport_unregister_device(adapter->pdev);
J
Jean Delvare 已提交
242
 err_free:
L
Linus Torvalds 已提交
243 244 245
	kfree(adapter);
}

J
Jean Delvare 已提交
246
static void i2c_parport_detach(struct parport *port)
L
Linus Torvalds 已提交
247
{
248
	struct i2c_par *adapter, *_n;
L
Linus Torvalds 已提交
249 250

	/* Walk the list */
251 252
	mutex_lock(&adapter_list_lock);
	list_for_each_entry_safe(adapter, _n, &adapter_list, node) {
L
Linus Torvalds 已提交
253
		if (adapter->pdev->port == port) {
254 255 256 257
			if (adapter->ara) {
				parport_disable_irq(port);
				i2c_unregister_device(adapter->ara);
			}
258 259
			i2c_del_adapter(&adapter->adapter);

L
Linus Torvalds 已提交
260 261 262
			/* Un-init if needed (power off...) */
			if (adapter_parm[type].init.val)
				line_set(port, 0, &adapter_parm[type].init);
J
Jean Delvare 已提交
263

264
			parport_release(adapter->pdev);
L
Linus Torvalds 已提交
265
			parport_unregister_device(adapter->pdev);
266
			list_del(&adapter->node);
L
Linus Torvalds 已提交
267 268 269
			kfree(adapter);
		}
	}
270
	mutex_unlock(&adapter_list_lock);
L
Linus Torvalds 已提交
271 272
}

273
static struct parport_driver i2c_parport_driver = {
L
Linus Torvalds 已提交
274 275 276 277 278 279 280 281 282
	.name	= "i2c-parport",
	.attach	= i2c_parport_attach,
	.detach	= i2c_parport_detach,
};

/* ----- Module loading, unloading and information ------------------------ */

static int __init i2c_parport_init(void)
{
283 284 285 286 287 288
	if (type < 0) {
		printk(KERN_WARNING "i2c-parport: adapter type unspecified\n");
		return -ENODEV;
	}

	if (type >= ARRAY_SIZE(adapter_parm)) {
L
Linus Torvalds 已提交
289
		printk(KERN_WARNING "i2c-parport: invalid type (%d)\n", type);
290
		return -ENODEV;
L
Linus Torvalds 已提交
291
	}
292

293
	return parport_register_driver(&i2c_parport_driver);
L
Linus Torvalds 已提交
294 295 296 297
}

static void __exit i2c_parport_exit(void)
{
298
	parport_unregister_driver(&i2c_parport_driver);
L
Linus Torvalds 已提交
299 300 301 302 303 304 305 306
}

MODULE_AUTHOR("Jean Delvare <khali@linux-fr.org>");
MODULE_DESCRIPTION("I2C bus over parallel port");
MODULE_LICENSE("GPL");

module_init(i2c_parport_init);
module_exit(i2c_parport_exit);