Root/
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 | #include <linux/slab.h> |
19 | |
20 | #include <asm/io.h> |
21 | #include <asm/irq.h> |
22 | |
23 | #define AX_SPR_BUSY (1<<7) |
24 | #define AX_SPR_ACK (1<<6) |
25 | #define AX_SPR_PE (1<<5) |
26 | #define AX_SPR_SLCT (1<<4) |
27 | #define AX_SPR_ERR (1<<3) |
28 | |
29 | #define AX_CPR_nDOE (1<<5) |
30 | #define AX_CPR_SLCTIN (1<<3) |
31 | #define AX_CPR_nINIT (1<<2) |
32 | #define AX_CPR_ATFD (1<<1) |
33 | #define AX_CPR_STRB (1<<0) |
34 | |
35 | struct ax_drvdata { |
36 | struct parport *parport; |
37 | struct parport_state suspend; |
38 | |
39 | struct device *dev; |
40 | struct resource *io; |
41 | |
42 | unsigned char irq_enabled; |
43 | |
44 | void __iomem *base; |
45 | void __iomem *spp_data; |
46 | void __iomem *spp_spr; |
47 | void __iomem *spp_cpr; |
48 | }; |
49 | |
50 | static inline struct ax_drvdata *pp_to_drv(struct parport *p) |
51 | { |
52 | return p->private_data; |
53 | } |
54 | |
55 | static unsigned char |
56 | parport_ax88796_read_data(struct parport *p) |
57 | { |
58 | struct ax_drvdata *dd = pp_to_drv(p); |
59 | |
60 | return readb(dd->spp_data); |
61 | } |
62 | |
63 | static void |
64 | parport_ax88796_write_data(struct parport *p, unsigned char data) |
65 | { |
66 | struct ax_drvdata *dd = pp_to_drv(p); |
67 | |
68 | writeb(data, dd->spp_data); |
69 | } |
70 | |
71 | static unsigned char |
72 | parport_ax88796_read_control(struct parport *p) |
73 | { |
74 | struct ax_drvdata *dd = pp_to_drv(p); |
75 | unsigned int cpr = readb(dd->spp_cpr); |
76 | unsigned int ret = 0; |
77 | |
78 | if (!(cpr & AX_CPR_STRB)) |
79 | ret |= PARPORT_CONTROL_STROBE; |
80 | |
81 | if (!(cpr & AX_CPR_ATFD)) |
82 | ret |= PARPORT_CONTROL_AUTOFD; |
83 | |
84 | if (cpr & AX_CPR_nINIT) |
85 | ret |= PARPORT_CONTROL_INIT; |
86 | |
87 | if (!(cpr & AX_CPR_SLCTIN)) |
88 | ret |= PARPORT_CONTROL_SELECT; |
89 | |
90 | return ret; |
91 | } |
92 | |
93 | static void |
94 | parport_ax88796_write_control(struct parport *p, unsigned char control) |
95 | { |
96 | struct ax_drvdata *dd = pp_to_drv(p); |
97 | unsigned int cpr = readb(dd->spp_cpr); |
98 | |
99 | cpr &= AX_CPR_nDOE; |
100 | |
101 | if (!(control & PARPORT_CONTROL_STROBE)) |
102 | cpr |= AX_CPR_STRB; |
103 | |
104 | if (!(control & PARPORT_CONTROL_AUTOFD)) |
105 | cpr |= AX_CPR_ATFD; |
106 | |
107 | if (control & PARPORT_CONTROL_INIT) |
108 | cpr |= AX_CPR_nINIT; |
109 | |
110 | if (!(control & PARPORT_CONTROL_SELECT)) |
111 | cpr |= AX_CPR_SLCTIN; |
112 | |
113 | dev_dbg(dd->dev, "write_control: ctrl=%02x, cpr=%02x\n", control, cpr); |
114 | writeb(cpr, dd->spp_cpr); |
115 | |
116 | if (parport_ax88796_read_control(p) != control) { |
117 | dev_err(dd->dev, "write_control: read != set (%02x, %02x)\n", |
118 | parport_ax88796_read_control(p), control); |
119 | } |
120 | } |
121 | |
122 | static unsigned char |
123 | parport_ax88796_read_status(struct parport *p) |
124 | { |
125 | struct ax_drvdata *dd = pp_to_drv(p); |
126 | unsigned int status = readb(dd->spp_spr); |
127 | unsigned int ret = 0; |
128 | |
129 | if (status & AX_SPR_BUSY) |
130 | ret |= PARPORT_STATUS_BUSY; |
131 | |
132 | if (status & AX_SPR_ACK) |
133 | ret |= PARPORT_STATUS_ACK; |
134 | |
135 | if (status & AX_SPR_ERR) |
136 | ret |= PARPORT_STATUS_ERROR; |
137 | |
138 | if (status & AX_SPR_SLCT) |
139 | ret |= PARPORT_STATUS_SELECT; |
140 | |
141 | if (status & AX_SPR_PE) |
142 | ret |= PARPORT_STATUS_PAPEROUT; |
143 | |
144 | return ret; |
145 | } |
146 | |
147 | static unsigned char |
148 | parport_ax88796_frob_control(struct parport *p, unsigned char mask, |
149 | unsigned char val) |
150 | { |
151 | struct ax_drvdata *dd = pp_to_drv(p); |
152 | unsigned char old = parport_ax88796_read_control(p); |
153 | |
154 | dev_dbg(dd->dev, "frob: mask=%02x, val=%02x, old=%02x\n", |
155 | mask, val, old); |
156 | |
157 | parport_ax88796_write_control(p, (old & ~mask) | val); |
158 | return old; |
159 | } |
160 | |
161 | static void |
162 | parport_ax88796_enable_irq(struct parport *p) |
163 | { |
164 | struct ax_drvdata *dd = pp_to_drv(p); |
165 | unsigned long flags; |
166 | |
167 | local_irq_save(flags); |
168 | if (!dd->irq_enabled) { |
169 | enable_irq(p->irq); |
170 | dd->irq_enabled = 1; |
171 | } |
172 | local_irq_restore(flags); |
173 | } |
174 | |
175 | static void |
176 | parport_ax88796_disable_irq(struct parport *p) |
177 | { |
178 | struct ax_drvdata *dd = pp_to_drv(p); |
179 | unsigned long flags; |
180 | |
181 | local_irq_save(flags); |
182 | if (dd->irq_enabled) { |
183 | disable_irq(p->irq); |
184 | dd->irq_enabled = 0; |
185 | } |
186 | local_irq_restore(flags); |
187 | } |
188 | |
189 | static void |
190 | parport_ax88796_data_forward(struct parport *p) |
191 | { |
192 | struct ax_drvdata *dd = pp_to_drv(p); |
193 | void __iomem *cpr = dd->spp_cpr; |
194 | |
195 | writeb((readb(cpr) & ~AX_CPR_nDOE), cpr); |
196 | } |
197 | |
198 | static void |
199 | parport_ax88796_data_reverse(struct parport *p) |
200 | { |
201 | struct ax_drvdata *dd = pp_to_drv(p); |
202 | void __iomem *cpr = dd->spp_cpr; |
203 | |
204 | writeb(readb(cpr) | AX_CPR_nDOE, cpr); |
205 | } |
206 | |
207 | static void |
208 | parport_ax88796_init_state(struct pardevice *d, struct parport_state *s) |
209 | { |
210 | struct ax_drvdata *dd = pp_to_drv(d->port); |
211 | |
212 | memset(s, 0, sizeof(struct parport_state)); |
213 | |
214 | dev_dbg(dd->dev, "init_state: %p: state=%p\n", d, s); |
215 | s->u.ax88796.cpr = readb(dd->spp_cpr); |
216 | } |
217 | |
218 | static void |
219 | parport_ax88796_save_state(struct parport *p, struct parport_state *s) |
220 | { |
221 | struct ax_drvdata *dd = pp_to_drv(p); |
222 | |
223 | dev_dbg(dd->dev, "save_state: %p: state=%p\n", p, s); |
224 | s->u.ax88796.cpr = readb(dd->spp_cpr); |
225 | } |
226 | |
227 | static void |
228 | parport_ax88796_restore_state(struct parport *p, struct parport_state *s) |
229 | { |
230 | struct ax_drvdata *dd = pp_to_drv(p); |
231 | |
232 | dev_dbg(dd->dev, "restore_state: %p: state=%p\n", p, s); |
233 | writeb(s->u.ax88796.cpr, dd->spp_cpr); |
234 | } |
235 | |
236 | static struct parport_operations parport_ax88796_ops = { |
237 | .write_data = parport_ax88796_write_data, |
238 | .read_data = parport_ax88796_read_data, |
239 | |
240 | .write_control = parport_ax88796_write_control, |
241 | .read_control = parport_ax88796_read_control, |
242 | .frob_control = parport_ax88796_frob_control, |
243 | |
244 | .read_status = parport_ax88796_read_status, |
245 | |
246 | .enable_irq = parport_ax88796_enable_irq, |
247 | .disable_irq = parport_ax88796_disable_irq, |
248 | |
249 | .data_forward = parport_ax88796_data_forward, |
250 | .data_reverse = parport_ax88796_data_reverse, |
251 | |
252 | .init_state = parport_ax88796_init_state, |
253 | .save_state = parport_ax88796_save_state, |
254 | .restore_state = parport_ax88796_restore_state, |
255 | |
256 | .epp_write_data = parport_ieee1284_epp_write_data, |
257 | .epp_read_data = parport_ieee1284_epp_read_data, |
258 | .epp_write_addr = parport_ieee1284_epp_write_addr, |
259 | .epp_read_addr = parport_ieee1284_epp_read_addr, |
260 | |
261 | .ecp_write_data = parport_ieee1284_ecp_write_data, |
262 | .ecp_read_data = parport_ieee1284_ecp_read_data, |
263 | .ecp_write_addr = parport_ieee1284_ecp_write_addr, |
264 | |
265 | .compat_write_data = parport_ieee1284_write_compat, |
266 | .nibble_read_data = parport_ieee1284_read_nibble, |
267 | .byte_read_data = parport_ieee1284_read_byte, |
268 | |
269 | .owner = THIS_MODULE, |
270 | }; |
271 | |
272 | static int parport_ax88796_probe(struct platform_device *pdev) |
273 | { |
274 | struct device *_dev = &pdev->dev; |
275 | struct ax_drvdata *dd; |
276 | struct parport *pp = NULL; |
277 | struct resource *res; |
278 | unsigned long size; |
279 | int spacing; |
280 | int irq; |
281 | int ret; |
282 | |
283 | dd = kzalloc(sizeof(struct ax_drvdata), GFP_KERNEL); |
284 | if (dd == NULL) { |
285 | dev_err(_dev, "no memory for private data\n"); |
286 | return -ENOMEM; |
287 | } |
288 | |
289 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
290 | if (res == NULL) { |
291 | dev_err(_dev, "no MEM specified\n"); |
292 | ret = -ENXIO; |
293 | goto exit_mem; |
294 | } |
295 | |
296 | size = resource_size(res); |
297 | spacing = size / 3; |
298 | |
299 | dd->io = request_mem_region(res->start, size, pdev->name); |
300 | if (dd->io == NULL) { |
301 | dev_err(_dev, "cannot reserve memory\n"); |
302 | ret = -ENXIO; |
303 | goto exit_mem; |
304 | } |
305 | |
306 | dd->base = ioremap(res->start, size); |
307 | if (dd->base == NULL) { |
308 | dev_err(_dev, "cannot ioremap region\n"); |
309 | ret = -ENXIO; |
310 | goto exit_res; |
311 | } |
312 | |
313 | irq = platform_get_irq(pdev, 0); |
314 | if (irq <= 0) |
315 | irq = PARPORT_IRQ_NONE; |
316 | |
317 | pp = parport_register_port((unsigned long)dd->base, irq, |
318 | PARPORT_DMA_NONE, |
319 | &parport_ax88796_ops); |
320 | |
321 | if (pp == NULL) { |
322 | dev_err(_dev, "failed to register parallel port\n"); |
323 | ret = -ENOMEM; |
324 | goto exit_unmap; |
325 | } |
326 | |
327 | pp->private_data = dd; |
328 | dd->parport = pp; |
329 | dd->dev = _dev; |
330 | |
331 | dd->spp_data = dd->base; |
332 | dd->spp_spr = dd->base + (spacing * 1); |
333 | dd->spp_cpr = dd->base + (spacing * 2); |
334 | |
335 | /* initialise the port controls */ |
336 | writeb(AX_CPR_STRB, dd->spp_cpr); |
337 | |
338 | if (irq >= 0) { |
339 | /* request irq */ |
340 | ret = request_irq(irq, parport_irq_handler, |
341 | IRQF_TRIGGER_FALLING, pdev->name, pp); |
342 | |
343 | if (ret < 0) |
344 | goto exit_port; |
345 | |
346 | dd->irq_enabled = 1; |
347 | } |
348 | |
349 | platform_set_drvdata(pdev, pp); |
350 | |
351 | dev_info(_dev, "attached parallel port driver\n"); |
352 | parport_announce_port(pp); |
353 | |
354 | return 0; |
355 | |
356 | exit_port: |
357 | parport_remove_port(pp); |
358 | exit_unmap: |
359 | iounmap(dd->base); |
360 | exit_res: |
361 | release_resource(dd->io); |
362 | kfree(dd->io); |
363 | exit_mem: |
364 | kfree(dd); |
365 | return ret; |
366 | } |
367 | |
368 | static int parport_ax88796_remove(struct platform_device *pdev) |
369 | { |
370 | struct parport *p = platform_get_drvdata(pdev); |
371 | struct ax_drvdata *dd = pp_to_drv(p); |
372 | |
373 | free_irq(p->irq, p); |
374 | parport_remove_port(p); |
375 | iounmap(dd->base); |
376 | release_resource(dd->io); |
377 | kfree(dd->io); |
378 | kfree(dd); |
379 | |
380 | return 0; |
381 | } |
382 | |
383 | #ifdef CONFIG_PM |
384 | |
385 | static int parport_ax88796_suspend(struct platform_device *dev, |
386 | pm_message_t state) |
387 | { |
388 | struct parport *p = platform_get_drvdata(dev); |
389 | struct ax_drvdata *dd = pp_to_drv(p); |
390 | |
391 | parport_ax88796_save_state(p, &dd->suspend); |
392 | writeb(AX_CPR_nDOE | AX_CPR_STRB, dd->spp_cpr); |
393 | return 0; |
394 | } |
395 | |
396 | static int parport_ax88796_resume(struct platform_device *dev) |
397 | { |
398 | struct parport *p = platform_get_drvdata(dev); |
399 | struct ax_drvdata *dd = pp_to_drv(p); |
400 | |
401 | parport_ax88796_restore_state(p, &dd->suspend); |
402 | return 0; |
403 | } |
404 | |
405 | #else |
406 | #define parport_ax88796_suspend NULL |
407 | #define parport_ax88796_resume NULL |
408 | #endif |
409 | |
410 | MODULE_ALIAS("platform:ax88796-pp"); |
411 | |
412 | static struct platform_driver axdrv = { |
413 | .driver = { |
414 | .name = "ax88796-pp", |
415 | .owner = THIS_MODULE, |
416 | }, |
417 | .probe = parport_ax88796_probe, |
418 | .remove = parport_ax88796_remove, |
419 | .suspend = parport_ax88796_suspend, |
420 | .resume = parport_ax88796_resume, |
421 | }; |
422 | |
423 | module_platform_driver(axdrv); |
424 | |
425 | MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>"); |
426 | MODULE_DESCRIPTION("AX88796 Parport parallel port driver"); |
427 | MODULE_LICENSE("GPL"); |
428 |
Branches:
ben-wpan
ben-wpan-stefan
javiroman/ks7010
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9