Commit | Line | Data |
---|---|---|
ba4dc61f SIG |
1 | /** |
2 | * ipoctal.c | |
3 | * | |
4 | * driver for the GE IP-OCTAL boards | |
5 | * Copyright (c) 2009 Nicolas Serafini, EIC2 SA | |
6 | * Copyright (c) 2010,2011 Samuel Iglesias Gonsalvez <siglesia@cern.ch>, CERN | |
7 | * Copyright (c) 2012 Samuel Iglesias Gonsalvez <siglesias@igalia.com>, Igalia | |
8 | * | |
9 | * This program is free software; you can redistribute it and/or modify it | |
10 | * under the terms of the GNU General Public License as published by the Free | |
32254363 | 11 | * Software Foundation; version 2 of the License. |
ba4dc61f SIG |
12 | */ |
13 | ||
ba4dc61f SIG |
14 | #include <linux/device.h> |
15 | #include <linux/module.h> | |
ba4dc61f | 16 | #include <linux/interrupt.h> |
ba4dc61f SIG |
17 | #include <linux/sched.h> |
18 | #include <linux/tty.h> | |
19 | #include <linux/serial.h> | |
20 | #include <linux/tty_flip.h> | |
21 | #include <linux/slab.h> | |
ba4dc61f SIG |
22 | #include <linux/atomic.h> |
23 | #include "../ipack.h" | |
24 | #include "ipoctal.h" | |
25 | #include "scc2698.h" | |
26 | ||
27 | #define IP_OCTAL_MANUFACTURER_ID 0xF0 | |
28 | #define IP_OCTAL_232_ID 0x22 | |
29 | #define IP_OCTAL_422_ID 0x2A | |
30 | #define IP_OCTAL_485_ID 0x48 | |
31 | ||
32 | #define IP_OCTAL_ID_SPACE_VECTOR 0x41 | |
33 | #define IP_OCTAL_NB_BLOCKS 4 | |
34 | ||
35 | static struct ipack_driver driver; | |
36 | static const struct tty_operations ipoctal_fops; | |
37 | ||
38 | struct ipoctal { | |
39 | struct list_head list; | |
40 | struct ipack_device *dev; | |
41 | unsigned int board_id; | |
42 | struct scc2698_channel *chan_regs; | |
43 | struct scc2698_block *block_regs; | |
44 | struct ipoctal_stats chan_stats[NR_CHANNELS]; | |
ba4dc61f SIG |
45 | unsigned int nb_bytes[NR_CHANNELS]; |
46 | unsigned int count_wr[NR_CHANNELS]; | |
ba4dc61f | 47 | wait_queue_head_t queue[NR_CHANNELS]; |
ba4dc61f SIG |
48 | spinlock_t lock[NR_CHANNELS]; |
49 | unsigned int pointer_read[NR_CHANNELS]; | |
50 | unsigned int pointer_write[NR_CHANNELS]; | |
51 | atomic_t open[NR_CHANNELS]; | |
52 | unsigned char write; | |
53 | struct tty_port tty_port[NR_CHANNELS]; | |
54 | struct tty_driver *tty_drv; | |
55 | }; | |
56 | ||
57 | /* Linked list to save the registered devices */ | |
58 | static LIST_HEAD(ipoctal_list); | |
59 | ||
60 | static inline void ipoctal_write_io_reg(struct ipoctal *ipoctal, | |
61 | unsigned char *dest, | |
62 | unsigned char value) | |
63 | { | |
64 | unsigned long offset; | |
65 | ||
5a81b4a0 | 66 | offset = ((void __iomem *) dest) - ipoctal->dev->io_space.address; |
ec440335 SIG |
67 | ipoctal->dev->bus->ops->write8(ipoctal->dev, IPACK_IO_SPACE, offset, |
68 | value); | |
ba4dc61f SIG |
69 | } |
70 | ||
71 | static inline void ipoctal_write_cr_cmd(struct ipoctal *ipoctal, | |
72 | unsigned char *dest, | |
73 | unsigned char value) | |
74 | { | |
75 | ipoctal_write_io_reg(ipoctal, dest, value); | |
76 | } | |
77 | ||
78 | static inline unsigned char ipoctal_read_io_reg(struct ipoctal *ipoctal, | |
79 | unsigned char *src) | |
80 | { | |
81 | unsigned long offset; | |
82 | unsigned char value; | |
83 | ||
5a81b4a0 | 84 | offset = ((void __iomem *) src) - ipoctal->dev->io_space.address; |
ec440335 SIG |
85 | ipoctal->dev->bus->ops->read8(ipoctal->dev, IPACK_IO_SPACE, offset, |
86 | &value); | |
ba4dc61f SIG |
87 | return value; |
88 | } | |
89 | ||
90 | static struct ipoctal *ipoctal_find_board(struct tty_struct *tty) | |
91 | { | |
92 | struct ipoctal *p; | |
93 | ||
94 | list_for_each_entry(p, &ipoctal_list, list) { | |
95 | if (tty->driver->major == p->tty_drv->major) | |
96 | return p; | |
97 | } | |
98 | ||
99 | return NULL; | |
100 | } | |
101 | ||
102 | static int ipoctal_port_activate(struct tty_port *port, struct tty_struct *tty) | |
103 | { | |
104 | struct ipoctal *ipoctal; | |
105 | int channel = tty->index; | |
106 | ||
107 | ipoctal = ipoctal_find_board(tty); | |
108 | ||
109 | if (ipoctal == NULL) { | |
42b38207 SIG |
110 | dev_err(tty->dev, "Device not found. Major %d\n", |
111 | tty->driver->major); | |
ba4dc61f SIG |
112 | return -ENODEV; |
113 | } | |
114 | ||
115 | ipoctal_write_io_reg(ipoctal, &ipoctal->chan_regs[channel].u.w.cr, | |
116 | CR_ENABLE_RX); | |
ba4dc61f SIG |
117 | return 0; |
118 | } | |
119 | ||
120 | static int ipoctal_open(struct tty_struct *tty, struct file *file) | |
121 | { | |
122 | int channel = tty->index; | |
123 | int res; | |
124 | struct ipoctal *ipoctal; | |
125 | ||
126 | ipoctal = ipoctal_find_board(tty); | |
127 | ||
128 | if (ipoctal == NULL) { | |
42b38207 SIG |
129 | dev_err(tty->dev, "Device not found. Major %d\n", |
130 | tty->driver->major); | |
ba4dc61f SIG |
131 | return -ENODEV; |
132 | } | |
133 | ||
134 | if (atomic_read(&ipoctal->open[channel])) | |
135 | return -EBUSY; | |
136 | ||
1337b07e SIG |
137 | tty->driver_data = ipoctal; |
138 | ||
ba4dc61f SIG |
139 | res = tty_port_open(&ipoctal->tty_port[channel], tty, file); |
140 | if (res) | |
141 | return res; | |
142 | ||
143 | atomic_inc(&ipoctal->open[channel]); | |
144 | return 0; | |
145 | } | |
146 | ||
147 | static void ipoctal_reset_stats(struct ipoctal_stats *stats) | |
148 | { | |
149 | stats->tx = 0; | |
150 | stats->rx = 0; | |
151 | stats->rcv_break = 0; | |
152 | stats->framing_err = 0; | |
153 | stats->overrun_err = 0; | |
154 | stats->parity_err = 0; | |
155 | } | |
156 | ||
157 | static void ipoctal_free_channel(struct tty_struct *tty) | |
158 | { | |
159 | int channel = tty->index; | |
160 | struct ipoctal *ipoctal = tty->driver_data; | |
161 | ||
162 | if (ipoctal == NULL) | |
163 | return; | |
164 | ||
165 | ipoctal_reset_stats(&ipoctal->chan_stats[channel]); | |
166 | ipoctal->pointer_read[channel] = 0; | |
167 | ipoctal->pointer_write[channel] = 0; | |
168 | ipoctal->nb_bytes[channel] = 0; | |
169 | } | |
170 | ||
171 | static void ipoctal_close(struct tty_struct *tty, struct file *filp) | |
172 | { | |
173 | int channel = tty->index; | |
174 | struct ipoctal *ipoctal = tty->driver_data; | |
175 | ||
176 | tty_port_close(&ipoctal->tty_port[channel], tty, filp); | |
177 | ||
178 | if (atomic_dec_and_test(&ipoctal->open[channel])) | |
179 | ipoctal_free_channel(tty); | |
180 | } | |
181 | ||
182 | static int ipoctal_get_icount(struct tty_struct *tty, | |
183 | struct serial_icounter_struct *icount) | |
184 | { | |
185 | struct ipoctal *ipoctal = tty->driver_data; | |
186 | int channel = tty->index; | |
187 | ||
188 | icount->cts = 0; | |
189 | icount->dsr = 0; | |
190 | icount->rng = 0; | |
191 | icount->dcd = 0; | |
192 | icount->rx = ipoctal->chan_stats[channel].rx; | |
193 | icount->tx = ipoctal->chan_stats[channel].tx; | |
194 | icount->frame = ipoctal->chan_stats[channel].framing_err; | |
195 | icount->parity = ipoctal->chan_stats[channel].parity_err; | |
196 | icount->brk = ipoctal->chan_stats[channel].rcv_break; | |
197 | return 0; | |
198 | } | |
199 | ||
200 | static int ipoctal_irq_handler(void *arg) | |
201 | { | |
202 | unsigned int channel; | |
203 | unsigned int block; | |
204 | unsigned char isr; | |
205 | unsigned char sr; | |
206 | unsigned char isr_tx_rdy, isr_rx_rdy; | |
207 | unsigned char value; | |
208 | unsigned char flag; | |
209 | struct tty_struct *tty; | |
210 | struct ipoctal *ipoctal = (struct ipoctal *) arg; | |
211 | ||
212 | /* Check all channels */ | |
213 | for (channel = 0; channel < NR_CHANNELS; channel++) { | |
214 | /* If there is no client, skip the check */ | |
215 | if (!atomic_read(&ipoctal->open[channel])) | |
216 | continue; | |
217 | ||
218 | tty = tty_port_tty_get(&ipoctal->tty_port[channel]); | |
219 | if (!tty) | |
220 | continue; | |
221 | ||
222 | /* | |
223 | * The HW is organized in pair of channels. | |
224 | * See which register we need to read from | |
225 | */ | |
226 | block = channel / 2; | |
227 | isr = ipoctal_read_io_reg(ipoctal, | |
228 | &ipoctal->block_regs[block].u.r.isr); | |
229 | sr = ipoctal_read_io_reg(ipoctal, | |
230 | &ipoctal->chan_regs[channel].u.r.sr); | |
231 | ||
232 | if ((channel % 2) == 1) { | |
233 | isr_tx_rdy = isr & ISR_TxRDY_B; | |
234 | isr_rx_rdy = isr & ISR_RxRDY_FFULL_B; | |
235 | } else { | |
236 | isr_tx_rdy = isr & ISR_TxRDY_A; | |
237 | isr_rx_rdy = isr & ISR_RxRDY_FFULL_A; | |
238 | } | |
239 | ||
240 | /* In case of RS-485, change from TX to RX when finishing TX. | |
241 | * Half-duplex. | |
242 | */ | |
243 | if ((ipoctal->board_id == IP_OCTAL_485_ID) && | |
244 | (sr & SR_TX_EMPTY) && | |
245 | (ipoctal->nb_bytes[channel] == 0)) { | |
246 | ipoctal_write_io_reg(ipoctal, | |
247 | &ipoctal->chan_regs[channel].u.w.cr, | |
248 | CR_DISABLE_TX); | |
249 | ipoctal_write_cr_cmd(ipoctal, | |
250 | &ipoctal->chan_regs[channel].u.w.cr, | |
251 | CR_CMD_NEGATE_RTSN); | |
252 | ipoctal_write_io_reg(ipoctal, | |
253 | &ipoctal->chan_regs[channel].u.w.cr, | |
254 | CR_ENABLE_RX); | |
255 | ipoctal->write = 1; | |
256 | wake_up_interruptible(&ipoctal->queue[channel]); | |
257 | } | |
258 | ||
259 | /* RX data */ | |
260 | if (isr_rx_rdy && (sr & SR_RX_READY)) { | |
261 | value = ipoctal_read_io_reg(ipoctal, | |
262 | &ipoctal->chan_regs[channel].u.r.rhr); | |
263 | flag = TTY_NORMAL; | |
264 | ||
265 | /* Error: count statistics */ | |
266 | if (sr & SR_ERROR) { | |
267 | ipoctal_write_cr_cmd(ipoctal, | |
268 | &ipoctal->chan_regs[channel].u.w.cr, | |
269 | CR_CMD_RESET_ERR_STATUS); | |
270 | ||
271 | if (sr & SR_OVERRUN_ERROR) { | |
ba4dc61f SIG |
272 | ipoctal->chan_stats[channel].overrun_err++; |
273 | /* Overrun doesn't affect the current character*/ | |
274 | tty_insert_flip_char(tty, 0, TTY_OVERRUN); | |
275 | } | |
276 | if (sr & SR_PARITY_ERROR) { | |
ba4dc61f SIG |
277 | ipoctal->chan_stats[channel].parity_err++; |
278 | flag = TTY_PARITY; | |
279 | } | |
280 | if (sr & SR_FRAMING_ERROR) { | |
ba4dc61f SIG |
281 | ipoctal->chan_stats[channel].framing_err++; |
282 | flag = TTY_FRAME; | |
283 | } | |
284 | if (sr & SR_RECEIVED_BREAK) { | |
ba4dc61f SIG |
285 | ipoctal->chan_stats[channel].rcv_break++; |
286 | flag = TTY_BREAK; | |
287 | } | |
288 | } | |
289 | ||
290 | tty_insert_flip_char(tty, value, flag); | |
291 | } | |
292 | ||
293 | /* TX of each character */ | |
294 | if (isr_tx_rdy && (sr & SR_TX_READY)) { | |
295 | unsigned int *pointer_write = | |
296 | &ipoctal->pointer_write[channel]; | |
297 | ||
298 | if (ipoctal->nb_bytes[channel] <= 0) { | |
299 | ipoctal->nb_bytes[channel] = 0; | |
300 | continue; | |
301 | } | |
59d6a29e | 302 | |
db1db294 | 303 | value = ipoctal->tty_port[channel].xmit_buf[*pointer_write]; |
ba4dc61f SIG |
304 | ipoctal_write_io_reg(ipoctal, |
305 | &ipoctal->chan_regs[channel].u.w.thr, | |
306 | value); | |
307 | ipoctal->chan_stats[channel].tx++; | |
308 | ipoctal->count_wr[channel]++; | |
309 | (*pointer_write)++; | |
310 | *pointer_write = *pointer_write % PAGE_SIZE; | |
311 | ipoctal->nb_bytes[channel]--; | |
ba4dc61f SIG |
312 | |
313 | if ((ipoctal->nb_bytes[channel] == 0) && | |
314 | (waitqueue_active(&ipoctal->queue[channel]))) { | |
315 | ||
316 | if (ipoctal->board_id != IP_OCTAL_485_ID) { | |
317 | ipoctal->write = 1; | |
318 | wake_up_interruptible(&ipoctal->queue[channel]); | |
319 | } | |
320 | } | |
321 | } | |
322 | ||
323 | tty_flip_buffer_push(tty); | |
324 | tty_kref_put(tty); | |
325 | } | |
326 | return IRQ_HANDLED; | |
327 | } | |
328 | ||
329 | static int ipoctal_check_model(struct ipack_device *dev, unsigned char *id) | |
330 | { | |
331 | unsigned char manufacturerID; | |
332 | unsigned char board_id; | |
333 | ||
ec440335 | 334 | dev->bus->ops->read8(dev, IPACK_ID_SPACE, |
ba4dc61f SIG |
335 | IPACK_IDPROM_OFFSET_MANUFACTURER_ID, &manufacturerID); |
336 | if (manufacturerID != IP_OCTAL_MANUFACTURER_ID) | |
337 | return -ENODEV; | |
338 | ||
ec440335 | 339 | dev->bus->ops->read8(dev, IPACK_ID_SPACE, |
ba4dc61f SIG |
340 | IPACK_IDPROM_OFFSET_MODEL, (unsigned char *)&board_id); |
341 | ||
342 | switch (board_id) { | |
343 | case IP_OCTAL_232_ID: | |
344 | case IP_OCTAL_422_ID: | |
345 | case IP_OCTAL_485_ID: | |
346 | *id = board_id; | |
347 | break; | |
348 | default: | |
349 | return -ENODEV; | |
350 | } | |
351 | ||
352 | return 0; | |
353 | } | |
354 | ||
355 | static const struct tty_port_operations ipoctal_tty_port_ops = { | |
356 | .dtr_rts = NULL, | |
357 | .activate = ipoctal_port_activate, | |
358 | }; | |
359 | ||
360 | static int ipoctal_inst_slot(struct ipoctal *ipoctal, unsigned int bus_nr, | |
361 | unsigned int slot, unsigned int vector) | |
362 | { | |
363 | int res = 0; | |
364 | int i; | |
365 | struct tty_driver *tty; | |
366 | char name[20]; | |
367 | unsigned char board_id; | |
368 | ||
ec440335 SIG |
369 | res = ipoctal->dev->bus->ops->map_space(ipoctal->dev, 0, |
370 | IPACK_ID_SPACE); | |
ba4dc61f | 371 | if (res) { |
42b38207 SIG |
372 | dev_err(&ipoctal->dev->dev, |
373 | "Unable to map slot [%d:%d] ID space!\n", | |
374 | bus_nr, slot); | |
ba4dc61f SIG |
375 | return res; |
376 | } | |
377 | ||
378 | res = ipoctal_check_model(ipoctal->dev, &board_id); | |
379 | if (res) { | |
ec440335 SIG |
380 | ipoctal->dev->bus->ops->unmap_space(ipoctal->dev, |
381 | IPACK_ID_SPACE); | |
ba4dc61f SIG |
382 | goto out_unregister_id_space; |
383 | } | |
384 | ipoctal->board_id = board_id; | |
385 | ||
ec440335 SIG |
386 | res = ipoctal->dev->bus->ops->map_space(ipoctal->dev, 0, |
387 | IPACK_IO_SPACE); | |
ba4dc61f | 388 | if (res) { |
42b38207 SIG |
389 | dev_err(&ipoctal->dev->dev, |
390 | "Unable to map slot [%d:%d] IO space!\n", | |
391 | bus_nr, slot); | |
ba4dc61f SIG |
392 | goto out_unregister_id_space; |
393 | } | |
394 | ||
ec440335 | 395 | res = ipoctal->dev->bus->ops->map_space(ipoctal->dev, |
ba4dc61f SIG |
396 | 0x8000, IPACK_MEM_SPACE); |
397 | if (res) { | |
42b38207 SIG |
398 | dev_err(&ipoctal->dev->dev, |
399 | "Unable to map slot [%d:%d] MEM space!\n", | |
400 | bus_nr, slot); | |
ba4dc61f SIG |
401 | goto out_unregister_io_space; |
402 | } | |
403 | ||
404 | /* Save the virtual address to access the registers easily */ | |
405 | ipoctal->chan_regs = | |
406 | (struct scc2698_channel *) ipoctal->dev->io_space.address; | |
407 | ipoctal->block_regs = | |
408 | (struct scc2698_block *) ipoctal->dev->io_space.address; | |
409 | ||
410 | /* Disable RX and TX before touching anything */ | |
411 | for (i = 0; i < NR_CHANNELS ; i++) { | |
412 | ipoctal_write_io_reg(ipoctal, &ipoctal->chan_regs[i].u.w.cr, | |
413 | CR_DISABLE_RX | CR_DISABLE_TX); | |
4eed84a8 SIG |
414 | ipoctal_write_cr_cmd(ipoctal, &ipoctal->chan_regs[i].u.w.cr, |
415 | CR_CMD_RESET_RX); | |
416 | ipoctal_write_cr_cmd(ipoctal, &ipoctal->chan_regs[i].u.w.cr, | |
417 | CR_CMD_RESET_TX); | |
418 | ipoctal_write_io_reg(ipoctal, | |
419 | &ipoctal->chan_regs[i].u.w.mr, | |
420 | MR1_CHRL_8_BITS | MR1_ERROR_CHAR | | |
421 | MR1_RxINT_RxRDY); /* mr1 */ | |
422 | ipoctal_write_io_reg(ipoctal, | |
423 | &ipoctal->chan_regs[i].u.w.mr, | |
424 | 0); /* mr2 */ | |
425 | ipoctal_write_io_reg(ipoctal, | |
426 | &ipoctal->chan_regs[i].u.w.csr, | |
427 | TX_CLK_9600 | RX_CLK_9600); | |
ba4dc61f SIG |
428 | } |
429 | ||
430 | for (i = 0; i < IP_OCTAL_NB_BLOCKS; i++) { | |
431 | ipoctal_write_io_reg(ipoctal, | |
432 | &ipoctal->block_regs[i].u.w.acr, | |
433 | ACR_BRG_SET2); | |
434 | ipoctal_write_io_reg(ipoctal, | |
435 | &ipoctal->block_regs[i].u.w.opcr, | |
436 | OPCR_MPP_OUTPUT | OPCR_MPOa_RTSN | | |
437 | OPCR_MPOb_RTSN); | |
438 | ipoctal_write_io_reg(ipoctal, | |
439 | &ipoctal->block_regs[i].u.w.imr, | |
440 | IMR_TxRDY_A | IMR_RxRDY_FFULL_A | | |
441 | IMR_DELTA_BREAK_A | IMR_TxRDY_B | | |
442 | IMR_RxRDY_FFULL_B | IMR_DELTA_BREAK_B); | |
443 | } | |
444 | ||
445 | /* | |
446 | * IP-OCTAL has different addresses to copy its IRQ vector. | |
447 | * Depending of the carrier these addresses are accesible or not. | |
448 | * More info in the datasheet. | |
449 | */ | |
ec440335 | 450 | ipoctal->dev->bus->ops->request_irq(ipoctal->dev, vector, |
ba4dc61f | 451 | ipoctal_irq_handler, ipoctal); |
7183f337 SIG |
452 | ipoctal->dev->bus->ops->write8(ipoctal->dev, IPACK_MEM_SPACE, 0, |
453 | vector); | |
ba4dc61f SIG |
454 | |
455 | /* Register the TTY device */ | |
456 | ||
457 | /* Each IP-OCTAL channel is a TTY port */ | |
458 | tty = alloc_tty_driver(NR_CHANNELS); | |
459 | ||
460 | if (!tty) { | |
461 | res = -ENOMEM; | |
462 | goto out_unregister_slot_unmap; | |
463 | } | |
464 | ||
465 | /* Fill struct tty_driver with ipoctal data */ | |
466 | tty->owner = THIS_MODULE; | |
467 | tty->driver_name = "ipoctal"; | |
468 | sprintf(name, "ipoctal.%d.%d.", bus_nr, slot); | |
469 | tty->name = name; | |
470 | tty->major = 0; | |
471 | ||
472 | tty->minor_start = 0; | |
473 | tty->type = TTY_DRIVER_TYPE_SERIAL; | |
474 | tty->subtype = SERIAL_TYPE_NORMAL; | |
475 | tty->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; | |
476 | tty->init_termios = tty_std_termios; | |
477 | tty->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; | |
478 | tty->init_termios.c_ispeed = 9600; | |
479 | tty->init_termios.c_ospeed = 9600; | |
480 | ||
481 | tty_set_operations(tty, &ipoctal_fops); | |
482 | res = tty_register_driver(tty); | |
483 | if (res) { | |
42b38207 | 484 | dev_err(&ipoctal->dev->dev, "Can't register tty driver.\n"); |
ba4dc61f SIG |
485 | put_tty_driver(tty); |
486 | goto out_unregister_slot_unmap; | |
487 | } | |
488 | ||
489 | /* Save struct tty_driver for use it when uninstalling the device */ | |
490 | ipoctal->tty_drv = tty; | |
491 | ||
492 | for (i = 0; i < NR_CHANNELS; i++) { | |
493 | tty_port_init(&ipoctal->tty_port[i]); | |
494 | tty_port_alloc_xmit_buf(&ipoctal->tty_port[i]); | |
495 | ipoctal->tty_port[i].ops = &ipoctal_tty_port_ops; | |
496 | ||
497 | ipoctal_reset_stats(&ipoctal->chan_stats[i]); | |
498 | ipoctal->nb_bytes[i] = 0; | |
499 | init_waitqueue_head(&ipoctal->queue[i]); | |
ba4dc61f SIG |
500 | |
501 | spin_lock_init(&ipoctal->lock[i]); | |
502 | ipoctal->pointer_read[i] = 0; | |
503 | ipoctal->pointer_write[i] = 0; | |
504 | ipoctal->nb_bytes[i] = 0; | |
734cc178 | 505 | tty_port_register_device(&ipoctal->tty_port[i], tty, i, NULL); |
ba4dc61f SIG |
506 | |
507 | /* | |
508 | * Enable again the RX. TX will be enabled when | |
509 | * there is something to send | |
510 | */ | |
511 | ipoctal_write_io_reg(ipoctal, &ipoctal->chan_regs[i].u.w.cr, | |
512 | CR_ENABLE_RX); | |
513 | } | |
514 | ||
515 | return 0; | |
516 | ||
517 | out_unregister_slot_unmap: | |
ec440335 | 518 | ipoctal->dev->bus->ops->unmap_space(ipoctal->dev, IPACK_ID_SPACE); |
ba4dc61f | 519 | out_unregister_io_space: |
ec440335 | 520 | ipoctal->dev->bus->ops->unmap_space(ipoctal->dev, IPACK_IO_SPACE); |
ba4dc61f | 521 | out_unregister_id_space: |
ec440335 | 522 | ipoctal->dev->bus->ops->unmap_space(ipoctal->dev, IPACK_MEM_SPACE); |
ba4dc61f SIG |
523 | return res; |
524 | } | |
525 | ||
526 | static inline int ipoctal_copy_write_buffer(struct ipoctal *ipoctal, | |
527 | unsigned int channel, | |
528 | const unsigned char *buf, | |
529 | int count) | |
530 | { | |
531 | unsigned long flags; | |
532 | int i; | |
533 | unsigned int *pointer_read = &ipoctal->pointer_read[channel]; | |
534 | ||
535 | /* Copy the bytes from the user buffer to the internal one */ | |
536 | for (i = 0; i < count; i++) { | |
537 | if (i <= (PAGE_SIZE - ipoctal->nb_bytes[channel])) { | |
538 | spin_lock_irqsave(&ipoctal->lock[channel], flags); | |
539 | ipoctal->tty_port[channel].xmit_buf[*pointer_read] = buf[i]; | |
540 | *pointer_read = (*pointer_read + 1) % PAGE_SIZE; | |
541 | ipoctal->nb_bytes[channel]++; | |
542 | spin_unlock_irqrestore(&ipoctal->lock[channel], flags); | |
543 | } else { | |
544 | break; | |
545 | } | |
546 | } | |
547 | return i; | |
548 | } | |
549 | ||
550 | static int ipoctal_write(struct ipoctal *ipoctal, unsigned int channel, | |
551 | const unsigned char *buf, int count) | |
552 | { | |
553 | ipoctal->nb_bytes[channel] = 0; | |
554 | ipoctal->count_wr[channel] = 0; | |
555 | ||
556 | ipoctal_copy_write_buffer(ipoctal, channel, buf, count); | |
557 | ||
ba4dc61f SIG |
558 | /* As the IP-OCTAL 485 only supports half duplex, do it manually */ |
559 | if (ipoctal->board_id == IP_OCTAL_485_ID) { | |
560 | ipoctal_write_io_reg(ipoctal, | |
561 | &ipoctal->chan_regs[channel].u.w.cr, | |
562 | CR_DISABLE_RX); | |
563 | ipoctal_write_cr_cmd(ipoctal, | |
564 | &ipoctal->chan_regs[channel].u.w.cr, | |
565 | CR_CMD_ASSERT_RTSN); | |
566 | } | |
567 | ||
568 | /* | |
569 | * Send a packet and then disable TX to avoid failure after several send | |
570 | * operations | |
571 | */ | |
572 | ipoctal_write_io_reg(ipoctal, | |
573 | &ipoctal->chan_regs[channel].u.w.cr, | |
574 | CR_ENABLE_TX); | |
575 | wait_event_interruptible(ipoctal->queue[channel], ipoctal->write); | |
576 | ipoctal_write_io_reg(ipoctal, | |
577 | &ipoctal->chan_regs[channel].u.w.cr, | |
578 | CR_DISABLE_TX); | |
579 | ||
580 | ipoctal->write = 0; | |
581 | return ipoctal->count_wr[channel]; | |
582 | } | |
583 | ||
584 | static int ipoctal_write_tty(struct tty_struct *tty, | |
585 | const unsigned char *buf, int count) | |
586 | { | |
587 | unsigned int channel = tty->index; | |
588 | struct ipoctal *ipoctal = tty->driver_data; | |
589 | ||
590 | return ipoctal_write(ipoctal, channel, buf, count); | |
591 | } | |
592 | ||
593 | static int ipoctal_write_room(struct tty_struct *tty) | |
594 | { | |
595 | int channel = tty->index; | |
596 | struct ipoctal *ipoctal = tty->driver_data; | |
597 | ||
598 | return PAGE_SIZE - ipoctal->nb_bytes[channel]; | |
599 | } | |
600 | ||
601 | static int ipoctal_chars_in_buffer(struct tty_struct *tty) | |
602 | { | |
603 | int channel = tty->index; | |
604 | struct ipoctal *ipoctal = tty->driver_data; | |
605 | ||
606 | return ipoctal->nb_bytes[channel]; | |
607 | } | |
608 | ||
609 | static void ipoctal_set_termios(struct tty_struct *tty, | |
610 | struct ktermios *old_termios) | |
611 | { | |
612 | unsigned int cflag; | |
613 | unsigned char mr1 = 0; | |
614 | unsigned char mr2 = 0; | |
615 | unsigned char csr = 0; | |
616 | unsigned int channel = tty->index; | |
617 | struct ipoctal *ipoctal = tty->driver_data; | |
618 | speed_t baud; | |
619 | ||
857196e2 | 620 | cflag = tty->termios.c_cflag; |
ba4dc61f SIG |
621 | |
622 | /* Disable and reset everything before change the setup */ | |
623 | ipoctal_write_io_reg(ipoctal, &ipoctal->chan_regs[channel].u.w.cr, | |
624 | CR_DISABLE_RX | CR_DISABLE_TX); | |
625 | ipoctal_write_cr_cmd(ipoctal, &ipoctal->chan_regs[channel].u.w.cr, | |
626 | CR_CMD_RESET_RX); | |
627 | ipoctal_write_cr_cmd(ipoctal, &ipoctal->chan_regs[channel].u.w.cr, | |
628 | CR_CMD_RESET_TX); | |
629 | ipoctal_write_cr_cmd(ipoctal, &ipoctal->chan_regs[channel].u.w.cr, | |
630 | CR_CMD_RESET_ERR_STATUS); | |
631 | ipoctal_write_cr_cmd(ipoctal, &ipoctal->chan_regs[channel].u.w.cr, | |
632 | CR_CMD_RESET_MR); | |
633 | ||
634 | /* Set Bits per chars */ | |
635 | switch (cflag & CSIZE) { | |
636 | case CS6: | |
637 | mr1 |= MR1_CHRL_6_BITS; | |
638 | break; | |
639 | case CS7: | |
640 | mr1 |= MR1_CHRL_7_BITS; | |
641 | break; | |
642 | case CS8: | |
643 | default: | |
644 | mr1 |= MR1_CHRL_8_BITS; | |
645 | /* By default, select CS8 */ | |
857196e2 | 646 | tty->termios.c_cflag = (cflag & ~CSIZE) | CS8; |
ba4dc61f SIG |
647 | break; |
648 | } | |
649 | ||
650 | /* Set Parity */ | |
651 | if (cflag & PARENB) | |
652 | if (cflag & PARODD) | |
653 | mr1 |= MR1_PARITY_ON | MR1_PARITY_ODD; | |
654 | else | |
655 | mr1 |= MR1_PARITY_ON | MR1_PARITY_EVEN; | |
656 | else | |
657 | mr1 |= MR1_PARITY_OFF; | |
658 | ||
659 | /* Mark or space parity is not supported */ | |
857196e2 | 660 | tty->termios.c_cflag &= ~CMSPAR; |
ba4dc61f SIG |
661 | |
662 | /* Set stop bits */ | |
663 | if (cflag & CSTOPB) | |
664 | mr2 |= MR2_STOP_BITS_LENGTH_2; | |
665 | else | |
666 | mr2 |= MR2_STOP_BITS_LENGTH_1; | |
667 | ||
668 | /* Set the flow control */ | |
669 | switch (ipoctal->board_id) { | |
670 | case IP_OCTAL_232_ID: | |
671 | if (cflag & CRTSCTS) { | |
672 | mr1 |= MR1_RxRTS_CONTROL_ON; | |
673 | mr2 |= MR2_TxRTS_CONTROL_OFF | MR2_CTS_ENABLE_TX_ON; | |
ba4dc61f SIG |
674 | } else { |
675 | mr1 |= MR1_RxRTS_CONTROL_OFF; | |
676 | mr2 |= MR2_TxRTS_CONTROL_OFF | MR2_CTS_ENABLE_TX_OFF; | |
ba4dc61f SIG |
677 | } |
678 | break; | |
679 | case IP_OCTAL_422_ID: | |
680 | mr1 |= MR1_RxRTS_CONTROL_OFF; | |
681 | mr2 |= MR2_TxRTS_CONTROL_OFF | MR2_CTS_ENABLE_TX_OFF; | |
ba4dc61f SIG |
682 | break; |
683 | case IP_OCTAL_485_ID: | |
684 | mr1 |= MR1_RxRTS_CONTROL_OFF; | |
685 | mr2 |= MR2_TxRTS_CONTROL_ON | MR2_CTS_ENABLE_TX_OFF; | |
ba4dc61f SIG |
686 | break; |
687 | default: | |
688 | return; | |
689 | break; | |
690 | } | |
691 | ||
692 | baud = tty_get_baud_rate(tty); | |
857196e2 | 693 | tty_termios_encode_baud_rate(&tty->termios, baud, baud); |
ba4dc61f SIG |
694 | |
695 | /* Set baud rate */ | |
857196e2 | 696 | switch (baud) { |
ba4dc61f SIG |
697 | case 75: |
698 | csr |= TX_CLK_75 | RX_CLK_75; | |
699 | break; | |
700 | case 110: | |
701 | csr |= TX_CLK_110 | RX_CLK_110; | |
702 | break; | |
703 | case 150: | |
704 | csr |= TX_CLK_150 | RX_CLK_150; | |
705 | break; | |
706 | case 300: | |
707 | csr |= TX_CLK_300 | RX_CLK_300; | |
708 | break; | |
709 | case 600: | |
710 | csr |= TX_CLK_600 | RX_CLK_600; | |
711 | break; | |
712 | case 1200: | |
713 | csr |= TX_CLK_1200 | RX_CLK_1200; | |
714 | break; | |
715 | case 1800: | |
716 | csr |= TX_CLK_1800 | RX_CLK_1800; | |
717 | break; | |
718 | case 2000: | |
719 | csr |= TX_CLK_2000 | RX_CLK_2000; | |
720 | break; | |
721 | case 2400: | |
722 | csr |= TX_CLK_2400 | RX_CLK_2400; | |
723 | break; | |
724 | case 4800: | |
725 | csr |= TX_CLK_4800 | RX_CLK_4800; | |
726 | break; | |
727 | case 9600: | |
728 | csr |= TX_CLK_9600 | RX_CLK_9600; | |
729 | break; | |
730 | case 19200: | |
731 | csr |= TX_CLK_19200 | RX_CLK_19200; | |
732 | break; | |
733 | case 38400: | |
734 | default: | |
735 | csr |= TX_CLK_38400 | RX_CLK_38400; | |
736 | /* In case of default, we establish 38400 bps */ | |
857196e2 | 737 | tty_termios_encode_baud_rate(&tty->termios, 38400, 38400); |
ba4dc61f SIG |
738 | break; |
739 | } | |
740 | ||
741 | mr1 |= MR1_ERROR_CHAR; | |
742 | mr1 |= MR1_RxINT_RxRDY; | |
743 | ||
744 | /* Write the control registers */ | |
745 | ipoctal_write_io_reg(ipoctal, &ipoctal->chan_regs[channel].u.w.mr, mr1); | |
746 | ipoctal_write_io_reg(ipoctal, &ipoctal->chan_regs[channel].u.w.mr, mr2); | |
747 | ipoctal_write_io_reg(ipoctal, &ipoctal->chan_regs[channel].u.w.csr, csr); | |
748 | ||
ba4dc61f SIG |
749 | /* Enable again the RX */ |
750 | ipoctal_write_io_reg(ipoctal, &ipoctal->chan_regs[channel].u.w.cr, | |
751 | CR_ENABLE_RX); | |
752 | } | |
753 | ||
754 | static void ipoctal_hangup(struct tty_struct *tty) | |
755 | { | |
756 | unsigned long flags; | |
757 | int channel = tty->index; | |
758 | struct ipoctal *ipoctal = tty->driver_data; | |
759 | ||
760 | if (ipoctal == NULL) | |
761 | return; | |
762 | ||
763 | spin_lock_irqsave(&ipoctal->lock[channel], flags); | |
764 | ipoctal->nb_bytes[channel] = 0; | |
765 | ipoctal->pointer_read[channel] = 0; | |
766 | ipoctal->pointer_write[channel] = 0; | |
767 | spin_unlock_irqrestore(&ipoctal->lock[channel], flags); | |
768 | ||
769 | tty_port_hangup(&ipoctal->tty_port[channel]); | |
770 | ||
771 | ipoctal_write_io_reg(ipoctal, &ipoctal->chan_regs[channel].u.w.cr, | |
772 | CR_DISABLE_RX | CR_DISABLE_TX); | |
773 | ipoctal_write_cr_cmd(ipoctal, &ipoctal->chan_regs[channel].u.w.cr, | |
774 | CR_CMD_RESET_RX); | |
775 | ipoctal_write_cr_cmd(ipoctal, &ipoctal->chan_regs[channel].u.w.cr, | |
776 | CR_CMD_RESET_TX); | |
777 | ipoctal_write_cr_cmd(ipoctal, &ipoctal->chan_regs[channel].u.w.cr, | |
778 | CR_CMD_RESET_ERR_STATUS); | |
779 | ipoctal_write_cr_cmd(ipoctal, &ipoctal->chan_regs[channel].u.w.cr, | |
780 | CR_CMD_RESET_MR); | |
781 | ||
782 | clear_bit(ASYNCB_INITIALIZED, &ipoctal->tty_port[channel].flags); | |
783 | wake_up_interruptible(&ipoctal->tty_port[channel].open_wait); | |
784 | } | |
785 | ||
786 | static const struct tty_operations ipoctal_fops = { | |
787 | .ioctl = NULL, | |
788 | .open = ipoctal_open, | |
789 | .close = ipoctal_close, | |
790 | .write = ipoctal_write_tty, | |
791 | .set_termios = ipoctal_set_termios, | |
792 | .write_room = ipoctal_write_room, | |
793 | .chars_in_buffer = ipoctal_chars_in_buffer, | |
794 | .get_icount = ipoctal_get_icount, | |
795 | .hangup = ipoctal_hangup, | |
796 | }; | |
797 | ||
798 | static int ipoctal_match(struct ipack_device *dev) | |
799 | { | |
800 | int res; | |
801 | unsigned char board_id; | |
802 | ||
ec440335 SIG |
803 | if ((!dev->bus->ops) || (!dev->bus->ops->map_space) || |
804 | (!dev->bus->ops->unmap_space)) | |
805 | return 0; | |
806 | ||
807 | res = dev->bus->ops->map_space(dev, 0, IPACK_ID_SPACE); | |
ba4dc61f | 808 | if (res) |
ec440335 | 809 | return 0; |
ba4dc61f SIG |
810 | |
811 | res = ipoctal_check_model(dev, &board_id); | |
ec440335 SIG |
812 | dev->bus->ops->unmap_space(dev, IPACK_ID_SPACE); |
813 | if (!res) | |
814 | return 1; | |
815 | ||
816 | return 0; | |
ba4dc61f SIG |
817 | } |
818 | ||
819 | static int ipoctal_probe(struct ipack_device *dev) | |
820 | { | |
821 | int res; | |
822 | struct ipoctal *ipoctal; | |
823 | ||
824 | ipoctal = kzalloc(sizeof(struct ipoctal), GFP_KERNEL); | |
825 | if (ipoctal == NULL) | |
826 | return -ENOMEM; | |
827 | ||
828 | ipoctal->dev = dev; | |
829 | res = ipoctal_inst_slot(ipoctal, dev->bus_nr, dev->slot, dev->irq); | |
830 | if (res) | |
831 | goto out_uninst; | |
832 | ||
833 | list_add_tail(&ipoctal->list, &ipoctal_list); | |
834 | return 0; | |
835 | ||
836 | out_uninst: | |
837 | kfree(ipoctal); | |
838 | return res; | |
839 | } | |
840 | ||
841 | static void __ipoctal_remove(struct ipoctal *ipoctal) | |
842 | { | |
843 | int i; | |
844 | ||
845 | for (i = 0; i < NR_CHANNELS; i++) { | |
846 | tty_unregister_device(ipoctal->tty_drv, i); | |
847 | tty_port_free_xmit_buf(&ipoctal->tty_port[i]); | |
848 | } | |
849 | ||
850 | tty_unregister_driver(ipoctal->tty_drv); | |
851 | put_tty_driver(ipoctal->tty_drv); | |
ba4dc61f SIG |
852 | list_del(&ipoctal->list); |
853 | kfree(ipoctal); | |
854 | } | |
855 | ||
856 | static void ipoctal_remove(struct ipack_device *device) | |
857 | { | |
858 | struct ipoctal *ipoctal, *next; | |
859 | ||
860 | list_for_each_entry_safe(ipoctal, next, &ipoctal_list, list) { | |
861 | if (ipoctal->dev == device) | |
862 | __ipoctal_remove(ipoctal); | |
863 | } | |
864 | } | |
865 | ||
866 | static struct ipack_driver_ops ipoctal_drv_ops = { | |
867 | .match = ipoctal_match, | |
868 | .probe = ipoctal_probe, | |
869 | .remove = ipoctal_remove, | |
870 | }; | |
871 | ||
872 | static int __init ipoctal_init(void) | |
873 | { | |
ba4dc61f | 874 | driver.ops = &ipoctal_drv_ops; |
ec440335 | 875 | return ipack_driver_register(&driver, THIS_MODULE, KBUILD_MODNAME); |
ba4dc61f SIG |
876 | } |
877 | ||
878 | static void __exit ipoctal_exit(void) | |
879 | { | |
880 | struct ipoctal *p, *next; | |
881 | ||
882 | list_for_each_entry_safe(p, next, &ipoctal_list, list) | |
597d473f | 883 | p->dev->bus->ops->remove_device(p->dev); |
ba4dc61f SIG |
884 | |
885 | ipack_driver_unregister(&driver); | |
886 | } | |
887 | ||
888 | MODULE_DESCRIPTION("IP-Octal 232, 422 and 485 device driver"); | |
889 | MODULE_LICENSE("GPL"); | |
890 | ||
891 | module_init(ipoctal_init); | |
892 | module_exit(ipoctal_exit); |