diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/parport/parport_mfc3.c |
Linux-2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/parport/parport_mfc3.c')
-rw-r--r-- | drivers/parport/parport_mfc3.c | 404 |
1 files changed, 404 insertions, 0 deletions
diff --git a/drivers/parport/parport_mfc3.c b/drivers/parport/parport_mfc3.c new file mode 100644 index 00000000000..c85364765ef --- /dev/null +++ b/drivers/parport/parport_mfc3.c | |||
@@ -0,0 +1,404 @@ | |||
1 | /* Low-level parallel port routines for the Multiface 3 card | ||
2 | * | ||
3 | * Author: Joerg Dorchain <joerg@dorchain.net> | ||
4 | * | ||
5 | * (C) The elitist m68k Users(TM) | ||
6 | * | ||
7 | * based on the existing parport_amiga and lp_mfc | ||
8 | * | ||
9 | * | ||
10 | * From the MFC3 documentation: | ||
11 | * | ||
12 | * Miscellaneous PIA Details | ||
13 | * ------------------------- | ||
14 | * | ||
15 | * The two open-drain interrupt outputs /IRQA and /IRQB are routed to | ||
16 | * /INT2 of the Z2 bus. | ||
17 | * | ||
18 | * The CPU data bus of the PIA (D0-D7) is connected to D8-D15 on the Z2 | ||
19 | * bus. This means that any PIA registers are accessed at even addresses. | ||
20 | * | ||
21 | * Centronics Pin Connections for the PIA | ||
22 | * -------------------------------------- | ||
23 | * | ||
24 | * The following table shows the connections between the PIA and the | ||
25 | * Centronics interface connector. These connections implement a single, but | ||
26 | * very complete, Centronics type interface. The Pin column gives the pin | ||
27 | * numbers of the PIA. The Centronics pin numbers can be found in the section | ||
28 | * "Parallel Connectors". | ||
29 | * | ||
30 | * | ||
31 | * Pin | PIA | Dir | Centronics Names | ||
32 | * -------+-----+-----+--------------------------------------------------------- | ||
33 | * 19 | CB2 | --> | /STROBE (aka /DRDY) | ||
34 | * 10-17 | PBx | <-> | DATA0 - DATA7 | ||
35 | * 18 | CB1 | <-- | /ACK | ||
36 | * 40 | CA1 | <-- | BUSY | ||
37 | * 3 | PA1 | <-- | PAPER-OUT (aka POUT) | ||
38 | * 4 | PA2 | <-- | SELECTED (aka SEL) | ||
39 | * 9 | PA7 | --> | /INIT (aka /RESET or /INPUT-PRIME) | ||
40 | * 6 | PA4 | <-- | /ERROR (aka /FAULT) | ||
41 | * 7 | PA5 | --> | DIR (aka /SELECT-IN) | ||
42 | * 8 | PA6 | --> | /AUTO-FEED-XT | ||
43 | * 39 | CA2 | --> | open | ||
44 | * 5 | PA3 | <-- | /ACK (same as CB1!) | ||
45 | * 2 | PA0 | <-- | BUSY (same as CA1!) | ||
46 | * -------+-----+-----+--------------------------------------------------------- | ||
47 | * | ||
48 | * Should be enough to understand some of the driver. | ||
49 | * | ||
50 | * Per convention for normal use the port registers are visible. | ||
51 | * If you need the data direction registers, restore the value in the | ||
52 | * control register. | ||
53 | */ | ||
54 | |||
55 | #include "multiface.h" | ||
56 | #include <linux/module.h> | ||
57 | #include <linux/init.h> | ||
58 | #include <linux/parport.h> | ||
59 | #include <linux/delay.h> | ||
60 | #include <linux/mc6821.h> | ||
61 | #include <linux/zorro.h> | ||
62 | #include <linux/interrupt.h> | ||
63 | #include <asm/setup.h> | ||
64 | #include <asm/amigahw.h> | ||
65 | #include <asm/irq.h> | ||
66 | #include <asm/amigaints.h> | ||
67 | |||
68 | /* Maximum Number of Cards supported */ | ||
69 | #define MAX_MFC 5 | ||
70 | |||
71 | #undef DEBUG | ||
72 | #ifdef DEBUG | ||
73 | #define DPRINTK printk | ||
74 | #else | ||
75 | static inline int DPRINTK(void *nothing, ...) {return 0;} | ||
76 | #endif | ||
77 | |||
78 | static struct parport *this_port[MAX_MFC] = {NULL, }; | ||
79 | static volatile int dummy; /* for trigger readds */ | ||
80 | |||
81 | #define pia(dev) ((struct pia *)(dev->base)) | ||
82 | static struct parport_operations pp_mfc3_ops; | ||
83 | |||
84 | static void mfc3_write_data(struct parport *p, unsigned char data) | ||
85 | { | ||
86 | DPRINTK(KERN_DEBUG "write_data %c\n",data); | ||
87 | |||
88 | dummy = pia(p)->pprb; /* clears irq bit */ | ||
89 | /* Triggers also /STROBE.*/ | ||
90 | pia(p)->pprb = data; | ||
91 | } | ||
92 | |||
93 | static unsigned char mfc3_read_data(struct parport *p) | ||
94 | { | ||
95 | /* clears interrupt bit. Triggers also /STROBE. */ | ||
96 | return pia(p)->pprb; | ||
97 | } | ||
98 | |||
99 | static unsigned char control_pc_to_mfc3(unsigned char control) | ||
100 | { | ||
101 | unsigned char ret = 32|64; | ||
102 | |||
103 | if (control & PARPORT_CONTROL_SELECT) /* XXX: What is SELECP? */ | ||
104 | ret &= ~32; /* /SELECT_IN */ | ||
105 | if (control & PARPORT_CONTROL_INIT) /* INITP */ | ||
106 | ret |= 128; | ||
107 | if (control & PARPORT_CONTROL_AUTOFD) /* AUTOLF */ | ||
108 | ret &= ~64; | ||
109 | if (control & PARPORT_CONTROL_STROBE) /* Strobe */ | ||
110 | /* Handled directly by hardware */; | ||
111 | return ret; | ||
112 | } | ||
113 | |||
114 | static unsigned char control_mfc3_to_pc(unsigned char control) | ||
115 | { | ||
116 | unsigned char ret = PARPORT_CONTROL_STROBE | ||
117 | | PARPORT_CONTROL_AUTOFD | PARPORT_CONTROL_SELECT; | ||
118 | |||
119 | if (control & 128) /* /INITP */ | ||
120 | ret |= PARPORT_CONTROL_INIT; | ||
121 | if (control & 64) /* /AUTOLF */ | ||
122 | ret &= ~PARPORT_CONTROL_AUTOFD; | ||
123 | if (control & 32) /* /SELECT_IN */ | ||
124 | ret &= ~PARPORT_CONTROL_SELECT; | ||
125 | return ret; | ||
126 | } | ||
127 | |||
128 | static void mfc3_write_control(struct parport *p, unsigned char control) | ||
129 | { | ||
130 | DPRINTK(KERN_DEBUG "write_control %02x\n",control); | ||
131 | pia(p)->ppra = (pia(p)->ppra & 0x1f) | control_pc_to_mfc3(control); | ||
132 | } | ||
133 | |||
134 | static unsigned char mfc3_read_control( struct parport *p) | ||
135 | { | ||
136 | DPRINTK(KERN_DEBUG "read_control \n"); | ||
137 | return control_mfc3_to_pc(pia(p)->ppra & 0xe0); | ||
138 | } | ||
139 | |||
140 | static unsigned char mfc3_frob_control( struct parport *p, unsigned char mask, unsigned char val) | ||
141 | { | ||
142 | unsigned char old; | ||
143 | |||
144 | DPRINTK(KERN_DEBUG "frob_control mask %02x, value %02x\n",mask,val); | ||
145 | old = mfc3_read_control(p); | ||
146 | mfc3_write_control(p, (old & ~mask) ^ val); | ||
147 | return old; | ||
148 | } | ||
149 | |||
150 | #if 0 /* currently unused */ | ||
151 | static unsigned char status_pc_to_mfc3(unsigned char status) | ||
152 | { | ||
153 | unsigned char ret = 1; | ||
154 | |||
155 | if (status & PARPORT_STATUS_BUSY) /* Busy */ | ||
156 | ret &= ~1; | ||
157 | if (status & PARPORT_STATUS_ACK) /* Ack */ | ||
158 | ret |= 8; | ||
159 | if (status & PARPORT_STATUS_PAPEROUT) /* PaperOut */ | ||
160 | ret |= 2; | ||
161 | if (status & PARPORT_STATUS_SELECT) /* select */ | ||
162 | ret |= 4; | ||
163 | if (status & PARPORT_STATUS_ERROR) /* error */ | ||
164 | ret |= 16; | ||
165 | return ret; | ||
166 | } | ||
167 | #endif | ||
168 | |||
169 | static unsigned char status_mfc3_to_pc(unsigned char status) | ||
170 | { | ||
171 | unsigned char ret = PARPORT_STATUS_BUSY; | ||
172 | |||
173 | if (status & 1) /* Busy */ | ||
174 | ret &= ~PARPORT_STATUS_BUSY; | ||
175 | if (status & 2) /* PaperOut */ | ||
176 | ret |= PARPORT_STATUS_PAPEROUT; | ||
177 | if (status & 4) /* Selected */ | ||
178 | ret |= PARPORT_STATUS_SELECT; | ||
179 | if (status & 8) /* Ack */ | ||
180 | ret |= PARPORT_STATUS_ACK; | ||
181 | if (status & 16) /* /ERROR */ | ||
182 | ret |= PARPORT_STATUS_ERROR; | ||
183 | |||
184 | return ret; | ||
185 | } | ||
186 | |||
187 | #if 0 /* currently unused */ | ||
188 | static void mfc3_write_status( struct parport *p, unsigned char status) | ||
189 | { | ||
190 | DPRINTK(KERN_DEBUG "write_status %02x\n",status); | ||
191 | pia(p)->ppra = (pia(p)->ppra & 0xe0) | status_pc_to_mfc3(status); | ||
192 | } | ||
193 | #endif | ||
194 | |||
195 | static unsigned char mfc3_read_status(struct parport *p) | ||
196 | { | ||
197 | unsigned char status; | ||
198 | |||
199 | status = status_mfc3_to_pc(pia(p)->ppra & 0x1f); | ||
200 | DPRINTK(KERN_DEBUG "read_status %02x\n", status); | ||
201 | return status; | ||
202 | } | ||
203 | |||
204 | #if 0 /* currently unused */ | ||
205 | static void mfc3_change_mode( struct parport *p, int m) | ||
206 | { | ||
207 | /* XXX: This port only has one mode, and I am | ||
208 | not sure about the corresponding PC-style mode*/ | ||
209 | } | ||
210 | #endif | ||
211 | |||
212 | static int use_cnt = 0; | ||
213 | |||
214 | static irqreturn_t mfc3_interrupt(int irq, void *dev_id, struct pt_regs *regs) | ||
215 | { | ||
216 | int i; | ||
217 | |||
218 | for( i = 0; i < MAX_MFC; i++) | ||
219 | if (this_port[i] != NULL) | ||
220 | if (pia(this_port[i])->crb & 128) { /* Board caused interrupt */ | ||
221 | dummy = pia(this_port[i])->pprb; /* clear irq bit */ | ||
222 | parport_generic_irq(irq, this_port[i], regs); | ||
223 | } | ||
224 | return IRQ_HANDLED; | ||
225 | } | ||
226 | |||
227 | static void mfc3_enable_irq(struct parport *p) | ||
228 | { | ||
229 | pia(p)->crb |= PIA_C1_ENABLE_IRQ; | ||
230 | } | ||
231 | |||
232 | static void mfc3_disable_irq(struct parport *p) | ||
233 | { | ||
234 | pia(p)->crb &= ~PIA_C1_ENABLE_IRQ; | ||
235 | } | ||
236 | |||
237 | static void mfc3_data_forward(struct parport *p) | ||
238 | { | ||
239 | DPRINTK(KERN_DEBUG "forward\n"); | ||
240 | pia(p)->crb &= ~PIA_DDR; /* make data direction register visible */ | ||
241 | pia(p)->pddrb = 255; /* all pins output */ | ||
242 | pia(p)->crb |= PIA_DDR; /* make data register visible - default */ | ||
243 | } | ||
244 | |||
245 | static void mfc3_data_reverse(struct parport *p) | ||
246 | { | ||
247 | DPRINTK(KERN_DEBUG "reverse\n"); | ||
248 | pia(p)->crb &= ~PIA_DDR; /* make data direction register visible */ | ||
249 | pia(p)->pddrb = 0; /* all pins input */ | ||
250 | pia(p)->crb |= PIA_DDR; /* make data register visible - default */ | ||
251 | } | ||
252 | |||
253 | static void mfc3_init_state(struct pardevice *dev, struct parport_state *s) | ||
254 | { | ||
255 | s->u.amiga.data = 0; | ||
256 | s->u.amiga.datadir = 255; | ||
257 | s->u.amiga.status = 0; | ||
258 | s->u.amiga.statusdir = 0xe0; | ||
259 | } | ||
260 | |||
261 | static void mfc3_save_state(struct parport *p, struct parport_state *s) | ||
262 | { | ||
263 | s->u.amiga.data = pia(p)->pprb; | ||
264 | pia(p)->crb &= ~PIA_DDR; | ||
265 | s->u.amiga.datadir = pia(p)->pddrb; | ||
266 | pia(p)->crb |= PIA_DDR; | ||
267 | s->u.amiga.status = pia(p)->ppra; | ||
268 | pia(p)->cra &= ~PIA_DDR; | ||
269 | s->u.amiga.statusdir = pia(p)->pddrb; | ||
270 | pia(p)->cra |= PIA_DDR; | ||
271 | } | ||
272 | |||
273 | static void mfc3_restore_state(struct parport *p, struct parport_state *s) | ||
274 | { | ||
275 | pia(p)->pprb = s->u.amiga.data; | ||
276 | pia(p)->crb &= ~PIA_DDR; | ||
277 | pia(p)->pddrb = s->u.amiga.datadir; | ||
278 | pia(p)->crb |= PIA_DDR; | ||
279 | pia(p)->ppra = s->u.amiga.status; | ||
280 | pia(p)->cra &= ~PIA_DDR; | ||
281 | pia(p)->pddrb = s->u.amiga.statusdir; | ||
282 | pia(p)->cra |= PIA_DDR; | ||
283 | } | ||
284 | |||
285 | static struct parport_operations pp_mfc3_ops = { | ||
286 | .write_data = mfc3_write_data, | ||
287 | .read_data = mfc3_read_data, | ||
288 | |||
289 | .write_control = mfc3_write_control, | ||
290 | .read_control = mfc3_read_control, | ||
291 | .frob_control = mfc3_frob_control, | ||
292 | |||
293 | .read_status = mfc3_read_status, | ||
294 | |||
295 | .enable_irq = mfc3_enable_irq, | ||
296 | .disable_irq = mfc3_disable_irq, | ||
297 | |||
298 | .data_forward = mfc3_data_forward, | ||
299 | .data_reverse = mfc3_data_reverse, | ||
300 | |||
301 | .init_state = mfc3_init_state, | ||
302 | .save_state = mfc3_save_state, | ||
303 | .restore_state = mfc3_restore_state, | ||
304 | |||
305 | .epp_write_data = parport_ieee1284_epp_write_data, | ||
306 | .epp_read_data = parport_ieee1284_epp_read_data, | ||
307 | .epp_write_addr = parport_ieee1284_epp_write_addr, | ||
308 | .epp_read_addr = parport_ieee1284_epp_read_addr, | ||
309 | |||
310 | .ecp_write_data = parport_ieee1284_ecp_write_data, | ||
311 | .ecp_read_data = parport_ieee1284_ecp_read_data, | ||
312 | .ecp_write_addr = parport_ieee1284_ecp_write_addr, | ||
313 | |||
314 | .compat_write_data = parport_ieee1284_write_compat, | ||
315 | .nibble_read_data = parport_ieee1284_read_nibble, | ||
316 | .byte_read_data = parport_ieee1284_read_byte, | ||
317 | |||
318 | .owner = THIS_MODULE, | ||
319 | }; | ||
320 | |||
321 | /* ----------- Initialisation code --------------------------------- */ | ||
322 | |||
323 | static int __init parport_mfc3_init(void) | ||
324 | { | ||
325 | struct parport *p; | ||
326 | int pias = 0; | ||
327 | struct pia *pp; | ||
328 | struct zorro_dev *z = NULL; | ||
329 | |||
330 | if (!MACH_IS_AMIGA) | ||
331 | return -ENODEV; | ||
332 | |||
333 | while ((z = zorro_find_device(ZORRO_PROD_BSC_MULTIFACE_III, z))) { | ||
334 | unsigned long piabase = z->resource.start+PIABASE; | ||
335 | if (!request_mem_region(piabase, sizeof(struct pia), "PIA")) | ||
336 | continue; | ||
337 | |||
338 | pp = (struct pia *)ZTWO_VADDR(piabase); | ||
339 | pp->crb = 0; | ||
340 | pp->pddrb = 255; /* all data pins output */ | ||
341 | pp->crb = PIA_DDR|32|8; | ||
342 | dummy = pp->pddrb; /* reading clears interrupt */ | ||
343 | pp->cra = 0; | ||
344 | pp->pddra = 0xe0; /* /RESET, /DIR ,/AUTO-FEED output */ | ||
345 | pp->cra = PIA_DDR; | ||
346 | pp->ppra = 0; /* reset printer */ | ||
347 | udelay(10); | ||
348 | pp->ppra = 128; | ||
349 | p = parport_register_port((unsigned long)pp, IRQ_AMIGA_PORTS, | ||
350 | PARPORT_DMA_NONE, &pp_mfc3_ops); | ||
351 | if (!p) | ||
352 | goto out_port; | ||
353 | |||
354 | if (p->irq != PARPORT_IRQ_NONE) { | ||
355 | if (use_cnt++ == 0) | ||
356 | if (request_irq(IRQ_AMIGA_PORTS, mfc3_interrupt, SA_SHIRQ, p->name, &pp_mfc3_ops)) | ||
357 | goto out_irq; | ||
358 | } | ||
359 | |||
360 | this_port[pias++] = p; | ||
361 | printk(KERN_INFO "%s: Multiface III port using irq\n", p->name); | ||
362 | /* XXX: set operating mode */ | ||
363 | |||
364 | p->private_data = (void *)piabase; | ||
365 | parport_announce_port (p); | ||
366 | |||
367 | if (pias >= MAX_MFC) | ||
368 | break; | ||
369 | continue; | ||
370 | |||
371 | out_irq: | ||
372 | parport_put_port(p); | ||
373 | out_port: | ||
374 | release_mem_region(piabase, sizeof(struct pia)); | ||
375 | } | ||
376 | |||
377 | return pias ? 0 : -ENODEV; | ||
378 | } | ||
379 | |||
380 | static void __exit parport_mfc3_exit(void) | ||
381 | { | ||
382 | int i; | ||
383 | |||
384 | for (i = 0; i < MAX_MFC; i++) { | ||
385 | if (!this_port[i]) | ||
386 | continue; | ||
387 | parport_remove_port(this_port[i]); | ||
388 | if (!this_port[i]->irq != PARPORT_IRQ_NONE) { | ||
389 | if (--use_cnt == 0) | ||
390 | free_irq(IRQ_AMIGA_PORTS, &pp_mfc3_ops); | ||
391 | } | ||
392 | release_mem_region(ZTWO_PADDR(this_port[i]->private_data), sizeof(struct pia)); | ||
393 | parport_put_port(this_port[i]); | ||
394 | } | ||
395 | } | ||
396 | |||
397 | |||
398 | MODULE_AUTHOR("Joerg Dorchain <joerg@dorchain.net>"); | ||
399 | MODULE_DESCRIPTION("Parport Driver for Multiface 3 expansion cards Paralllel Port"); | ||
400 | MODULE_SUPPORTED_DEVICE("Multiface 3 Parallel Port"); | ||
401 | MODULE_LICENSE("GPL"); | ||
402 | |||
403 | module_init(parport_mfc3_init) | ||
404 | module_exit(parport_mfc3_exit) | ||