1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * icom.c |
4 | * |
5 | * Copyright (C) 2001 IBM Corporation. All rights reserved. |
6 | * |
7 | * Serial device driver. |
8 | * |
9 | * Based on code from serial.c |
10 | */ |
11 | #include <linux/module.h> |
12 | #include <linux/kernel.h> |
13 | #include <linux/errno.h> |
14 | #include <linux/signal.h> |
15 | #include <linux/timer.h> |
16 | #include <linux/interrupt.h> |
17 | #include <linux/tty.h> |
18 | #include <linux/termios.h> |
19 | #include <linux/fs.h> |
20 | #include <linux/tty_flip.h> |
21 | #include <linux/serial.h> |
22 | #include <linux/serial_core.h> |
23 | #include <linux/serial_reg.h> |
24 | #include <linux/major.h> |
25 | #include <linux/string.h> |
26 | #include <linux/fcntl.h> |
27 | #include <linux/ptrace.h> |
28 | #include <linux/ioport.h> |
29 | #include <linux/mm.h> |
30 | #include <linux/slab.h> |
31 | #include <linux/init.h> |
32 | #include <linux/delay.h> |
33 | #include <linux/pci.h> |
34 | #include <linux/vmalloc.h> |
35 | #include <linux/smp.h> |
36 | #include <linux/spinlock.h> |
37 | #include <linux/kref.h> |
38 | #include <linux/firmware.h> |
39 | #include <linux/bitops.h> |
40 | |
41 | #include <linux/io.h> |
42 | #include <asm/irq.h> |
43 | #include <linux/uaccess.h> |
44 | |
45 | /*#define ICOM_TRACE enable port trace capabilities */ |
46 | |
47 | #define ICOM_DRIVER_NAME "icom" |
48 | #define NR_PORTS 128 |
49 | |
50 | static const unsigned int icom_acfg_baud[] = { |
51 | 300, |
52 | 600, |
53 | 900, |
54 | 1200, |
55 | 1800, |
56 | 2400, |
57 | 3600, |
58 | 4800, |
59 | 7200, |
60 | 9600, |
61 | 14400, |
62 | 19200, |
63 | 28800, |
64 | 38400, |
65 | 57600, |
66 | 76800, |
67 | 115200, |
68 | 153600, |
69 | 230400, |
70 | 307200, |
71 | 460800, |
72 | }; |
73 | #define BAUD_TABLE_LIMIT (ARRAY_SIZE(icom_acfg_baud) - 1) |
74 | |
75 | struct icom_regs { |
76 | u32 control; /* Adapter Control Register */ |
77 | u32 interrupt; /* Adapter Interrupt Register */ |
78 | u32 int_mask; /* Adapter Interrupt Mask Reg */ |
79 | u32 int_pri; /* Adapter Interrupt Priority r */ |
80 | u32 int_reg_b; /* Adapter non-masked Interrupt */ |
81 | u32 resvd01; |
82 | u32 resvd02; |
83 | u32 resvd03; |
84 | u32 control_2; /* Adapter Control Register 2 */ |
85 | u32 interrupt_2; /* Adapter Interrupt Register 2 */ |
86 | u32 int_mask_2; /* Adapter Interrupt Mask 2 */ |
87 | u32 int_pri_2; /* Adapter Interrupt Prior 2 */ |
88 | u32 int_reg_2b; /* Adapter non-masked 2 */ |
89 | }; |
90 | |
91 | struct func_dram { |
92 | u32 reserved[108]; /* 0-1B0 reserved by personality code */ |
93 | u32 RcvStatusAddr; /* 1B0-1B3 Status Address for Next rcv */ |
94 | u8 RcvStnAddr; /* 1B4 Receive Station Addr */ |
95 | u8 IdleState; /* 1B5 Idle State */ |
96 | u8 IdleMonitor; /* 1B6 Idle Monitor */ |
97 | u8 FlagFillIdleTimer; /* 1B7 Flag Fill Idle Timer */ |
98 | u32 XmitStatusAddr; /* 1B8-1BB Transmit Status Address */ |
99 | u8 StartXmitCmd; /* 1BC Start Xmit Command */ |
100 | u8 HDLCConfigReg; /* 1BD Reserved */ |
101 | u8 CauseCode; /* 1BE Cause code for fatal error */ |
102 | u8 xchar; /* 1BF High priority send */ |
103 | u32 reserved3; /* 1C0-1C3 Reserved */ |
104 | u8 PrevCmdReg; /* 1C4 Reserved */ |
105 | u8 CmdReg; /* 1C5 Command Register */ |
106 | u8 async_config2; /* 1C6 Async Config Byte 2 */ |
107 | u8 async_config3; /* 1C7 Async Config Byte 3 */ |
108 | u8 dce_resvd[20]; /* 1C8-1DB DCE Rsvd */ |
109 | u8 dce_resvd21; /* 1DC DCE Rsvd (21st byte */ |
110 | u8 misc_flags; /* 1DD misc flags */ |
111 | #define V2_HARDWARE 0x40 |
112 | #define ICOM_HDW_ACTIVE 0x01 |
113 | u8 call_length; /* 1DE Phone #/CFI buff ln */ |
114 | u8 call_length2; /* 1DF Upper byte (unused) */ |
115 | u32 call_addr; /* 1E0-1E3 Phn #/CFI buff addr */ |
116 | u16 timer_value; /* 1E4-1E5 general timer value */ |
117 | u8 timer_command; /* 1E6 general timer cmd */ |
118 | u8 dce_command; /* 1E7 dce command reg */ |
119 | u8 dce_cmd_status; /* 1E8 dce command stat */ |
120 | u8 x21_r1_ioff; /* 1E9 dce ready counter */ |
121 | u8 x21_r0_ioff; /* 1EA dce not ready ctr */ |
122 | u8 x21_ralt_ioff; /* 1EB dce CNR counter */ |
123 | u8 x21_r1_ion; /* 1EC dce ready I on ctr */ |
124 | u8 rsvd_ier; /* 1ED Rsvd for IER (if ne */ |
125 | u8 ier; /* 1EE Interrupt Enable */ |
126 | u8 isr; /* 1EF Input Signal Reg */ |
127 | u8 osr; /* 1F0 Output Signal Reg */ |
128 | u8 reset; /* 1F1 Reset/Reload Reg */ |
129 | u8 disable; /* 1F2 Disable Reg */ |
130 | u8 sync; /* 1F3 Sync Reg */ |
131 | u8 error_stat; /* 1F4 Error Status */ |
132 | u8 cable_id; /* 1F5 Cable ID */ |
133 | u8 cs_length; /* 1F6 CS Load Length */ |
134 | u8 mac_length; /* 1F7 Mac Load Length */ |
135 | u32 cs_load_addr; /* 1F8-1FB Call Load PCI Addr */ |
136 | u32 mac_load_addr; /* 1FC-1FF Mac Load PCI Addr */ |
137 | }; |
138 | |
139 | /* |
140 | * adapter defines and structures |
141 | */ |
142 | #define ICOM_CONTROL_START_A 0x00000008 |
143 | #define ICOM_CONTROL_STOP_A 0x00000004 |
144 | #define ICOM_CONTROL_START_B 0x00000002 |
145 | #define ICOM_CONTROL_STOP_B 0x00000001 |
146 | #define ICOM_CONTROL_START_C 0x00000008 |
147 | #define ICOM_CONTROL_STOP_C 0x00000004 |
148 | #define ICOM_CONTROL_START_D 0x00000002 |
149 | #define ICOM_CONTROL_STOP_D 0x00000001 |
150 | #define ICOM_IRAM_OFFSET 0x1000 |
151 | #define ICOM_IRAM_SIZE 0x0C00 |
152 | #define ICOM_DCE_IRAM_OFFSET 0x0A00 |
153 | #define ICOM_CABLE_ID_VALID 0x01 |
154 | #define ICOM_CABLE_ID_MASK 0xF0 |
155 | #define ICOM_DISABLE 0x80 |
156 | #define CMD_XMIT_RCV_ENABLE 0xC0 |
157 | #define CMD_XMIT_ENABLE 0x40 |
158 | #define CMD_RCV_DISABLE 0x00 |
159 | #define CMD_RCV_ENABLE 0x80 |
160 | #define CMD_RESTART 0x01 |
161 | #define CMD_HOLD_XMIT 0x02 |
162 | #define CMD_SND_BREAK 0x04 |
163 | #define RS232_CABLE 0x06 |
164 | #define V24_CABLE 0x0E |
165 | #define V35_CABLE 0x0C |
166 | #define V36_CABLE 0x02 |
167 | #define NO_CABLE 0x00 |
168 | #define START_DOWNLOAD 0x80 |
169 | #define ICOM_INT_MASK_PRC_A 0x00003FFF |
170 | #define ICOM_INT_MASK_PRC_B 0x3FFF0000 |
171 | #define ICOM_INT_MASK_PRC_C 0x00003FFF |
172 | #define ICOM_INT_MASK_PRC_D 0x3FFF0000 |
173 | #define INT_RCV_COMPLETED 0x1000 |
174 | #define INT_XMIT_COMPLETED 0x2000 |
175 | #define INT_IDLE_DETECT 0x0800 |
176 | #define INT_RCV_DISABLED 0x0400 |
177 | #define INT_XMIT_DISABLED 0x0200 |
178 | #define INT_RCV_XMIT_SHUTDOWN 0x0100 |
179 | #define INT_FATAL_ERROR 0x0080 |
180 | #define INT_CABLE_PULL 0x0020 |
181 | #define INT_SIGNAL_CHANGE 0x0010 |
182 | #define HDLC_PPP_PURE_ASYNC 0x02 |
183 | #define HDLC_FF_FILL 0x00 |
184 | #define HDLC_HDW_FLOW 0x01 |
185 | #define START_XMIT 0x80 |
186 | #define ICOM_ACFG_DRIVE1 0x20 |
187 | #define ICOM_ACFG_NO_PARITY 0x00 |
188 | #define ICOM_ACFG_PARITY_ENAB 0x02 |
189 | #define ICOM_ACFG_PARITY_ODD 0x01 |
190 | #define ICOM_ACFG_8BPC 0x00 |
191 | #define ICOM_ACFG_7BPC 0x04 |
192 | #define ICOM_ACFG_6BPC 0x08 |
193 | #define ICOM_ACFG_5BPC 0x0C |
194 | #define ICOM_ACFG_1STOP_BIT 0x00 |
195 | #define ICOM_ACFG_2STOP_BIT 0x10 |
196 | #define ICOM_DTR 0x80 |
197 | #define ICOM_RTS 0x40 |
198 | #define ICOM_RI 0x08 |
199 | #define ICOM_DSR 0x80 |
200 | #define ICOM_DCD 0x20 |
201 | #define ICOM_CTS 0x40 |
202 | |
203 | #define NUM_XBUFFS 1 |
204 | #define NUM_RBUFFS 2 |
205 | #define RCV_BUFF_SZ 0x0200 |
206 | #define XMIT_BUFF_SZ 0x1000 |
207 | struct statusArea { |
208 | /**********************************************/ |
209 | /* Transmit Status Area */ |
210 | /**********************************************/ |
211 | struct xmit_status_area{ |
212 | __le32 leNext; /* Next entry in Little Endian on Adapter */ |
213 | __le32 leNextASD; |
214 | __le32 leBuffer; /* Buffer for entry in LE for Adapter */ |
215 | __le16 leLengthASD; |
216 | __le16 leOffsetASD; |
217 | __le16 leLength; /* Length of data in segment */ |
218 | __le16 flags; |
219 | #define SA_FLAGS_DONE 0x0080 /* Done with Segment */ |
220 | #define SA_FLAGS_CONTINUED 0x8000 /* More Segments */ |
221 | #define SA_FLAGS_IDLE 0x4000 /* Mark IDLE after frm */ |
222 | #define SA_FLAGS_READY_TO_XMIT 0x0800 |
223 | #define SA_FLAGS_STAT_MASK 0x007F |
224 | } xmit[NUM_XBUFFS]; |
225 | |
226 | /**********************************************/ |
227 | /* Receive Status Area */ |
228 | /**********************************************/ |
229 | struct { |
230 | __le32 leNext; /* Next entry in Little Endian on Adapter */ |
231 | __le32 leNextASD; |
232 | __le32 leBuffer; /* Buffer for entry in LE for Adapter */ |
233 | __le16 WorkingLength; /* size of segment */ |
234 | __le16 reserv01; |
235 | __le16 leLength; /* Length of data in segment */ |
236 | __le16 flags; |
237 | #define SA_FL_RCV_DONE 0x0010 /* Data ready */ |
238 | #define SA_FLAGS_OVERRUN 0x0040 |
239 | #define SA_FLAGS_PARITY_ERROR 0x0080 |
240 | #define SA_FLAGS_FRAME_ERROR 0x0001 |
241 | #define SA_FLAGS_FRAME_TRUNC 0x0002 |
242 | #define SA_FLAGS_BREAK_DET 0x0004 /* set conditionally by device driver, not hardware */ |
243 | #define SA_FLAGS_RCV_MASK 0xFFE6 |
244 | } rcv[NUM_RBUFFS]; |
245 | }; |
246 | |
247 | struct icom_adapter; |
248 | |
249 | |
250 | #define ICOM_MAJOR 243 |
251 | #define ICOM_MINOR_START 0 |
252 | |
253 | struct icom_port { |
254 | struct uart_port uart_port; |
255 | unsigned char cable_id; |
256 | unsigned char read_status_mask; |
257 | unsigned char ignore_status_mask; |
258 | void __iomem * int_reg; |
259 | struct icom_regs __iomem *global_reg; |
260 | struct func_dram __iomem *dram; |
261 | int port; |
262 | struct statusArea *statStg; |
263 | dma_addr_t statStg_pci; |
264 | __le32 *xmitRestart; |
265 | dma_addr_t xmitRestart_pci; |
266 | unsigned char *xmit_buf; |
267 | dma_addr_t xmit_buf_pci; |
268 | unsigned char *recv_buf; |
269 | dma_addr_t recv_buf_pci; |
270 | int next_rcv; |
271 | int status; |
272 | #define ICOM_PORT_ACTIVE 1 /* Port exists. */ |
273 | #define ICOM_PORT_OFF 0 /* Port does not exist. */ |
274 | struct icom_adapter *adapter; |
275 | }; |
276 | |
277 | struct icom_adapter { |
278 | void __iomem * base_addr; |
279 | unsigned long base_addr_pci; |
280 | struct pci_dev *pci_dev; |
281 | struct icom_port port_info[4]; |
282 | int index; |
283 | int version; |
284 | #define ADAPTER_V1 0x0001 |
285 | #define ADAPTER_V2 0x0002 |
286 | u32 subsystem_id; |
287 | #define FOUR_PORT_MODEL 0x0252 |
288 | #define V2_TWO_PORTS_RVX 0x021A |
289 | #define V2_ONE_PORT_RVX_ONE_PORT_IMBED_MDM 0x0251 |
290 | int numb_ports; |
291 | struct list_head icom_adapter_entry; |
292 | struct kref kref; |
293 | }; |
294 | |
295 | /* prototype */ |
296 | extern void iCom_sercons_init(void); |
297 | |
298 | struct lookup_proc_table { |
299 | u32 __iomem *global_control_reg; |
300 | unsigned long processor_id; |
301 | }; |
302 | |
303 | struct lookup_int_table { |
304 | u32 __iomem *global_int_mask; |
305 | unsigned long processor_id; |
306 | }; |
307 | |
308 | static inline struct icom_port *to_icom_port(struct uart_port *port) |
309 | { |
310 | return container_of(port, struct icom_port, uart_port); |
311 | } |
312 | |
313 | static const struct pci_device_id icom_pci_table[] = { |
314 | { |
315 | .vendor = PCI_VENDOR_ID_IBM, |
316 | .device = PCI_DEVICE_ID_IBM_ICOM_DEV_ID_1, |
317 | .subvendor = PCI_ANY_ID, |
318 | .subdevice = PCI_ANY_ID, |
319 | .driver_data = ADAPTER_V1, |
320 | }, |
321 | { |
322 | .vendor = PCI_VENDOR_ID_IBM, |
323 | .device = PCI_DEVICE_ID_IBM_ICOM_DEV_ID_2, |
324 | .subvendor = PCI_VENDOR_ID_IBM, |
325 | .subdevice = PCI_DEVICE_ID_IBM_ICOM_V2_TWO_PORTS_RVX, |
326 | .driver_data = ADAPTER_V2, |
327 | }, |
328 | { |
329 | .vendor = PCI_VENDOR_ID_IBM, |
330 | .device = PCI_DEVICE_ID_IBM_ICOM_DEV_ID_2, |
331 | .subvendor = PCI_VENDOR_ID_IBM, |
332 | .subdevice = PCI_DEVICE_ID_IBM_ICOM_V2_ONE_PORT_RVX_ONE_PORT_MDM, |
333 | .driver_data = ADAPTER_V2, |
334 | }, |
335 | { |
336 | .vendor = PCI_VENDOR_ID_IBM, |
337 | .device = PCI_DEVICE_ID_IBM_ICOM_DEV_ID_2, |
338 | .subvendor = PCI_VENDOR_ID_IBM, |
339 | .subdevice = PCI_DEVICE_ID_IBM_ICOM_FOUR_PORT_MODEL, |
340 | .driver_data = ADAPTER_V2, |
341 | }, |
342 | { |
343 | .vendor = PCI_VENDOR_ID_IBM, |
344 | .device = PCI_DEVICE_ID_IBM_ICOM_DEV_ID_2, |
345 | .subvendor = PCI_VENDOR_ID_IBM, |
346 | .subdevice = PCI_DEVICE_ID_IBM_ICOM_V2_ONE_PORT_RVX_ONE_PORT_MDM_PCIE, |
347 | .driver_data = ADAPTER_V2, |
348 | }, |
349 | {} |
350 | }; |
351 | |
352 | static struct lookup_proc_table start_proc[4] = { |
353 | {NULL, ICOM_CONTROL_START_A}, |
354 | {NULL, ICOM_CONTROL_START_B}, |
355 | {NULL, ICOM_CONTROL_START_C}, |
356 | {NULL, ICOM_CONTROL_START_D} |
357 | }; |
358 | |
359 | |
360 | static struct lookup_proc_table stop_proc[4] = { |
361 | {NULL, ICOM_CONTROL_STOP_A}, |
362 | {NULL, ICOM_CONTROL_STOP_B}, |
363 | {NULL, ICOM_CONTROL_STOP_C}, |
364 | {NULL, ICOM_CONTROL_STOP_D} |
365 | }; |
366 | |
367 | static struct lookup_int_table int_mask_tbl[4] = { |
368 | {NULL, ICOM_INT_MASK_PRC_A}, |
369 | {NULL, ICOM_INT_MASK_PRC_B}, |
370 | {NULL, ICOM_INT_MASK_PRC_C}, |
371 | {NULL, ICOM_INT_MASK_PRC_D}, |
372 | }; |
373 | |
374 | |
375 | MODULE_DEVICE_TABLE(pci, icom_pci_table); |
376 | |
377 | static LIST_HEAD(icom_adapter_head); |
378 | |
379 | /* spinlock for adapter initialization and changing adapter operations */ |
380 | static DEFINE_SPINLOCK(icom_lock); |
381 | |
382 | #ifdef ICOM_TRACE |
383 | static inline void trace(struct icom_port *icom_port, char *trace_pt, |
384 | unsigned long trace_data) |
385 | { |
386 | dev_info(&icom_port->adapter->pci_dev->dev, ":%d:%s - %lx\n" , |
387 | icom_port->port, trace_pt, trace_data); |
388 | } |
389 | #else |
390 | static inline void trace(struct icom_port *icom_port, char *trace_pt, unsigned long trace_data) {}; |
391 | #endif |
392 | static void icom_kref_release(struct kref *kref); |
393 | |
394 | static void free_port_memory(struct icom_port *icom_port) |
395 | { |
396 | struct pci_dev *dev = icom_port->adapter->pci_dev; |
397 | |
398 | trace(icom_port, trace_pt: "RET_PORT_MEM" , trace_data: 0); |
399 | if (icom_port->recv_buf) { |
400 | dma_free_coherent(dev: &dev->dev, size: 4096, cpu_addr: icom_port->recv_buf, |
401 | dma_handle: icom_port->recv_buf_pci); |
402 | icom_port->recv_buf = NULL; |
403 | } |
404 | if (icom_port->xmit_buf) { |
405 | dma_free_coherent(dev: &dev->dev, size: 4096, cpu_addr: icom_port->xmit_buf, |
406 | dma_handle: icom_port->xmit_buf_pci); |
407 | icom_port->xmit_buf = NULL; |
408 | } |
409 | if (icom_port->statStg) { |
410 | dma_free_coherent(dev: &dev->dev, size: 4096, cpu_addr: icom_port->statStg, |
411 | dma_handle: icom_port->statStg_pci); |
412 | icom_port->statStg = NULL; |
413 | } |
414 | |
415 | if (icom_port->xmitRestart) { |
416 | dma_free_coherent(dev: &dev->dev, size: 4096, cpu_addr: icom_port->xmitRestart, |
417 | dma_handle: icom_port->xmitRestart_pci); |
418 | icom_port->xmitRestart = NULL; |
419 | } |
420 | } |
421 | |
422 | static int get_port_memory(struct icom_port *icom_port) |
423 | { |
424 | int index; |
425 | unsigned long stgAddr; |
426 | unsigned long startStgAddr; |
427 | unsigned long offset; |
428 | struct pci_dev *dev = icom_port->adapter->pci_dev; |
429 | |
430 | icom_port->xmit_buf = |
431 | dma_alloc_coherent(dev: &dev->dev, size: 4096, dma_handle: &icom_port->xmit_buf_pci, |
432 | GFP_KERNEL); |
433 | if (!icom_port->xmit_buf) { |
434 | dev_err(&dev->dev, "Can not allocate Transmit buffer\n" ); |
435 | return -ENOMEM; |
436 | } |
437 | |
438 | trace(icom_port, trace_pt: "GET_PORT_MEM" , |
439 | trace_data: (unsigned long) icom_port->xmit_buf); |
440 | |
441 | icom_port->recv_buf = |
442 | dma_alloc_coherent(dev: &dev->dev, size: 4096, dma_handle: &icom_port->recv_buf_pci, |
443 | GFP_KERNEL); |
444 | if (!icom_port->recv_buf) { |
445 | dev_err(&dev->dev, "Can not allocate Receive buffer\n" ); |
446 | free_port_memory(icom_port); |
447 | return -ENOMEM; |
448 | } |
449 | trace(icom_port, trace_pt: "GET_PORT_MEM" , |
450 | trace_data: (unsigned long) icom_port->recv_buf); |
451 | |
452 | icom_port->statStg = |
453 | dma_alloc_coherent(dev: &dev->dev, size: 4096, dma_handle: &icom_port->statStg_pci, |
454 | GFP_KERNEL); |
455 | if (!icom_port->statStg) { |
456 | dev_err(&dev->dev, "Can not allocate Status buffer\n" ); |
457 | free_port_memory(icom_port); |
458 | return -ENOMEM; |
459 | } |
460 | trace(icom_port, trace_pt: "GET_PORT_MEM" , |
461 | trace_data: (unsigned long) icom_port->statStg); |
462 | |
463 | icom_port->xmitRestart = |
464 | dma_alloc_coherent(dev: &dev->dev, size: 4096, dma_handle: &icom_port->xmitRestart_pci, |
465 | GFP_KERNEL); |
466 | if (!icom_port->xmitRestart) { |
467 | dev_err(&dev->dev, |
468 | "Can not allocate xmit Restart buffer\n" ); |
469 | free_port_memory(icom_port); |
470 | return -ENOMEM; |
471 | } |
472 | |
473 | /* FODs: Frame Out Descriptor Queue, this is a FIFO queue that |
474 | indicates that frames are to be transmitted |
475 | */ |
476 | |
477 | stgAddr = (unsigned long) icom_port->statStg; |
478 | for (index = 0; index < NUM_XBUFFS; index++) { |
479 | trace(icom_port, trace_pt: "FOD_ADDR" , trace_data: stgAddr); |
480 | stgAddr = stgAddr + sizeof(icom_port->statStg->xmit[0]); |
481 | if (index < (NUM_XBUFFS - 1)) { |
482 | memset(&icom_port->statStg->xmit[index], 0, sizeof(struct xmit_status_area)); |
483 | icom_port->statStg->xmit[index].leLengthASD = |
484 | cpu_to_le16(XMIT_BUFF_SZ); |
485 | trace(icom_port, trace_pt: "FOD_ADDR" , trace_data: stgAddr); |
486 | trace(icom_port, trace_pt: "FOD_XBUFF" , |
487 | trace_data: (unsigned long) icom_port->xmit_buf); |
488 | icom_port->statStg->xmit[index].leBuffer = |
489 | cpu_to_le32(icom_port->xmit_buf_pci); |
490 | } else if (index == (NUM_XBUFFS - 1)) { |
491 | memset(&icom_port->statStg->xmit[index], 0, sizeof(struct xmit_status_area)); |
492 | icom_port->statStg->xmit[index].leLengthASD = |
493 | cpu_to_le16(XMIT_BUFF_SZ); |
494 | trace(icom_port, trace_pt: "FOD_XBUFF" , |
495 | trace_data: (unsigned long) icom_port->xmit_buf); |
496 | icom_port->statStg->xmit[index].leBuffer = |
497 | cpu_to_le32(icom_port->xmit_buf_pci); |
498 | } else { |
499 | memset(&icom_port->statStg->xmit[index], 0, sizeof(struct xmit_status_area)); |
500 | } |
501 | } |
502 | /* FIDs */ |
503 | startStgAddr = stgAddr; |
504 | |
505 | /* fill in every entry, even if no buffer */ |
506 | for (index = 0; index < NUM_RBUFFS; index++) { |
507 | trace(icom_port, trace_pt: "FID_ADDR" , trace_data: stgAddr); |
508 | stgAddr = stgAddr + sizeof(icom_port->statStg->rcv[0]); |
509 | icom_port->statStg->rcv[index].leLength = 0; |
510 | icom_port->statStg->rcv[index].WorkingLength = |
511 | cpu_to_le16(RCV_BUFF_SZ); |
512 | if (index < (NUM_RBUFFS - 1) ) { |
513 | offset = stgAddr - (unsigned long) icom_port->statStg; |
514 | icom_port->statStg->rcv[index].leNext = |
515 | cpu_to_le32(icom_port-> statStg_pci + offset); |
516 | trace(icom_port, trace_pt: "FID_RBUFF" , |
517 | trace_data: (unsigned long) icom_port->recv_buf); |
518 | icom_port->statStg->rcv[index].leBuffer = |
519 | cpu_to_le32(icom_port->recv_buf_pci); |
520 | } else if (index == (NUM_RBUFFS -1) ) { |
521 | offset = startStgAddr - (unsigned long) icom_port->statStg; |
522 | icom_port->statStg->rcv[index].leNext = |
523 | cpu_to_le32(icom_port-> statStg_pci + offset); |
524 | trace(icom_port, trace_pt: "FID_RBUFF" , |
525 | trace_data: (unsigned long) icom_port->recv_buf + 2048); |
526 | icom_port->statStg->rcv[index].leBuffer = |
527 | cpu_to_le32(icom_port->recv_buf_pci + 2048); |
528 | } else { |
529 | icom_port->statStg->rcv[index].leNext = 0; |
530 | icom_port->statStg->rcv[index].leBuffer = 0; |
531 | } |
532 | } |
533 | |
534 | return 0; |
535 | } |
536 | |
537 | static void stop_processor(struct icom_port *icom_port) |
538 | { |
539 | unsigned long temp; |
540 | unsigned long flags; |
541 | int port; |
542 | |
543 | spin_lock_irqsave(&icom_lock, flags); |
544 | |
545 | port = icom_port->port; |
546 | if (port >= ARRAY_SIZE(stop_proc)) { |
547 | dev_err(&icom_port->adapter->pci_dev->dev, |
548 | "Invalid port assignment\n" ); |
549 | goto unlock; |
550 | } |
551 | |
552 | if (port == 0 || port == 1) |
553 | stop_proc[port].global_control_reg = &icom_port->global_reg->control; |
554 | else |
555 | stop_proc[port].global_control_reg = &icom_port->global_reg->control_2; |
556 | |
557 | temp = readl(addr: stop_proc[port].global_control_reg); |
558 | temp = (temp & ~start_proc[port].processor_id) | stop_proc[port].processor_id; |
559 | writel(val: temp, addr: stop_proc[port].global_control_reg); |
560 | |
561 | /* write flush */ |
562 | readl(addr: stop_proc[port].global_control_reg); |
563 | |
564 | unlock: |
565 | spin_unlock_irqrestore(lock: &icom_lock, flags); |
566 | } |
567 | |
568 | static void start_processor(struct icom_port *icom_port) |
569 | { |
570 | unsigned long temp; |
571 | unsigned long flags; |
572 | int port; |
573 | |
574 | spin_lock_irqsave(&icom_lock, flags); |
575 | |
576 | port = icom_port->port; |
577 | if (port >= ARRAY_SIZE(start_proc)) { |
578 | dev_err(&icom_port->adapter->pci_dev->dev, |
579 | "Invalid port assignment\n" ); |
580 | goto unlock; |
581 | } |
582 | |
583 | if (port == 0 || port == 1) |
584 | start_proc[port].global_control_reg = &icom_port->global_reg->control; |
585 | else |
586 | start_proc[port].global_control_reg = &icom_port->global_reg->control_2; |
587 | |
588 | temp = readl(addr: start_proc[port].global_control_reg); |
589 | temp = (temp & ~stop_proc[port].processor_id) | start_proc[port].processor_id; |
590 | writel(val: temp, addr: start_proc[port].global_control_reg); |
591 | |
592 | /* write flush */ |
593 | readl(addr: start_proc[port].global_control_reg); |
594 | |
595 | unlock: |
596 | spin_unlock_irqrestore(lock: &icom_lock, flags); |
597 | } |
598 | |
599 | static void load_code(struct icom_port *icom_port) |
600 | { |
601 | const struct firmware *fw; |
602 | char __iomem *iram_ptr; |
603 | int index; |
604 | int status = 0; |
605 | void __iomem *dram_ptr = icom_port->dram; |
606 | dma_addr_t temp_pci; |
607 | unsigned char *new_page = NULL; |
608 | unsigned char cable_id = NO_CABLE; |
609 | struct pci_dev *dev = icom_port->adapter->pci_dev; |
610 | |
611 | /* Clear out any pending interrupts */ |
612 | writew(val: 0x3FFF, addr: icom_port->int_reg); |
613 | |
614 | trace(icom_port, trace_pt: "CLEAR_INTERRUPTS" , trace_data: 0); |
615 | |
616 | /* Stop processor */ |
617 | stop_processor(icom_port); |
618 | |
619 | /* Zero out DRAM */ |
620 | memset_io(dram_ptr, 0, 512); |
621 | |
622 | /* Load Call Setup into Adapter */ |
623 | if (request_firmware(fw: &fw, name: "icom_call_setup.bin" , device: &dev->dev) < 0) { |
624 | dev_err(&dev->dev,"Unable to load icom_call_setup.bin firmware image\n" ); |
625 | status = -1; |
626 | goto load_code_exit; |
627 | } |
628 | |
629 | if (fw->size > ICOM_DCE_IRAM_OFFSET) { |
630 | dev_err(&dev->dev, "Invalid firmware image for icom_call_setup.bin found.\n" ); |
631 | release_firmware(fw); |
632 | status = -1; |
633 | goto load_code_exit; |
634 | } |
635 | |
636 | iram_ptr = (char __iomem *)icom_port->dram + ICOM_IRAM_OFFSET; |
637 | for (index = 0; index < fw->size; index++) |
638 | writeb(val: fw->data[index], addr: &iram_ptr[index]); |
639 | |
640 | release_firmware(fw); |
641 | |
642 | /* Load Resident DCE portion of Adapter */ |
643 | if (request_firmware(fw: &fw, name: "icom_res_dce.bin" , device: &dev->dev) < 0) { |
644 | dev_err(&dev->dev,"Unable to load icom_res_dce.bin firmware image\n" ); |
645 | status = -1; |
646 | goto load_code_exit; |
647 | } |
648 | |
649 | if (fw->size > ICOM_IRAM_SIZE) { |
650 | dev_err(&dev->dev, "Invalid firmware image for icom_res_dce.bin found.\n" ); |
651 | release_firmware(fw); |
652 | status = -1; |
653 | goto load_code_exit; |
654 | } |
655 | |
656 | iram_ptr = (char __iomem *) icom_port->dram + ICOM_IRAM_OFFSET; |
657 | for (index = ICOM_DCE_IRAM_OFFSET; index < fw->size; index++) |
658 | writeb(val: fw->data[index], addr: &iram_ptr[index]); |
659 | |
660 | release_firmware(fw); |
661 | |
662 | /* Set Hardware level */ |
663 | if (icom_port->adapter->version == ADAPTER_V2) |
664 | writeb(V2_HARDWARE, addr: &(icom_port->dram->misc_flags)); |
665 | |
666 | /* Start the processor in Adapter */ |
667 | start_processor(icom_port); |
668 | |
669 | writeb(val: (HDLC_PPP_PURE_ASYNC | HDLC_FF_FILL), |
670 | addr: &(icom_port->dram->HDLCConfigReg)); |
671 | writeb(val: 0x04, addr: &(icom_port->dram->FlagFillIdleTimer)); /* 0.5 seconds */ |
672 | writeb(val: 0x00, addr: &(icom_port->dram->CmdReg)); |
673 | writeb(val: 0x10, addr: &(icom_port->dram->async_config3)); |
674 | writeb(val: (ICOM_ACFG_DRIVE1 | ICOM_ACFG_NO_PARITY | ICOM_ACFG_8BPC | |
675 | ICOM_ACFG_1STOP_BIT), addr: &(icom_port->dram->async_config2)); |
676 | |
677 | /*Set up data in icom DRAM to indicate where personality |
678 | *code is located and its length. |
679 | */ |
680 | new_page = dma_alloc_coherent(dev: &dev->dev, size: 4096, dma_handle: &temp_pci, GFP_KERNEL); |
681 | |
682 | if (!new_page) { |
683 | dev_err(&dev->dev, "Can not allocate DMA buffer\n" ); |
684 | status = -1; |
685 | goto load_code_exit; |
686 | } |
687 | |
688 | if (request_firmware(fw: &fw, name: "icom_asc.bin" , device: &dev->dev) < 0) { |
689 | dev_err(&dev->dev,"Unable to load icom_asc.bin firmware image\n" ); |
690 | status = -1; |
691 | goto load_code_exit; |
692 | } |
693 | |
694 | if (fw->size > ICOM_DCE_IRAM_OFFSET) { |
695 | dev_err(&dev->dev, "Invalid firmware image for icom_asc.bin found.\n" ); |
696 | release_firmware(fw); |
697 | status = -1; |
698 | goto load_code_exit; |
699 | } |
700 | |
701 | for (index = 0; index < fw->size; index++) |
702 | new_page[index] = fw->data[index]; |
703 | |
704 | writeb(val: (char) ((fw->size + 16)/16), addr: &icom_port->dram->mac_length); |
705 | writel(val: temp_pci, addr: &icom_port->dram->mac_load_addr); |
706 | |
707 | release_firmware(fw); |
708 | |
709 | /*Setting the syncReg to 0x80 causes adapter to start downloading |
710 | the personality code into adapter instruction RAM. |
711 | Once code is loaded, it will begin executing and, based on |
712 | information provided above, will start DMAing data from |
713 | shared memory to adapter DRAM. |
714 | */ |
715 | /* the wait loop below verifies this write operation has been done |
716 | and processed |
717 | */ |
718 | writeb(START_DOWNLOAD, addr: &icom_port->dram->sync); |
719 | |
720 | /* Wait max 1 Sec for data download and processor to start */ |
721 | for (index = 0; index < 10; index++) { |
722 | msleep(msecs: 100); |
723 | if (readb(addr: &icom_port->dram->misc_flags) & ICOM_HDW_ACTIVE) |
724 | break; |
725 | } |
726 | |
727 | if (index == 10) |
728 | status = -1; |
729 | |
730 | /* |
731 | * check Cable ID |
732 | */ |
733 | cable_id = readb(addr: &icom_port->dram->cable_id); |
734 | |
735 | if (cable_id & ICOM_CABLE_ID_VALID) { |
736 | /* Get cable ID into the lower 4 bits (standard form) */ |
737 | cable_id = (cable_id & ICOM_CABLE_ID_MASK) >> 4; |
738 | icom_port->cable_id = cable_id; |
739 | } else { |
740 | dev_err(&dev->dev,"Invalid or no cable attached\n" ); |
741 | icom_port->cable_id = NO_CABLE; |
742 | } |
743 | |
744 | load_code_exit: |
745 | |
746 | if (status != 0) { |
747 | /* Clear out any pending interrupts */ |
748 | writew(val: 0x3FFF, addr: icom_port->int_reg); |
749 | |
750 | /* Turn off port */ |
751 | writeb(ICOM_DISABLE, addr: &(icom_port->dram->disable)); |
752 | |
753 | /* Stop processor */ |
754 | stop_processor(icom_port); |
755 | |
756 | dev_err(&icom_port->adapter->pci_dev->dev,"Port not operational\n" ); |
757 | } |
758 | |
759 | if (new_page != NULL) |
760 | dma_free_coherent(dev: &dev->dev, size: 4096, cpu_addr: new_page, dma_handle: temp_pci); |
761 | } |
762 | |
763 | static int startup(struct icom_port *icom_port) |
764 | { |
765 | unsigned long temp; |
766 | unsigned char cable_id, raw_cable_id; |
767 | unsigned long flags; |
768 | int port; |
769 | |
770 | trace(icom_port, trace_pt: "STARTUP" , trace_data: 0); |
771 | |
772 | if (!icom_port->dram) { |
773 | /* should NEVER be NULL */ |
774 | dev_err(&icom_port->adapter->pci_dev->dev, |
775 | "Unusable Port, port configuration missing\n" ); |
776 | return -ENODEV; |
777 | } |
778 | |
779 | /* |
780 | * check Cable ID |
781 | */ |
782 | raw_cable_id = readb(addr: &icom_port->dram->cable_id); |
783 | trace(icom_port, trace_pt: "CABLE_ID" , trace_data: raw_cable_id); |
784 | |
785 | /* Get cable ID into the lower 4 bits (standard form) */ |
786 | cable_id = (raw_cable_id & ICOM_CABLE_ID_MASK) >> 4; |
787 | |
788 | /* Check for valid Cable ID */ |
789 | if (!(raw_cable_id & ICOM_CABLE_ID_VALID) || |
790 | (cable_id != icom_port->cable_id)) { |
791 | |
792 | /* reload adapter code, pick up any potential changes in cable id */ |
793 | load_code(icom_port); |
794 | |
795 | /* still no sign of cable, error out */ |
796 | raw_cable_id = readb(addr: &icom_port->dram->cable_id); |
797 | cable_id = (raw_cable_id & ICOM_CABLE_ID_MASK) >> 4; |
798 | if (!(raw_cable_id & ICOM_CABLE_ID_VALID) || |
799 | (icom_port->cable_id == NO_CABLE)) |
800 | return -EIO; |
801 | } |
802 | |
803 | /* |
804 | * Finally, clear and enable interrupts |
805 | */ |
806 | spin_lock_irqsave(&icom_lock, flags); |
807 | port = icom_port->port; |
808 | if (port >= ARRAY_SIZE(int_mask_tbl)) { |
809 | dev_err(&icom_port->adapter->pci_dev->dev, |
810 | "Invalid port assignment\n" ); |
811 | goto unlock; |
812 | } |
813 | |
814 | if (port == 0 || port == 1) |
815 | int_mask_tbl[port].global_int_mask = &icom_port->global_reg->int_mask; |
816 | else |
817 | int_mask_tbl[port].global_int_mask = &icom_port->global_reg->int_mask_2; |
818 | |
819 | if (port == 0 || port == 2) |
820 | writew(val: 0x00FF, addr: icom_port->int_reg); |
821 | else |
822 | writew(val: 0x3F00, addr: icom_port->int_reg); |
823 | |
824 | temp = readl(addr: int_mask_tbl[port].global_int_mask); |
825 | writel(val: temp & ~int_mask_tbl[port].processor_id, addr: int_mask_tbl[port].global_int_mask); |
826 | |
827 | /* write flush */ |
828 | readl(addr: int_mask_tbl[port].global_int_mask); |
829 | |
830 | unlock: |
831 | spin_unlock_irqrestore(lock: &icom_lock, flags); |
832 | return 0; |
833 | } |
834 | |
835 | static void shutdown(struct icom_port *icom_port) |
836 | { |
837 | unsigned long temp; |
838 | unsigned char cmdReg; |
839 | unsigned long flags; |
840 | int port; |
841 | |
842 | spin_lock_irqsave(&icom_lock, flags); |
843 | trace(icom_port, trace_pt: "SHUTDOWN" , trace_data: 0); |
844 | |
845 | /* |
846 | * disable all interrupts |
847 | */ |
848 | port = icom_port->port; |
849 | if (port >= ARRAY_SIZE(int_mask_tbl)) { |
850 | dev_err(&icom_port->adapter->pci_dev->dev, |
851 | "Invalid port assignment\n" ); |
852 | goto unlock; |
853 | } |
854 | if (port == 0 || port == 1) |
855 | int_mask_tbl[port].global_int_mask = &icom_port->global_reg->int_mask; |
856 | else |
857 | int_mask_tbl[port].global_int_mask = &icom_port->global_reg->int_mask_2; |
858 | |
859 | temp = readl(addr: int_mask_tbl[port].global_int_mask); |
860 | writel(val: temp | int_mask_tbl[port].processor_id, addr: int_mask_tbl[port].global_int_mask); |
861 | |
862 | /* write flush */ |
863 | readl(addr: int_mask_tbl[port].global_int_mask); |
864 | |
865 | unlock: |
866 | spin_unlock_irqrestore(lock: &icom_lock, flags); |
867 | |
868 | /* |
869 | * disable break condition |
870 | */ |
871 | cmdReg = readb(addr: &icom_port->dram->CmdReg); |
872 | if (cmdReg & CMD_SND_BREAK) { |
873 | writeb(val: cmdReg & ~CMD_SND_BREAK, addr: &icom_port->dram->CmdReg); |
874 | } |
875 | } |
876 | |
877 | static int icom_write(struct uart_port *port) |
878 | { |
879 | struct icom_port *icom_port = to_icom_port(port); |
880 | unsigned long data_count; |
881 | unsigned char cmdReg; |
882 | unsigned long offset; |
883 | int temp_tail = port->state->xmit.tail; |
884 | |
885 | trace(icom_port, trace_pt: "WRITE" , trace_data: 0); |
886 | |
887 | if (le16_to_cpu(icom_port->statStg->xmit[0].flags) & |
888 | SA_FLAGS_READY_TO_XMIT) { |
889 | trace(icom_port, trace_pt: "WRITE_FULL" , trace_data: 0); |
890 | return 0; |
891 | } |
892 | |
893 | data_count = 0; |
894 | while ((port->state->xmit.head != temp_tail) && |
895 | (data_count <= XMIT_BUFF_SZ)) { |
896 | |
897 | icom_port->xmit_buf[data_count++] = |
898 | port->state->xmit.buf[temp_tail]; |
899 | |
900 | temp_tail++; |
901 | temp_tail &= (UART_XMIT_SIZE - 1); |
902 | } |
903 | |
904 | if (data_count) { |
905 | icom_port->statStg->xmit[0].flags = |
906 | cpu_to_le16(SA_FLAGS_READY_TO_XMIT); |
907 | icom_port->statStg->xmit[0].leLength = |
908 | cpu_to_le16(data_count); |
909 | offset = |
910 | (unsigned long) &icom_port->statStg->xmit[0] - |
911 | (unsigned long) icom_port->statStg; |
912 | *icom_port->xmitRestart = |
913 | cpu_to_le32(icom_port->statStg_pci + offset); |
914 | cmdReg = readb(addr: &icom_port->dram->CmdReg); |
915 | writeb(val: cmdReg | CMD_XMIT_RCV_ENABLE, |
916 | addr: &icom_port->dram->CmdReg); |
917 | writeb(START_XMIT, addr: &icom_port->dram->StartXmitCmd); |
918 | trace(icom_port, trace_pt: "WRITE_START" , trace_data: data_count); |
919 | /* write flush */ |
920 | readb(addr: &icom_port->dram->StartXmitCmd); |
921 | } |
922 | |
923 | return data_count; |
924 | } |
925 | |
926 | static inline void check_modem_status(struct icom_port *icom_port) |
927 | { |
928 | static char old_status = 0; |
929 | char delta_status; |
930 | unsigned char status; |
931 | |
932 | uart_port_lock(up: &icom_port->uart_port); |
933 | |
934 | /*modem input register */ |
935 | status = readb(addr: &icom_port->dram->isr); |
936 | trace(icom_port, trace_pt: "CHECK_MODEM" , trace_data: status); |
937 | delta_status = status ^ old_status; |
938 | if (delta_status) { |
939 | if (delta_status & ICOM_RI) |
940 | icom_port->uart_port.icount.rng++; |
941 | if (delta_status & ICOM_DSR) |
942 | icom_port->uart_port.icount.dsr++; |
943 | if (delta_status & ICOM_DCD) |
944 | uart_handle_dcd_change(uport: &icom_port->uart_port, |
945 | active: delta_status & ICOM_DCD); |
946 | if (delta_status & ICOM_CTS) |
947 | uart_handle_cts_change(uport: &icom_port->uart_port, |
948 | active: delta_status & ICOM_CTS); |
949 | |
950 | wake_up_interruptible(&icom_port->uart_port.state-> |
951 | port.delta_msr_wait); |
952 | old_status = status; |
953 | } |
954 | uart_port_unlock(up: &icom_port->uart_port); |
955 | } |
956 | |
957 | static void xmit_interrupt(u16 port_int_reg, struct icom_port *icom_port) |
958 | { |
959 | u16 count, i; |
960 | |
961 | if (port_int_reg & (INT_XMIT_COMPLETED)) { |
962 | trace(icom_port, trace_pt: "XMIT_COMPLETE" , trace_data: 0); |
963 | |
964 | /* clear buffer in use bit */ |
965 | icom_port->statStg->xmit[0].flags &= |
966 | cpu_to_le16(~SA_FLAGS_READY_TO_XMIT); |
967 | |
968 | count = le16_to_cpu(icom_port->statStg->xmit[0].leLength); |
969 | icom_port->uart_port.icount.tx += count; |
970 | |
971 | for (i=0; i<count && |
972 | !uart_circ_empty(&icom_port->uart_port.state->xmit); i++) { |
973 | |
974 | icom_port->uart_port.state->xmit.tail++; |
975 | icom_port->uart_port.state->xmit.tail &= |
976 | (UART_XMIT_SIZE - 1); |
977 | } |
978 | |
979 | if (!icom_write(port: &icom_port->uart_port)) |
980 | /* activate write queue */ |
981 | uart_write_wakeup(port: &icom_port->uart_port); |
982 | } else |
983 | trace(icom_port, trace_pt: "XMIT_DISABLED" , trace_data: 0); |
984 | } |
985 | |
986 | static void recv_interrupt(u16 port_int_reg, struct icom_port *icom_port) |
987 | { |
988 | short int count, rcv_buff; |
989 | struct tty_port *port = &icom_port->uart_port.state->port; |
990 | u16 status; |
991 | struct uart_icount *icount; |
992 | unsigned long offset; |
993 | unsigned char flag; |
994 | |
995 | trace(icom_port, trace_pt: "RCV_COMPLETE" , trace_data: 0); |
996 | rcv_buff = icom_port->next_rcv; |
997 | |
998 | status = le16_to_cpu(icom_port->statStg->rcv[rcv_buff].flags); |
999 | while (status & SA_FL_RCV_DONE) { |
1000 | int first = -1; |
1001 | |
1002 | trace(icom_port, trace_pt: "FID_STATUS" , trace_data: status); |
1003 | count = le16_to_cpu(icom_port->statStg->rcv[rcv_buff].leLength); |
1004 | |
1005 | trace(icom_port, trace_pt: "RCV_COUNT" , trace_data: count); |
1006 | |
1007 | trace(icom_port, trace_pt: "REAL_COUNT" , trace_data: count); |
1008 | |
1009 | offset = le32_to_cpu(icom_port->statStg->rcv[rcv_buff].leBuffer) - |
1010 | icom_port->recv_buf_pci; |
1011 | |
1012 | /* Block copy all but the last byte as this may have status */ |
1013 | if (count > 0) { |
1014 | first = icom_port->recv_buf[offset]; |
1015 | tty_insert_flip_string(port, chars: icom_port->recv_buf + offset, size: count - 1); |
1016 | } |
1017 | |
1018 | icount = &icom_port->uart_port.icount; |
1019 | icount->rx += count; |
1020 | |
1021 | /* Break detect logic */ |
1022 | if ((status & SA_FLAGS_FRAME_ERROR) |
1023 | && first == 0) { |
1024 | status &= ~SA_FLAGS_FRAME_ERROR; |
1025 | status |= SA_FLAGS_BREAK_DET; |
1026 | trace(icom_port, trace_pt: "BREAK_DET" , trace_data: 0); |
1027 | } |
1028 | |
1029 | flag = TTY_NORMAL; |
1030 | |
1031 | if (status & |
1032 | (SA_FLAGS_BREAK_DET | SA_FLAGS_PARITY_ERROR | |
1033 | SA_FLAGS_FRAME_ERROR | SA_FLAGS_OVERRUN)) { |
1034 | |
1035 | if (status & SA_FLAGS_BREAK_DET) |
1036 | icount->brk++; |
1037 | if (status & SA_FLAGS_PARITY_ERROR) |
1038 | icount->parity++; |
1039 | if (status & SA_FLAGS_FRAME_ERROR) |
1040 | icount->frame++; |
1041 | if (status & SA_FLAGS_OVERRUN) |
1042 | icount->overrun++; |
1043 | |
1044 | /* |
1045 | * Now check to see if character should be |
1046 | * ignored, and mask off conditions which |
1047 | * should be ignored. |
1048 | */ |
1049 | if (status & icom_port->ignore_status_mask) { |
1050 | trace(icom_port, trace_pt: "IGNORE_CHAR" , trace_data: 0); |
1051 | goto ignore_char; |
1052 | } |
1053 | |
1054 | status &= icom_port->read_status_mask; |
1055 | |
1056 | if (status & SA_FLAGS_BREAK_DET) { |
1057 | flag = TTY_BREAK; |
1058 | } else if (status & SA_FLAGS_PARITY_ERROR) { |
1059 | trace(icom_port, trace_pt: "PARITY_ERROR" , trace_data: 0); |
1060 | flag = TTY_PARITY; |
1061 | } else if (status & SA_FLAGS_FRAME_ERROR) |
1062 | flag = TTY_FRAME; |
1063 | |
1064 | } |
1065 | |
1066 | tty_insert_flip_char(port, ch: *(icom_port->recv_buf + offset + count - 1), flag); |
1067 | |
1068 | if (status & SA_FLAGS_OVERRUN) |
1069 | /* |
1070 | * Overrun is special, since it's |
1071 | * reported immediately, and doesn't |
1072 | * affect the current character |
1073 | */ |
1074 | tty_insert_flip_char(port, ch: 0, TTY_OVERRUN); |
1075 | ignore_char: |
1076 | icom_port->statStg->rcv[rcv_buff].flags = 0; |
1077 | icom_port->statStg->rcv[rcv_buff].leLength = 0; |
1078 | icom_port->statStg->rcv[rcv_buff].WorkingLength = |
1079 | cpu_to_le16(RCV_BUFF_SZ); |
1080 | |
1081 | rcv_buff++; |
1082 | if (rcv_buff == NUM_RBUFFS) |
1083 | rcv_buff = 0; |
1084 | |
1085 | status = le16_to_cpu(icom_port->statStg->rcv[rcv_buff].flags); |
1086 | } |
1087 | icom_port->next_rcv = rcv_buff; |
1088 | |
1089 | tty_flip_buffer_push(port); |
1090 | } |
1091 | |
1092 | static void process_interrupt(u16 port_int_reg, |
1093 | struct icom_port *icom_port) |
1094 | { |
1095 | |
1096 | uart_port_lock(up: &icom_port->uart_port); |
1097 | trace(icom_port, trace_pt: "INTERRUPT" , trace_data: port_int_reg); |
1098 | |
1099 | if (port_int_reg & (INT_XMIT_COMPLETED | INT_XMIT_DISABLED)) |
1100 | xmit_interrupt(port_int_reg, icom_port); |
1101 | |
1102 | if (port_int_reg & INT_RCV_COMPLETED) |
1103 | recv_interrupt(port_int_reg, icom_port); |
1104 | |
1105 | uart_port_unlock(up: &icom_port->uart_port); |
1106 | } |
1107 | |
1108 | static irqreturn_t icom_interrupt(int irq, void *dev_id) |
1109 | { |
1110 | void __iomem * int_reg; |
1111 | u32 adapter_interrupts; |
1112 | u16 port_int_reg; |
1113 | struct icom_adapter *icom_adapter; |
1114 | struct icom_port *icom_port; |
1115 | |
1116 | /* find icom_port for this interrupt */ |
1117 | icom_adapter = (struct icom_adapter *) dev_id; |
1118 | |
1119 | if (icom_adapter->version == ADAPTER_V2) { |
1120 | int_reg = icom_adapter->base_addr + 0x8024; |
1121 | |
1122 | adapter_interrupts = readl(addr: int_reg); |
1123 | |
1124 | if (adapter_interrupts & 0x00003FFF) { |
1125 | /* port 2 interrupt, NOTE: for all ADAPTER_V2, port 2 will be active */ |
1126 | icom_port = &icom_adapter->port_info[2]; |
1127 | port_int_reg = (u16) adapter_interrupts; |
1128 | process_interrupt(port_int_reg, icom_port); |
1129 | check_modem_status(icom_port); |
1130 | } |
1131 | if (adapter_interrupts & 0x3FFF0000) { |
1132 | /* port 3 interrupt */ |
1133 | icom_port = &icom_adapter->port_info[3]; |
1134 | if (icom_port->status == ICOM_PORT_ACTIVE) { |
1135 | port_int_reg = |
1136 | (u16) (adapter_interrupts >> 16); |
1137 | process_interrupt(port_int_reg, icom_port); |
1138 | check_modem_status(icom_port); |
1139 | } |
1140 | } |
1141 | |
1142 | /* Clear out any pending interrupts */ |
1143 | writel(val: adapter_interrupts, addr: int_reg); |
1144 | |
1145 | int_reg = icom_adapter->base_addr + 0x8004; |
1146 | } else { |
1147 | int_reg = icom_adapter->base_addr + 0x4004; |
1148 | } |
1149 | |
1150 | adapter_interrupts = readl(addr: int_reg); |
1151 | |
1152 | if (adapter_interrupts & 0x00003FFF) { |
1153 | /* port 0 interrupt, NOTE: for all adapters, port 0 will be active */ |
1154 | icom_port = &icom_adapter->port_info[0]; |
1155 | port_int_reg = (u16) adapter_interrupts; |
1156 | process_interrupt(port_int_reg, icom_port); |
1157 | check_modem_status(icom_port); |
1158 | } |
1159 | if (adapter_interrupts & 0x3FFF0000) { |
1160 | /* port 1 interrupt */ |
1161 | icom_port = &icom_adapter->port_info[1]; |
1162 | if (icom_port->status == ICOM_PORT_ACTIVE) { |
1163 | port_int_reg = (u16) (adapter_interrupts >> 16); |
1164 | process_interrupt(port_int_reg, icom_port); |
1165 | check_modem_status(icom_port); |
1166 | } |
1167 | } |
1168 | |
1169 | /* Clear out any pending interrupts */ |
1170 | writel(val: adapter_interrupts, addr: int_reg); |
1171 | |
1172 | /* flush the write */ |
1173 | adapter_interrupts = readl(addr: int_reg); |
1174 | |
1175 | return IRQ_HANDLED; |
1176 | } |
1177 | |
1178 | /* |
1179 | * ------------------------------------------------------------------ |
1180 | * Begin serial-core API |
1181 | * ------------------------------------------------------------------ |
1182 | */ |
1183 | static unsigned int icom_tx_empty(struct uart_port *port) |
1184 | { |
1185 | struct icom_port *icom_port = to_icom_port(port); |
1186 | int ret; |
1187 | unsigned long flags; |
1188 | |
1189 | uart_port_lock_irqsave(up: port, flags: &flags); |
1190 | if (le16_to_cpu(icom_port->statStg->xmit[0].flags) & |
1191 | SA_FLAGS_READY_TO_XMIT) |
1192 | ret = TIOCSER_TEMT; |
1193 | else |
1194 | ret = 0; |
1195 | |
1196 | uart_port_unlock_irqrestore(up: port, flags); |
1197 | return ret; |
1198 | } |
1199 | |
1200 | static void icom_set_mctrl(struct uart_port *port, unsigned int mctrl) |
1201 | { |
1202 | struct icom_port *icom_port = to_icom_port(port); |
1203 | unsigned char local_osr; |
1204 | |
1205 | trace(icom_port, trace_pt: "SET_MODEM" , trace_data: 0); |
1206 | local_osr = readb(addr: &icom_port->dram->osr); |
1207 | |
1208 | if (mctrl & TIOCM_RTS) { |
1209 | trace(icom_port, trace_pt: "RAISE_RTS" , trace_data: 0); |
1210 | local_osr |= ICOM_RTS; |
1211 | } else { |
1212 | trace(icom_port, trace_pt: "LOWER_RTS" , trace_data: 0); |
1213 | local_osr &= ~ICOM_RTS; |
1214 | } |
1215 | |
1216 | if (mctrl & TIOCM_DTR) { |
1217 | trace(icom_port, trace_pt: "RAISE_DTR" , trace_data: 0); |
1218 | local_osr |= ICOM_DTR; |
1219 | } else { |
1220 | trace(icom_port, trace_pt: "LOWER_DTR" , trace_data: 0); |
1221 | local_osr &= ~ICOM_DTR; |
1222 | } |
1223 | |
1224 | writeb(val: local_osr, addr: &icom_port->dram->osr); |
1225 | } |
1226 | |
1227 | static unsigned int icom_get_mctrl(struct uart_port *port) |
1228 | { |
1229 | struct icom_port *icom_port = to_icom_port(port); |
1230 | unsigned char status; |
1231 | unsigned int result; |
1232 | |
1233 | trace(icom_port, trace_pt: "GET_MODEM" , trace_data: 0); |
1234 | |
1235 | status = readb(addr: &icom_port->dram->isr); |
1236 | |
1237 | result = ((status & ICOM_DCD) ? TIOCM_CAR : 0) |
1238 | | ((status & ICOM_RI) ? TIOCM_RNG : 0) |
1239 | | ((status & ICOM_DSR) ? TIOCM_DSR : 0) |
1240 | | ((status & ICOM_CTS) ? TIOCM_CTS : 0); |
1241 | return result; |
1242 | } |
1243 | |
1244 | static void icom_stop_tx(struct uart_port *port) |
1245 | { |
1246 | struct icom_port *icom_port = to_icom_port(port); |
1247 | unsigned char cmdReg; |
1248 | |
1249 | trace(icom_port, trace_pt: "STOP" , trace_data: 0); |
1250 | cmdReg = readb(addr: &icom_port->dram->CmdReg); |
1251 | writeb(val: cmdReg | CMD_HOLD_XMIT, addr: &icom_port->dram->CmdReg); |
1252 | } |
1253 | |
1254 | static void icom_start_tx(struct uart_port *port) |
1255 | { |
1256 | struct icom_port *icom_port = to_icom_port(port); |
1257 | unsigned char cmdReg; |
1258 | |
1259 | trace(icom_port, trace_pt: "START" , trace_data: 0); |
1260 | cmdReg = readb(addr: &icom_port->dram->CmdReg); |
1261 | if ((cmdReg & CMD_HOLD_XMIT) == CMD_HOLD_XMIT) |
1262 | writeb(val: cmdReg & ~CMD_HOLD_XMIT, |
1263 | addr: &icom_port->dram->CmdReg); |
1264 | |
1265 | icom_write(port); |
1266 | } |
1267 | |
1268 | static void icom_send_xchar(struct uart_port *port, char ch) |
1269 | { |
1270 | struct icom_port *icom_port = to_icom_port(port); |
1271 | unsigned char xdata; |
1272 | int index; |
1273 | unsigned long flags; |
1274 | |
1275 | trace(icom_port, trace_pt: "SEND_XCHAR" , trace_data: ch); |
1276 | |
1277 | /* wait .1 sec to send char */ |
1278 | for (index = 0; index < 10; index++) { |
1279 | uart_port_lock_irqsave(up: port, flags: &flags); |
1280 | xdata = readb(addr: &icom_port->dram->xchar); |
1281 | if (xdata == 0x00) { |
1282 | trace(icom_port, trace_pt: "QUICK_WRITE" , trace_data: 0); |
1283 | writeb(val: ch, addr: &icom_port->dram->xchar); |
1284 | |
1285 | /* flush write operation */ |
1286 | xdata = readb(addr: &icom_port->dram->xchar); |
1287 | uart_port_unlock_irqrestore(up: port, flags); |
1288 | break; |
1289 | } |
1290 | uart_port_unlock_irqrestore(up: port, flags); |
1291 | msleep(msecs: 10); |
1292 | } |
1293 | } |
1294 | |
1295 | static void icom_stop_rx(struct uart_port *port) |
1296 | { |
1297 | struct icom_port *icom_port = to_icom_port(port); |
1298 | unsigned char cmdReg; |
1299 | |
1300 | cmdReg = readb(addr: &icom_port->dram->CmdReg); |
1301 | writeb(val: cmdReg & ~CMD_RCV_ENABLE, addr: &icom_port->dram->CmdReg); |
1302 | } |
1303 | |
1304 | static void icom_break(struct uart_port *port, int break_state) |
1305 | { |
1306 | struct icom_port *icom_port = to_icom_port(port); |
1307 | unsigned char cmdReg; |
1308 | unsigned long flags; |
1309 | |
1310 | uart_port_lock_irqsave(up: port, flags: &flags); |
1311 | trace(icom_port, trace_pt: "BREAK" , trace_data: 0); |
1312 | cmdReg = readb(addr: &icom_port->dram->CmdReg); |
1313 | if (break_state == -1) { |
1314 | writeb(val: cmdReg | CMD_SND_BREAK, addr: &icom_port->dram->CmdReg); |
1315 | } else { |
1316 | writeb(val: cmdReg & ~CMD_SND_BREAK, addr: &icom_port->dram->CmdReg); |
1317 | } |
1318 | uart_port_unlock_irqrestore(up: port, flags); |
1319 | } |
1320 | |
1321 | static int icom_open(struct uart_port *port) |
1322 | { |
1323 | struct icom_port *icom_port = to_icom_port(port); |
1324 | int retval; |
1325 | |
1326 | kref_get(kref: &icom_port->adapter->kref); |
1327 | retval = startup(icom_port); |
1328 | |
1329 | if (retval) { |
1330 | kref_put(kref: &icom_port->adapter->kref, release: icom_kref_release); |
1331 | trace(icom_port, trace_pt: "STARTUP_ERROR" , trace_data: 0); |
1332 | return retval; |
1333 | } |
1334 | |
1335 | return 0; |
1336 | } |
1337 | |
1338 | static void icom_close(struct uart_port *port) |
1339 | { |
1340 | struct icom_port *icom_port = to_icom_port(port); |
1341 | unsigned char cmdReg; |
1342 | |
1343 | trace(icom_port, trace_pt: "CLOSE" , trace_data: 0); |
1344 | |
1345 | /* stop receiver */ |
1346 | cmdReg = readb(addr: &icom_port->dram->CmdReg); |
1347 | writeb(val: cmdReg & ~CMD_RCV_ENABLE, addr: &icom_port->dram->CmdReg); |
1348 | |
1349 | shutdown(icom_port); |
1350 | |
1351 | kref_put(kref: &icom_port->adapter->kref, release: icom_kref_release); |
1352 | } |
1353 | |
1354 | static void icom_set_termios(struct uart_port *port, struct ktermios *termios, |
1355 | const struct ktermios *old_termios) |
1356 | { |
1357 | struct icom_port *icom_port = to_icom_port(port); |
1358 | int baud; |
1359 | unsigned cflag, iflag; |
1360 | char new_config2; |
1361 | char new_config3 = 0; |
1362 | char tmp_byte; |
1363 | int index; |
1364 | int rcv_buff, xmit_buff; |
1365 | unsigned long offset; |
1366 | unsigned long flags; |
1367 | |
1368 | uart_port_lock_irqsave(up: port, flags: &flags); |
1369 | trace(icom_port, trace_pt: "CHANGE_SPEED" , trace_data: 0); |
1370 | |
1371 | cflag = termios->c_cflag; |
1372 | iflag = termios->c_iflag; |
1373 | |
1374 | new_config2 = ICOM_ACFG_DRIVE1; |
1375 | |
1376 | /* byte size and parity */ |
1377 | switch (cflag & CSIZE) { |
1378 | case CS5: /* 5 bits/char */ |
1379 | new_config2 |= ICOM_ACFG_5BPC; |
1380 | break; |
1381 | case CS6: /* 6 bits/char */ |
1382 | new_config2 |= ICOM_ACFG_6BPC; |
1383 | break; |
1384 | case CS7: /* 7 bits/char */ |
1385 | new_config2 |= ICOM_ACFG_7BPC; |
1386 | break; |
1387 | case CS8: /* 8 bits/char */ |
1388 | new_config2 |= ICOM_ACFG_8BPC; |
1389 | break; |
1390 | default: |
1391 | break; |
1392 | } |
1393 | if (cflag & CSTOPB) { |
1394 | /* 2 stop bits */ |
1395 | new_config2 |= ICOM_ACFG_2STOP_BIT; |
1396 | } |
1397 | if (cflag & PARENB) { |
1398 | /* parity bit enabled */ |
1399 | new_config2 |= ICOM_ACFG_PARITY_ENAB; |
1400 | trace(icom_port, trace_pt: "PARENB" , trace_data: 0); |
1401 | } |
1402 | if (cflag & PARODD) { |
1403 | /* odd parity */ |
1404 | new_config2 |= ICOM_ACFG_PARITY_ODD; |
1405 | trace(icom_port, trace_pt: "PARODD" , trace_data: 0); |
1406 | } |
1407 | |
1408 | /* Determine divisor based on baud rate */ |
1409 | baud = uart_get_baud_rate(port, termios, old: old_termios, |
1410 | min: icom_acfg_baud[0], |
1411 | max: icom_acfg_baud[BAUD_TABLE_LIMIT]); |
1412 | if (!baud) |
1413 | baud = 9600; /* B0 transition handled in rs_set_termios */ |
1414 | |
1415 | for (index = 0; index < BAUD_TABLE_LIMIT; index++) { |
1416 | if (icom_acfg_baud[index] == baud) { |
1417 | new_config3 = index; |
1418 | break; |
1419 | } |
1420 | } |
1421 | |
1422 | uart_update_timeout(port, cflag, baud); |
1423 | |
1424 | /* CTS flow control flag and modem status interrupts */ |
1425 | tmp_byte = readb(addr: &(icom_port->dram->HDLCConfigReg)); |
1426 | if (cflag & CRTSCTS) |
1427 | tmp_byte |= HDLC_HDW_FLOW; |
1428 | else |
1429 | tmp_byte &= ~HDLC_HDW_FLOW; |
1430 | writeb(val: tmp_byte, addr: &(icom_port->dram->HDLCConfigReg)); |
1431 | |
1432 | /* |
1433 | * Set up parity check flag |
1434 | */ |
1435 | icom_port->read_status_mask = SA_FLAGS_OVERRUN | SA_FL_RCV_DONE; |
1436 | if (iflag & INPCK) |
1437 | icom_port->read_status_mask |= |
1438 | SA_FLAGS_FRAME_ERROR | SA_FLAGS_PARITY_ERROR; |
1439 | |
1440 | if ((iflag & BRKINT) || (iflag & PARMRK)) |
1441 | icom_port->read_status_mask |= SA_FLAGS_BREAK_DET; |
1442 | |
1443 | /* |
1444 | * Characters to ignore |
1445 | */ |
1446 | icom_port->ignore_status_mask = 0; |
1447 | if (iflag & IGNPAR) |
1448 | icom_port->ignore_status_mask |= |
1449 | SA_FLAGS_PARITY_ERROR | SA_FLAGS_FRAME_ERROR; |
1450 | if (iflag & IGNBRK) { |
1451 | icom_port->ignore_status_mask |= SA_FLAGS_BREAK_DET; |
1452 | /* |
1453 | * If we're ignore parity and break indicators, ignore |
1454 | * overruns too. (For real raw support). |
1455 | */ |
1456 | if (iflag & IGNPAR) |
1457 | icom_port->ignore_status_mask |= SA_FLAGS_OVERRUN; |
1458 | } |
1459 | |
1460 | /* |
1461 | * !!! ignore all characters if CREAD is not set |
1462 | */ |
1463 | if ((cflag & CREAD) == 0) |
1464 | icom_port->ignore_status_mask |= SA_FL_RCV_DONE; |
1465 | |
1466 | /* Turn off Receiver to prepare for reset */ |
1467 | writeb(CMD_RCV_DISABLE, addr: &icom_port->dram->CmdReg); |
1468 | |
1469 | for (index = 0; index < 10; index++) { |
1470 | if (readb(addr: &icom_port->dram->PrevCmdReg) == 0x00) { |
1471 | break; |
1472 | } |
1473 | } |
1474 | |
1475 | /* clear all current buffers of data */ |
1476 | for (rcv_buff = 0; rcv_buff < NUM_RBUFFS; rcv_buff++) { |
1477 | icom_port->statStg->rcv[rcv_buff].flags = 0; |
1478 | icom_port->statStg->rcv[rcv_buff].leLength = 0; |
1479 | icom_port->statStg->rcv[rcv_buff].WorkingLength = |
1480 | cpu_to_le16(RCV_BUFF_SZ); |
1481 | } |
1482 | |
1483 | for (xmit_buff = 0; xmit_buff < NUM_XBUFFS; xmit_buff++) { |
1484 | icom_port->statStg->xmit[xmit_buff].flags = 0; |
1485 | } |
1486 | |
1487 | /* activate changes and start xmit and receiver here */ |
1488 | /* Enable the receiver */ |
1489 | writeb(val: new_config3, addr: &(icom_port->dram->async_config3)); |
1490 | writeb(val: new_config2, addr: &(icom_port->dram->async_config2)); |
1491 | tmp_byte = readb(addr: &(icom_port->dram->HDLCConfigReg)); |
1492 | tmp_byte |= HDLC_PPP_PURE_ASYNC | HDLC_FF_FILL; |
1493 | writeb(val: tmp_byte, addr: &(icom_port->dram->HDLCConfigReg)); |
1494 | writeb(val: 0x04, addr: &(icom_port->dram->FlagFillIdleTimer)); /* 0.5 seconds */ |
1495 | writeb(val: 0xFF, addr: &(icom_port->dram->ier)); /* enable modem signal interrupts */ |
1496 | |
1497 | /* reset processor */ |
1498 | writeb(CMD_RESTART, addr: &icom_port->dram->CmdReg); |
1499 | |
1500 | for (index = 0; index < 10; index++) { |
1501 | if (readb(addr: &icom_port->dram->CmdReg) == 0x00) { |
1502 | break; |
1503 | } |
1504 | } |
1505 | |
1506 | /* Enable Transmitter and Receiver */ |
1507 | offset = |
1508 | (unsigned long) &icom_port->statStg->rcv[0] - |
1509 | (unsigned long) icom_port->statStg; |
1510 | writel(val: icom_port->statStg_pci + offset, |
1511 | addr: &icom_port->dram->RcvStatusAddr); |
1512 | icom_port->next_rcv = 0; |
1513 | *icom_port->xmitRestart = 0; |
1514 | writel(val: icom_port->xmitRestart_pci, |
1515 | addr: &icom_port->dram->XmitStatusAddr); |
1516 | trace(icom_port, trace_pt: "XR_ENAB" , trace_data: 0); |
1517 | writeb(CMD_XMIT_RCV_ENABLE, addr: &icom_port->dram->CmdReg); |
1518 | |
1519 | uart_port_unlock_irqrestore(up: port, flags); |
1520 | } |
1521 | |
1522 | static const char *icom_type(struct uart_port *port) |
1523 | { |
1524 | return "icom" ; |
1525 | } |
1526 | |
1527 | static void icom_config_port(struct uart_port *port, int flags) |
1528 | { |
1529 | port->type = PORT_ICOM; |
1530 | } |
1531 | |
1532 | static const struct uart_ops icom_ops = { |
1533 | .tx_empty = icom_tx_empty, |
1534 | .set_mctrl = icom_set_mctrl, |
1535 | .get_mctrl = icom_get_mctrl, |
1536 | .stop_tx = icom_stop_tx, |
1537 | .start_tx = icom_start_tx, |
1538 | .send_xchar = icom_send_xchar, |
1539 | .stop_rx = icom_stop_rx, |
1540 | .break_ctl = icom_break, |
1541 | .startup = icom_open, |
1542 | .shutdown = icom_close, |
1543 | .set_termios = icom_set_termios, |
1544 | .type = icom_type, |
1545 | .config_port = icom_config_port, |
1546 | }; |
1547 | |
1548 | #define ICOM_CONSOLE NULL |
1549 | |
1550 | static struct uart_driver icom_uart_driver = { |
1551 | .owner = THIS_MODULE, |
1552 | .driver_name = ICOM_DRIVER_NAME, |
1553 | .dev_name = "ttyA" , |
1554 | .major = ICOM_MAJOR, |
1555 | .minor = ICOM_MINOR_START, |
1556 | .nr = NR_PORTS, |
1557 | .cons = ICOM_CONSOLE, |
1558 | }; |
1559 | |
1560 | static int icom_init_ports(struct icom_adapter *icom_adapter) |
1561 | { |
1562 | u32 subsystem_id = icom_adapter->subsystem_id; |
1563 | int i; |
1564 | struct icom_port *icom_port; |
1565 | |
1566 | if (icom_adapter->version == ADAPTER_V1) { |
1567 | icom_adapter->numb_ports = 2; |
1568 | |
1569 | for (i = 0; i < 2; i++) { |
1570 | icom_port = &icom_adapter->port_info[i]; |
1571 | icom_port->port = i; |
1572 | icom_port->status = ICOM_PORT_ACTIVE; |
1573 | } |
1574 | } else { |
1575 | if (subsystem_id == PCI_DEVICE_ID_IBM_ICOM_FOUR_PORT_MODEL) { |
1576 | icom_adapter->numb_ports = 4; |
1577 | |
1578 | for (i = 0; i < 4; i++) { |
1579 | icom_port = &icom_adapter->port_info[i]; |
1580 | |
1581 | icom_port->port = i; |
1582 | icom_port->status = ICOM_PORT_ACTIVE; |
1583 | } |
1584 | } else { |
1585 | icom_adapter->numb_ports = 4; |
1586 | |
1587 | icom_adapter->port_info[0].port = 0; |
1588 | icom_adapter->port_info[0].status = ICOM_PORT_ACTIVE; |
1589 | icom_adapter->port_info[1].status = ICOM_PORT_OFF; |
1590 | icom_adapter->port_info[2].port = 2; |
1591 | icom_adapter->port_info[2].status = ICOM_PORT_ACTIVE; |
1592 | icom_adapter->port_info[3].status = ICOM_PORT_OFF; |
1593 | } |
1594 | } |
1595 | |
1596 | return 0; |
1597 | } |
1598 | |
1599 | static void icom_port_active(struct icom_port *icom_port, struct icom_adapter *icom_adapter, int port_num) |
1600 | { |
1601 | if (icom_adapter->version == ADAPTER_V1) { |
1602 | icom_port->global_reg = icom_adapter->base_addr + 0x4000; |
1603 | icom_port->int_reg = icom_adapter->base_addr + |
1604 | 0x4004 + 2 - 2 * port_num; |
1605 | } else { |
1606 | icom_port->global_reg = icom_adapter->base_addr + 0x8000; |
1607 | if (icom_port->port < 2) |
1608 | icom_port->int_reg = icom_adapter->base_addr + |
1609 | 0x8004 + 2 - 2 * icom_port->port; |
1610 | else |
1611 | icom_port->int_reg = icom_adapter->base_addr + |
1612 | 0x8024 + 2 - 2 * (icom_port->port - 2); |
1613 | } |
1614 | } |
1615 | static int icom_load_ports(struct icom_adapter *icom_adapter) |
1616 | { |
1617 | struct icom_port *icom_port; |
1618 | int port_num; |
1619 | |
1620 | for (port_num = 0; port_num < icom_adapter->numb_ports; port_num++) { |
1621 | |
1622 | icom_port = &icom_adapter->port_info[port_num]; |
1623 | |
1624 | if (icom_port->status == ICOM_PORT_ACTIVE) { |
1625 | icom_port_active(icom_port, icom_adapter, port_num); |
1626 | icom_port->dram = icom_adapter->base_addr + |
1627 | 0x2000 * icom_port->port; |
1628 | |
1629 | icom_port->adapter = icom_adapter; |
1630 | |
1631 | /* get port memory */ |
1632 | if (get_port_memory(icom_port) != 0) { |
1633 | dev_err(&icom_port->adapter->pci_dev->dev, |
1634 | "Memory allocation for port FAILED\n" ); |
1635 | } |
1636 | } |
1637 | } |
1638 | return 0; |
1639 | } |
1640 | |
1641 | static int icom_alloc_adapter(struct icom_adapter |
1642 | **icom_adapter_ref) |
1643 | { |
1644 | int adapter_count = 0; |
1645 | struct icom_adapter *icom_adapter; |
1646 | struct icom_adapter *cur_adapter_entry; |
1647 | |
1648 | icom_adapter = kzalloc(size: sizeof(struct icom_adapter), GFP_KERNEL); |
1649 | |
1650 | if (!icom_adapter) { |
1651 | return -ENOMEM; |
1652 | } |
1653 | |
1654 | list_for_each_entry(cur_adapter_entry, &icom_adapter_head, |
1655 | icom_adapter_entry) { |
1656 | if (cur_adapter_entry->index != adapter_count) { |
1657 | break; |
1658 | } |
1659 | adapter_count++; |
1660 | } |
1661 | |
1662 | icom_adapter->index = adapter_count; |
1663 | list_add_tail(new: &icom_adapter->icom_adapter_entry, |
1664 | head: &cur_adapter_entry->icom_adapter_entry); |
1665 | |
1666 | *icom_adapter_ref = icom_adapter; |
1667 | return 0; |
1668 | } |
1669 | |
1670 | static void icom_free_adapter(struct icom_adapter *icom_adapter) |
1671 | { |
1672 | list_del(entry: &icom_adapter->icom_adapter_entry); |
1673 | kfree(objp: icom_adapter); |
1674 | } |
1675 | |
1676 | static void icom_kref_release(struct kref *kref) |
1677 | { |
1678 | struct icom_adapter *icom_adapter = container_of(kref, |
1679 | struct icom_adapter, kref); |
1680 | struct icom_port *icom_port; |
1681 | int index; |
1682 | |
1683 | for (index = 0; index < icom_adapter->numb_ports; index++) { |
1684 | icom_port = &icom_adapter->port_info[index]; |
1685 | |
1686 | if (icom_port->status == ICOM_PORT_ACTIVE) { |
1687 | dev_info(&icom_adapter->pci_dev->dev, |
1688 | "Device removed\n" ); |
1689 | |
1690 | uart_remove_one_port(reg: &icom_uart_driver, |
1691 | port: &icom_port->uart_port); |
1692 | |
1693 | /* be sure that DTR and RTS are dropped */ |
1694 | writeb(val: 0x00, addr: &icom_port->dram->osr); |
1695 | |
1696 | /* Wait 0.1 Sec for simple Init to complete */ |
1697 | msleep(msecs: 100); |
1698 | |
1699 | /* Stop proccessor */ |
1700 | stop_processor(icom_port); |
1701 | |
1702 | free_port_memory(icom_port); |
1703 | } |
1704 | } |
1705 | |
1706 | free_irq(icom_adapter->pci_dev->irq, (void *) icom_adapter); |
1707 | iounmap(addr: icom_adapter->base_addr); |
1708 | pci_release_regions(icom_adapter->pci_dev); |
1709 | icom_free_adapter(icom_adapter); |
1710 | } |
1711 | |
1712 | static int icom_probe(struct pci_dev *dev, |
1713 | const struct pci_device_id *ent) |
1714 | { |
1715 | int index; |
1716 | unsigned int command_reg; |
1717 | int retval; |
1718 | struct icom_adapter *icom_adapter; |
1719 | struct icom_port *icom_port; |
1720 | |
1721 | retval = pci_enable_device(dev); |
1722 | if (retval) { |
1723 | dev_err(&dev->dev, "Device enable FAILED\n" ); |
1724 | return retval; |
1725 | } |
1726 | |
1727 | retval = pci_request_regions(dev, "icom" ); |
1728 | if (retval) { |
1729 | dev_err(&dev->dev, "pci_request_regions FAILED\n" ); |
1730 | pci_disable_device(dev); |
1731 | return retval; |
1732 | } |
1733 | |
1734 | pci_set_master(dev); |
1735 | |
1736 | retval = pci_read_config_dword(dev, PCI_COMMAND, val: &command_reg); |
1737 | if (retval) { |
1738 | dev_err(&dev->dev, "PCI Config read FAILED\n" ); |
1739 | goto probe_exit0; |
1740 | } |
1741 | |
1742 | pci_write_config_dword(dev, PCI_COMMAND, |
1743 | val: command_reg | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER |
1744 | | PCI_COMMAND_PARITY | PCI_COMMAND_SERR); |
1745 | |
1746 | if (ent->driver_data == ADAPTER_V1) { |
1747 | pci_write_config_dword(dev, where: 0x44, val: 0x8300830A); |
1748 | } else { |
1749 | pci_write_config_dword(dev, where: 0x44, val: 0x42004200); |
1750 | pci_write_config_dword(dev, where: 0x48, val: 0x42004200); |
1751 | } |
1752 | |
1753 | |
1754 | retval = icom_alloc_adapter(icom_adapter_ref: &icom_adapter); |
1755 | if (retval) { |
1756 | dev_err(&dev->dev, "icom_alloc_adapter FAILED\n" ); |
1757 | retval = -EIO; |
1758 | goto probe_exit0; |
1759 | } |
1760 | |
1761 | icom_adapter->base_addr_pci = pci_resource_start(dev, 0); |
1762 | icom_adapter->pci_dev = dev; |
1763 | icom_adapter->version = ent->driver_data; |
1764 | icom_adapter->subsystem_id = ent->subdevice; |
1765 | |
1766 | |
1767 | retval = icom_init_ports(icom_adapter); |
1768 | if (retval) { |
1769 | dev_err(&dev->dev, "Port configuration failed\n" ); |
1770 | goto probe_exit1; |
1771 | } |
1772 | |
1773 | icom_adapter->base_addr = pci_ioremap_bar(pdev: dev, bar: 0); |
1774 | |
1775 | if (!icom_adapter->base_addr) { |
1776 | retval = -ENOMEM; |
1777 | goto probe_exit1; |
1778 | } |
1779 | |
1780 | /* save off irq and request irq line */ |
1781 | retval = request_irq(irq: dev->irq, handler: icom_interrupt, IRQF_SHARED, ICOM_DRIVER_NAME, dev: (void *)icom_adapter); |
1782 | if (retval) { |
1783 | goto probe_exit2; |
1784 | } |
1785 | |
1786 | retval = icom_load_ports(icom_adapter); |
1787 | |
1788 | for (index = 0; index < icom_adapter->numb_ports; index++) { |
1789 | icom_port = &icom_adapter->port_info[index]; |
1790 | |
1791 | if (icom_port->status == ICOM_PORT_ACTIVE) { |
1792 | icom_port->uart_port.irq = icom_port->adapter->pci_dev->irq; |
1793 | icom_port->uart_port.type = PORT_ICOM; |
1794 | icom_port->uart_port.iotype = UPIO_MEM; |
1795 | icom_port->uart_port.membase = |
1796 | (unsigned char __iomem *)icom_adapter->base_addr_pci; |
1797 | icom_port->uart_port.fifosize = 16; |
1798 | icom_port->uart_port.ops = &icom_ops; |
1799 | icom_port->uart_port.line = |
1800 | icom_port->port + icom_adapter->index * 4; |
1801 | if (uart_add_one_port (reg: &icom_uart_driver, port: &icom_port->uart_port)) { |
1802 | icom_port->status = ICOM_PORT_OFF; |
1803 | dev_err(&dev->dev, "Device add failed\n" ); |
1804 | } else |
1805 | dev_info(&dev->dev, "Device added\n" ); |
1806 | } |
1807 | } |
1808 | |
1809 | kref_init(kref: &icom_adapter->kref); |
1810 | return 0; |
1811 | |
1812 | probe_exit2: |
1813 | iounmap(addr: icom_adapter->base_addr); |
1814 | probe_exit1: |
1815 | icom_free_adapter(icom_adapter); |
1816 | |
1817 | probe_exit0: |
1818 | pci_release_regions(dev); |
1819 | pci_disable_device(dev); |
1820 | |
1821 | return retval; |
1822 | } |
1823 | |
1824 | static void icom_remove(struct pci_dev *dev) |
1825 | { |
1826 | struct icom_adapter *icom_adapter; |
1827 | |
1828 | list_for_each_entry(icom_adapter, &icom_adapter_head, |
1829 | icom_adapter_entry) { |
1830 | if (icom_adapter->pci_dev == dev) { |
1831 | kref_put(kref: &icom_adapter->kref, release: icom_kref_release); |
1832 | return; |
1833 | } |
1834 | } |
1835 | |
1836 | dev_err(&dev->dev, "Unable to find device to remove\n" ); |
1837 | } |
1838 | |
1839 | static struct pci_driver icom_pci_driver = { |
1840 | .name = ICOM_DRIVER_NAME, |
1841 | .id_table = icom_pci_table, |
1842 | .probe = icom_probe, |
1843 | .remove = icom_remove, |
1844 | }; |
1845 | |
1846 | static int __init icom_init(void) |
1847 | { |
1848 | int ret; |
1849 | |
1850 | ret = uart_register_driver(uart: &icom_uart_driver); |
1851 | if (ret) |
1852 | return ret; |
1853 | |
1854 | ret = pci_register_driver(&icom_pci_driver); |
1855 | |
1856 | if (ret < 0) |
1857 | uart_unregister_driver(uart: &icom_uart_driver); |
1858 | |
1859 | return ret; |
1860 | } |
1861 | |
1862 | static void __exit icom_exit(void) |
1863 | { |
1864 | pci_unregister_driver(dev: &icom_pci_driver); |
1865 | uart_unregister_driver(uart: &icom_uart_driver); |
1866 | } |
1867 | |
1868 | module_init(icom_init); |
1869 | module_exit(icom_exit); |
1870 | |
1871 | MODULE_AUTHOR("Michael Anderson <mjanders@us.ibm.com>" ); |
1872 | MODULE_DESCRIPTION("IBM iSeries Serial IOA driver" ); |
1873 | MODULE_LICENSE("GPL" ); |
1874 | MODULE_FIRMWARE("icom_call_setup.bin" ); |
1875 | MODULE_FIRMWARE("icom_res_dce.bin" ); |
1876 | MODULE_FIRMWARE("icom_asc.bin" ); |
1877 | |