Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
420 | giacomo | 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/kernel.h> |
||
29 | #include <linux/ioport.h> |
||
30 | #include <linux/module.h> |
||
31 | #include <linux/delay.h> |
||
32 | #include <linux/slab.h> |
||
33 | #include <linux/init.h> |
||
34 | #include <linux/interrupt.h> |
||
35 | #include <linux/pci.h> |
||
36 | #include <linux/wait.h> |
||
37 | |||
38 | #include <linux/i2c.h> |
||
39 | #include <linux/i2c-algo-pcf.h> |
||
40 | |||
41 | #include <asm/io.h> |
||
42 | #include <asm/irq.h> |
||
43 | |||
44 | #include "../algos/i2c-algo-pcf.h" |
||
45 | |||
46 | #define DEFAULT_BASE 0x330 |
||
47 | |||
48 | static int base; |
||
49 | static int irq; |
||
50 | static int clock = 0x1c; |
||
51 | static int own = 0x55; |
||
52 | static int mmapped; |
||
53 | static int i2c_debug; |
||
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 | |||
63 | /* ----- global defines ----------------------------------------------- */ |
||
64 | #define DEB(x) if (i2c_debug>=1) x |
||
65 | #define DEB2(x) if (i2c_debug>=2) x |
||
66 | #define DEB3(x) if (i2c_debug>=3) x |
||
67 | #define DEBE(x) x /* error messages */ |
||
68 | |||
69 | /* ----- local functions ---------------------------------------------- */ |
||
70 | |||
71 | static void pcf_isa_setbyte(void *data, int ctl, int val) |
||
72 | { |
||
73 | int address = ctl ? (base + 1) : base; |
||
74 | |||
75 | /* enable irq if any specified for serial operation */ |
||
76 | if (ctl && irq && (val & I2C_PCF_ESO)) { |
||
77 | val |= I2C_PCF_ENI; |
||
78 | } |
||
79 | |||
80 | DEB3(printk(KERN_DEBUG "i2c-elektor: Write 0x%X 0x%02X\n", address, val & 255)); |
||
81 | |||
82 | switch (mmapped) { |
||
83 | case 0: /* regular I/O */ |
||
84 | outb(val, address); |
||
85 | break; |
||
86 | case 2: /* double mapped I/O needed for UP2000 board, |
||
87 | I don't know why this... */ |
||
88 | writeb(val, address); |
||
89 | /* fall */ |
||
90 | case 1: /* memory mapped I/O */ |
||
91 | writeb(val, address); |
||
92 | break; |
||
93 | } |
||
94 | } |
||
95 | |||
96 | static int pcf_isa_getbyte(void *data, int ctl) |
||
97 | { |
||
98 | int address = ctl ? (base + 1) : base; |
||
99 | int val = mmapped ? readb(address) : inb(address); |
||
100 | |||
101 | DEB3(printk(KERN_DEBUG "i2c-elektor: Read 0x%X 0x%02X\n", address, val)); |
||
102 | |||
103 | return (val); |
||
104 | } |
||
105 | |||
106 | static int pcf_isa_getown(void *data) |
||
107 | { |
||
108 | return (own); |
||
109 | } |
||
110 | |||
111 | |||
112 | static int pcf_isa_getclock(void *data) |
||
113 | { |
||
114 | return (clock); |
||
115 | } |
||
116 | |||
117 | static void pcf_isa_waitforpin(void) { |
||
118 | |||
119 | int timeout = 2; |
||
120 | |||
121 | if (irq > 0) { |
||
122 | cli(); |
||
123 | if (pcf_pending == 0) { |
||
124 | interruptible_sleep_on_timeout(&pcf_wait, timeout*HZ ); |
||
125 | } else |
||
126 | pcf_pending = 0; |
||
127 | sti(); |
||
128 | } else { |
||
129 | udelay(100); |
||
130 | } |
||
131 | } |
||
132 | |||
133 | |||
134 | static irqreturn_t pcf_isa_handler(int this_irq, void *dev_id, struct pt_regs *regs) { |
||
135 | pcf_pending = 1; |
||
136 | wake_up_interruptible(&pcf_wait); |
||
137 | return IRQ_HANDLED; |
||
138 | } |
||
139 | |||
140 | |||
141 | static int pcf_isa_init(void) |
||
142 | { |
||
143 | if (!mmapped) { |
||
144 | if (!request_region(base, 2, "i2c (isa bus adapter)")) { |
||
145 | printk(KERN_ERR |
||
146 | "i2c-elektor: requested I/O region (0x%X:2) " |
||
147 | "is in use.\n", base); |
||
148 | return -ENODEV; |
||
149 | } |
||
150 | } |
||
151 | if (irq > 0) { |
||
152 | if (request_irq(irq, pcf_isa_handler, 0, "PCF8584", 0) < 0) { |
||
153 | printk(KERN_ERR "i2c-elektor: Request irq%d failed\n", irq); |
||
154 | irq = 0; |
||
155 | } else |
||
156 | enable_irq(irq); |
||
157 | } |
||
158 | return 0; |
||
159 | } |
||
160 | |||
161 | /* ------------------------------------------------------------------------ |
||
162 | * Encapsulate the above functions in the correct operations structure. |
||
163 | * This is only done when more than one hardware adapter is supported. |
||
164 | */ |
||
165 | static struct i2c_algo_pcf_data pcf_isa_data = { |
||
166 | .setpcf = pcf_isa_setbyte, |
||
167 | .getpcf = pcf_isa_getbyte, |
||
168 | .getown = pcf_isa_getown, |
||
169 | .getclock = pcf_isa_getclock, |
||
170 | .waitforpin = pcf_isa_waitforpin, |
||
171 | .udelay = 10, |
||
172 | .mdelay = 10, |
||
173 | .timeout = 100, |
||
174 | }; |
||
175 | |||
176 | static struct i2c_adapter pcf_isa_ops = { |
||
177 | .owner = THIS_MODULE, |
||
178 | .id = I2C_HW_P_ELEK, |
||
179 | .algo_data = &pcf_isa_data, |
||
180 | .name = "PCF8584 ISA adapter", |
||
181 | }; |
||
182 | |||
183 | static int __init i2c_pcfisa_init(void) |
||
184 | { |
||
185 | #ifdef __alpha__ |
||
186 | /* check to see we have memory mapped PCF8584 connected to the |
||
187 | Cypress cy82c693 PCI-ISA bridge as on UP2000 board */ |
||
188 | if (base == 0) { |
||
189 | |||
190 | struct pci_dev *cy693_dev = |
||
191 | pci_find_device(PCI_VENDOR_ID_CONTAQ, |
||
192 | PCI_DEVICE_ID_CONTAQ_82C693, NULL); |
||
193 | |||
194 | if (cy693_dev) { |
||
195 | char config; |
||
196 | /* yeap, we've found cypress, let's check config */ |
||
197 | if (!pci_read_config_byte(cy693_dev, 0x47, &config)) { |
||
198 | |||
199 | DEB3(printk(KERN_DEBUG "i2c-elektor: found cy82c693, config register 0x47 = 0x%02x.\n", config)); |
||
200 | |||
201 | /* UP2000 board has this register set to 0xe1, |
||
202 | but the most significant bit as seems can be |
||
203 | reset during the proper initialisation |
||
204 | sequence if guys from API decides to do that |
||
205 | (so, we can even enable Tsunami Pchip |
||
206 | window for the upper 1 Gb) */ |
||
207 | |||
208 | /* so just check for ROMCS at 0xe0000, |
||
209 | ROMCS enabled for writes |
||
210 | and external XD Bus buffer in use. */ |
||
211 | if ((config & 0x7f) == 0x61) { |
||
212 | /* seems to be UP2000 like board */ |
||
213 | base = 0xe0000; |
||
214 | /* I don't know why we need to |
||
215 | write twice */ |
||
216 | mmapped = 2; |
||
217 | /* UP2000 drives ISA with |
||
218 | 8.25 MHz (PCI/4) clock |
||
219 | (this can be read from cypress) */ |
||
220 | clock = I2C_PCF_CLK | I2C_PCF_TRNS90; |
||
221 | printk(KERN_INFO "i2c-elektor: found API UP2000 like board, will probe PCF8584 later.\n"); |
||
222 | } |
||
223 | } |
||
224 | } |
||
225 | } |
||
226 | #endif |
||
227 | |||
228 | /* sanity checks for mmapped I/O */ |
||
229 | if (mmapped && base < 0xc8000) { |
||
230 | printk(KERN_ERR "i2c-elektor: incorrect base address (0x%0X) specified for mmapped I/O.\n", base); |
||
231 | return -ENODEV; |
||
232 | } |
||
233 | |||
234 | printk(KERN_INFO "i2c-elektor: i2c pcf8584-isa adapter driver\n"); |
||
235 | |||
236 | if (base == 0) { |
||
237 | base = DEFAULT_BASE; |
||
238 | } |
||
239 | |||
240 | init_waitqueue_head(&pcf_wait); |
||
241 | if (pcf_isa_init()) |
||
242 | return -ENODEV; |
||
243 | if (i2c_pcf_add_bus(&pcf_isa_ops) < 0) |
||
244 | goto fail; |
||
245 | |||
246 | printk(KERN_ERR "i2c-elektor: found device at %#x.\n", base); |
||
247 | |||
248 | return 0; |
||
249 | |||
250 | fail: |
||
251 | if (irq > 0) { |
||
252 | disable_irq(irq); |
||
253 | free_irq(irq, 0); |
||
254 | } |
||
255 | |||
256 | if (!mmapped) |
||
257 | release_region(base , 2); |
||
258 | return -ENODEV; |
||
259 | } |
||
260 | |||
261 | static void i2c_pcfisa_exit(void) |
||
262 | { |
||
263 | i2c_pcf_del_bus(&pcf_isa_ops); |
||
264 | |||
265 | if (irq > 0) { |
||
266 | disable_irq(irq); |
||
267 | free_irq(irq, 0); |
||
268 | } |
||
269 | |||
270 | if (!mmapped) |
||
271 | release_region(base , 2); |
||
272 | } |
||
273 | |||
274 | MODULE_AUTHOR("Hans Berglund <hb@spacetec.no>"); |
||
275 | MODULE_DESCRIPTION("I2C-Bus adapter routines for PCF8584 ISA bus adapter"); |
||
276 | MODULE_LICENSE("GPL"); |
||
277 | |||
278 | MODULE_PARM(base, "i"); |
||
279 | MODULE_PARM(irq, "i"); |
||
280 | MODULE_PARM(clock, "i"); |
||
281 | MODULE_PARM(own, "i"); |
||
282 | MODULE_PARM(mmapped, "i"); |
||
283 | MODULE_PARM(i2c_debug, "i"); |
||
284 | |||
285 | module_init(i2c_pcfisa_init); |
||
286 | module_exit(i2c_pcfisa_exit); |