| 1 | /* ------------------------------------------------------------------------- */ |
|---|
| 2 | /* i2c-pcf-epp.c i2c-hw access for PCF8584 style EPP parallel port adapters */ |
|---|
| 3 | /* ------------------------------------------------------------------------- */ |
|---|
| 4 | |
|---|
| 5 | #include <linux/kernel.h> |
|---|
| 6 | #include <linux/ioport.h> |
|---|
| 7 | #include <linux/module.h> |
|---|
| 8 | #include <linux/delay.h> |
|---|
| 9 | #include <linux/slab.h> |
|---|
| 10 | #include <linux/version.h> |
|---|
| 11 | #include <linux/init.h> |
|---|
| 12 | #include <asm/irq.h> |
|---|
| 13 | #include <asm/io.h> |
|---|
| 14 | |
|---|
| 15 | #include "i2c.h" |
|---|
| 16 | #include "i2c-algo-pcf.h" |
|---|
| 17 | #include "i2c-pcf8584.h" |
|---|
| 18 | #include "i2c-pcf-epp.h" |
|---|
| 19 | |
|---|
| 20 | #define DEFAULT_BASE 0x378 |
|---|
| 21 | #define DEFAULT_IRQ 7 |
|---|
| 22 | #define DEFAULT_CLOCK 0x1c |
|---|
| 23 | #define DEFAULT_OWN 0x55 |
|---|
| 24 | |
|---|
| 25 | static int base = 0; |
|---|
| 26 | static int irq = 0; |
|---|
| 27 | static int clock = 0; |
|---|
| 28 | static int own = 0; |
|---|
| 29 | static int i2c_debug=0; |
|---|
| 30 | static struct i2c_pcf_epp gpe; |
|---|
| 31 | #if (LINUX_VERSION_CODE < 0x020301) |
|---|
| 32 | static struct wait_queue *pcf_wait = NULL; |
|---|
| 33 | #else |
|---|
| 34 | static wait_queue_head_t pcf_wait; |
|---|
| 35 | #endif |
|---|
| 36 | static int pcf_pending; |
|---|
| 37 | |
|---|
| 38 | /* ----- global defines ----------------------------------------------- */ |
|---|
| 39 | #define DEB(x) if (i2c_debug>=1) x |
|---|
| 40 | #define DEB2(x) if (i2c_debug>=2) x |
|---|
| 41 | #define DEB3(x) if (i2c_debug>=3) x |
|---|
| 42 | #define DEBE(x) x /* error messages */ |
|---|
| 43 | |
|---|
| 44 | /* --- Convenience defines for the EPP/SPP port: */ |
|---|
| 45 | #define BASE ((struct i2c_pcf_epp *)(data))->pe_base |
|---|
| 46 | #define DATA BASE /* SPP data port */ |
|---|
| 47 | #define STAT (BASE+1) /* SPP status port */ |
|---|
| 48 | #define CTRL (BASE+2) /* SPP control port */ |
|---|
| 49 | #define EADD (BASE+3) /* EPP address port */ |
|---|
| 50 | #define EDAT (BASE+4) /* EPP data port */ |
|---|
| 51 | |
|---|
| 52 | /* ----- local functions ---------------------------------------------- */ |
|---|
| 53 | |
|---|
| 54 | static void pcf_epp_setbyte(void *data, int ctl, int val) |
|---|
| 55 | { |
|---|
| 56 | if (ctl) { |
|---|
| 57 | if (gpe.pe_irq > 0) { |
|---|
| 58 | DEB3(printk("i2c-pcf-epp.o: Write control 0x%x\n", |
|---|
| 59 | val|I2C_PCF_ENI)); |
|---|
| 60 | // set A0 pin HIGH |
|---|
| 61 | outb(inb(CTRL) | PARPORT_CONTROL_INIT, CTRL); |
|---|
| 62 | // DEB3(printk("i2c-pcf-epp.o: CTRL port = 0x%x\n", inb(CTRL))); |
|---|
| 63 | // DEB3(printk("i2c-pcf-epp.o: STAT port = 0x%x\n", inb(STAT))); |
|---|
| 64 | |
|---|
| 65 | // EPP write data cycle |
|---|
| 66 | outb(val | I2C_PCF_ENI, EDAT); |
|---|
| 67 | } else { |
|---|
| 68 | DEB3(printk("i2c-pcf-epp.o: Write control 0x%x\n", val)); |
|---|
| 69 | // set A0 pin HIGH |
|---|
| 70 | outb(inb(CTRL) | PARPORT_CONTROL_INIT, CTRL); |
|---|
| 71 | outb(val, CTRL); |
|---|
| 72 | } |
|---|
| 73 | } else { |
|---|
| 74 | DEB3(printk("i2c-pcf-epp.o: Write data 0x%x\n", val)); |
|---|
| 75 | // set A0 pin LO |
|---|
| 76 | outb(inb(CTRL) & ~PARPORT_CONTROL_INIT, CTRL); |
|---|
| 77 | // DEB3(printk("i2c-pcf-epp.o: CTRL port = 0x%x\n", inb(CTRL))); |
|---|
| 78 | // DEB3(printk("i2c-pcf-epp.o: STAT port = 0x%x\n", inb(STAT))); |
|---|
| 79 | outb(val, EDAT); |
|---|
| 80 | } |
|---|
| 81 | } |
|---|
| 82 | |
|---|
| 83 | static int pcf_epp_getbyte(void *data, int ctl) |
|---|
| 84 | { |
|---|
| 85 | int val; |
|---|
| 86 | |
|---|
| 87 | if (ctl) { |
|---|
| 88 | // set A0 pin HIGH |
|---|
| 89 | outb(inb(CTRL) | PARPORT_CONTROL_INIT, CTRL); |
|---|
| 90 | val = inb(EDAT); |
|---|
| 91 | DEB3(printk("i2c-pcf-epp.o: Read control 0x%x\n", val)); |
|---|
| 92 | } else { |
|---|
| 93 | // set A0 pin LOW |
|---|
| 94 | outb(inb(CTRL) & ~PARPORT_CONTROL_INIT, CTRL); |
|---|
| 95 | val = inb(EDAT); |
|---|
| 96 | DEB3(printk("i2c-pcf-epp.o: Read data 0x%x\n", val)); |
|---|
| 97 | } |
|---|
| 98 | return (val); |
|---|
| 99 | } |
|---|
| 100 | |
|---|
| 101 | static int pcf_epp_getown(void *data) |
|---|
| 102 | { |
|---|
| 103 | return (gpe.pe_own); |
|---|
| 104 | } |
|---|
| 105 | |
|---|
| 106 | |
|---|
| 107 | static int pcf_epp_getclock(void *data) |
|---|
| 108 | { |
|---|
| 109 | return (gpe.pe_clock); |
|---|
| 110 | } |
|---|
| 111 | |
|---|
| 112 | #if 0 |
|---|
| 113 | static void pcf_epp_sleep(unsigned long timeout) |
|---|
| 114 | { |
|---|
| 115 | schedule_timeout( timeout * HZ); |
|---|
| 116 | } |
|---|
| 117 | #endif |
|---|
| 118 | |
|---|
| 119 | static void pcf_epp_waitforpin(void) { |
|---|
| 120 | int timeout = 10; |
|---|
| 121 | |
|---|
| 122 | if (gpe.pe_irq > 0) { |
|---|
| 123 | cli(); |
|---|
| 124 | if (pcf_pending == 0) { |
|---|
| 125 | interruptible_sleep_on_timeout(&pcf_wait, timeout*HZ); |
|---|
| 126 | //udelay(100); |
|---|
| 127 | } else { |
|---|
| 128 | pcf_pending = 0; |
|---|
| 129 | } |
|---|
| 130 | sti(); |
|---|
| 131 | } else { |
|---|
| 132 | udelay(100); |
|---|
| 133 | } |
|---|
| 134 | } |
|---|
| 135 | |
|---|
| 136 | static void pcf_epp_handler(int this_irq, void *dev_id, struct pt_regs *regs) { |
|---|
| 137 | pcf_pending = 1; |
|---|
| 138 | wake_up_interruptible(&pcf_wait); |
|---|
| 139 | DEB3(printk("i2c-pcf-epp.o: in interrupt handler.\n")); |
|---|
| 140 | } |
|---|
| 141 | |
|---|
| 142 | |
|---|
| 143 | static int pcf_epp_init(void *data) |
|---|
| 144 | { |
|---|
| 145 | if (check_region(gpe.pe_base, 5) < 0 ) { |
|---|
| 146 | return -ENODEV; |
|---|
| 147 | } else { |
|---|
| 148 | request_region(gpe.pe_base, 5, "i2c (EPP parallel port adapter)"); |
|---|
| 149 | } |
|---|
| 150 | |
|---|
| 151 | DEB3(printk("i2c-pcf-epp.o: init status port = 0x%x\n", inb(0x379))); |
|---|
| 152 | |
|---|
| 153 | if (gpe.pe_irq > 0) { |
|---|
| 154 | if (request_irq(gpe.pe_irq, pcf_epp_handler, 0, "PCF8584", 0) < 0) { |
|---|
| 155 | printk("i2c-pcf-epp.o: Request irq%d failed\n", gpe.pe_irq); |
|---|
| 156 | gpe.pe_irq = 0; |
|---|
| 157 | } else |
|---|
| 158 | disable_irq(gpe.pe_irq); |
|---|
| 159 | enable_irq(gpe.pe_irq); |
|---|
| 160 | } |
|---|
| 161 | // EPP mode initialize |
|---|
| 162 | // enable interrupt from nINTR pin |
|---|
| 163 | outb(inb(CTRL)|0x14, CTRL); |
|---|
| 164 | // clear ERROR bit of STAT |
|---|
| 165 | outb(inb(STAT)|0x01, STAT); |
|---|
| 166 | outb(inb(STAT)&~0x01,STAT); |
|---|
| 167 | |
|---|
| 168 | return 0; |
|---|
| 169 | } |
|---|
| 170 | |
|---|
| 171 | |
|---|
| 172 | static void pcf_epp_exit(void) |
|---|
| 173 | { |
|---|
| 174 | if (gpe.pe_irq > 0) { |
|---|
| 175 | disable_irq(gpe.pe_irq); |
|---|
| 176 | free_irq(gpe.pe_irq, 0); |
|---|
| 177 | } |
|---|
| 178 | release_region(gpe.pe_base , 5); |
|---|
| 179 | } |
|---|
| 180 | |
|---|
| 181 | |
|---|
| 182 | static int pcf_epp_reg(struct i2c_client *client) |
|---|
| 183 | { |
|---|
| 184 | return 0; |
|---|
| 185 | } |
|---|
| 186 | |
|---|
| 187 | |
|---|
| 188 | static int pcf_epp_unreg(struct i2c_client *client) |
|---|
| 189 | { |
|---|
| 190 | return 0; |
|---|
| 191 | } |
|---|
| 192 | |
|---|
| 193 | static void pcf_epp_inc_use(struct i2c_adapter *adap) |
|---|
| 194 | { |
|---|
| 195 | #ifdef MODULE |
|---|
| 196 | MOD_INC_USE_COUNT; |
|---|
| 197 | #endif |
|---|
| 198 | } |
|---|
| 199 | |
|---|
| 200 | static void pcf_epp_dec_use(struct i2c_adapter *adap) |
|---|
| 201 | { |
|---|
| 202 | #ifdef MODULE |
|---|
| 203 | MOD_DEC_USE_COUNT; |
|---|
| 204 | #endif |
|---|
| 205 | } |
|---|
| 206 | |
|---|
| 207 | |
|---|
| 208 | /* ------------------------------------------------------------------------ |
|---|
| 209 | * Encapsulate the above functions in the correct operations structure. |
|---|
| 210 | * This is only done when more than one hardware adapter is supported. |
|---|
| 211 | */ |
|---|
| 212 | static struct i2c_algo_pcf_data pcf_epp_data = { |
|---|
| 213 | NULL, |
|---|
| 214 | pcf_epp_setbyte, |
|---|
| 215 | pcf_epp_getbyte, |
|---|
| 216 | pcf_epp_getown, |
|---|
| 217 | pcf_epp_getclock, |
|---|
| 218 | pcf_epp_waitforpin, |
|---|
| 219 | 80, 80, 100, /* waits, timeout */ |
|---|
| 220 | }; |
|---|
| 221 | |
|---|
| 222 | static struct i2c_adapter pcf_epp_ops = { |
|---|
| 223 | "PCF8584 EPP adapter", |
|---|
| 224 | I2C_HW_P_LP, |
|---|
| 225 | NULL, |
|---|
| 226 | &pcf_epp_data, |
|---|
| 227 | pcf_epp_inc_use, |
|---|
| 228 | pcf_epp_dec_use, |
|---|
| 229 | pcf_epp_reg, |
|---|
| 230 | pcf_epp_unreg, |
|---|
| 231 | }; |
|---|
| 232 | |
|---|
| 233 | int __init i2c_pcfepp_init(void) |
|---|
| 234 | { |
|---|
| 235 | struct i2c_pcf_epp *pepp = &gpe; |
|---|
| 236 | |
|---|
| 237 | printk("i2c-pcf-epp.o: i2c pcf8584-epp adapter module\n"); |
|---|
| 238 | if (base == 0) |
|---|
| 239 | pepp->pe_base = DEFAULT_BASE; |
|---|
| 240 | else |
|---|
| 241 | pepp->pe_base = base; |
|---|
| 242 | |
|---|
| 243 | if (irq == 0) |
|---|
| 244 | pepp->pe_irq = DEFAULT_IRQ; |
|---|
| 245 | else |
|---|
| 246 | pepp->pe_irq = irq; |
|---|
| 247 | |
|---|
| 248 | if (clock == 0) |
|---|
| 249 | pepp->pe_clock = DEFAULT_CLOCK; |
|---|
| 250 | else |
|---|
| 251 | pepp->pe_clock = clock; |
|---|
| 252 | |
|---|
| 253 | if (own == 0) |
|---|
| 254 | pepp->pe_own = DEFAULT_OWN; |
|---|
| 255 | else |
|---|
| 256 | pepp->pe_own = own; |
|---|
| 257 | |
|---|
| 258 | pcf_epp_data.data = (void *)pepp; |
|---|
| 259 | #if (LINUX_VERSION_CODE >= 0x020301) |
|---|
| 260 | init_waitqueue_head(&pcf_wait); |
|---|
| 261 | #endif |
|---|
| 262 | if (pcf_epp_init(pepp) == 0) { |
|---|
| 263 | if (i2c_pcf_add_bus(&pcf_epp_ops) < 0) |
|---|
| 264 | return -ENODEV; |
|---|
| 265 | } else { |
|---|
| 266 | return -ENODEV; |
|---|
| 267 | } |
|---|
| 268 | printk("i2c-pcf-epp.o: found device at %#x.\n", pepp->pe_base); |
|---|
| 269 | return 0; |
|---|
| 270 | } |
|---|
| 271 | |
|---|
| 272 | |
|---|
| 273 | EXPORT_NO_SYMBOLS; |
|---|
| 274 | |
|---|
| 275 | #ifdef MODULE |
|---|
| 276 | MODULE_AUTHOR("Hans Berglund <hb@spacetec.no> \n modified by Ryosuke Tajima <rosk@jsk.t.u-tokyo.ac.jp>"); |
|---|
| 277 | MODULE_DESCRIPTION("I2C-Bus adapter routines for PCF8584 EPP parallel port adapter"); |
|---|
| 278 | |
|---|
| 279 | MODULE_PARM(base, "i"); |
|---|
| 280 | MODULE_PARM(irq, "i"); |
|---|
| 281 | MODULE_PARM(clock, "i"); |
|---|
| 282 | MODULE_PARM(own, "i"); |
|---|
| 283 | |
|---|
| 284 | int init_module(void) |
|---|
| 285 | { |
|---|
| 286 | return i2c_pcfepp_init(); |
|---|
| 287 | } |
|---|
| 288 | |
|---|
| 289 | void cleanup_module(void) |
|---|
| 290 | { |
|---|
| 291 | i2c_pcf_del_bus(&pcf_epp_ops); |
|---|
| 292 | pcf_epp_exit(); |
|---|
| 293 | } |
|---|
| 294 | |
|---|
| 295 | #endif |
|---|
| 296 | |
|---|
| 297 | |
|---|