| 1 | /* ------------------------------------------------------------------------- */ |
|---|
| 2 | /* pcf-isa.c i2c-hw access for PCF8584 style isa bus adapters */ |
|---|
| 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 | #include <linux/kernel.h> |
|---|
| 23 | #include <linux/ioport.h> |
|---|
| 24 | #include <linux/module.h> |
|---|
| 25 | #include <linux/delay.h> |
|---|
| 26 | #include <linux/malloc.h> |
|---|
| 27 | #include <linux/version.h> |
|---|
| 28 | #if LINUX_VERSION_CODE >= 0x020135 |
|---|
| 29 | #include <linux/init.h> |
|---|
| 30 | #else |
|---|
| 31 | #define __init |
|---|
| 32 | #endif |
|---|
| 33 | #include <asm/irq.h> |
|---|
| 34 | #include <asm/io.h> |
|---|
| 35 | |
|---|
| 36 | #include "i2c.h" |
|---|
| 37 | #include "i2c-algo-pcf.h" |
|---|
| 38 | #include "i2c-elektor.h" |
|---|
| 39 | #include "pcf8584.h" |
|---|
| 40 | |
|---|
| 41 | #define DEFAULT_BASE 0x300 |
|---|
| 42 | #define DEFAULT_IRQ 0 |
|---|
| 43 | #define DEFAULT_CLOCK 0x1c |
|---|
| 44 | #define DEFAULT_OWN 0x55 |
|---|
| 45 | |
|---|
| 46 | static int base = 0; |
|---|
| 47 | static int irq = 0; |
|---|
| 48 | static int clock = 0; |
|---|
| 49 | static int own = 0; |
|---|
| 50 | static int i2c_debug=0; |
|---|
| 51 | static struct pcf_isa gpi; |
|---|
| 52 | static struct wait_queue *pcf_wait = NULL; |
|---|
| 53 | static int pcf_pending; |
|---|
| 54 | |
|---|
| 55 | |
|---|
| 56 | /* ----- global defines ----------------------------------------------- */ |
|---|
| 57 | #define DEB(x) if (i2c_debug>=1) x |
|---|
| 58 | #define DEB2(x) if (i2c_debug>=2) x |
|---|
| 59 | #define DEB3(x) if (i2c_debug>=3) x |
|---|
| 60 | #define DEBE(x) x /* error messages */ |
|---|
| 61 | |
|---|
| 62 | |
|---|
| 63 | /* --- Convenience defines for the i2c port: */ |
|---|
| 64 | #define BASE ((struct pcf_isa *)(data))->pi_base |
|---|
| 65 | #define DATA BASE /* Adapter data port */ |
|---|
| 66 | #define CTRL (BASE+1) /* Adapter control port */ |
|---|
| 67 | |
|---|
| 68 | /* ----- local functions ---------------------------------------------- */ |
|---|
| 69 | |
|---|
| 70 | static void pcf_isa_setbyte(void *data, int ctl, int val) |
|---|
| 71 | { |
|---|
| 72 | if (ctl) { |
|---|
| 73 | if (gpi.pi_irq > 0) { |
|---|
| 74 | DEB3(printk("Write control 0x%x\n", val|PCF_ENI)); |
|---|
| 75 | outb(val | PCF_ENI, CTRL); |
|---|
| 76 | } else { |
|---|
| 77 | DEB3(printk("Write control 0x%x\n", val)); |
|---|
| 78 | outb(val, CTRL); |
|---|
| 79 | } |
|---|
| 80 | } else { |
|---|
| 81 | DEB3(printk("Write data 0x%x\n", val)); |
|---|
| 82 | outb(val, DATA); |
|---|
| 83 | } |
|---|
| 84 | } |
|---|
| 85 | |
|---|
| 86 | static int pcf_isa_getbyte(void *data, int ctl) |
|---|
| 87 | { |
|---|
| 88 | int val; |
|---|
| 89 | |
|---|
| 90 | if (ctl) { |
|---|
| 91 | val = inb(CTRL); |
|---|
| 92 | DEB3(printk("Read control 0x%x\n", val)); |
|---|
| 93 | } else { |
|---|
| 94 | val = inb(DATA); |
|---|
| 95 | DEB3(printk("Read data 0x%x\n", val)); |
|---|
| 96 | } |
|---|
| 97 | return (val); |
|---|
| 98 | } |
|---|
| 99 | |
|---|
| 100 | static int pcf_isa_getown(void *data) |
|---|
| 101 | { |
|---|
| 102 | return (gpi.pi_own); |
|---|
| 103 | } |
|---|
| 104 | |
|---|
| 105 | |
|---|
| 106 | static int pcf_isa_getclock(void *data) |
|---|
| 107 | { |
|---|
| 108 | return (gpi.pi_clock); |
|---|
| 109 | } |
|---|
| 110 | |
|---|
| 111 | |
|---|
| 112 | |
|---|
| 113 | #if LINUX_VERSION_CODE < 0x02017f |
|---|
| 114 | static void schedule_timeout(int j) |
|---|
| 115 | { |
|---|
| 116 | current->state = TASK_INTERRUPTIBLE; |
|---|
| 117 | current->timeout = jiffies + j; |
|---|
| 118 | schedule(); |
|---|
| 119 | } |
|---|
| 120 | #endif |
|---|
| 121 | |
|---|
| 122 | #if 0 |
|---|
| 123 | static void pcf_isa_sleep(unsigned long timeout) |
|---|
| 124 | { |
|---|
| 125 | schedule_timeout( timeout * HZ); |
|---|
| 126 | } |
|---|
| 127 | #endif |
|---|
| 128 | |
|---|
| 129 | |
|---|
| 130 | static void pcf_isa_waitforpin(void) { |
|---|
| 131 | |
|---|
| 132 | int timeout = 2; |
|---|
| 133 | |
|---|
| 134 | if (gpi.pi_irq > 0) { |
|---|
| 135 | cli(); |
|---|
| 136 | if (pcf_pending == 0) { |
|---|
| 137 | #if LINUX_VERSION_CODE < 0x02017f |
|---|
| 138 | current->timeout = jiffies + timeout * HZ; |
|---|
| 139 | interruptible_sleep_on(&pcf_wait); |
|---|
| 140 | #else |
|---|
| 141 | interruptible_sleep_on_timeout(&pcf_wait, timeout*HZ ); |
|---|
| 142 | #endif |
|---|
| 143 | } |
|---|
| 144 | else |
|---|
| 145 | pcf_pending = 0; |
|---|
| 146 | sti(); |
|---|
| 147 | #if LINUX_VERSION_CODE < 0x02017f |
|---|
| 148 | current->timeout = 0; |
|---|
| 149 | #endif |
|---|
| 150 | } |
|---|
| 151 | else { |
|---|
| 152 | udelay(100); |
|---|
| 153 | } |
|---|
| 154 | } |
|---|
| 155 | |
|---|
| 156 | |
|---|
| 157 | static void pcf_isa_handler(int this_irq, void *dev_id, struct pt_regs *regs) { |
|---|
| 158 | |
|---|
| 159 | pcf_pending = 1; |
|---|
| 160 | wake_up_interruptible(&pcf_wait); |
|---|
| 161 | } |
|---|
| 162 | |
|---|
| 163 | |
|---|
| 164 | static int pcf_isa_init(void) |
|---|
| 165 | { |
|---|
| 166 | if (check_region(gpi.pi_base, 2) < 0 ) { |
|---|
| 167 | return -ENODEV; |
|---|
| 168 | } else { |
|---|
| 169 | request_region(gpi.pi_base, 2, "i2c (isa bus adapter)"); |
|---|
| 170 | } |
|---|
| 171 | if (gpi.pi_irq > 0) { |
|---|
| 172 | if (request_irq(gpi.pi_irq, pcf_isa_handler, 0, "PCF8584", 0) < 0) { |
|---|
| 173 | printk("Request irq%d failed\n", gpi.pi_irq); |
|---|
| 174 | gpi.pi_irq = 0; |
|---|
| 175 | } |
|---|
| 176 | else |
|---|
| 177 | enable_irq(gpi.pi_irq); |
|---|
| 178 | } |
|---|
| 179 | return 0; |
|---|
| 180 | } |
|---|
| 181 | |
|---|
| 182 | |
|---|
| 183 | static void pcf_isa_exit(void) |
|---|
| 184 | { |
|---|
| 185 | if (gpi.pi_irq > 0) { |
|---|
| 186 | disable_irq(gpi.pi_irq); |
|---|
| 187 | free_irq(gpi.pi_irq, 0); |
|---|
| 188 | } |
|---|
| 189 | release_region(gpi.pi_base , 2); |
|---|
| 190 | } |
|---|
| 191 | |
|---|
| 192 | |
|---|
| 193 | static int pcf_isa_reg(struct i2c_client *client) |
|---|
| 194 | { |
|---|
| 195 | return 0; |
|---|
| 196 | } |
|---|
| 197 | |
|---|
| 198 | |
|---|
| 199 | static int pcf_isa_unreg(struct i2c_client *client) |
|---|
| 200 | { |
|---|
| 201 | return 0; |
|---|
| 202 | } |
|---|
| 203 | |
|---|
| 204 | static void pcf_isa_inc_use(struct pcf_adapter *adap) |
|---|
| 205 | { |
|---|
| 206 | #ifdef MODULE |
|---|
| 207 | MOD_INC_USE_COUNT; |
|---|
| 208 | #endif |
|---|
| 209 | } |
|---|
| 210 | |
|---|
| 211 | static void pcf_isa_dec_use(struct pcf_adapter *adap) |
|---|
| 212 | { |
|---|
| 213 | #ifdef MODULE |
|---|
| 214 | MOD_DEC_USE_COUNT; |
|---|
| 215 | #endif |
|---|
| 216 | } |
|---|
| 217 | |
|---|
| 218 | |
|---|
| 219 | /* ------------------------------------------------------------------------ |
|---|
| 220 | * Encapsulate the above functions in the correct operations structure. |
|---|
| 221 | * This is only done when more than one hardware adapter is supported. |
|---|
| 222 | */ |
|---|
| 223 | struct pcf_adapter pcf_isa_ops = { |
|---|
| 224 | "PCF8584 ISA adapter", |
|---|
| 225 | HW_P_ELEK, |
|---|
| 226 | NULL, |
|---|
| 227 | pcf_isa_setbyte, |
|---|
| 228 | pcf_isa_getbyte, |
|---|
| 229 | pcf_isa_getown, |
|---|
| 230 | pcf_isa_getclock, |
|---|
| 231 | pcf_isa_waitforpin, |
|---|
| 232 | pcf_isa_reg, |
|---|
| 233 | pcf_isa_unreg, |
|---|
| 234 | pcf_isa_inc_use, |
|---|
| 235 | pcf_isa_dec_use, |
|---|
| 236 | 80, 80, 100, /* waits, timeout */ |
|---|
| 237 | }; |
|---|
| 238 | |
|---|
| 239 | int __init pcfisa_init(void) |
|---|
| 240 | { |
|---|
| 241 | |
|---|
| 242 | struct pcf_isa *pisa = &gpi; |
|---|
| 243 | |
|---|
| 244 | if (base == 0) |
|---|
| 245 | pisa->pi_base = DEFAULT_BASE; |
|---|
| 246 | else |
|---|
| 247 | pisa->pi_base = base; |
|---|
| 248 | |
|---|
| 249 | if (irq == 0) |
|---|
| 250 | pisa->pi_irq = DEFAULT_IRQ; |
|---|
| 251 | else |
|---|
| 252 | pisa->pi_irq = irq; |
|---|
| 253 | |
|---|
| 254 | if (clock == 0) |
|---|
| 255 | pisa->pi_clock = DEFAULT_CLOCK; |
|---|
| 256 | else |
|---|
| 257 | pisa->pi_clock = clock; |
|---|
| 258 | |
|---|
| 259 | if (own == 0) |
|---|
| 260 | pisa->pi_own = DEFAULT_OWN; |
|---|
| 261 | else |
|---|
| 262 | pisa->pi_own = own; |
|---|
| 263 | |
|---|
| 264 | pcf_isa_ops.data = (void *)pisa; |
|---|
| 265 | if (pcf_isa_init() == 0) { |
|---|
| 266 | i2c_pcf_add_bus(&pcf_isa_ops); |
|---|
| 267 | } else { |
|---|
| 268 | return -ENODEV; |
|---|
| 269 | } |
|---|
| 270 | printk("pcf_isa: found device at %#x.\n", pisa->pi_base); |
|---|
| 271 | return 0; |
|---|
| 272 | } |
|---|
| 273 | |
|---|
| 274 | |
|---|
| 275 | #ifdef MODULE |
|---|
| 276 | MODULE_AUTHOR("Hans Berglund <hb@spacetec.no>"); |
|---|
| 277 | MODULE_DESCRIPTION("I2C-Bus adapter routines for PCF8584 ISA bus adapter"); |
|---|
| 278 | |
|---|
| 279 | MODULE_PARM(base, "i"); |
|---|
| 280 | MODULE_PARM(irq, "i"); |
|---|
| 281 | MODULE_PARM(clock, "i"); |
|---|
| 282 | MODULE_PARM(own, "i"); |
|---|
| 283 | |
|---|
| 284 | EXPORT_NO_SYMBOLS; |
|---|
| 285 | |
|---|
| 286 | int init_module(void) |
|---|
| 287 | { |
|---|
| 288 | return pcfisa_init(); |
|---|
| 289 | } |
|---|
| 290 | |
|---|
| 291 | void cleanup_module(void) |
|---|
| 292 | { |
|---|
| 293 | i2c_pcf_del_bus(&pcf_isa_ops); |
|---|
| 294 | pcf_isa_exit(); |
|---|
| 295 | } |
|---|
| 296 | |
|---|
| 297 | #endif |
|---|
| 298 | |
|---|
| 299 | |
|---|