]>
Commit | Line | Data |
---|---|---|
d0aeaa83 SM |
1 | /* |
2 | * Probe module for 8250/16550-type Exar chips PCI serial ports. | |
3 | * | |
4 | * Based on drivers/tty/serial/8250/8250_pci.c, | |
5 | * | |
6 | * Copyright (C) 2017 Sudip Mukherjee, All Rights Reserved. | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or modify | |
9 | * it under the terms of the GNU General Public License as published by | |
10 | * the Free Software Foundation; either version 2 of the License. | |
11 | */ | |
4076cf08 | 12 | #include <linux/acpi.h> |
413058df | 13 | #include <linux/dmi.h> |
d0aeaa83 SM |
14 | #include <linux/io.h> |
15 | #include <linux/kernel.h> | |
16 | #include <linux/module.h> | |
17 | #include <linux/pci.h> | |
380b1e2f | 18 | #include <linux/property.h> |
d0aeaa83 SM |
19 | #include <linux/serial_core.h> |
20 | #include <linux/serial_reg.h> | |
21 | #include <linux/slab.h> | |
22 | #include <linux/string.h> | |
23 | #include <linux/tty.h> | |
24 | #include <linux/8250_pci.h> | |
25 | ||
26 | #include <asm/byteorder.h> | |
27 | ||
28 | #include "8250.h" | |
29 | ||
fc6cc961 JK |
30 | #define PCI_DEVICE_ID_COMMTECH_4224PCI335 0x0002 |
31 | #define PCI_DEVICE_ID_COMMTECH_4222PCI335 0x0004 | |
32 | #define PCI_DEVICE_ID_COMMTECH_2324PCI335 0x000a | |
33 | #define PCI_DEVICE_ID_COMMTECH_2328PCI335 0x000b | |
34 | #define PCI_DEVICE_ID_COMMTECH_4224PCIE 0x0020 | |
35 | #define PCI_DEVICE_ID_COMMTECH_4228PCIE 0x0021 | |
36 | #define PCI_DEVICE_ID_COMMTECH_4222PCIE 0x0022 | |
37 | #define PCI_DEVICE_ID_EXAR_XR17V4358 0x4358 | |
38 | #define PCI_DEVICE_ID_EXAR_XR17V8358 0x8358 | |
d0aeaa83 | 39 | |
7e12357e JK |
40 | #define UART_EXAR_8XMODE 0x88 /* 8X sampling rate select */ |
41 | ||
42 | #define UART_EXAR_FCTR 0x08 /* Feature Control Register */ | |
43 | #define UART_FCTR_EXAR_IRDA 0x10 /* IrDa data encode select */ | |
44 | #define UART_FCTR_EXAR_485 0x20 /* Auto 485 half duplex dir ctl */ | |
45 | #define UART_FCTR_EXAR_TRGA 0x00 /* FIFO trigger table A */ | |
46 | #define UART_FCTR_EXAR_TRGB 0x60 /* FIFO trigger table B */ | |
47 | #define UART_FCTR_EXAR_TRGC 0x80 /* FIFO trigger table C */ | |
48 | #define UART_FCTR_EXAR_TRGD 0xc0 /* FIFO trigger table D programmable */ | |
49 | ||
50 | #define UART_EXAR_TXTRG 0x0a /* Tx FIFO trigger level write-only */ | |
51 | #define UART_EXAR_RXTRG 0x0b /* Rx FIFO trigger level write-only */ | |
52 | ||
d0aeaa83 SM |
53 | #define UART_EXAR_MPIOINT_7_0 0x8f /* MPIOINT[7:0] */ |
54 | #define UART_EXAR_MPIOLVL_7_0 0x90 /* MPIOLVL[7:0] */ | |
55 | #define UART_EXAR_MPIO3T_7_0 0x91 /* MPIO3T[7:0] */ | |
56 | #define UART_EXAR_MPIOINV_7_0 0x92 /* MPIOINV[7:0] */ | |
57 | #define UART_EXAR_MPIOSEL_7_0 0x93 /* MPIOSEL[7:0] */ | |
58 | #define UART_EXAR_MPIOOD_7_0 0x94 /* MPIOOD[7:0] */ | |
59 | #define UART_EXAR_MPIOINT_15_8 0x95 /* MPIOINT[15:8] */ | |
60 | #define UART_EXAR_MPIOLVL_15_8 0x96 /* MPIOLVL[15:8] */ | |
61 | #define UART_EXAR_MPIO3T_15_8 0x97 /* MPIO3T[15:8] */ | |
62 | #define UART_EXAR_MPIOINV_15_8 0x98 /* MPIOINV[15:8] */ | |
63 | #define UART_EXAR_MPIOSEL_15_8 0x99 /* MPIOSEL[15:8] */ | |
64 | #define UART_EXAR_MPIOOD_15_8 0x9a /* MPIOOD[15:8] */ | |
65 | ||
413058df JK |
66 | #define UART_EXAR_RS485_DLY(x) ((x) << 4) |
67 | ||
68 | /* | |
69 | * IOT2040 MPIO wiring semantics: | |
70 | * | |
71 | * MPIO Port Function | |
72 | * ---- ---- -------- | |
73 | * 0 2 Mode bit 0 | |
74 | * 1 2 Mode bit 1 | |
75 | * 2 2 Terminate bus | |
76 | * 3 - <reserved> | |
77 | * 4 3 Mode bit 0 | |
78 | * 5 3 Mode bit 1 | |
79 | * 6 3 Terminate bus | |
80 | * 7 - <reserved> | |
81 | * 8 2 Enable | |
82 | * 9 3 Enable | |
83 | * 10 - Red LED | |
84 | * 11..15 - <unused> | |
85 | */ | |
86 | ||
87 | /* IOT2040 MPIOs 0..7 */ | |
88 | #define IOT2040_UART_MODE_RS232 0x01 | |
89 | #define IOT2040_UART_MODE_RS485 0x02 | |
90 | #define IOT2040_UART_MODE_RS422 0x03 | |
91 | #define IOT2040_UART_TERMINATE_BUS 0x04 | |
92 | ||
93 | #define IOT2040_UART1_MASK 0x0f | |
94 | #define IOT2040_UART2_SHIFT 4 | |
95 | ||
96 | #define IOT2040_UARTS_DEFAULT_MODE 0x11 /* both RS232 */ | |
97 | #define IOT2040_UARTS_GPIO_LO_MODE 0x88 /* reserved pins as input */ | |
98 | ||
99 | /* IOT2040 MPIOs 8..15 */ | |
100 | #define IOT2040_UARTS_ENABLE 0x03 | |
101 | #define IOT2040_UARTS_GPIO_HI_MODE 0xF8 /* enable & LED as outputs */ | |
102 | ||
d0aeaa83 SM |
103 | struct exar8250; |
104 | ||
0d963ebf JK |
105 | struct exar8250_platform { |
106 | int (*rs485_config)(struct uart_port *, struct serial_rs485 *); | |
107 | int (*register_gpio)(struct pci_dev *, struct uart_8250_port *); | |
108 | }; | |
109 | ||
d0aeaa83 SM |
110 | /** |
111 | * struct exar8250_board - board information | |
112 | * @num_ports: number of serial ports | |
113 | * @reg_shift: describes UART register mapping in PCI memory | |
114 | */ | |
115 | struct exar8250_board { | |
116 | unsigned int num_ports; | |
117 | unsigned int reg_shift; | |
118 | bool has_slave; | |
119 | int (*setup)(struct exar8250 *, struct pci_dev *, | |
120 | struct uart_8250_port *, int); | |
121 | void (*exit)(struct pci_dev *pcidev); | |
122 | }; | |
123 | ||
124 | struct exar8250 { | |
125 | unsigned int nr; | |
126 | struct exar8250_board *board; | |
127 | int line[0]; | |
128 | }; | |
129 | ||
130 | static int default_setup(struct exar8250 *priv, struct pci_dev *pcidev, | |
131 | int idx, unsigned int offset, | |
132 | struct uart_8250_port *port) | |
133 | { | |
134 | const struct exar8250_board *board = priv->board; | |
135 | unsigned int bar = 0; | |
136 | ||
24572af4 JK |
137 | if (!pcim_iomap_table(pcidev)[bar] && !pcim_iomap(pcidev, bar, 0)) |
138 | return -ENOMEM; | |
139 | ||
d0aeaa83 SM |
140 | port->port.iotype = UPIO_MEM; |
141 | port->port.mapbase = pci_resource_start(pcidev, bar) + offset; | |
142 | port->port.membase = pcim_iomap_table(pcidev)[bar] + offset; | |
143 | port->port.regshift = board->reg_shift; | |
144 | ||
145 | return 0; | |
146 | } | |
147 | ||
fc6cc961 JK |
148 | static int |
149 | pci_fastcom335_setup(struct exar8250 *priv, struct pci_dev *pcidev, | |
150 | struct uart_8250_port *port, int idx) | |
151 | { | |
152 | unsigned int offset = idx * 0x200; | |
153 | unsigned int baud = 1843200; | |
154 | u8 __iomem *p; | |
155 | int err; | |
156 | ||
fc6cc961 JK |
157 | port->port.uartclk = baud * 16; |
158 | ||
159 | err = default_setup(priv, pcidev, idx, offset, port); | |
160 | if (err) | |
161 | return err; | |
162 | ||
163 | p = port->port.membase; | |
164 | ||
165 | writeb(0x00, p + UART_EXAR_8XMODE); | |
166 | writeb(UART_FCTR_EXAR_TRGD, p + UART_EXAR_FCTR); | |
167 | writeb(32, p + UART_EXAR_TXTRG); | |
168 | writeb(32, p + UART_EXAR_RXTRG); | |
169 | ||
170 | /* | |
171 | * Setup Multipurpose Input/Output pins. | |
172 | */ | |
173 | if (idx == 0) { | |
174 | switch (pcidev->device) { | |
175 | case PCI_DEVICE_ID_COMMTECH_4222PCI335: | |
176 | case PCI_DEVICE_ID_COMMTECH_4224PCI335: | |
177 | writeb(0x78, p + UART_EXAR_MPIOLVL_7_0); | |
178 | writeb(0x00, p + UART_EXAR_MPIOINV_7_0); | |
179 | writeb(0x00, p + UART_EXAR_MPIOSEL_7_0); | |
180 | break; | |
181 | case PCI_DEVICE_ID_COMMTECH_2324PCI335: | |
182 | case PCI_DEVICE_ID_COMMTECH_2328PCI335: | |
183 | writeb(0x00, p + UART_EXAR_MPIOLVL_7_0); | |
184 | writeb(0xc0, p + UART_EXAR_MPIOINV_7_0); | |
185 | writeb(0xc0, p + UART_EXAR_MPIOSEL_7_0); | |
186 | break; | |
187 | } | |
188 | writeb(0x00, p + UART_EXAR_MPIOINT_7_0); | |
189 | writeb(0x00, p + UART_EXAR_MPIO3T_7_0); | |
190 | writeb(0x00, p + UART_EXAR_MPIOOD_7_0); | |
191 | } | |
192 | ||
193 | return 0; | |
194 | } | |
195 | ||
d0aeaa83 SM |
196 | static int |
197 | pci_connect_tech_setup(struct exar8250 *priv, struct pci_dev *pcidev, | |
198 | struct uart_8250_port *port, int idx) | |
199 | { | |
200 | unsigned int offset = idx * 0x200; | |
201 | unsigned int baud = 1843200; | |
202 | ||
203 | port->port.uartclk = baud * 16; | |
204 | return default_setup(priv, pcidev, idx, offset, port); | |
205 | } | |
206 | ||
207 | static int | |
208 | pci_xr17c154_setup(struct exar8250 *priv, struct pci_dev *pcidev, | |
209 | struct uart_8250_port *port, int idx) | |
210 | { | |
211 | unsigned int offset = idx * 0x200; | |
212 | unsigned int baud = 921600; | |
213 | ||
214 | port->port.uartclk = baud * 16; | |
215 | return default_setup(priv, pcidev, idx, offset, port); | |
216 | } | |
217 | ||
bea8be65 | 218 | static void setup_gpio(struct pci_dev *pcidev, u8 __iomem *p) |
d0aeaa83 | 219 | { |
bea8be65 JK |
220 | /* |
221 | * The Commtech adapters required the MPIOs to be driven low. The Exar | |
222 | * devices will export them as GPIOs, so we pre-configure them safely | |
223 | * as inputs. | |
224 | */ | |
225 | u8 dir = pcidev->vendor == PCI_VENDOR_ID_EXAR ? 0xff : 0x00; | |
226 | ||
d0aeaa83 SM |
227 | writeb(0x00, p + UART_EXAR_MPIOINT_7_0); |
228 | writeb(0x00, p + UART_EXAR_MPIOLVL_7_0); | |
229 | writeb(0x00, p + UART_EXAR_MPIO3T_7_0); | |
230 | writeb(0x00, p + UART_EXAR_MPIOINV_7_0); | |
bea8be65 | 231 | writeb(dir, p + UART_EXAR_MPIOSEL_7_0); |
d0aeaa83 SM |
232 | writeb(0x00, p + UART_EXAR_MPIOOD_7_0); |
233 | writeb(0x00, p + UART_EXAR_MPIOINT_15_8); | |
234 | writeb(0x00, p + UART_EXAR_MPIOLVL_15_8); | |
235 | writeb(0x00, p + UART_EXAR_MPIO3T_15_8); | |
236 | writeb(0x00, p + UART_EXAR_MPIOINV_15_8); | |
bea8be65 | 237 | writeb(dir, p + UART_EXAR_MPIOSEL_15_8); |
d0aeaa83 SM |
238 | writeb(0x00, p + UART_EXAR_MPIOOD_15_8); |
239 | } | |
240 | ||
241 | static void * | |
380b1e2f JK |
242 | __xr17v35x_register_gpio(struct pci_dev *pcidev, |
243 | const struct property_entry *properties) | |
d0aeaa83 SM |
244 | { |
245 | struct platform_device *pdev; | |
246 | ||
247 | pdev = platform_device_alloc("gpio_exar", PLATFORM_DEVID_AUTO); | |
248 | if (!pdev) | |
249 | return NULL; | |
250 | ||
d3936d74 | 251 | pdev->dev.parent = &pcidev->dev; |
4076cf08 | 252 | ACPI_COMPANION_SET(&pdev->dev, ACPI_COMPANION(&pcidev->dev)); |
d3936d74 | 253 | |
380b1e2f JK |
254 | if (platform_device_add_properties(pdev, properties) < 0 || |
255 | platform_device_add(pdev) < 0) { | |
d0aeaa83 SM |
256 | platform_device_put(pdev); |
257 | return NULL; | |
258 | } | |
259 | ||
260 | return pdev; | |
261 | } | |
262 | ||
380b1e2f | 263 | static const struct property_entry exar_gpio_properties[] = { |
a589e211 | 264 | PROPERTY_ENTRY_U32("exar,first-pin", 0), |
380b1e2f JK |
265 | PROPERTY_ENTRY_U32("ngpios", 16), |
266 | { } | |
267 | }; | |
268 | ||
0d963ebf JK |
269 | static int xr17v35x_register_gpio(struct pci_dev *pcidev, |
270 | struct uart_8250_port *port) | |
271 | { | |
272 | if (pcidev->vendor == PCI_VENDOR_ID_EXAR) | |
273 | port->port.private_data = | |
380b1e2f | 274 | __xr17v35x_register_gpio(pcidev, exar_gpio_properties); |
0d963ebf JK |
275 | |
276 | return 0; | |
277 | } | |
278 | ||
279 | static const struct exar8250_platform exar8250_default_platform = { | |
280 | .register_gpio = xr17v35x_register_gpio, | |
281 | }; | |
282 | ||
413058df JK |
283 | static int iot2040_rs485_config(struct uart_port *port, |
284 | struct serial_rs485 *rs485) | |
285 | { | |
286 | bool is_rs485 = !!(rs485->flags & SER_RS485_ENABLED); | |
287 | u8 __iomem *p = port->membase; | |
288 | u8 mask = IOT2040_UART1_MASK; | |
289 | u8 mode, value; | |
290 | ||
291 | if (is_rs485) { | |
292 | if (rs485->flags & SER_RS485_RX_DURING_TX) | |
293 | mode = IOT2040_UART_MODE_RS422; | |
294 | else | |
295 | mode = IOT2040_UART_MODE_RS485; | |
296 | ||
297 | if (rs485->flags & SER_RS485_TERMINATE_BUS) | |
298 | mode |= IOT2040_UART_TERMINATE_BUS; | |
299 | } else { | |
300 | mode = IOT2040_UART_MODE_RS232; | |
301 | } | |
302 | ||
303 | if (port->line == 3) { | |
304 | mask <<= IOT2040_UART2_SHIFT; | |
305 | mode <<= IOT2040_UART2_SHIFT; | |
306 | } | |
307 | ||
308 | value = readb(p + UART_EXAR_MPIOLVL_7_0); | |
309 | value &= ~mask; | |
310 | value |= mode; | |
311 | writeb(value, p + UART_EXAR_MPIOLVL_7_0); | |
312 | ||
313 | value = readb(p + UART_EXAR_FCTR); | |
314 | if (is_rs485) | |
315 | value |= UART_FCTR_EXAR_485; | |
316 | else | |
317 | value &= ~UART_FCTR_EXAR_485; | |
318 | writeb(value, p + UART_EXAR_FCTR); | |
319 | ||
320 | if (is_rs485) | |
321 | writeb(UART_EXAR_RS485_DLY(4), p + UART_MSR); | |
322 | ||
323 | port->rs485 = *rs485; | |
324 | ||
325 | return 0; | |
326 | } | |
327 | ||
328 | static const struct property_entry iot2040_gpio_properties[] = { | |
a589e211 | 329 | PROPERTY_ENTRY_U32("exar,first-pin", 10), |
413058df JK |
330 | PROPERTY_ENTRY_U32("ngpios", 1), |
331 | { } | |
332 | }; | |
333 | ||
334 | static int iot2040_register_gpio(struct pci_dev *pcidev, | |
335 | struct uart_8250_port *port) | |
336 | { | |
337 | u8 __iomem *p = port->port.membase; | |
338 | ||
339 | writeb(IOT2040_UARTS_DEFAULT_MODE, p + UART_EXAR_MPIOLVL_7_0); | |
340 | writeb(IOT2040_UARTS_GPIO_LO_MODE, p + UART_EXAR_MPIOSEL_7_0); | |
341 | writeb(IOT2040_UARTS_ENABLE, p + UART_EXAR_MPIOLVL_15_8); | |
342 | writeb(IOT2040_UARTS_GPIO_HI_MODE, p + UART_EXAR_MPIOSEL_15_8); | |
343 | ||
344 | port->port.private_data = | |
345 | __xr17v35x_register_gpio(pcidev, iot2040_gpio_properties); | |
346 | ||
347 | return 0; | |
348 | } | |
349 | ||
350 | static const struct exar8250_platform iot2040_platform = { | |
351 | .rs485_config = iot2040_rs485_config, | |
352 | .register_gpio = iot2040_register_gpio, | |
353 | }; | |
354 | ||
355 | static const struct dmi_system_id exar_platforms[] = { | |
356 | { | |
357 | .matches = { | |
358 | DMI_EXACT_MATCH(DMI_BOARD_NAME, "SIMATIC IOT2000"), | |
359 | DMI_EXACT_MATCH(DMI_BOARD_ASSET_TAG, | |
360 | "6ES7647-0AA00-1YA2"), | |
361 | }, | |
362 | .driver_data = (void *)&iot2040_platform, | |
363 | }, | |
364 | {} | |
365 | }; | |
366 | ||
d0aeaa83 SM |
367 | static int |
368 | pci_xr17v35x_setup(struct exar8250 *priv, struct pci_dev *pcidev, | |
369 | struct uart_8250_port *port, int idx) | |
370 | { | |
371 | const struct exar8250_board *board = priv->board; | |
0d963ebf | 372 | const struct exar8250_platform *platform; |
413058df | 373 | const struct dmi_system_id *dmi_match; |
d0aeaa83 SM |
374 | unsigned int offset = idx * 0x400; |
375 | unsigned int baud = 7812500; | |
376 | u8 __iomem *p; | |
377 | int ret; | |
378 | ||
413058df JK |
379 | dmi_match = dmi_first_match(exar_platforms); |
380 | if (dmi_match) | |
381 | platform = dmi_match->driver_data; | |
382 | else | |
383 | platform = &exar8250_default_platform; | |
0d963ebf | 384 | |
d0aeaa83 | 385 | port->port.uartclk = baud * 16; |
0d963ebf JK |
386 | port->port.rs485_config = platform->rs485_config; |
387 | ||
d0aeaa83 SM |
388 | /* |
389 | * Setup the uart clock for the devices on expansion slot to | |
390 | * half the clock speed of the main chip (which is 125MHz) | |
391 | */ | |
392 | if (board->has_slave && idx >= 8) | |
393 | port->port.uartclk /= 2; | |
394 | ||
5b5f252d JK |
395 | ret = default_setup(priv, pcidev, idx, offset, port); |
396 | if (ret) | |
397 | return ret; | |
d0aeaa83 | 398 | |
5b5f252d | 399 | p = port->port.membase; |
d0aeaa83 SM |
400 | |
401 | writeb(0x00, p + UART_EXAR_8XMODE); | |
402 | writeb(UART_FCTR_EXAR_TRGD, p + UART_EXAR_FCTR); | |
403 | writeb(128, p + UART_EXAR_TXTRG); | |
404 | writeb(128, p + UART_EXAR_RXTRG); | |
d0aeaa83 | 405 | |
5b5f252d JK |
406 | if (idx == 0) { |
407 | /* Setup Multipurpose Input/Output pins. */ | |
bea8be65 | 408 | setup_gpio(pcidev, p); |
d0aeaa83 | 409 | |
0d963ebf | 410 | ret = platform->register_gpio(pcidev, port); |
5b5f252d | 411 | } |
d0aeaa83 | 412 | |
0d963ebf | 413 | return ret; |
d0aeaa83 SM |
414 | } |
415 | ||
416 | static void pci_xr17v35x_exit(struct pci_dev *pcidev) | |
417 | { | |
418 | struct exar8250 *priv = pci_get_drvdata(pcidev); | |
419 | struct uart_8250_port *port = serial8250_get_port(priv->line[0]); | |
420 | struct platform_device *pdev = port->port.private_data; | |
421 | ||
422 | platform_device_unregister(pdev); | |
423 | port->port.private_data = NULL; | |
424 | } | |
425 | ||
426 | static int | |
427 | exar_pci_probe(struct pci_dev *pcidev, const struct pci_device_id *ent) | |
428 | { | |
429 | unsigned int nr_ports, i, bar = 0, maxnr; | |
430 | struct exar8250_board *board; | |
431 | struct uart_8250_port uart; | |
432 | struct exar8250 *priv; | |
433 | int rc; | |
434 | ||
435 | board = (struct exar8250_board *)ent->driver_data; | |
436 | if (!board) | |
437 | return -EINVAL; | |
438 | ||
439 | rc = pcim_enable_device(pcidev); | |
440 | if (rc) | |
441 | return rc; | |
442 | ||
443 | maxnr = pci_resource_len(pcidev, bar) >> (board->reg_shift + 3); | |
444 | ||
445 | nr_ports = board->num_ports ? board->num_ports : pcidev->device & 0x0f; | |
446 | ||
447 | priv = devm_kzalloc(&pcidev->dev, sizeof(*priv) + | |
448 | sizeof(unsigned int) * nr_ports, | |
449 | GFP_KERNEL); | |
450 | if (!priv) | |
451 | return -ENOMEM; | |
452 | ||
453 | priv->board = board; | |
454 | ||
172c33cb JK |
455 | pci_set_master(pcidev); |
456 | ||
457 | rc = pci_alloc_irq_vectors(pcidev, 1, 1, PCI_IRQ_ALL_TYPES); | |
458 | if (rc < 0) | |
459 | return rc; | |
460 | ||
d0aeaa83 SM |
461 | memset(&uart, 0, sizeof(uart)); |
462 | uart.port.flags = UPF_SKIP_TEST | UPF_BOOT_AUTOCONF | UPF_SHARE_IRQ | |
463 | | UPF_EXAR_EFR; | |
172c33cb | 464 | uart.port.irq = pci_irq_vector(pcidev, 0); |
d0aeaa83 SM |
465 | uart.port.dev = &pcidev->dev; |
466 | ||
467 | for (i = 0; i < nr_ports && i < maxnr; i++) { | |
468 | rc = board->setup(priv, pcidev, &uart, i); | |
469 | if (rc) { | |
470 | dev_err(&pcidev->dev, "Failed to setup port %u\n", i); | |
471 | break; | |
472 | } | |
473 | ||
474 | dev_dbg(&pcidev->dev, "Setup PCI port: port %lx, irq %d, type %d\n", | |
475 | uart.port.iobase, uart.port.irq, uart.port.iotype); | |
476 | ||
477 | priv->line[i] = serial8250_register_8250_port(&uart); | |
478 | if (priv->line[i] < 0) { | |
479 | dev_err(&pcidev->dev, | |
480 | "Couldn't register serial port %lx, irq %d, type %d, error %d\n", | |
481 | uart.port.iobase, uart.port.irq, | |
482 | uart.port.iotype, priv->line[i]); | |
483 | break; | |
484 | } | |
485 | } | |
486 | priv->nr = i; | |
487 | pci_set_drvdata(pcidev, priv); | |
488 | return 0; | |
489 | } | |
490 | ||
491 | static void exar_pci_remove(struct pci_dev *pcidev) | |
492 | { | |
493 | struct exar8250 *priv = pci_get_drvdata(pcidev); | |
494 | unsigned int i; | |
495 | ||
496 | for (i = 0; i < priv->nr; i++) | |
497 | serial8250_unregister_port(priv->line[i]); | |
498 | ||
499 | if (priv->board->exit) | |
500 | priv->board->exit(pcidev); | |
501 | } | |
502 | ||
503 | static int __maybe_unused exar_suspend(struct device *dev) | |
504 | { | |
505 | struct pci_dev *pcidev = to_pci_dev(dev); | |
506 | struct exar8250 *priv = pci_get_drvdata(pcidev); | |
507 | unsigned int i; | |
508 | ||
509 | for (i = 0; i < priv->nr; i++) | |
510 | if (priv->line[i] >= 0) | |
511 | serial8250_suspend_port(priv->line[i]); | |
512 | ||
513 | /* Ensure that every init quirk is properly torn down */ | |
514 | if (priv->board->exit) | |
515 | priv->board->exit(pcidev); | |
516 | ||
517 | return 0; | |
518 | } | |
519 | ||
520 | static int __maybe_unused exar_resume(struct device *dev) | |
521 | { | |
522 | struct pci_dev *pcidev = to_pci_dev(dev); | |
523 | struct exar8250 *priv = pci_get_drvdata(pcidev); | |
524 | unsigned int i; | |
525 | ||
526 | for (i = 0; i < priv->nr; i++) | |
527 | if (priv->line[i] >= 0) | |
528 | serial8250_resume_port(priv->line[i]); | |
529 | ||
530 | return 0; | |
531 | } | |
532 | ||
533 | static SIMPLE_DEV_PM_OPS(exar_pci_pm, exar_suspend, exar_resume); | |
534 | ||
fc6cc961 JK |
535 | static const struct exar8250_board pbn_fastcom335_2 = { |
536 | .num_ports = 2, | |
537 | .setup = pci_fastcom335_setup, | |
538 | }; | |
539 | ||
540 | static const struct exar8250_board pbn_fastcom335_4 = { | |
541 | .num_ports = 4, | |
542 | .setup = pci_fastcom335_setup, | |
543 | }; | |
544 | ||
545 | static const struct exar8250_board pbn_fastcom335_8 = { | |
546 | .num_ports = 8, | |
547 | .setup = pci_fastcom335_setup, | |
548 | }; | |
549 | ||
d0aeaa83 SM |
550 | static const struct exar8250_board pbn_connect = { |
551 | .setup = pci_connect_tech_setup, | |
552 | }; | |
553 | ||
554 | static const struct exar8250_board pbn_exar_ibm_saturn = { | |
555 | .num_ports = 1, | |
556 | .setup = pci_xr17c154_setup, | |
557 | }; | |
558 | ||
559 | static const struct exar8250_board pbn_exar_XR17C15x = { | |
560 | .setup = pci_xr17c154_setup, | |
561 | }; | |
562 | ||
563 | static const struct exar8250_board pbn_exar_XR17V35x = { | |
564 | .setup = pci_xr17v35x_setup, | |
565 | .exit = pci_xr17v35x_exit, | |
566 | }; | |
567 | ||
568 | static const struct exar8250_board pbn_exar_XR17V4358 = { | |
569 | .num_ports = 12, | |
570 | .has_slave = true, | |
571 | .setup = pci_xr17v35x_setup, | |
572 | .exit = pci_xr17v35x_exit, | |
573 | }; | |
574 | ||
575 | static const struct exar8250_board pbn_exar_XR17V8358 = { | |
576 | .num_ports = 16, | |
577 | .has_slave = true, | |
578 | .setup = pci_xr17v35x_setup, | |
579 | .exit = pci_xr17v35x_exit, | |
580 | }; | |
581 | ||
582 | #define CONNECT_DEVICE(devid, sdevid, bd) { \ | |
583 | PCI_DEVICE_SUB( \ | |
584 | PCI_VENDOR_ID_EXAR, \ | |
585 | PCI_DEVICE_ID_EXAR_##devid, \ | |
586 | PCI_SUBVENDOR_ID_CONNECT_TECH, \ | |
587 | PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_##sdevid), 0, 0, \ | |
588 | (kernel_ulong_t)&bd \ | |
589 | } | |
590 | ||
591 | #define EXAR_DEVICE(vend, devid, bd) { \ | |
592 | PCI_VDEVICE(vend, PCI_DEVICE_ID_##devid), (kernel_ulong_t)&bd \ | |
593 | } | |
594 | ||
595 | #define IBM_DEVICE(devid, sdevid, bd) { \ | |
596 | PCI_DEVICE_SUB( \ | |
597 | PCI_VENDOR_ID_EXAR, \ | |
598 | PCI_DEVICE_ID_EXAR_##devid, \ | |
599 | PCI_VENDOR_ID_IBM, \ | |
600 | PCI_SUBDEVICE_ID_IBM_##sdevid), 0, 0, \ | |
601 | (kernel_ulong_t)&bd \ | |
602 | } | |
603 | ||
604 | static struct pci_device_id exar_pci_tbl[] = { | |
605 | CONNECT_DEVICE(XR17C152, UART_2_232, pbn_connect), | |
606 | CONNECT_DEVICE(XR17C154, UART_4_232, pbn_connect), | |
607 | CONNECT_DEVICE(XR17C158, UART_8_232, pbn_connect), | |
608 | CONNECT_DEVICE(XR17C152, UART_1_1, pbn_connect), | |
609 | CONNECT_DEVICE(XR17C154, UART_2_2, pbn_connect), | |
610 | CONNECT_DEVICE(XR17C158, UART_4_4, pbn_connect), | |
611 | CONNECT_DEVICE(XR17C152, UART_2, pbn_connect), | |
612 | CONNECT_DEVICE(XR17C154, UART_4, pbn_connect), | |
613 | CONNECT_DEVICE(XR17C158, UART_8, pbn_connect), | |
614 | CONNECT_DEVICE(XR17C152, UART_2_485, pbn_connect), | |
615 | CONNECT_DEVICE(XR17C154, UART_4_485, pbn_connect), | |
616 | CONNECT_DEVICE(XR17C158, UART_8_485, pbn_connect), | |
617 | ||
618 | IBM_DEVICE(XR17C152, SATURN_SERIAL_ONE_PORT, pbn_exar_ibm_saturn), | |
619 | ||
620 | /* Exar Corp. XR17C15[248] Dual/Quad/Octal UART */ | |
621 | EXAR_DEVICE(EXAR, EXAR_XR17C152, pbn_exar_XR17C15x), | |
622 | EXAR_DEVICE(EXAR, EXAR_XR17C154, pbn_exar_XR17C15x), | |
623 | EXAR_DEVICE(EXAR, EXAR_XR17C158, pbn_exar_XR17C15x), | |
624 | ||
625 | /* Exar Corp. XR17V[48]35[248] Dual/Quad/Octal/Hexa PCIe UARTs */ | |
626 | EXAR_DEVICE(EXAR, EXAR_XR17V352, pbn_exar_XR17V35x), | |
627 | EXAR_DEVICE(EXAR, EXAR_XR17V354, pbn_exar_XR17V35x), | |
628 | EXAR_DEVICE(EXAR, EXAR_XR17V358, pbn_exar_XR17V35x), | |
629 | EXAR_DEVICE(EXAR, EXAR_XR17V4358, pbn_exar_XR17V4358), | |
630 | EXAR_DEVICE(EXAR, EXAR_XR17V8358, pbn_exar_XR17V8358), | |
631 | EXAR_DEVICE(COMMTECH, COMMTECH_4222PCIE, pbn_exar_XR17V35x), | |
632 | EXAR_DEVICE(COMMTECH, COMMTECH_4224PCIE, pbn_exar_XR17V35x), | |
633 | EXAR_DEVICE(COMMTECH, COMMTECH_4228PCIE, pbn_exar_XR17V35x), | |
fc6cc961 JK |
634 | |
635 | EXAR_DEVICE(COMMTECH, COMMTECH_4222PCI335, pbn_fastcom335_2), | |
636 | EXAR_DEVICE(COMMTECH, COMMTECH_4224PCI335, pbn_fastcom335_4), | |
637 | EXAR_DEVICE(COMMTECH, COMMTECH_2324PCI335, pbn_fastcom335_4), | |
638 | EXAR_DEVICE(COMMTECH, COMMTECH_2328PCI335, pbn_fastcom335_8), | |
d0aeaa83 SM |
639 | { 0, } |
640 | }; | |
641 | MODULE_DEVICE_TABLE(pci, exar_pci_tbl); | |
642 | ||
643 | static struct pci_driver exar_pci_driver = { | |
644 | .name = "exar_serial", | |
645 | .probe = exar_pci_probe, | |
646 | .remove = exar_pci_remove, | |
647 | .driver = { | |
648 | .pm = &exar_pci_pm, | |
649 | }, | |
650 | .id_table = exar_pci_tbl, | |
651 | }; | |
652 | module_pci_driver(exar_pci_driver); | |
653 | ||
654 | MODULE_LICENSE("GPL"); | |
2b57b7ff | 655 | MODULE_DESCRIPTION("Exar Serial Driver"); |
d0aeaa83 | 656 | MODULE_AUTHOR("Sudip Mukherjee <sudip.mukherjee@codethink.co.uk>"); |