blob: 75b4b442a63673ac0e9a357fef3a7532e8a36eba [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/* ------------------------------------------------------------------------- */
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
Linus Torvalds1da177e2005-04-16 15:20:36 -070028#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
48static int base;
Stig Telfer3634ff62005-10-08 00:21:48 +020049static u8 __iomem *base_iomem;
50
Linus Torvalds1da177e2005-04-16 15:20:36 -070051static int irq;
52static int clock = 0x1c;
53static int own = 0x55;
54static int mmapped;
55
56/* vdovikin: removed static struct i2c_pcf_isa gpi; code -
57 this module in real supports only one device, due to missing arguments
58 in some functions, called from the algo-pcf module. Sometimes it's
59 need to be rewriten - but for now just remove this for simpler reading */
60
61static wait_queue_head_t pcf_wait;
62static int pcf_pending;
63static spinlock_t lock;
64
65/* ----- local functions ---------------------------------------------- */
66
67static void pcf_isa_setbyte(void *data, int ctl, int val)
68{
Stig Telfer3634ff62005-10-08 00:21:48 +020069 u8 __iomem *address = ctl ? (base_iomem + 1) : base_iomem;
Linus Torvalds1da177e2005-04-16 15:20:36 -070070
71 /* enable irq if any specified for serial operation */
72 if (ctl && irq && (val & I2C_PCF_ESO)) {
73 val |= I2C_PCF_ENI;
74 }
75
Stig Telfer3634ff62005-10-08 00:21:48 +020076 pr_debug("i2c-elektor: Write %p 0x%02X\n", address, val);
77 iowrite8(val, address);
78#ifdef __alpha__
79 /* API UP2000 needs some hardware fudging to make the write stick */
80 iowrite8(val, address);
81#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -070082}
83
84static int pcf_isa_getbyte(void *data, int ctl)
85{
Stig Telfer3634ff62005-10-08 00:21:48 +020086 u8 __iomem *address = ctl ? (base_iomem + 1) : base_iomem;
87 int val = ioread8(address);
Linus Torvalds1da177e2005-04-16 15:20:36 -070088
Stig Telfer3634ff62005-10-08 00:21:48 +020089 pr_debug("i2c-elektor: Read %p 0x%02X\n", address, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -070090 return (val);
91}
92
93static int pcf_isa_getown(void *data)
94{
95 return (own);
96}
97
98
99static int pcf_isa_getclock(void *data)
100{
101 return (clock);
102}
103
104static void pcf_isa_waitforpin(void) {
105 DEFINE_WAIT(wait);
106 int timeout = 2;
107 unsigned long flags;
108
109 if (irq > 0) {
110 spin_lock_irqsave(&lock, flags);
111 if (pcf_pending == 0) {
112 spin_unlock_irqrestore(&lock, flags);
113 prepare_to_wait(&pcf_wait, &wait, TASK_INTERRUPTIBLE);
114 if (schedule_timeout(timeout*HZ)) {
115 spin_lock_irqsave(&lock, flags);
116 if (pcf_pending == 1) {
117 pcf_pending = 0;
118 }
119 spin_unlock_irqrestore(&lock, flags);
120 }
121 finish_wait(&pcf_wait, &wait);
122 } else {
123 pcf_pending = 0;
124 spin_unlock_irqrestore(&lock, flags);
125 }
126 } else {
127 udelay(100);
128 }
129}
130
131
132static irqreturn_t pcf_isa_handler(int this_irq, void *dev_id, struct pt_regs *regs) {
133 spin_lock(&lock);
134 pcf_pending = 1;
135 spin_unlock(&lock);
136 wake_up_interruptible(&pcf_wait);
137 return IRQ_HANDLED;
138}
139
140
141static int pcf_isa_init(void)
142{
143 spin_lock_init(&lock);
144 if (!mmapped) {
145 if (!request_region(base, 2, "i2c (isa bus adapter)")) {
146 printk(KERN_ERR
147 "i2c-elektor: requested I/O region (0x%X:2) "
148 "is in use.\n", base);
149 return -ENODEV;
150 }
Stig Telfer3634ff62005-10-08 00:21:48 +0200151 base_iomem = ioport_map(base, 2);
152 if (!base_iomem) {
153 printk(KERN_ERR "i2c-elektor: remap of I/O region "
154 "%#x failed\n", base);
155 release_region(base, 2);
156 return -ENODEV;
157 }
158 } else {
159 if (!request_mem_region(base, 2, "i2c-elektor")) {
160 printk(KERN_ERR "i2c-elektor: requested memory region "
161 "(%#x:2) is in use\n", base);
162 return -ENODEV;
163 }
164 base_iomem = ioremap(base, 2);
165 if (base_iomem == NULL) {
166 printk(KERN_ERR "i2c-elektor: remap of memory region "
167 "%#x failed\n", base);
168 release_mem_region(base, 2);
169 return -ENODEV;
170 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700171 }
Stig Telfer3634ff62005-10-08 00:21:48 +0200172 pr_debug("i2c-elektor: registers %#x remapped to %p\n", base,
173 base_iomem);
174
Linus Torvalds1da177e2005-04-16 15:20:36 -0700175 if (irq > 0) {
176 if (request_irq(irq, pcf_isa_handler, 0, "PCF8584", NULL) < 0) {
177 printk(KERN_ERR "i2c-elektor: Request irq%d failed\n", irq);
178 irq = 0;
179 } else
180 enable_irq(irq);
181 }
182 return 0;
183}
184
185/* ------------------------------------------------------------------------
186 * Encapsulate the above functions in the correct operations structure.
187 * This is only done when more than one hardware adapter is supported.
188 */
189static struct i2c_algo_pcf_data pcf_isa_data = {
190 .setpcf = pcf_isa_setbyte,
191 .getpcf = pcf_isa_getbyte,
192 .getown = pcf_isa_getown,
193 .getclock = pcf_isa_getclock,
194 .waitforpin = pcf_isa_waitforpin,
195 .udelay = 10,
196 .mdelay = 10,
197 .timeout = 100,
198};
199
200static struct i2c_adapter pcf_isa_ops = {
201 .owner = THIS_MODULE,
202 .class = I2C_CLASS_HWMON,
203 .id = I2C_HW_P_ELEK,
204 .algo_data = &pcf_isa_data,
205 .name = "PCF8584 ISA adapter",
206};
207
208static int __init i2c_pcfisa_init(void)
209{
210#ifdef __alpha__
211 /* check to see we have memory mapped PCF8584 connected to the
212 Cypress cy82c693 PCI-ISA bridge as on UP2000 board */
213 if (base == 0) {
214 struct pci_dev *cy693_dev;
215
216 cy693_dev = pci_get_device(PCI_VENDOR_ID_CONTAQ,
217 PCI_DEVICE_ID_CONTAQ_82C693, NULL);
218 if (cy693_dev) {
Stig Telfer3634ff62005-10-08 00:21:48 +0200219 unsigned char config;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700220 /* yeap, we've found cypress, let's check config */
221 if (!pci_read_config_byte(cy693_dev, 0x47, &config)) {
222
223 pr_debug("i2c-elektor: found cy82c693, config register 0x47 = 0x%02x.\n", config);
224
225 /* UP2000 board has this register set to 0xe1,
226 but the most significant bit as seems can be
227 reset during the proper initialisation
228 sequence if guys from API decides to do that
229 (so, we can even enable Tsunami Pchip
230 window for the upper 1 Gb) */
231
232 /* so just check for ROMCS at 0xe0000,
233 ROMCS enabled for writes
234 and external XD Bus buffer in use. */
235 if ((config & 0x7f) == 0x61) {
236 /* seems to be UP2000 like board */
237 base = 0xe0000;
Stig Telfer3634ff62005-10-08 00:21:48 +0200238 mmapped = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700239 /* UP2000 drives ISA with
240 8.25 MHz (PCI/4) clock
241 (this can be read from cypress) */
242 clock = I2C_PCF_CLK | I2C_PCF_TRNS90;
243 printk(KERN_INFO "i2c-elektor: found API UP2000 like board, will probe PCF8584 later.\n");
244 }
245 }
246 pci_dev_put(cy693_dev);
247 }
248 }
249#endif
250
251 /* sanity checks for mmapped I/O */
252 if (mmapped && base < 0xc8000) {
253 printk(KERN_ERR "i2c-elektor: incorrect base address (0x%0X) specified for mmapped I/O.\n", base);
254 return -ENODEV;
255 }
256
257 printk(KERN_INFO "i2c-elektor: i2c pcf8584-isa adapter driver\n");
258
259 if (base == 0) {
260 base = DEFAULT_BASE;
261 }
262
263 init_waitqueue_head(&pcf_wait);
264 if (pcf_isa_init())
265 return -ENODEV;
266 if (i2c_pcf_add_bus(&pcf_isa_ops) < 0)
267 goto fail;
268
269 printk(KERN_ERR "i2c-elektor: found device at %#x.\n", base);
270
271 return 0;
272
273 fail:
274 if (irq > 0) {
275 disable_irq(irq);
276 free_irq(irq, NULL);
277 }
278
Stig Telfer3634ff62005-10-08 00:21:48 +0200279 if (!mmapped) {
280 ioport_unmap(base_iomem);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281 release_region(base , 2);
Stig Telfer3634ff62005-10-08 00:21:48 +0200282 } else {
283 iounmap(base_iomem);
284 release_mem_region(base, 2);
285 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700286 return -ENODEV;
287}
288
289static void i2c_pcfisa_exit(void)
290{
291 i2c_pcf_del_bus(&pcf_isa_ops);
292
293 if (irq > 0) {
294 disable_irq(irq);
295 free_irq(irq, NULL);
296 }
297
Stig Telfer3634ff62005-10-08 00:21:48 +0200298 if (!mmapped) {
299 ioport_unmap(base_iomem);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700300 release_region(base , 2);
Stig Telfer3634ff62005-10-08 00:21:48 +0200301 } else {
302 iounmap(base_iomem);
303 release_mem_region(base, 2);
304 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700305}
306
307MODULE_AUTHOR("Hans Berglund <hb@spacetec.no>");
308MODULE_DESCRIPTION("I2C-Bus adapter routines for PCF8584 ISA bus adapter");
309MODULE_LICENSE("GPL");
310
311module_param(base, int, 0);
312module_param(irq, int, 0);
313module_param(clock, int, 0);
314module_param(own, int, 0);
315module_param(mmapped, int, 0);
316
317module_init(i2c_pcfisa_init);
318module_exit(i2c_pcfisa_exit);