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