aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorBen Dooks <ben-linux@fluff.org>2006-06-25 08:48:03 -0400
committerLinus Torvalds <torvalds@g5.osdl.org>2006-06-25 13:01:09 -0400
commitad4063b0b2ffd7c8359b62c830e88152fc39ab20 (patch)
tree8bfc0132bd7bf851dbc495b4f9a7460904d332b8 /drivers
parent11e64757f9fb32f13f51596bbf01988f42fca764 (diff)
[PATCH] AX88796 parallel port driver
Driver for the simple parallel port interface on the Asix AX88796 chip on an platform_bus. [akpm@osdl.org: x86_64 build fix] Signed-off-by: Ben Dooks <ben-linux@fluff.org> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/parport/Kconfig12
-rw-r--r--drivers/parport/Makefile3
-rw-r--r--drivers/parport/parport_ax88796.c443
3 files changed, 457 insertions, 1 deletions
diff --git a/drivers/parport/Kconfig b/drivers/parport/Kconfig
index 4d8dc27ea9d1..c7fa28a28b9f 100644
--- a/drivers/parport/Kconfig
+++ b/drivers/parport/Kconfig
@@ -136,6 +136,18 @@ config PARPORT_SUNBPP
136 found on many Sun machines. Note that many of the newer Ultras 136 found on many Sun machines. Note that many of the newer Ultras
137 actually have pc style hardware instead. 137 actually have pc style hardware instead.
138 138
139config PARPORT_AX88796
140 tristate "AX88796 Parallel Port"
141 depends on PARPORT
142 select PARPORT_NOT_PC
143 help
144 Say Y here if you need support for the parallel port hardware on
145 the AX88796 network controller chip. This code is also available
146 as a module (say M), called parport_ax88796.
147
148 The driver is not dependant on the AX88796 network driver, and
149 should not interfere with the networking functions of the chip.
150
139config PARPORT_1284 151config PARPORT_1284
140 bool "IEEE 1284 transfer modes" 152 bool "IEEE 1284 transfer modes"
141 depends on PARPORT 153 depends on PARPORT
diff --git a/drivers/parport/Makefile b/drivers/parport/Makefile
index a19de35f8de2..696b8d4ca887 100644
--- a/drivers/parport/Makefile
+++ b/drivers/parport/Makefile
@@ -17,4 +17,5 @@ obj-$(CONFIG_PARPORT_MFC3) += parport_mfc3.o
17obj-$(CONFIG_PARPORT_ATARI) += parport_atari.o 17obj-$(CONFIG_PARPORT_ATARI) += parport_atari.o
18obj-$(CONFIG_PARPORT_SUNBPP) += parport_sunbpp.o 18obj-$(CONFIG_PARPORT_SUNBPP) += parport_sunbpp.o
19obj-$(CONFIG_PARPORT_GSC) += parport_gsc.o 19obj-$(CONFIG_PARPORT_GSC) += parport_gsc.o
20obj-$(CONFIG_PARPORT_IP32) += parport_ip32.o 20obj-$(CONFIG_PARPORT_AX88796) += parport_ax88796.o
21obj-$(CONFIG_PARPORT_IP32) += parport_ip32.o \ No newline at end of file
diff --git a/drivers/parport/parport_ax88796.c b/drivers/parport/parport_ax88796.c
new file mode 100644
index 000000000000..4baa719439a2
--- /dev/null
+++ b/drivers/parport/parport_ax88796.c
@@ -0,0 +1,443 @@
1/* linux/drivers/parport/parport_ax88796.c
2 *
3 * (c) 2005,2006 Simtec Electronics
4 * Ben Dooks <ben@simtec.co.uk>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 *
10*/
11
12#include <linux/module.h>
13#include <linux/kernel.h>
14#include <linux/parport.h>
15#include <linux/interrupt.h>
16#include <linux/errno.h>
17#include <linux/platform_device.h>
18
19#include <asm/io.h>
20#include <asm/irq.h>
21
22#define AX_SPR_BUSY (1<<7)
23#define AX_SPR_ACK (1<<6)
24#define AX_SPR_PE (1<<5)
25#define AX_SPR_SLCT (1<<4)
26#define AX_SPR_ERR (1<<3)
27
28#define AX_CPR_nDOE (1<<5)
29#define AX_CPR_SLCTIN (1<<3)
30#define AX_CPR_nINIT (1<<2)
31#define AX_CPR_ATFD (1<<1)
32#define AX_CPR_STRB (1<<0)
33
34struct ax_drvdata {
35 struct parport *parport;
36 struct parport_state suspend;
37
38 struct device *dev;
39 struct resource *io;
40
41 unsigned char irq_enabled;
42
43 void __iomem *base;
44 void __iomem *spp_data;
45 void __iomem *spp_spr;
46 void __iomem *spp_cpr;
47};
48
49static inline struct ax_drvdata *pp_to_drv(struct parport *p)
50{
51 return p->private_data;
52}
53
54static unsigned char
55parport_ax88796_read_data(struct parport *p)
56{
57 struct ax_drvdata *dd = pp_to_drv(p);
58
59 return readb(dd->spp_data);
60}
61
62static void
63parport_ax88796_write_data(struct parport *p, unsigned char data)
64{
65 struct ax_drvdata *dd = pp_to_drv(p);
66
67 writeb(data, dd->spp_data);
68}
69
70static unsigned char
71parport_ax88796_read_control(struct parport *p)
72{
73 struct ax_drvdata *dd = pp_to_drv(p);
74 unsigned int cpr = readb(dd->spp_cpr);
75 unsigned int ret = 0;
76
77 if (!(cpr & AX_CPR_STRB))
78 ret |= PARPORT_CONTROL_STROBE;
79
80 if (!(cpr & AX_CPR_ATFD))
81 ret |= PARPORT_CONTROL_AUTOFD;
82
83 if (cpr & AX_CPR_nINIT)
84 ret |= PARPORT_CONTROL_INIT;
85
86 if (!(cpr & AX_CPR_SLCTIN))
87 ret |= PARPORT_CONTROL_SELECT;
88
89 return ret;
90}
91
92static void
93parport_ax88796_write_control(struct parport *p, unsigned char control)
94{
95 struct ax_drvdata *dd = pp_to_drv(p);
96 unsigned int cpr = readb(dd->spp_cpr);
97
98 cpr &= AX_CPR_nDOE;
99
100 if (!(control & PARPORT_CONTROL_STROBE))
101 cpr |= AX_CPR_STRB;
102
103 if (!(control & PARPORT_CONTROL_AUTOFD))
104 cpr |= AX_CPR_ATFD;
105
106 if (control & PARPORT_CONTROL_INIT)
107 cpr |= AX_CPR_nINIT;
108
109 if (!(control & PARPORT_CONTROL_SELECT))
110 cpr |= AX_CPR_SLCTIN;
111
112 dev_dbg(dd->dev, "write_control: ctrl=%02x, cpr=%02x\n", control, cpr);
113 writeb(cpr, dd->spp_cpr);
114
115 if (parport_ax88796_read_control(p) != control) {
116 dev_err(dd->dev, "write_control: read != set (%02x, %02x)\n",
117 parport_ax88796_read_control(p), control);
118 }
119}
120
121static unsigned char
122parport_ax88796_read_status(struct parport *p)
123{
124 struct ax_drvdata *dd = pp_to_drv(p);
125 unsigned int status = readb(dd->spp_spr);
126 unsigned int ret = 0;
127
128 if (status & AX_SPR_BUSY)
129 ret |= PARPORT_STATUS_BUSY;
130
131 if (status & AX_SPR_ACK)
132 ret |= PARPORT_STATUS_ACK;
133
134 if (status & AX_SPR_ERR)
135 ret |= PARPORT_STATUS_ERROR;
136
137 if (status & AX_SPR_SLCT)
138 ret |= PARPORT_STATUS_SELECT;
139
140 if (status & AX_SPR_PE)
141 ret |= PARPORT_STATUS_PAPEROUT;
142
143 return ret;
144}
145
146static unsigned char
147parport_ax88796_frob_control(struct parport *p, unsigned char mask,
148 unsigned char val)
149{
150 struct ax_drvdata *dd = pp_to_drv(p);
151 unsigned char old = parport_ax88796_read_control(p);
152
153 dev_dbg(dd->dev, "frob: mask=%02x, val=%02x, old=%02x\n",
154 mask, val, old);
155
156 parport_ax88796_write_control(p, (old & ~mask) | val);
157 return old;
158}
159
160static void
161parport_ax88796_enable_irq(struct parport *p)
162{
163 struct ax_drvdata *dd = pp_to_drv(p);
164 unsigned long flags;
165
166 local_irq_save(flags);
167 if (!dd->irq_enabled) {
168 enable_irq(p->irq);
169 dd->irq_enabled = 1;
170 }
171 local_irq_restore(flags);
172}
173
174static void
175parport_ax88796_disable_irq(struct parport *p)
176{
177 struct ax_drvdata *dd = pp_to_drv(p);
178 unsigned long flags;
179
180 local_irq_save(flags);
181 if (dd->irq_enabled) {
182 disable_irq(p->irq);
183 dd->irq_enabled = 0;
184 }
185 local_irq_restore(flags);
186}
187
188static void
189parport_ax88796_data_forward(struct parport *p)
190{
191 struct ax_drvdata *dd = pp_to_drv(p);
192 void __iomem *cpr = dd->spp_cpr;
193
194 writeb((readb(cpr) & ~AX_CPR_nDOE), cpr);
195}
196
197static void
198parport_ax88796_data_reverse(struct parport *p)
199{
200 struct ax_drvdata *dd = pp_to_drv(p);
201 void __iomem *cpr = dd->spp_cpr;
202
203 writeb(readb(cpr) | AX_CPR_nDOE, cpr);
204}
205
206static void
207parport_ax88796_init_state(struct pardevice *d, struct parport_state *s)
208{
209 struct ax_drvdata *dd = pp_to_drv(d->port);
210
211 memset(s, 0, sizeof(struct parport_state));
212
213 dev_dbg(dd->dev, "init_state: %p: state=%p\n", d, s);
214 s->u.ax88796.cpr = readb(dd->spp_cpr);
215}
216
217static void
218parport_ax88796_save_state(struct parport *p, struct parport_state *s)
219{
220 struct ax_drvdata *dd = pp_to_drv(p);
221
222 dev_dbg(dd->dev, "save_state: %p: state=%p\n", p, s);
223 s->u.ax88796.cpr = readb(dd->spp_cpr);
224}
225
226static void
227parport_ax88796_restore_state(struct parport *p, struct parport_state *s)
228{
229 struct ax_drvdata *dd = pp_to_drv(p);
230
231 dev_dbg(dd->dev, "restore_state: %p: state=%p\n", p, s);
232 writeb(s->u.ax88796.cpr, dd->spp_cpr);
233}
234
235static irqreturn_t
236parport_ax88796_interrupt(int irq, void *dev_id, struct pt_regs *regs)
237{
238 parport_generic_irq(irq, dev_id, regs);
239 return IRQ_HANDLED;
240}
241
242
243static struct parport_operations parport_ax88796_ops = {
244 .write_data = parport_ax88796_write_data,
245 .read_data = parport_ax88796_read_data,
246
247 .write_control = parport_ax88796_write_control,
248 .read_control = parport_ax88796_read_control,
249 .frob_control = parport_ax88796_frob_control,
250
251 .read_status = parport_ax88796_read_status,
252
253 .enable_irq = parport_ax88796_enable_irq,
254 .disable_irq = parport_ax88796_disable_irq,
255
256 .data_forward = parport_ax88796_data_forward,
257 .data_reverse = parport_ax88796_data_reverse,
258
259 .init_state = parport_ax88796_init_state,
260 .save_state = parport_ax88796_save_state,
261 .restore_state = parport_ax88796_restore_state,
262
263 .epp_write_data = parport_ieee1284_epp_write_data,
264 .epp_read_data = parport_ieee1284_epp_read_data,
265 .epp_write_addr = parport_ieee1284_epp_write_addr,
266 .epp_read_addr = parport_ieee1284_epp_read_addr,
267
268 .ecp_write_data = parport_ieee1284_ecp_write_data,
269 .ecp_read_data = parport_ieee1284_ecp_read_data,
270 .ecp_write_addr = parport_ieee1284_ecp_write_addr,
271
272 .compat_write_data = parport_ieee1284_write_compat,
273 .nibble_read_data = parport_ieee1284_read_nibble,
274 .byte_read_data = parport_ieee1284_read_byte,
275
276 .owner = THIS_MODULE,
277};
278
279static int parport_ax88796_probe(struct platform_device *pdev)
280{
281 struct device *_dev = &pdev->dev;
282 struct ax_drvdata *dd;
283 struct parport *pp = NULL;
284 struct resource *res;
285 unsigned long size;
286 int spacing;
287 int irq;
288 int ret;
289
290 dd = kzalloc(sizeof(struct ax_drvdata), GFP_KERNEL);
291 if (dd == NULL) {
292 dev_err(_dev, "no memory for private data\n");
293 return -ENOMEM;
294 }
295
296 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
297 if (res == NULL) {
298 dev_err(_dev, "no MEM specified\n");
299 ret = -ENXIO;
300 goto exit_mem;
301 }
302
303 size = (res->end - res->start) + 1;
304 spacing = size / 3;
305
306 dd->io = request_mem_region(res->start, size, pdev->name);
307 if (dd->io == NULL) {
308 dev_err(_dev, "cannot reserve memory\n");
309 ret = -ENXIO;
310 goto exit_mem;
311 }
312
313 dd->base = ioremap(res->start, size);
314 if (dd->base == NULL) {
315 dev_err(_dev, "cannot ioremap region\n");
316 ret = -ENXIO;
317 goto exit_res;
318 }
319
320 irq = platform_get_irq(pdev, 0);
321 if (irq <= 0)
322 irq = PARPORT_IRQ_NONE;
323
324 pp = parport_register_port((unsigned long)dd->base, irq,
325 PARPORT_DMA_NONE,
326 &parport_ax88796_ops);
327
328 if (pp == NULL) {
329 dev_err(_dev, "failed to register parallel port\n");
330 ret = -ENOMEM;
331 goto exit_unmap;
332 }
333
334 pp->private_data = dd;
335 dd->parport = pp;
336 dd->dev = _dev;
337
338 dd->spp_data = dd->base;
339 dd->spp_spr = dd->base + (spacing * 1);
340 dd->spp_cpr = dd->base + (spacing * 2);
341
342 /* initialise the port controls */
343 writeb(AX_CPR_STRB, dd->spp_cpr);
344
345 if (irq >= 0) {
346 /* request irq */
347 ret = request_irq(irq, parport_ax88796_interrupt,
348 SA_TRIGGER_FALLING, pdev->name, pp);
349
350 if (ret < 0)
351 goto exit_port;
352
353 dd->irq_enabled = 1;
354 }
355
356 platform_set_drvdata(pdev, pp);
357
358 dev_info(_dev, "attached parallel port driver\n");
359 parport_announce_port(pp);
360
361 return 0;
362
363 exit_port:
364 parport_remove_port(pp);
365 exit_unmap:
366 iounmap(dd->base);
367 exit_res:
368 release_resource(dd->io);
369 kfree(dd->io);
370 exit_mem:
371 kfree(dd);
372 return ret;
373}
374
375static int parport_ax88796_remove(struct platform_device *pdev)
376{
377 struct parport *p = platform_get_drvdata(pdev);
378 struct ax_drvdata *dd = pp_to_drv(p);
379
380 free_irq(p->irq, p);
381 parport_remove_port(p);
382 iounmap(dd->base);
383 release_resource(dd->io);
384 kfree(dd->io);
385 kfree(dd);
386
387 return 0;
388}
389
390#ifdef CONFIG_PM
391
392static int parport_ax88796_suspend(struct platform_device *dev,
393 pm_message_t state)
394{
395 struct parport *p = platform_get_drvdata(dev);
396 struct ax_drvdata *dd = pp_to_drv(p);
397
398 parport_ax88796_save_state(p, &dd->suspend);
399 writeb(AX_CPR_nDOE | AX_CPR_STRB, dd->spp_cpr);
400 return 0;
401}
402
403static int parport_ax88796_resume(struct platform_device *dev)
404{
405 struct parport *p = platform_get_drvdata(dev);
406 struct ax_drvdata *dd = pp_to_drv(p);
407
408 parport_ax88796_restore_state(p, &dd->suspend);
409 return 0;
410}
411
412#else
413#define parport_ax88796_suspend NULL
414#define parport_ax88796_resume NULL
415#endif
416
417static struct platform_driver axdrv = {
418 .driver = {
419 .name = "ax88796-pp",
420 .owner = THIS_MODULE,
421 },
422 .probe = parport_ax88796_probe,
423 .remove = parport_ax88796_remove,
424 .suspend = parport_ax88796_suspend,
425 .resume = parport_ax88796_resume,
426};
427
428static int __init parport_ax88796_init(void)
429{
430 return platform_driver_register(&axdrv);
431}
432
433static void __exit parport_ax88796_exit(void)
434{
435 platform_driver_unregister(&axdrv);
436}
437
438module_init(parport_ax88796_init)
439module_exit(parport_ax88796_exit)
440
441MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
442MODULE_DESCRIPTION("AX88796 Parport parallel port driver");
443MODULE_LICENSE("GPL");