Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/drivers/i2c/busses/i2c-parport-light.c
15111 views
1
/* ------------------------------------------------------------------------ *
2
* i2c-parport-light.c I2C bus over parallel port *
3
* ------------------------------------------------------------------------ *
4
Copyright (C) 2003-2010 Jean Delvare <[email protected]>
5
6
Based on older i2c-velleman.c driver
7
Copyright (C) 1995-2000 Simon G. Vogl
8
With some changes from:
9
Frodo Looijaard <[email protected]>
10
Kyösti Mälkki <[email protected]>
11
12
This program is free software; you can redistribute it and/or modify
13
it under the terms of the GNU General Public License as published by
14
the Free Software Foundation; either version 2 of the License, or
15
(at your option) any later version.
16
17
This program is distributed in the hope that it will be useful,
18
but WITHOUT ANY WARRANTY; without even the implied warranty of
19
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20
GNU General Public License for more details.
21
22
You should have received a copy of the GNU General Public License
23
along with this program; if not, write to the Free Software
24
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25
* ------------------------------------------------------------------------ */
26
27
#include <linux/kernel.h>
28
#include <linux/module.h>
29
#include <linux/init.h>
30
#include <linux/delay.h>
31
#include <linux/platform_device.h>
32
#include <linux/ioport.h>
33
#include <linux/i2c.h>
34
#include <linux/i2c-algo-bit.h>
35
#include <linux/i2c-smbus.h>
36
#include <linux/io.h>
37
#include "i2c-parport.h"
38
39
#define DEFAULT_BASE 0x378
40
#define DRVNAME "i2c-parport-light"
41
42
static struct platform_device *pdev;
43
44
static u16 base;
45
module_param(base, ushort, 0);
46
MODULE_PARM_DESC(base, "Base I/O address");
47
48
static int irq;
49
module_param(irq, int, 0);
50
MODULE_PARM_DESC(irq, "IRQ (optional)");
51
52
/* ----- Low-level parallel port access ----------------------------------- */
53
54
static inline void port_write(unsigned char p, unsigned char d)
55
{
56
outb(d, base+p);
57
}
58
59
static inline unsigned char port_read(unsigned char p)
60
{
61
return inb(base+p);
62
}
63
64
/* ----- Unified line operation functions --------------------------------- */
65
66
static inline void line_set(int state, const struct lineop *op)
67
{
68
u8 oldval = port_read(op->port);
69
70
/* Touch only the bit(s) needed */
71
if ((op->inverted && !state) || (!op->inverted && state))
72
port_write(op->port, oldval | op->val);
73
else
74
port_write(op->port, oldval & ~op->val);
75
}
76
77
static inline int line_get(const struct lineop *op)
78
{
79
u8 oldval = port_read(op->port);
80
81
return ((op->inverted && (oldval & op->val) != op->val)
82
|| (!op->inverted && (oldval & op->val) == op->val));
83
}
84
85
/* ----- I2C algorithm call-back functions and structures ----------------- */
86
87
static void parport_setscl(void *data, int state)
88
{
89
line_set(state, &adapter_parm[type].setscl);
90
}
91
92
static void parport_setsda(void *data, int state)
93
{
94
line_set(state, &adapter_parm[type].setsda);
95
}
96
97
static int parport_getscl(void *data)
98
{
99
return line_get(&adapter_parm[type].getscl);
100
}
101
102
static int parport_getsda(void *data)
103
{
104
return line_get(&adapter_parm[type].getsda);
105
}
106
107
/* Encapsulate the functions above in the correct structure
108
Note that getscl will be set to NULL by the attaching code for adapters
109
that cannot read SCL back */
110
static struct i2c_algo_bit_data parport_algo_data = {
111
.setsda = parport_setsda,
112
.setscl = parport_setscl,
113
.getsda = parport_getsda,
114
.getscl = parport_getscl,
115
.udelay = 50,
116
.timeout = HZ,
117
};
118
119
/* ----- Driver registration ---------------------------------------------- */
120
121
static struct i2c_adapter parport_adapter = {
122
.owner = THIS_MODULE,
123
.class = I2C_CLASS_HWMON,
124
.algo_data = &parport_algo_data,
125
.name = "Parallel port adapter (light)",
126
};
127
128
/* SMBus alert support */
129
static struct i2c_smbus_alert_setup alert_data = {
130
.alert_edge_triggered = 1,
131
};
132
static struct i2c_client *ara;
133
static struct lineop parport_ctrl_irq = {
134
.val = (1 << 4),
135
.port = PORT_CTRL,
136
};
137
138
static int __devinit i2c_parport_probe(struct platform_device *pdev)
139
{
140
int err;
141
142
/* Reset hardware to a sane state (SCL and SDA high) */
143
parport_setsda(NULL, 1);
144
parport_setscl(NULL, 1);
145
/* Other init if needed (power on...) */
146
if (adapter_parm[type].init.val) {
147
line_set(1, &adapter_parm[type].init);
148
/* Give powered devices some time to settle */
149
msleep(100);
150
}
151
152
parport_adapter.dev.parent = &pdev->dev;
153
err = i2c_bit_add_bus(&parport_adapter);
154
if (err) {
155
dev_err(&pdev->dev, "Unable to register with I2C\n");
156
return err;
157
}
158
159
/* Setup SMBus alert if supported */
160
if (adapter_parm[type].smbus_alert && irq) {
161
alert_data.irq = irq;
162
ara = i2c_setup_smbus_alert(&parport_adapter, &alert_data);
163
if (ara)
164
line_set(1, &parport_ctrl_irq);
165
else
166
dev_warn(&pdev->dev, "Failed to register ARA client\n");
167
}
168
169
return 0;
170
}
171
172
static int __devexit i2c_parport_remove(struct platform_device *pdev)
173
{
174
if (ara) {
175
line_set(0, &parport_ctrl_irq);
176
i2c_unregister_device(ara);
177
ara = NULL;
178
}
179
i2c_del_adapter(&parport_adapter);
180
181
/* Un-init if needed (power off...) */
182
if (adapter_parm[type].init.val)
183
line_set(0, &adapter_parm[type].init);
184
185
return 0;
186
}
187
188
static struct platform_driver i2c_parport_driver = {
189
.driver = {
190
.owner = THIS_MODULE,
191
.name = DRVNAME,
192
},
193
.probe = i2c_parport_probe,
194
.remove = __devexit_p(i2c_parport_remove),
195
};
196
197
static int __init i2c_parport_device_add(u16 address)
198
{
199
int err;
200
201
pdev = platform_device_alloc(DRVNAME, -1);
202
if (!pdev) {
203
err = -ENOMEM;
204
printk(KERN_ERR DRVNAME ": Device allocation failed\n");
205
goto exit;
206
}
207
208
err = platform_device_add(pdev);
209
if (err) {
210
printk(KERN_ERR DRVNAME ": Device addition failed (%d)\n",
211
err);
212
goto exit_device_put;
213
}
214
215
return 0;
216
217
exit_device_put:
218
platform_device_put(pdev);
219
exit:
220
return err;
221
}
222
223
static int __init i2c_parport_init(void)
224
{
225
int err;
226
227
if (type < 0) {
228
printk(KERN_ERR DRVNAME ": adapter type unspecified\n");
229
return -ENODEV;
230
}
231
232
if (type >= ARRAY_SIZE(adapter_parm)) {
233
printk(KERN_ERR DRVNAME ": invalid type (%d)\n", type);
234
return -ENODEV;
235
}
236
237
if (base == 0) {
238
pr_info(DRVNAME ": using default base 0x%x\n", DEFAULT_BASE);
239
base = DEFAULT_BASE;
240
}
241
242
if (!request_region(base, 3, DRVNAME))
243
return -EBUSY;
244
245
if (irq != 0)
246
pr_info(DRVNAME ": using irq %d\n", irq);
247
248
if (!adapter_parm[type].getscl.val)
249
parport_algo_data.getscl = NULL;
250
251
/* Sets global pdev as a side effect */
252
err = i2c_parport_device_add(base);
253
if (err)
254
goto exit_release;
255
256
err = platform_driver_register(&i2c_parport_driver);
257
if (err)
258
goto exit_device;
259
260
return 0;
261
262
exit_device:
263
platform_device_unregister(pdev);
264
exit_release:
265
release_region(base, 3);
266
return err;
267
}
268
269
static void __exit i2c_parport_exit(void)
270
{
271
platform_driver_unregister(&i2c_parport_driver);
272
platform_device_unregister(pdev);
273
release_region(base, 3);
274
}
275
276
MODULE_AUTHOR("Jean Delvare <[email protected]>");
277
MODULE_DESCRIPTION("I2C bus over parallel port (light)");
278
MODULE_LICENSE("GPL");
279
280
module_init(i2c_parport_init);
281
module_exit(i2c_parport_exit);
282
283