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