diff options
Diffstat (limited to 'drivers/i2c/busses/i2c-elektor.c')
-rw-r--r-- | drivers/i2c/busses/i2c-elektor.c | 295 |
1 files changed, 295 insertions, 0 deletions
diff --git a/drivers/i2c/busses/i2c-elektor.c b/drivers/i2c/busses/i2c-elektor.c new file mode 100644 index 000000000000..0a7720000a0c --- /dev/null +++ b/drivers/i2c/busses/i2c-elektor.c | |||
@@ -0,0 +1,295 @@ | |||
1 | /* ------------------------------------------------------------------------- */ | ||
2 | /* i2c-elektor.c i2c-hw access for PCF8584 style isa bus adaptes */ | ||
3 | /* ------------------------------------------------------------------------- */ | ||
4 | /* Copyright (C) 1995-97 Simon G. Vogl | ||
5 | 1998-99 Hans Berglund | ||
6 | |||
7 | This program is free software; you can redistribute it and/or modify | ||
8 | it under the terms of the GNU General Public License as published by | ||
9 | the Free Software Foundation; either version 2 of the License, or | ||
10 | (at your option) any later version. | ||
11 | |||
12 | This program is distributed in the hope that it will be useful, | ||
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | GNU General Public License for more details. | ||
16 | |||
17 | You should have received a copy of the GNU General Public License | ||
18 | along with this program; if not, write to the Free Software | ||
19 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ | ||
20 | /* ------------------------------------------------------------------------- */ | ||
21 | |||
22 | /* With some changes from Kyösti Mälkki <kmalkki@cc.hut.fi> and even | ||
23 | Frodo Looijaard <frodol@dds.nl> */ | ||
24 | |||
25 | /* Partialy rewriten by Oleg I. Vdovikin for mmapped support of | ||
26 | for Alpha Processor Inc. UP-2000(+) boards */ | ||
27 | |||
28 | #include <linux/config.h> | ||
29 | #include <linux/kernel.h> | ||
30 | #include <linux/ioport.h> | ||
31 | #include <linux/module.h> | ||
32 | #include <linux/delay.h> | ||
33 | #include <linux/slab.h> | ||
34 | #include <linux/init.h> | ||
35 | #include <linux/interrupt.h> | ||
36 | #include <linux/pci.h> | ||
37 | #include <linux/wait.h> | ||
38 | |||
39 | #include <linux/i2c.h> | ||
40 | #include <linux/i2c-algo-pcf.h> | ||
41 | |||
42 | #include <asm/io.h> | ||
43 | #include <asm/irq.h> | ||
44 | |||
45 | #include "../algos/i2c-algo-pcf.h" | ||
46 | |||
47 | #define DEFAULT_BASE 0x330 | ||
48 | |||
49 | static int base; | ||
50 | static int irq; | ||
51 | static int clock = 0x1c; | ||
52 | static int own = 0x55; | ||
53 | static int mmapped; | ||
54 | |||
55 | /* vdovikin: removed static struct i2c_pcf_isa gpi; code - | ||
56 | this module in real supports only one device, due to missing arguments | ||
57 | in some functions, called from the algo-pcf module. Sometimes it's | ||
58 | need to be rewriten - but for now just remove this for simpler reading */ | ||
59 | |||
60 | static wait_queue_head_t pcf_wait; | ||
61 | static int pcf_pending; | ||
62 | static spinlock_t lock; | ||
63 | |||
64 | /* ----- local functions ---------------------------------------------- */ | ||
65 | |||
66 | static void pcf_isa_setbyte(void *data, int ctl, int val) | ||
67 | { | ||
68 | int address = ctl ? (base + 1) : base; | ||
69 | |||
70 | /* enable irq if any specified for serial operation */ | ||
71 | if (ctl && irq && (val & I2C_PCF_ESO)) { | ||
72 | val |= I2C_PCF_ENI; | ||
73 | } | ||
74 | |||
75 | pr_debug("i2c-elektor: Write 0x%X 0x%02X\n", address, val & 255); | ||
76 | |||
77 | switch (mmapped) { | ||
78 | case 0: /* regular I/O */ | ||
79 | outb(val, address); | ||
80 | break; | ||
81 | case 2: /* double mapped I/O needed for UP2000 board, | ||
82 | I don't know why this... */ | ||
83 | writeb(val, (void *)address); | ||
84 | /* fall */ | ||
85 | case 1: /* memory mapped I/O */ | ||
86 | writeb(val, (void *)address); | ||
87 | break; | ||
88 | } | ||
89 | } | ||
90 | |||
91 | static int pcf_isa_getbyte(void *data, int ctl) | ||
92 | { | ||
93 | int address = ctl ? (base + 1) : base; | ||
94 | int val = mmapped ? readb((void *)address) : inb(address); | ||
95 | |||
96 | pr_debug("i2c-elektor: Read 0x%X 0x%02X\n", address, val); | ||
97 | |||
98 | return (val); | ||
99 | } | ||
100 | |||
101 | static int pcf_isa_getown(void *data) | ||
102 | { | ||
103 | return (own); | ||
104 | } | ||
105 | |||
106 | |||
107 | static int pcf_isa_getclock(void *data) | ||
108 | { | ||
109 | return (clock); | ||
110 | } | ||
111 | |||
112 | static void pcf_isa_waitforpin(void) { | ||
113 | DEFINE_WAIT(wait); | ||
114 | int timeout = 2; | ||
115 | unsigned long flags; | ||
116 | |||
117 | if (irq > 0) { | ||
118 | spin_lock_irqsave(&lock, flags); | ||
119 | if (pcf_pending == 0) { | ||
120 | spin_unlock_irqrestore(&lock, flags); | ||
121 | prepare_to_wait(&pcf_wait, &wait, TASK_INTERRUPTIBLE); | ||
122 | if (schedule_timeout(timeout*HZ)) { | ||
123 | spin_lock_irqsave(&lock, flags); | ||
124 | if (pcf_pending == 1) { | ||
125 | pcf_pending = 0; | ||
126 | } | ||
127 | spin_unlock_irqrestore(&lock, flags); | ||
128 | } | ||
129 | finish_wait(&pcf_wait, &wait); | ||
130 | } else { | ||
131 | pcf_pending = 0; | ||
132 | spin_unlock_irqrestore(&lock, flags); | ||
133 | } | ||
134 | } else { | ||
135 | udelay(100); | ||
136 | } | ||
137 | } | ||
138 | |||
139 | |||
140 | static irqreturn_t pcf_isa_handler(int this_irq, void *dev_id, struct pt_regs *regs) { | ||
141 | spin_lock(&lock); | ||
142 | pcf_pending = 1; | ||
143 | spin_unlock(&lock); | ||
144 | wake_up_interruptible(&pcf_wait); | ||
145 | return IRQ_HANDLED; | ||
146 | } | ||
147 | |||
148 | |||
149 | static int pcf_isa_init(void) | ||
150 | { | ||
151 | spin_lock_init(&lock); | ||
152 | if (!mmapped) { | ||
153 | if (!request_region(base, 2, "i2c (isa bus adapter)")) { | ||
154 | printk(KERN_ERR | ||
155 | "i2c-elektor: requested I/O region (0x%X:2) " | ||
156 | "is in use.\n", base); | ||
157 | return -ENODEV; | ||
158 | } | ||
159 | } | ||
160 | if (irq > 0) { | ||
161 | if (request_irq(irq, pcf_isa_handler, 0, "PCF8584", NULL) < 0) { | ||
162 | printk(KERN_ERR "i2c-elektor: Request irq%d failed\n", irq); | ||
163 | irq = 0; | ||
164 | } else | ||
165 | enable_irq(irq); | ||
166 | } | ||
167 | return 0; | ||
168 | } | ||
169 | |||
170 | /* ------------------------------------------------------------------------ | ||
171 | * Encapsulate the above functions in the correct operations structure. | ||
172 | * This is only done when more than one hardware adapter is supported. | ||
173 | */ | ||
174 | static struct i2c_algo_pcf_data pcf_isa_data = { | ||
175 | .setpcf = pcf_isa_setbyte, | ||
176 | .getpcf = pcf_isa_getbyte, | ||
177 | .getown = pcf_isa_getown, | ||
178 | .getclock = pcf_isa_getclock, | ||
179 | .waitforpin = pcf_isa_waitforpin, | ||
180 | .udelay = 10, | ||
181 | .mdelay = 10, | ||
182 | .timeout = 100, | ||
183 | }; | ||
184 | |||
185 | static struct i2c_adapter pcf_isa_ops = { | ||
186 | .owner = THIS_MODULE, | ||
187 | .class = I2C_CLASS_HWMON, | ||
188 | .id = I2C_HW_P_ELEK, | ||
189 | .algo_data = &pcf_isa_data, | ||
190 | .name = "PCF8584 ISA adapter", | ||
191 | }; | ||
192 | |||
193 | static int __init i2c_pcfisa_init(void) | ||
194 | { | ||
195 | #ifdef __alpha__ | ||
196 | /* check to see we have memory mapped PCF8584 connected to the | ||
197 | Cypress cy82c693 PCI-ISA bridge as on UP2000 board */ | ||
198 | if (base == 0) { | ||
199 | struct pci_dev *cy693_dev; | ||
200 | |||
201 | cy693_dev = pci_get_device(PCI_VENDOR_ID_CONTAQ, | ||
202 | PCI_DEVICE_ID_CONTAQ_82C693, NULL); | ||
203 | if (cy693_dev) { | ||
204 | char config; | ||
205 | /* yeap, we've found cypress, let's check config */ | ||
206 | if (!pci_read_config_byte(cy693_dev, 0x47, &config)) { | ||
207 | |||
208 | pr_debug("i2c-elektor: found cy82c693, config register 0x47 = 0x%02x.\n", config); | ||
209 | |||
210 | /* UP2000 board has this register set to 0xe1, | ||
211 | but the most significant bit as seems can be | ||
212 | reset during the proper initialisation | ||
213 | sequence if guys from API decides to do that | ||
214 | (so, we can even enable Tsunami Pchip | ||
215 | window for the upper 1 Gb) */ | ||
216 | |||
217 | /* so just check for ROMCS at 0xe0000, | ||
218 | ROMCS enabled for writes | ||
219 | and external XD Bus buffer in use. */ | ||
220 | if ((config & 0x7f) == 0x61) { | ||
221 | /* seems to be UP2000 like board */ | ||
222 | base = 0xe0000; | ||
223 | /* I don't know why we need to | ||
224 | write twice */ | ||
225 | mmapped = 2; | ||
226 | /* UP2000 drives ISA with | ||
227 | 8.25 MHz (PCI/4) clock | ||
228 | (this can be read from cypress) */ | ||
229 | clock = I2C_PCF_CLK | I2C_PCF_TRNS90; | ||
230 | printk(KERN_INFO "i2c-elektor: found API UP2000 like board, will probe PCF8584 later.\n"); | ||
231 | } | ||
232 | } | ||
233 | pci_dev_put(cy693_dev); | ||
234 | } | ||
235 | } | ||
236 | #endif | ||
237 | |||
238 | /* sanity checks for mmapped I/O */ | ||
239 | if (mmapped && base < 0xc8000) { | ||
240 | printk(KERN_ERR "i2c-elektor: incorrect base address (0x%0X) specified for mmapped I/O.\n", base); | ||
241 | return -ENODEV; | ||
242 | } | ||
243 | |||
244 | printk(KERN_INFO "i2c-elektor: i2c pcf8584-isa adapter driver\n"); | ||
245 | |||
246 | if (base == 0) { | ||
247 | base = DEFAULT_BASE; | ||
248 | } | ||
249 | |||
250 | init_waitqueue_head(&pcf_wait); | ||
251 | if (pcf_isa_init()) | ||
252 | return -ENODEV; | ||
253 | if (i2c_pcf_add_bus(&pcf_isa_ops) < 0) | ||
254 | goto fail; | ||
255 | |||
256 | printk(KERN_ERR "i2c-elektor: found device at %#x.\n", base); | ||
257 | |||
258 | return 0; | ||
259 | |||
260 | fail: | ||
261 | if (irq > 0) { | ||
262 | disable_irq(irq); | ||
263 | free_irq(irq, NULL); | ||
264 | } | ||
265 | |||
266 | if (!mmapped) | ||
267 | release_region(base , 2); | ||
268 | return -ENODEV; | ||
269 | } | ||
270 | |||
271 | static void i2c_pcfisa_exit(void) | ||
272 | { | ||
273 | i2c_pcf_del_bus(&pcf_isa_ops); | ||
274 | |||
275 | if (irq > 0) { | ||
276 | disable_irq(irq); | ||
277 | free_irq(irq, NULL); | ||
278 | } | ||
279 | |||
280 | if (!mmapped) | ||
281 | release_region(base , 2); | ||
282 | } | ||
283 | |||
284 | MODULE_AUTHOR("Hans Berglund <hb@spacetec.no>"); | ||
285 | MODULE_DESCRIPTION("I2C-Bus adapter routines for PCF8584 ISA bus adapter"); | ||
286 | MODULE_LICENSE("GPL"); | ||
287 | |||
288 | module_param(base, int, 0); | ||
289 | module_param(irq, int, 0); | ||
290 | module_param(clock, int, 0); | ||
291 | module_param(own, int, 0); | ||
292 | module_param(mmapped, int, 0); | ||
293 | |||
294 | module_init(i2c_pcfisa_init); | ||
295 | module_exit(i2c_pcfisa_exit); | ||