1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* parport_sunbpp.c: Parallel-port routines for SBUS |
3 | * |
4 | * Author: Derrick J. Brashear <shadow@dementia.org> |
5 | * |
6 | * based on work by: |
7 | * Phil Blundell <philb@gnu.org> |
8 | * Tim Waugh <tim@cyberelk.demon.co.uk> |
9 | * Jose Renau <renau@acm.org> |
10 | * David Campbell <campbell@tirian.che.curtin.edu.au> |
11 | * Grant Guenther <grant@torque.net> |
12 | * Eddie C. Dost <ecd@skynet.be> |
13 | * Stephen Williams (steve@icarus.com) |
14 | * Gus Baldauf (gbaldauf@ix.netcom.com) |
15 | * Peter Zaitcev |
16 | * Tom Dyas |
17 | * |
18 | * Updated to new SBUS device framework: David S. Miller <davem@davemloft.net> |
19 | * |
20 | */ |
21 | |
22 | #include <linux/string.h> |
23 | #include <linux/module.h> |
24 | #include <linux/delay.h> |
25 | #include <linux/errno.h> |
26 | #include <linux/ioport.h> |
27 | #include <linux/kernel.h> |
28 | #include <linux/slab.h> |
29 | #include <linux/init.h> |
30 | #include <linux/of.h> |
31 | #include <linux/platform_device.h> |
32 | |
33 | #include <linux/parport.h> |
34 | |
35 | #include <asm/ptrace.h> |
36 | #include <linux/interrupt.h> |
37 | |
38 | #include <asm/io.h> |
39 | #include <asm/oplib.h> /* OpenProm Library */ |
40 | #include <asm/dma.h> /* BPP uses LSI 64854 for DMA */ |
41 | #include <asm/irq.h> |
42 | #include <asm/sunbpp.h> |
43 | |
44 | #undef __SUNBPP_DEBUG |
45 | #ifdef __SUNBPP_DEBUG |
46 | #define dprintk(x) printk x |
47 | #else |
48 | #define dprintk(x) |
49 | #endif |
50 | |
51 | static void parport_sunbpp_disable_irq(struct parport *p) |
52 | { |
53 | struct bpp_regs __iomem *regs = (struct bpp_regs __iomem *)p->base; |
54 | u32 tmp; |
55 | |
56 | tmp = sbus_readl(®s->p_csr); |
57 | tmp &= ~DMA_INT_ENAB; |
58 | sbus_writel(tmp, ®s->p_csr); |
59 | } |
60 | |
61 | static void parport_sunbpp_enable_irq(struct parport *p) |
62 | { |
63 | struct bpp_regs __iomem *regs = (struct bpp_regs __iomem *)p->base; |
64 | u32 tmp; |
65 | |
66 | tmp = sbus_readl(®s->p_csr); |
67 | tmp |= DMA_INT_ENAB; |
68 | sbus_writel(tmp, ®s->p_csr); |
69 | } |
70 | |
71 | static void parport_sunbpp_write_data(struct parport *p, unsigned char d) |
72 | { |
73 | struct bpp_regs __iomem *regs = (struct bpp_regs __iomem *)p->base; |
74 | |
75 | sbus_writeb(d, ®s->p_dr); |
76 | dprintk((KERN_DEBUG "wrote 0x%x\n" , d)); |
77 | } |
78 | |
79 | static unsigned char parport_sunbpp_read_data(struct parport *p) |
80 | { |
81 | struct bpp_regs __iomem *regs = (struct bpp_regs __iomem *)p->base; |
82 | |
83 | return sbus_readb(®s->p_dr); |
84 | } |
85 | |
86 | static unsigned char status_sunbpp_to_pc(struct parport *p) |
87 | { |
88 | struct bpp_regs __iomem *regs = (struct bpp_regs __iomem *)p->base; |
89 | unsigned char bits = 0; |
90 | unsigned char value_tcr = sbus_readb(®s->p_tcr); |
91 | unsigned char value_ir = sbus_readb(®s->p_ir); |
92 | |
93 | if (!(value_ir & P_IR_ERR)) |
94 | bits |= PARPORT_STATUS_ERROR; |
95 | if (!(value_ir & P_IR_SLCT)) |
96 | bits |= PARPORT_STATUS_SELECT; |
97 | if (!(value_ir & P_IR_PE)) |
98 | bits |= PARPORT_STATUS_PAPEROUT; |
99 | if (value_tcr & P_TCR_ACK) |
100 | bits |= PARPORT_STATUS_ACK; |
101 | if (!(value_tcr & P_TCR_BUSY)) |
102 | bits |= PARPORT_STATUS_BUSY; |
103 | |
104 | dprintk((KERN_DEBUG "tcr 0x%x ir 0x%x\n" , value_tcr, value_ir)); |
105 | dprintk((KERN_DEBUG "read status 0x%x\n" , bits)); |
106 | return bits; |
107 | } |
108 | |
109 | static unsigned char control_sunbpp_to_pc(struct parport *p) |
110 | { |
111 | struct bpp_regs __iomem *regs = (struct bpp_regs __iomem *)p->base; |
112 | unsigned char bits = 0; |
113 | unsigned char value_tcr = sbus_readb(®s->p_tcr); |
114 | unsigned char value_or = sbus_readb(®s->p_or); |
115 | |
116 | if (!(value_tcr & P_TCR_DS)) |
117 | bits |= PARPORT_CONTROL_STROBE; |
118 | if (!(value_or & P_OR_AFXN)) |
119 | bits |= PARPORT_CONTROL_AUTOFD; |
120 | if (!(value_or & P_OR_INIT)) |
121 | bits |= PARPORT_CONTROL_INIT; |
122 | if (value_or & P_OR_SLCT_IN) |
123 | bits |= PARPORT_CONTROL_SELECT; |
124 | |
125 | dprintk((KERN_DEBUG "tcr 0x%x or 0x%x\n" , value_tcr, value_or)); |
126 | dprintk((KERN_DEBUG "read control 0x%x\n" , bits)); |
127 | return bits; |
128 | } |
129 | |
130 | static unsigned char parport_sunbpp_read_control(struct parport *p) |
131 | { |
132 | return control_sunbpp_to_pc(p); |
133 | } |
134 | |
135 | static unsigned char parport_sunbpp_frob_control(struct parport *p, |
136 | unsigned char mask, |
137 | unsigned char val) |
138 | { |
139 | struct bpp_regs __iomem *regs = (struct bpp_regs __iomem *)p->base; |
140 | unsigned char value_tcr = sbus_readb(®s->p_tcr); |
141 | unsigned char value_or = sbus_readb(®s->p_or); |
142 | |
143 | dprintk((KERN_DEBUG "frob1: tcr 0x%x or 0x%x\n" , |
144 | value_tcr, value_or)); |
145 | if (mask & PARPORT_CONTROL_STROBE) { |
146 | if (val & PARPORT_CONTROL_STROBE) { |
147 | value_tcr &= ~P_TCR_DS; |
148 | } else { |
149 | value_tcr |= P_TCR_DS; |
150 | } |
151 | } |
152 | if (mask & PARPORT_CONTROL_AUTOFD) { |
153 | if (val & PARPORT_CONTROL_AUTOFD) { |
154 | value_or &= ~P_OR_AFXN; |
155 | } else { |
156 | value_or |= P_OR_AFXN; |
157 | } |
158 | } |
159 | if (mask & PARPORT_CONTROL_INIT) { |
160 | if (val & PARPORT_CONTROL_INIT) { |
161 | value_or &= ~P_OR_INIT; |
162 | } else { |
163 | value_or |= P_OR_INIT; |
164 | } |
165 | } |
166 | if (mask & PARPORT_CONTROL_SELECT) { |
167 | if (val & PARPORT_CONTROL_SELECT) { |
168 | value_or |= P_OR_SLCT_IN; |
169 | } else { |
170 | value_or &= ~P_OR_SLCT_IN; |
171 | } |
172 | } |
173 | |
174 | sbus_writeb(value_or, ®s->p_or); |
175 | sbus_writeb(value_tcr, ®s->p_tcr); |
176 | dprintk((KERN_DEBUG "frob2: tcr 0x%x or 0x%x\n" , |
177 | value_tcr, value_or)); |
178 | return parport_sunbpp_read_control(p); |
179 | } |
180 | |
181 | static void parport_sunbpp_write_control(struct parport *p, unsigned char d) |
182 | { |
183 | const unsigned char wm = (PARPORT_CONTROL_STROBE | |
184 | PARPORT_CONTROL_AUTOFD | |
185 | PARPORT_CONTROL_INIT | |
186 | PARPORT_CONTROL_SELECT); |
187 | |
188 | parport_sunbpp_frob_control (p, mask: wm, val: d & wm); |
189 | } |
190 | |
191 | static unsigned char parport_sunbpp_read_status(struct parport *p) |
192 | { |
193 | return status_sunbpp_to_pc(p); |
194 | } |
195 | |
196 | static void parport_sunbpp_data_forward (struct parport *p) |
197 | { |
198 | struct bpp_regs __iomem *regs = (struct bpp_regs __iomem *)p->base; |
199 | unsigned char value_tcr = sbus_readb(®s->p_tcr); |
200 | |
201 | dprintk((KERN_DEBUG "forward\n" )); |
202 | value_tcr &= ~P_TCR_DIR; |
203 | sbus_writeb(value_tcr, ®s->p_tcr); |
204 | } |
205 | |
206 | static void parport_sunbpp_data_reverse (struct parport *p) |
207 | { |
208 | struct bpp_regs __iomem *regs = (struct bpp_regs __iomem *)p->base; |
209 | u8 val = sbus_readb(®s->p_tcr); |
210 | |
211 | dprintk((KERN_DEBUG "reverse\n" )); |
212 | val |= P_TCR_DIR; |
213 | sbus_writeb(val, ®s->p_tcr); |
214 | } |
215 | |
216 | static void parport_sunbpp_init_state(struct pardevice *dev, struct parport_state *s) |
217 | { |
218 | s->u.pc.ctr = 0xc; |
219 | s->u.pc.ecr = 0x0; |
220 | } |
221 | |
222 | static void parport_sunbpp_save_state(struct parport *p, struct parport_state *s) |
223 | { |
224 | s->u.pc.ctr = parport_sunbpp_read_control(p); |
225 | } |
226 | |
227 | static void parport_sunbpp_restore_state(struct parport *p, struct parport_state *s) |
228 | { |
229 | parport_sunbpp_write_control(p, d: s->u.pc.ctr); |
230 | } |
231 | |
232 | static struct parport_operations parport_sunbpp_ops = |
233 | { |
234 | .write_data = parport_sunbpp_write_data, |
235 | .read_data = parport_sunbpp_read_data, |
236 | |
237 | .write_control = parport_sunbpp_write_control, |
238 | .read_control = parport_sunbpp_read_control, |
239 | .frob_control = parport_sunbpp_frob_control, |
240 | |
241 | .read_status = parport_sunbpp_read_status, |
242 | |
243 | .enable_irq = parport_sunbpp_enable_irq, |
244 | .disable_irq = parport_sunbpp_disable_irq, |
245 | |
246 | .data_forward = parport_sunbpp_data_forward, |
247 | .data_reverse = parport_sunbpp_data_reverse, |
248 | |
249 | .init_state = parport_sunbpp_init_state, |
250 | .save_state = parport_sunbpp_save_state, |
251 | .restore_state = parport_sunbpp_restore_state, |
252 | |
253 | .epp_write_data = parport_ieee1284_epp_write_data, |
254 | .epp_read_data = parport_ieee1284_epp_read_data, |
255 | .epp_write_addr = parport_ieee1284_epp_write_addr, |
256 | .epp_read_addr = parport_ieee1284_epp_read_addr, |
257 | |
258 | .ecp_write_data = parport_ieee1284_ecp_write_data, |
259 | .ecp_read_data = parport_ieee1284_ecp_read_data, |
260 | .ecp_write_addr = parport_ieee1284_ecp_write_addr, |
261 | |
262 | .compat_write_data = parport_ieee1284_write_compat, |
263 | .nibble_read_data = parport_ieee1284_read_nibble, |
264 | .byte_read_data = parport_ieee1284_read_byte, |
265 | |
266 | .owner = THIS_MODULE, |
267 | }; |
268 | |
269 | static int bpp_probe(struct platform_device *op) |
270 | { |
271 | struct parport_operations *ops; |
272 | struct bpp_regs __iomem *regs; |
273 | int irq, dma, err = 0, size; |
274 | unsigned char value_tcr; |
275 | void __iomem *base; |
276 | struct parport *p; |
277 | |
278 | irq = op->archdata.irqs[0]; |
279 | base = of_ioremap(&op->resource[0], 0, |
280 | resource_size(res: &op->resource[0]), |
281 | "sunbpp" ); |
282 | if (!base) |
283 | return -ENODEV; |
284 | |
285 | size = resource_size(res: &op->resource[0]); |
286 | dma = PARPORT_DMA_NONE; |
287 | |
288 | ops = kmemdup(p: &parport_sunbpp_ops, size: sizeof(struct parport_operations), |
289 | GFP_KERNEL); |
290 | if (!ops) { |
291 | err = -ENOMEM; |
292 | goto out_unmap; |
293 | } |
294 | |
295 | dprintk(("register_port\n" )); |
296 | if (!(p = parport_register_port(base: (unsigned long)base, irq, dma, ops))) { |
297 | err = -ENOMEM; |
298 | goto out_free_ops; |
299 | } |
300 | |
301 | p->size = size; |
302 | p->dev = &op->dev; |
303 | |
304 | if ((err = request_irq(irq: p->irq, handler: parport_irq_handler, |
305 | IRQF_SHARED, name: p->name, dev: p)) != 0) { |
306 | goto out_put_port; |
307 | } |
308 | |
309 | parport_sunbpp_enable_irq(p); |
310 | |
311 | regs = (struct bpp_regs __iomem *)p->base; |
312 | |
313 | value_tcr = sbus_readb(®s->p_tcr); |
314 | value_tcr &= ~P_TCR_DIR; |
315 | sbus_writeb(value_tcr, ®s->p_tcr); |
316 | |
317 | pr_info("%s: sunbpp at 0x%lx\n" , p->name, p->base); |
318 | |
319 | dev_set_drvdata(dev: &op->dev, data: p); |
320 | |
321 | parport_announce_port(port: p); |
322 | |
323 | return 0; |
324 | |
325 | out_put_port: |
326 | parport_put_port(p); |
327 | |
328 | out_free_ops: |
329 | kfree(objp: ops); |
330 | |
331 | out_unmap: |
332 | of_iounmap(&op->resource[0], base, size); |
333 | |
334 | return err; |
335 | } |
336 | |
337 | static void bpp_remove(struct platform_device *op) |
338 | { |
339 | struct parport *p = dev_get_drvdata(dev: &op->dev); |
340 | struct parport_operations *ops = p->ops; |
341 | |
342 | parport_remove_port(port: p); |
343 | |
344 | if (p->irq != PARPORT_IRQ_NONE) { |
345 | parport_sunbpp_disable_irq(p); |
346 | free_irq(p->irq, p); |
347 | } |
348 | |
349 | of_iounmap(&op->resource[0], (void __iomem *) p->base, p->size); |
350 | parport_put_port(p); |
351 | kfree(objp: ops); |
352 | |
353 | dev_set_drvdata(dev: &op->dev, NULL); |
354 | } |
355 | |
356 | static const struct of_device_id bpp_match[] = { |
357 | { |
358 | .name = "SUNW,bpp" , |
359 | }, |
360 | {}, |
361 | }; |
362 | |
363 | MODULE_DEVICE_TABLE(of, bpp_match); |
364 | |
365 | static struct platform_driver bpp_sbus_driver = { |
366 | .driver = { |
367 | .name = "bpp" , |
368 | .of_match_table = bpp_match, |
369 | }, |
370 | .probe = bpp_probe, |
371 | .remove_new = bpp_remove, |
372 | }; |
373 | |
374 | module_platform_driver(bpp_sbus_driver); |
375 | |
376 | MODULE_AUTHOR("Derrick J Brashear" ); |
377 | MODULE_DESCRIPTION("Parport Driver for Sparc bidirectional Port" ); |
378 | MODULE_VERSION("2.0" ); |
379 | MODULE_LICENSE("GPL" ); |
380 | |