diff options
author | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2012-11-16 11:14:18 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2012-11-16 11:14:18 -0500 |
commit | 05e5027efc9c0bb6d1d04cde279afbafca0a7929 (patch) | |
tree | 733b0291db6cc6c13844b97e3c7bf593b6711d66 /drivers/ipack/devices/ipoctal.c | |
parent | 76859725ad31ac480d55bf176e5bbe0f9ab6e6cb (diff) |
Staging: ipack: move out of staging
The ipack subsystem is cleaned up enough to now move out of the staging
tree, and into drivers/ipack.
Cc: Samuel Iglesias Gonsalvez <siglesias@igalia.com>
Cc: Jens Taprogge <jens.taprogge@taprogge.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/ipack/devices/ipoctal.c')
-rw-r--r-- | drivers/ipack/devices/ipoctal.c | 751 |
1 files changed, 751 insertions, 0 deletions
diff --git a/drivers/ipack/devices/ipoctal.c b/drivers/ipack/devices/ipoctal.c new file mode 100644 index 000000000000..783f120338d1 --- /dev/null +++ b/drivers/ipack/devices/ipoctal.c | |||
@@ -0,0 +1,751 @@ | |||
1 | /** | ||
2 | * ipoctal.c | ||
3 | * | ||
4 | * driver for the GE IP-OCTAL boards | ||
5 | * | ||
6 | * Copyright (C) 2009-2012 CERN (www.cern.ch) | ||
7 | * Author: Nicolas Serafini, EIC2 SA | ||
8 | * Author: Samuel Iglesias Gonsalvez <siglesias@igalia.com> | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify it | ||
11 | * under the terms of the GNU General Public License as published by the Free | ||
12 | * Software Foundation; version 2 of the License. | ||
13 | */ | ||
14 | |||
15 | #include <linux/device.h> | ||
16 | #include <linux/module.h> | ||
17 | #include <linux/interrupt.h> | ||
18 | #include <linux/sched.h> | ||
19 | #include <linux/tty.h> | ||
20 | #include <linux/serial.h> | ||
21 | #include <linux/tty_flip.h> | ||
22 | #include <linux/slab.h> | ||
23 | #include <linux/atomic.h> | ||
24 | #include <linux/io.h> | ||
25 | #include "../ipack.h" | ||
26 | #include "ipoctal.h" | ||
27 | #include "scc2698.h" | ||
28 | |||
29 | #define IP_OCTAL_ID_SPACE_VECTOR 0x41 | ||
30 | #define IP_OCTAL_NB_BLOCKS 4 | ||
31 | |||
32 | static const struct tty_operations ipoctal_fops; | ||
33 | |||
34 | struct ipoctal_channel { | ||
35 | struct ipoctal_stats stats; | ||
36 | unsigned int nb_bytes; | ||
37 | wait_queue_head_t queue; | ||
38 | spinlock_t lock; | ||
39 | unsigned int pointer_read; | ||
40 | unsigned int pointer_write; | ||
41 | atomic_t open; | ||
42 | struct tty_port tty_port; | ||
43 | union scc2698_channel __iomem *regs; | ||
44 | union scc2698_block __iomem *block_regs; | ||
45 | unsigned int board_id; | ||
46 | unsigned char *board_write; | ||
47 | u8 isr_rx_rdy_mask; | ||
48 | u8 isr_tx_rdy_mask; | ||
49 | }; | ||
50 | |||
51 | struct ipoctal { | ||
52 | struct ipack_device *dev; | ||
53 | unsigned int board_id; | ||
54 | struct ipoctal_channel channel[NR_CHANNELS]; | ||
55 | unsigned char write; | ||
56 | struct tty_driver *tty_drv; | ||
57 | u8 __iomem *mem8_space; | ||
58 | u8 __iomem *int_space; | ||
59 | }; | ||
60 | |||
61 | static int ipoctal_port_activate(struct tty_port *port, struct tty_struct *tty) | ||
62 | { | ||
63 | struct ipoctal_channel *channel; | ||
64 | |||
65 | channel = dev_get_drvdata(tty->dev); | ||
66 | |||
67 | iowrite8(CR_ENABLE_RX, &channel->regs->w.cr); | ||
68 | return 0; | ||
69 | } | ||
70 | |||
71 | static int ipoctal_open(struct tty_struct *tty, struct file *file) | ||
72 | { | ||
73 | int res; | ||
74 | struct ipoctal_channel *channel; | ||
75 | |||
76 | channel = dev_get_drvdata(tty->dev); | ||
77 | |||
78 | if (atomic_read(&channel->open)) | ||
79 | return -EBUSY; | ||
80 | |||
81 | tty->driver_data = channel; | ||
82 | |||
83 | res = tty_port_open(&channel->tty_port, tty, file); | ||
84 | if (res) | ||
85 | return res; | ||
86 | |||
87 | atomic_inc(&channel->open); | ||
88 | return 0; | ||
89 | } | ||
90 | |||
91 | static void ipoctal_reset_stats(struct ipoctal_stats *stats) | ||
92 | { | ||
93 | stats->tx = 0; | ||
94 | stats->rx = 0; | ||
95 | stats->rcv_break = 0; | ||
96 | stats->framing_err = 0; | ||
97 | stats->overrun_err = 0; | ||
98 | stats->parity_err = 0; | ||
99 | } | ||
100 | |||
101 | static void ipoctal_free_channel(struct ipoctal_channel *channel) | ||
102 | { | ||
103 | ipoctal_reset_stats(&channel->stats); | ||
104 | channel->pointer_read = 0; | ||
105 | channel->pointer_write = 0; | ||
106 | channel->nb_bytes = 0; | ||
107 | } | ||
108 | |||
109 | static void ipoctal_close(struct tty_struct *tty, struct file *filp) | ||
110 | { | ||
111 | struct ipoctal_channel *channel = tty->driver_data; | ||
112 | |||
113 | tty_port_close(&channel->tty_port, tty, filp); | ||
114 | |||
115 | if (atomic_dec_and_test(&channel->open)) | ||
116 | ipoctal_free_channel(channel); | ||
117 | } | ||
118 | |||
119 | static int ipoctal_get_icount(struct tty_struct *tty, | ||
120 | struct serial_icounter_struct *icount) | ||
121 | { | ||
122 | struct ipoctal_channel *channel = tty->driver_data; | ||
123 | |||
124 | icount->cts = 0; | ||
125 | icount->dsr = 0; | ||
126 | icount->rng = 0; | ||
127 | icount->dcd = 0; | ||
128 | icount->rx = channel->stats.rx; | ||
129 | icount->tx = channel->stats.tx; | ||
130 | icount->frame = channel->stats.framing_err; | ||
131 | icount->parity = channel->stats.parity_err; | ||
132 | icount->brk = channel->stats.rcv_break; | ||
133 | return 0; | ||
134 | } | ||
135 | |||
136 | static void ipoctal_irq_rx(struct ipoctal_channel *channel, | ||
137 | struct tty_struct *tty, u8 sr) | ||
138 | { | ||
139 | unsigned char value; | ||
140 | unsigned char flag = TTY_NORMAL; | ||
141 | u8 isr; | ||
142 | |||
143 | do { | ||
144 | value = ioread8(&channel->regs->r.rhr); | ||
145 | /* Error: count statistics */ | ||
146 | if (sr & SR_ERROR) { | ||
147 | iowrite8(CR_CMD_RESET_ERR_STATUS, &channel->regs->w.cr); | ||
148 | |||
149 | if (sr & SR_OVERRUN_ERROR) { | ||
150 | channel->stats.overrun_err++; | ||
151 | /* Overrun doesn't affect the current character*/ | ||
152 | tty_insert_flip_char(tty, 0, TTY_OVERRUN); | ||
153 | } | ||
154 | if (sr & SR_PARITY_ERROR) { | ||
155 | channel->stats.parity_err++; | ||
156 | flag = TTY_PARITY; | ||
157 | } | ||
158 | if (sr & SR_FRAMING_ERROR) { | ||
159 | channel->stats.framing_err++; | ||
160 | flag = TTY_FRAME; | ||
161 | } | ||
162 | if (sr & SR_RECEIVED_BREAK) { | ||
163 | iowrite8(CR_CMD_RESET_BREAK_CHANGE, &channel->regs->w.cr); | ||
164 | channel->stats.rcv_break++; | ||
165 | flag = TTY_BREAK; | ||
166 | } | ||
167 | } | ||
168 | tty_insert_flip_char(tty, value, flag); | ||
169 | |||
170 | /* Check if there are more characters in RX FIFO | ||
171 | * If there are more, the isr register for this channel | ||
172 | * has enabled the RxRDY|FFULL bit. | ||
173 | */ | ||
174 | isr = ioread8(&channel->block_regs->r.isr); | ||
175 | sr = ioread8(&channel->regs->r.sr); | ||
176 | } while (isr & channel->isr_rx_rdy_mask); | ||
177 | |||
178 | tty_flip_buffer_push(tty); | ||
179 | } | ||
180 | |||
181 | static void ipoctal_irq_tx(struct ipoctal_channel *channel) | ||
182 | { | ||
183 | unsigned char value; | ||
184 | unsigned int *pointer_write = &channel->pointer_write; | ||
185 | |||
186 | if (channel->nb_bytes <= 0) { | ||
187 | channel->nb_bytes = 0; | ||
188 | return; | ||
189 | } | ||
190 | |||
191 | value = channel->tty_port.xmit_buf[*pointer_write]; | ||
192 | iowrite8(value, &channel->regs->w.thr); | ||
193 | channel->stats.tx++; | ||
194 | (*pointer_write)++; | ||
195 | *pointer_write = *pointer_write % PAGE_SIZE; | ||
196 | channel->nb_bytes--; | ||
197 | |||
198 | if ((channel->nb_bytes == 0) && | ||
199 | (waitqueue_active(&channel->queue))) { | ||
200 | |||
201 | if (channel->board_id != IPACK1_DEVICE_ID_SBS_OCTAL_485) { | ||
202 | *channel->board_write = 1; | ||
203 | wake_up_interruptible(&channel->queue); | ||
204 | } | ||
205 | } | ||
206 | } | ||
207 | |||
208 | static void ipoctal_irq_channel(struct ipoctal_channel *channel) | ||
209 | { | ||
210 | u8 isr, sr; | ||
211 | struct tty_struct *tty; | ||
212 | |||
213 | /* If there is no client, skip the check */ | ||
214 | if (!atomic_read(&channel->open)) | ||
215 | return; | ||
216 | |||
217 | tty = tty_port_tty_get(&channel->tty_port); | ||
218 | if (!tty) | ||
219 | return; | ||
220 | /* The HW is organized in pair of channels. See which register we need | ||
221 | * to read from */ | ||
222 | isr = ioread8(&channel->block_regs->r.isr); | ||
223 | sr = ioread8(&channel->regs->r.sr); | ||
224 | |||
225 | /* In case of RS-485, change from TX to RX when finishing TX. | ||
226 | * Half-duplex. */ | ||
227 | if ((channel->board_id == IPACK1_DEVICE_ID_SBS_OCTAL_485) && | ||
228 | (sr & SR_TX_EMPTY) && (channel->nb_bytes == 0)) { | ||
229 | iowrite8(CR_DISABLE_TX, &channel->regs->w.cr); | ||
230 | iowrite8(CR_CMD_NEGATE_RTSN, &channel->regs->w.cr); | ||
231 | iowrite8(CR_ENABLE_RX, &channel->regs->w.cr); | ||
232 | *channel->board_write = 1; | ||
233 | wake_up_interruptible(&channel->queue); | ||
234 | } | ||
235 | |||
236 | /* RX data */ | ||
237 | if ((isr & channel->isr_rx_rdy_mask) && (sr & SR_RX_READY)) | ||
238 | ipoctal_irq_rx(channel, tty, sr); | ||
239 | |||
240 | /* TX of each character */ | ||
241 | if ((isr & channel->isr_tx_rdy_mask) && (sr & SR_TX_READY)) | ||
242 | ipoctal_irq_tx(channel); | ||
243 | |||
244 | tty_flip_buffer_push(tty); | ||
245 | tty_kref_put(tty); | ||
246 | } | ||
247 | |||
248 | static irqreturn_t ipoctal_irq_handler(void *arg) | ||
249 | { | ||
250 | unsigned int i; | ||
251 | struct ipoctal *ipoctal = (struct ipoctal *) arg; | ||
252 | |||
253 | /* Check all channels */ | ||
254 | for (i = 0; i < NR_CHANNELS; i++) | ||
255 | ipoctal_irq_channel(&ipoctal->channel[i]); | ||
256 | |||
257 | /* Clear the IPack device interrupt */ | ||
258 | readw(ipoctal->int_space + ACK_INT_REQ0); | ||
259 | readw(ipoctal->int_space + ACK_INT_REQ1); | ||
260 | |||
261 | return IRQ_HANDLED; | ||
262 | } | ||
263 | |||
264 | static const struct tty_port_operations ipoctal_tty_port_ops = { | ||
265 | .dtr_rts = NULL, | ||
266 | .activate = ipoctal_port_activate, | ||
267 | }; | ||
268 | |||
269 | static int ipoctal_inst_slot(struct ipoctal *ipoctal, unsigned int bus_nr, | ||
270 | unsigned int slot) | ||
271 | { | ||
272 | int res; | ||
273 | int i; | ||
274 | struct tty_driver *tty; | ||
275 | char name[20]; | ||
276 | struct ipoctal_channel *channel; | ||
277 | struct ipack_region *region; | ||
278 | void __iomem *addr; | ||
279 | union scc2698_channel __iomem *chan_regs; | ||
280 | union scc2698_block __iomem *block_regs; | ||
281 | |||
282 | ipoctal->board_id = ipoctal->dev->id_device; | ||
283 | |||
284 | region = &ipoctal->dev->region[IPACK_IO_SPACE]; | ||
285 | addr = devm_ioremap_nocache(&ipoctal->dev->dev, | ||
286 | region->start, region->size); | ||
287 | if (!addr) { | ||
288 | dev_err(&ipoctal->dev->dev, | ||
289 | "Unable to map slot [%d:%d] IO space!\n", | ||
290 | bus_nr, slot); | ||
291 | return -EADDRNOTAVAIL; | ||
292 | } | ||
293 | /* Save the virtual address to access the registers easily */ | ||
294 | chan_regs = | ||
295 | (union scc2698_channel __iomem *) addr; | ||
296 | block_regs = | ||
297 | (union scc2698_block __iomem *) addr; | ||
298 | |||
299 | region = &ipoctal->dev->region[IPACK_INT_SPACE]; | ||
300 | ipoctal->int_space = | ||
301 | devm_ioremap_nocache(&ipoctal->dev->dev, | ||
302 | region->start, region->size); | ||
303 | if (!ipoctal->int_space) { | ||
304 | dev_err(&ipoctal->dev->dev, | ||
305 | "Unable to map slot [%d:%d] INT space!\n", | ||
306 | bus_nr, slot); | ||
307 | return -EADDRNOTAVAIL; | ||
308 | } | ||
309 | |||
310 | region = &ipoctal->dev->region[IPACK_MEM8_SPACE]; | ||
311 | ipoctal->mem8_space = | ||
312 | devm_ioremap_nocache(&ipoctal->dev->dev, | ||
313 | region->start, 0x8000); | ||
314 | if (!addr) { | ||
315 | dev_err(&ipoctal->dev->dev, | ||
316 | "Unable to map slot [%d:%d] MEM8 space!\n", | ||
317 | bus_nr, slot); | ||
318 | return -EADDRNOTAVAIL; | ||
319 | } | ||
320 | |||
321 | |||
322 | /* Disable RX and TX before touching anything */ | ||
323 | for (i = 0; i < NR_CHANNELS ; i++) { | ||
324 | struct ipoctal_channel *channel = &ipoctal->channel[i]; | ||
325 | channel->regs = chan_regs + i; | ||
326 | channel->block_regs = block_regs + (i >> 1); | ||
327 | channel->board_write = &ipoctal->write; | ||
328 | channel->board_id = ipoctal->board_id; | ||
329 | if (i & 1) { | ||
330 | channel->isr_tx_rdy_mask = ISR_TxRDY_B; | ||
331 | channel->isr_rx_rdy_mask = ISR_RxRDY_FFULL_B; | ||
332 | } else { | ||
333 | channel->isr_tx_rdy_mask = ISR_TxRDY_A; | ||
334 | channel->isr_rx_rdy_mask = ISR_RxRDY_FFULL_A; | ||
335 | } | ||
336 | |||
337 | iowrite8(CR_DISABLE_RX | CR_DISABLE_TX, &channel->regs->w.cr); | ||
338 | iowrite8(CR_CMD_RESET_RX, &channel->regs->w.cr); | ||
339 | iowrite8(CR_CMD_RESET_TX, &channel->regs->w.cr); | ||
340 | iowrite8(MR1_CHRL_8_BITS | MR1_ERROR_CHAR | MR1_RxINT_RxRDY, | ||
341 | &channel->regs->w.mr); /* mr1 */ | ||
342 | iowrite8(0, &channel->regs->w.mr); /* mr2 */ | ||
343 | iowrite8(TX_CLK_9600 | RX_CLK_9600, &channel->regs->w.csr); | ||
344 | } | ||
345 | |||
346 | for (i = 0; i < IP_OCTAL_NB_BLOCKS; i++) { | ||
347 | iowrite8(ACR_BRG_SET2, &block_regs[i].w.acr); | ||
348 | iowrite8(OPCR_MPP_OUTPUT | OPCR_MPOa_RTSN | OPCR_MPOb_RTSN, | ||
349 | &block_regs[i].w.opcr); | ||
350 | iowrite8(IMR_TxRDY_A | IMR_RxRDY_FFULL_A | IMR_DELTA_BREAK_A | | ||
351 | IMR_TxRDY_B | IMR_RxRDY_FFULL_B | IMR_DELTA_BREAK_B, | ||
352 | &block_regs[i].w.imr); | ||
353 | } | ||
354 | |||
355 | /* | ||
356 | * IP-OCTAL has different addresses to copy its IRQ vector. | ||
357 | * Depending of the carrier these addresses are accesible or not. | ||
358 | * More info in the datasheet. | ||
359 | */ | ||
360 | ipoctal->dev->bus->ops->request_irq(ipoctal->dev, | ||
361 | ipoctal_irq_handler, ipoctal); | ||
362 | /* Dummy write */ | ||
363 | iowrite8(1, ipoctal->mem8_space + 1); | ||
364 | |||
365 | /* Register the TTY device */ | ||
366 | |||
367 | /* Each IP-OCTAL channel is a TTY port */ | ||
368 | tty = alloc_tty_driver(NR_CHANNELS); | ||
369 | |||
370 | if (!tty) | ||
371 | return -ENOMEM; | ||
372 | |||
373 | /* Fill struct tty_driver with ipoctal data */ | ||
374 | tty->owner = THIS_MODULE; | ||
375 | tty->driver_name = KBUILD_MODNAME; | ||
376 | sprintf(name, KBUILD_MODNAME ".%d.%d.", bus_nr, slot); | ||
377 | tty->name = name; | ||
378 | tty->major = 0; | ||
379 | |||
380 | tty->minor_start = 0; | ||
381 | tty->type = TTY_DRIVER_TYPE_SERIAL; | ||
382 | tty->subtype = SERIAL_TYPE_NORMAL; | ||
383 | tty->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; | ||
384 | tty->init_termios = tty_std_termios; | ||
385 | tty->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; | ||
386 | tty->init_termios.c_ispeed = 9600; | ||
387 | tty->init_termios.c_ospeed = 9600; | ||
388 | |||
389 | tty_set_operations(tty, &ipoctal_fops); | ||
390 | res = tty_register_driver(tty); | ||
391 | if (res) { | ||
392 | dev_err(&ipoctal->dev->dev, "Can't register tty driver.\n"); | ||
393 | put_tty_driver(tty); | ||
394 | return res; | ||
395 | } | ||
396 | |||
397 | /* Save struct tty_driver for use it when uninstalling the device */ | ||
398 | ipoctal->tty_drv = tty; | ||
399 | |||
400 | for (i = 0; i < NR_CHANNELS; i++) { | ||
401 | struct device *tty_dev; | ||
402 | |||
403 | channel = &ipoctal->channel[i]; | ||
404 | tty_port_init(&channel->tty_port); | ||
405 | tty_port_alloc_xmit_buf(&channel->tty_port); | ||
406 | channel->tty_port.ops = &ipoctal_tty_port_ops; | ||
407 | |||
408 | ipoctal_reset_stats(&channel->stats); | ||
409 | channel->nb_bytes = 0; | ||
410 | init_waitqueue_head(&channel->queue); | ||
411 | |||
412 | spin_lock_init(&channel->lock); | ||
413 | channel->pointer_read = 0; | ||
414 | channel->pointer_write = 0; | ||
415 | tty_dev = tty_port_register_device(&channel->tty_port, tty, i, NULL); | ||
416 | if (IS_ERR(tty_dev)) { | ||
417 | dev_err(&ipoctal->dev->dev, "Failed to register tty device.\n"); | ||
418 | continue; | ||
419 | } | ||
420 | dev_set_drvdata(tty_dev, channel); | ||
421 | |||
422 | /* | ||
423 | * Enable again the RX. TX will be enabled when | ||
424 | * there is something to send | ||
425 | */ | ||
426 | iowrite8(CR_ENABLE_RX, &channel->regs->w.cr); | ||
427 | } | ||
428 | |||
429 | return 0; | ||
430 | } | ||
431 | |||
432 | static inline int ipoctal_copy_write_buffer(struct ipoctal_channel *channel, | ||
433 | const unsigned char *buf, | ||
434 | int count) | ||
435 | { | ||
436 | unsigned long flags; | ||
437 | int i; | ||
438 | unsigned int *pointer_read = &channel->pointer_read; | ||
439 | |||
440 | /* Copy the bytes from the user buffer to the internal one */ | ||
441 | for (i = 0; i < count; i++) { | ||
442 | if (i <= (PAGE_SIZE - channel->nb_bytes)) { | ||
443 | spin_lock_irqsave(&channel->lock, flags); | ||
444 | channel->tty_port.xmit_buf[*pointer_read] = buf[i]; | ||
445 | *pointer_read = (*pointer_read + 1) % PAGE_SIZE; | ||
446 | channel->nb_bytes++; | ||
447 | spin_unlock_irqrestore(&channel->lock, flags); | ||
448 | } else { | ||
449 | break; | ||
450 | } | ||
451 | } | ||
452 | return i; | ||
453 | } | ||
454 | |||
455 | static int ipoctal_write_tty(struct tty_struct *tty, | ||
456 | const unsigned char *buf, int count) | ||
457 | { | ||
458 | struct ipoctal_channel *channel = tty->driver_data; | ||
459 | unsigned int char_copied; | ||
460 | |||
461 | char_copied = ipoctal_copy_write_buffer(channel, buf, count); | ||
462 | |||
463 | /* As the IP-OCTAL 485 only supports half duplex, do it manually */ | ||
464 | if (channel->board_id == IPACK1_DEVICE_ID_SBS_OCTAL_485) { | ||
465 | iowrite8(CR_DISABLE_RX, &channel->regs->w.cr); | ||
466 | iowrite8(CR_CMD_ASSERT_RTSN, &channel->regs->w.cr); | ||
467 | } | ||
468 | |||
469 | /* | ||
470 | * Send a packet and then disable TX to avoid failure after several send | ||
471 | * operations | ||
472 | */ | ||
473 | iowrite8(CR_ENABLE_TX, &channel->regs->w.cr); | ||
474 | wait_event_interruptible(channel->queue, *channel->board_write); | ||
475 | iowrite8(CR_DISABLE_TX, &channel->regs->w.cr); | ||
476 | |||
477 | *channel->board_write = 0; | ||
478 | return char_copied; | ||
479 | } | ||
480 | |||
481 | static int ipoctal_write_room(struct tty_struct *tty) | ||
482 | { | ||
483 | struct ipoctal_channel *channel = tty->driver_data; | ||
484 | |||
485 | return PAGE_SIZE - channel->nb_bytes; | ||
486 | } | ||
487 | |||
488 | static int ipoctal_chars_in_buffer(struct tty_struct *tty) | ||
489 | { | ||
490 | struct ipoctal_channel *channel = tty->driver_data; | ||
491 | |||
492 | return channel->nb_bytes; | ||
493 | } | ||
494 | |||
495 | static void ipoctal_set_termios(struct tty_struct *tty, | ||
496 | struct ktermios *old_termios) | ||
497 | { | ||
498 | unsigned int cflag; | ||
499 | unsigned char mr1 = 0; | ||
500 | unsigned char mr2 = 0; | ||
501 | unsigned char csr = 0; | ||
502 | struct ipoctal_channel *channel = tty->driver_data; | ||
503 | speed_t baud; | ||
504 | |||
505 | cflag = tty->termios.c_cflag; | ||
506 | |||
507 | /* Disable and reset everything before change the setup */ | ||
508 | iowrite8(CR_DISABLE_RX | CR_DISABLE_TX, &channel->regs->w.cr); | ||
509 | iowrite8(CR_CMD_RESET_RX, &channel->regs->w.cr); | ||
510 | iowrite8(CR_CMD_RESET_TX, &channel->regs->w.cr); | ||
511 | iowrite8(CR_CMD_RESET_ERR_STATUS, &channel->regs->w.cr); | ||
512 | iowrite8(CR_CMD_RESET_MR, &channel->regs->w.cr); | ||
513 | |||
514 | /* Set Bits per chars */ | ||
515 | switch (cflag & CSIZE) { | ||
516 | case CS6: | ||
517 | mr1 |= MR1_CHRL_6_BITS; | ||
518 | break; | ||
519 | case CS7: | ||
520 | mr1 |= MR1_CHRL_7_BITS; | ||
521 | break; | ||
522 | case CS8: | ||
523 | default: | ||
524 | mr1 |= MR1_CHRL_8_BITS; | ||
525 | /* By default, select CS8 */ | ||
526 | tty->termios.c_cflag = (cflag & ~CSIZE) | CS8; | ||
527 | break; | ||
528 | } | ||
529 | |||
530 | /* Set Parity */ | ||
531 | if (cflag & PARENB) | ||
532 | if (cflag & PARODD) | ||
533 | mr1 |= MR1_PARITY_ON | MR1_PARITY_ODD; | ||
534 | else | ||
535 | mr1 |= MR1_PARITY_ON | MR1_PARITY_EVEN; | ||
536 | else | ||
537 | mr1 |= MR1_PARITY_OFF; | ||
538 | |||
539 | /* Mark or space parity is not supported */ | ||
540 | tty->termios.c_cflag &= ~CMSPAR; | ||
541 | |||
542 | /* Set stop bits */ | ||
543 | if (cflag & CSTOPB) | ||
544 | mr2 |= MR2_STOP_BITS_LENGTH_2; | ||
545 | else | ||
546 | mr2 |= MR2_STOP_BITS_LENGTH_1; | ||
547 | |||
548 | /* Set the flow control */ | ||
549 | switch (channel->board_id) { | ||
550 | case IPACK1_DEVICE_ID_SBS_OCTAL_232: | ||
551 | if (cflag & CRTSCTS) { | ||
552 | mr1 |= MR1_RxRTS_CONTROL_ON; | ||
553 | mr2 |= MR2_TxRTS_CONTROL_OFF | MR2_CTS_ENABLE_TX_ON; | ||
554 | } else { | ||
555 | mr1 |= MR1_RxRTS_CONTROL_OFF; | ||
556 | mr2 |= MR2_TxRTS_CONTROL_OFF | MR2_CTS_ENABLE_TX_OFF; | ||
557 | } | ||
558 | break; | ||
559 | case IPACK1_DEVICE_ID_SBS_OCTAL_422: | ||
560 | mr1 |= MR1_RxRTS_CONTROL_OFF; | ||
561 | mr2 |= MR2_TxRTS_CONTROL_OFF | MR2_CTS_ENABLE_TX_OFF; | ||
562 | break; | ||
563 | case IPACK1_DEVICE_ID_SBS_OCTAL_485: | ||
564 | mr1 |= MR1_RxRTS_CONTROL_OFF; | ||
565 | mr2 |= MR2_TxRTS_CONTROL_ON | MR2_CTS_ENABLE_TX_OFF; | ||
566 | break; | ||
567 | default: | ||
568 | return; | ||
569 | break; | ||
570 | } | ||
571 | |||
572 | baud = tty_get_baud_rate(tty); | ||
573 | tty_termios_encode_baud_rate(&tty->termios, baud, baud); | ||
574 | |||
575 | /* Set baud rate */ | ||
576 | switch (baud) { | ||
577 | case 75: | ||
578 | csr |= TX_CLK_75 | RX_CLK_75; | ||
579 | break; | ||
580 | case 110: | ||
581 | csr |= TX_CLK_110 | RX_CLK_110; | ||
582 | break; | ||
583 | case 150: | ||
584 | csr |= TX_CLK_150 | RX_CLK_150; | ||
585 | break; | ||
586 | case 300: | ||
587 | csr |= TX_CLK_300 | RX_CLK_300; | ||
588 | break; | ||
589 | case 600: | ||
590 | csr |= TX_CLK_600 | RX_CLK_600; | ||
591 | break; | ||
592 | case 1200: | ||
593 | csr |= TX_CLK_1200 | RX_CLK_1200; | ||
594 | break; | ||
595 | case 1800: | ||
596 | csr |= TX_CLK_1800 | RX_CLK_1800; | ||
597 | break; | ||
598 | case 2000: | ||
599 | csr |= TX_CLK_2000 | RX_CLK_2000; | ||
600 | break; | ||
601 | case 2400: | ||
602 | csr |= TX_CLK_2400 | RX_CLK_2400; | ||
603 | break; | ||
604 | case 4800: | ||
605 | csr |= TX_CLK_4800 | RX_CLK_4800; | ||
606 | break; | ||
607 | case 9600: | ||
608 | csr |= TX_CLK_9600 | RX_CLK_9600; | ||
609 | break; | ||
610 | case 19200: | ||
611 | csr |= TX_CLK_19200 | RX_CLK_19200; | ||
612 | break; | ||
613 | case 38400: | ||
614 | default: | ||
615 | csr |= TX_CLK_38400 | RX_CLK_38400; | ||
616 | /* In case of default, we establish 38400 bps */ | ||
617 | tty_termios_encode_baud_rate(&tty->termios, 38400, 38400); | ||
618 | break; | ||
619 | } | ||
620 | |||
621 | mr1 |= MR1_ERROR_CHAR; | ||
622 | mr1 |= MR1_RxINT_RxRDY; | ||
623 | |||
624 | /* Write the control registers */ | ||
625 | iowrite8(mr1, &channel->regs->w.mr); | ||
626 | iowrite8(mr2, &channel->regs->w.mr); | ||
627 | iowrite8(csr, &channel->regs->w.csr); | ||
628 | |||
629 | /* Enable again the RX */ | ||
630 | iowrite8(CR_ENABLE_RX, &channel->regs->w.cr); | ||
631 | } | ||
632 | |||
633 | static void ipoctal_hangup(struct tty_struct *tty) | ||
634 | { | ||
635 | unsigned long flags; | ||
636 | struct ipoctal_channel *channel = tty->driver_data; | ||
637 | |||
638 | if (channel == NULL) | ||
639 | return; | ||
640 | |||
641 | spin_lock_irqsave(&channel->lock, flags); | ||
642 | channel->nb_bytes = 0; | ||
643 | channel->pointer_read = 0; | ||
644 | channel->pointer_write = 0; | ||
645 | spin_unlock_irqrestore(&channel->lock, flags); | ||
646 | |||
647 | tty_port_hangup(&channel->tty_port); | ||
648 | |||
649 | iowrite8(CR_DISABLE_RX | CR_DISABLE_TX, &channel->regs->w.cr); | ||
650 | iowrite8(CR_CMD_RESET_RX, &channel->regs->w.cr); | ||
651 | iowrite8(CR_CMD_RESET_TX, &channel->regs->w.cr); | ||
652 | iowrite8(CR_CMD_RESET_ERR_STATUS, &channel->regs->w.cr); | ||
653 | iowrite8(CR_CMD_RESET_MR, &channel->regs->w.cr); | ||
654 | |||
655 | clear_bit(ASYNCB_INITIALIZED, &channel->tty_port.flags); | ||
656 | wake_up_interruptible(&channel->tty_port.open_wait); | ||
657 | } | ||
658 | |||
659 | static const struct tty_operations ipoctal_fops = { | ||
660 | .ioctl = NULL, | ||
661 | .open = ipoctal_open, | ||
662 | .close = ipoctal_close, | ||
663 | .write = ipoctal_write_tty, | ||
664 | .set_termios = ipoctal_set_termios, | ||
665 | .write_room = ipoctal_write_room, | ||
666 | .chars_in_buffer = ipoctal_chars_in_buffer, | ||
667 | .get_icount = ipoctal_get_icount, | ||
668 | .hangup = ipoctal_hangup, | ||
669 | }; | ||
670 | |||
671 | static int ipoctal_probe(struct ipack_device *dev) | ||
672 | { | ||
673 | int res; | ||
674 | struct ipoctal *ipoctal; | ||
675 | |||
676 | ipoctal = kzalloc(sizeof(struct ipoctal), GFP_KERNEL); | ||
677 | if (ipoctal == NULL) | ||
678 | return -ENOMEM; | ||
679 | |||
680 | ipoctal->dev = dev; | ||
681 | res = ipoctal_inst_slot(ipoctal, dev->bus->bus_nr, dev->slot); | ||
682 | if (res) | ||
683 | goto out_uninst; | ||
684 | |||
685 | dev_set_drvdata(&dev->dev, ipoctal); | ||
686 | return 0; | ||
687 | |||
688 | out_uninst: | ||
689 | kfree(ipoctal); | ||
690 | return res; | ||
691 | } | ||
692 | |||
693 | static void __ipoctal_remove(struct ipoctal *ipoctal) | ||
694 | { | ||
695 | int i; | ||
696 | |||
697 | ipoctal->dev->bus->ops->free_irq(ipoctal->dev); | ||
698 | |||
699 | for (i = 0; i < NR_CHANNELS; i++) { | ||
700 | struct ipoctal_channel *channel = &ipoctal->channel[i]; | ||
701 | tty_unregister_device(ipoctal->tty_drv, i); | ||
702 | tty_port_free_xmit_buf(&channel->tty_port); | ||
703 | } | ||
704 | |||
705 | tty_unregister_driver(ipoctal->tty_drv); | ||
706 | put_tty_driver(ipoctal->tty_drv); | ||
707 | kfree(ipoctal); | ||
708 | } | ||
709 | |||
710 | static void ipoctal_remove(struct ipack_device *idev) | ||
711 | { | ||
712 | __ipoctal_remove(dev_get_drvdata(&idev->dev)); | ||
713 | } | ||
714 | |||
715 | static DEFINE_IPACK_DEVICE_TABLE(ipoctal_ids) = { | ||
716 | { IPACK_DEVICE(IPACK_ID_VERSION_1, IPACK1_VENDOR_ID_SBS, | ||
717 | IPACK1_DEVICE_ID_SBS_OCTAL_232) }, | ||
718 | { IPACK_DEVICE(IPACK_ID_VERSION_1, IPACK1_VENDOR_ID_SBS, | ||
719 | IPACK1_DEVICE_ID_SBS_OCTAL_422) }, | ||
720 | { IPACK_DEVICE(IPACK_ID_VERSION_1, IPACK1_VENDOR_ID_SBS, | ||
721 | IPACK1_DEVICE_ID_SBS_OCTAL_485) }, | ||
722 | { 0, }, | ||
723 | }; | ||
724 | |||
725 | MODULE_DEVICE_TABLE(ipack, ipoctal_ids); | ||
726 | |||
727 | static const struct ipack_driver_ops ipoctal_drv_ops = { | ||
728 | .probe = ipoctal_probe, | ||
729 | .remove = ipoctal_remove, | ||
730 | }; | ||
731 | |||
732 | static struct ipack_driver driver = { | ||
733 | .ops = &ipoctal_drv_ops, | ||
734 | .id_table = ipoctal_ids, | ||
735 | }; | ||
736 | |||
737 | static int __init ipoctal_init(void) | ||
738 | { | ||
739 | return ipack_driver_register(&driver, THIS_MODULE, KBUILD_MODNAME); | ||
740 | } | ||
741 | |||
742 | static void __exit ipoctal_exit(void) | ||
743 | { | ||
744 | ipack_driver_unregister(&driver); | ||
745 | } | ||
746 | |||
747 | MODULE_DESCRIPTION("IP-Octal 232, 422 and 485 device driver"); | ||
748 | MODULE_LICENSE("GPL"); | ||
749 | |||
750 | module_init(ipoctal_init); | ||
751 | module_exit(ipoctal_exit); | ||