diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/serial/jsm/jsm_driver.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/serial/jsm/jsm_driver.c')
-rw-r--r-- | drivers/serial/jsm/jsm_driver.c | 404 |
1 files changed, 404 insertions, 0 deletions
diff --git a/drivers/serial/jsm/jsm_driver.c b/drivers/serial/jsm/jsm_driver.c new file mode 100644 index 000000000000..d4847d4f1473 --- /dev/null +++ b/drivers/serial/jsm/jsm_driver.c | |||
@@ -0,0 +1,404 @@ | |||
1 | /************************************************************************ | ||
2 | * Copyright 2003 Digi International (www.digi.com) | ||
3 | * | ||
4 | * Copyright (C) 2004 IBM Corporation. All rights reserved. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2, or (at your option) | ||
9 | * any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; without even the | ||
13 | * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR | ||
14 | * PURPOSE. See the GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 * Temple Place - Suite 330, Boston, | ||
19 | * MA 02111-1307, USA. | ||
20 | * | ||
21 | * Contact Information: | ||
22 | * Scott H Kilau <Scott_Kilau@digi.com> | ||
23 | * Wendy Xiong <wendyx@us.ltcfwd.linux.ibm.com> | ||
24 | * | ||
25 | ***********************************************************************/ | ||
26 | #include <linux/moduleparam.h> | ||
27 | #include <linux/pci.h> | ||
28 | |||
29 | #include "jsm.h" | ||
30 | |||
31 | MODULE_AUTHOR("Digi International, http://www.digi.com"); | ||
32 | MODULE_DESCRIPTION("Driver for the Digi International Neo PCI based product line"); | ||
33 | MODULE_SUPPORTED_DEVICE("jsm"); | ||
34 | |||
35 | #define JSM_DRIVER_NAME "jsm" | ||
36 | #define NR_PORTS 32 | ||
37 | #define JSM_MINOR_START 0 | ||
38 | |||
39 | struct uart_driver jsm_uart_driver = { | ||
40 | .owner = THIS_MODULE, | ||
41 | .driver_name = JSM_DRIVER_NAME, | ||
42 | .dev_name = "ttyn", | ||
43 | .major = 253, | ||
44 | .minor = JSM_MINOR_START, | ||
45 | .nr = NR_PORTS, | ||
46 | .cons = NULL, | ||
47 | }; | ||
48 | |||
49 | int jsm_debug; | ||
50 | int jsm_rawreadok; | ||
51 | module_param(jsm_debug, int, 0); | ||
52 | module_param(jsm_rawreadok, int, 0); | ||
53 | MODULE_PARM_DESC(jsm_debug, "Driver debugging level"); | ||
54 | MODULE_PARM_DESC(jsm_rawreadok, "Bypass flip buffers on input"); | ||
55 | |||
56 | /* | ||
57 | * Globals | ||
58 | */ | ||
59 | int jsm_driver_state = DRIVER_INITIALIZED; | ||
60 | spinlock_t jsm_board_head_lock = SPIN_LOCK_UNLOCKED; | ||
61 | LIST_HEAD(jsm_board_head); | ||
62 | |||
63 | static struct pci_device_id jsm_pci_tbl[] = { | ||
64 | { PCI_DEVICE (PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_NEO_2DB9), 0, 0, 0 }, | ||
65 | { PCI_DEVICE (PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_NEO_2DB9PRI), 0, 0, 1 }, | ||
66 | { PCI_DEVICE (PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_NEO_2RJ45), 0, 0, 2 }, | ||
67 | { PCI_DEVICE (PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_NEO_2RJ45PRI), 0, 0, 3 }, | ||
68 | { 0,} /* 0 terminated list. */ | ||
69 | }; | ||
70 | MODULE_DEVICE_TABLE(pci, jsm_pci_tbl); | ||
71 | |||
72 | static struct board_id jsm_Ids[] = { | ||
73 | { PCI_DEVICE_NEO_2DB9_PCI_NAME, 2 }, | ||
74 | { PCI_DEVICE_NEO_2DB9PRI_PCI_NAME, 2 }, | ||
75 | { PCI_DEVICE_NEO_2RJ45_PCI_NAME, 2 }, | ||
76 | { PCI_DEVICE_NEO_2RJ45PRI_PCI_NAME, 2 }, | ||
77 | { NULL, 0 } | ||
78 | }; | ||
79 | |||
80 | char *jsm_driver_state_text[] = { | ||
81 | "Driver Initialized", | ||
82 | "Driver Ready." | ||
83 | }; | ||
84 | |||
85 | static int jsm_finalize_board_init(struct jsm_board *brd) | ||
86 | { | ||
87 | int rc = 0; | ||
88 | |||
89 | jsm_printk(INIT, INFO, &brd->pci_dev, "start\n"); | ||
90 | |||
91 | if (brd->irq) { | ||
92 | rc = request_irq(brd->irq, brd->bd_ops->intr, SA_INTERRUPT|SA_SHIRQ, "JSM", brd); | ||
93 | |||
94 | if (rc) { | ||
95 | printk(KERN_WARNING "Failed to hook IRQ %d\n",brd->irq); | ||
96 | brd->state = BOARD_FAILED; | ||
97 | brd->dpastatus = BD_NOFEP; | ||
98 | rc = -ENODEV; | ||
99 | } else | ||
100 | jsm_printk(INIT, INFO, &brd->pci_dev, | ||
101 | "Requested and received usage of IRQ %d\n", brd->irq); | ||
102 | } | ||
103 | return rc; | ||
104 | } | ||
105 | |||
106 | /* | ||
107 | * jsm_found_board() | ||
108 | * | ||
109 | * A board has been found, init it. | ||
110 | */ | ||
111 | static int jsm_found_board(struct pci_dev *pdev, int id) | ||
112 | { | ||
113 | struct jsm_board *brd; | ||
114 | int i = 0; | ||
115 | int rc = 0; | ||
116 | struct list_head *tmp; | ||
117 | struct jsm_board *cur_board_entry; | ||
118 | unsigned long lock_flags; | ||
119 | int adapter_count = 0; | ||
120 | int retval; | ||
121 | |||
122 | brd = kmalloc(sizeof(struct jsm_board), GFP_KERNEL); | ||
123 | if (!brd) { | ||
124 | dev_err(&pdev->dev, "memory allocation for board structure failed\n"); | ||
125 | return -ENOMEM; | ||
126 | } | ||
127 | memset(brd, 0, sizeof(struct jsm_board)); | ||
128 | |||
129 | spin_lock_irqsave(&jsm_board_head_lock, lock_flags); | ||
130 | list_for_each(tmp, &jsm_board_head) { | ||
131 | cur_board_entry = | ||
132 | list_entry(tmp, struct jsm_board, | ||
133 | jsm_board_entry); | ||
134 | if (cur_board_entry->boardnum != adapter_count) { | ||
135 | break; | ||
136 | } | ||
137 | adapter_count++; | ||
138 | } | ||
139 | |||
140 | list_add_tail(&brd->jsm_board_entry, &jsm_board_head); | ||
141 | spin_unlock_irqrestore(&jsm_board_head_lock, lock_flags); | ||
142 | |||
143 | /* store the info for the board we've found */ | ||
144 | brd->boardnum = adapter_count; | ||
145 | brd->pci_dev = pdev; | ||
146 | brd->name = jsm_Ids[id].name; | ||
147 | brd->maxports = jsm_Ids[id].maxports; | ||
148 | brd->dpastatus = BD_NOFEP; | ||
149 | init_waitqueue_head(&brd->state_wait); | ||
150 | |||
151 | spin_lock_init(&brd->bd_lock); | ||
152 | spin_lock_init(&brd->bd_intr_lock); | ||
153 | |||
154 | brd->state = BOARD_FOUND; | ||
155 | |||
156 | for (i = 0; i < brd->maxports; i++) | ||
157 | brd->channels[i] = NULL; | ||
158 | |||
159 | /* store which revision we have */ | ||
160 | pci_read_config_byte(pdev, PCI_REVISION_ID, &brd->rev); | ||
161 | |||
162 | brd->irq = pdev->irq; | ||
163 | |||
164 | switch(brd->pci_dev->device) { | ||
165 | |||
166 | case PCI_DEVICE_ID_NEO_2DB9: | ||
167 | case PCI_DEVICE_ID_NEO_2DB9PRI: | ||
168 | case PCI_DEVICE_ID_NEO_2RJ45: | ||
169 | case PCI_DEVICE_ID_NEO_2RJ45PRI: | ||
170 | |||
171 | /* | ||
172 | * This chip is set up 100% when we get to it. | ||
173 | * No need to enable global interrupts or anything. | ||
174 | */ | ||
175 | brd->dpatype = T_NEO | T_PCIBUS; | ||
176 | |||
177 | jsm_printk(INIT, INFO, &brd->pci_dev, | ||
178 | "jsm_found_board - NEO adapter\n"); | ||
179 | |||
180 | /* get the PCI Base Address Registers */ | ||
181 | brd->membase = pci_resource_start(pdev, 0); | ||
182 | brd->membase_end = pci_resource_end(pdev, 0); | ||
183 | |||
184 | if (brd->membase & 1) | ||
185 | brd->membase &= ~3; | ||
186 | else | ||
187 | brd->membase &= ~15; | ||
188 | |||
189 | /* Assign the board_ops struct */ | ||
190 | brd->bd_ops = &jsm_neo_ops; | ||
191 | |||
192 | brd->bd_uart_offset = 0x200; | ||
193 | brd->bd_dividend = 921600; | ||
194 | |||
195 | brd->re_map_membase = ioremap(brd->membase, 0x1000); | ||
196 | jsm_printk(INIT, INFO, &brd->pci_dev, | ||
197 | "remapped mem: 0x%p\n", brd->re_map_membase); | ||
198 | if (!brd->re_map_membase) { | ||
199 | kfree(brd); | ||
200 | dev_err(&pdev->dev, "card has no PCI Memory resources, failing board.\n"); | ||
201 | return -ENOMEM; | ||
202 | } | ||
203 | break; | ||
204 | |||
205 | default: | ||
206 | dev_err(&pdev->dev, "Did not find any compatible Neo or Classic PCI boards in system.\n"); | ||
207 | kfree(brd); | ||
208 | return -ENXIO; | ||
209 | } | ||
210 | |||
211 | /* | ||
212 | * Do tty device initialization. | ||
213 | */ | ||
214 | rc = jsm_finalize_board_init(brd); | ||
215 | if (rc < 0) { | ||
216 | dev_err(&pdev->dev, "Can't finalize board init (%d)\n", rc); | ||
217 | brd->state = BOARD_FAILED; | ||
218 | retval = -ENXIO; | ||
219 | goto failed0; | ||
220 | } | ||
221 | |||
222 | rc = jsm_tty_init(brd); | ||
223 | if (rc < 0) { | ||
224 | dev_err(&pdev->dev, "Can't init tty devices (%d)\n", rc); | ||
225 | brd->state = BOARD_FAILED; | ||
226 | retval = -ENXIO; | ||
227 | goto failed1; | ||
228 | } | ||
229 | |||
230 | rc = jsm_uart_port_init(brd); | ||
231 | if (rc < 0) { | ||
232 | dev_err(&pdev->dev, "Can't init uart port (%d)\n", rc); | ||
233 | brd->state = BOARD_FAILED; | ||
234 | retval = -ENXIO; | ||
235 | goto failed1; | ||
236 | } | ||
237 | |||
238 | brd->state = BOARD_READY; | ||
239 | brd->dpastatus = BD_RUNNING; | ||
240 | |||
241 | /* Log the information about the board */ | ||
242 | dev_info(&pdev->dev, "board %d: %s (rev %d), irq %d\n",adapter_count, brd->name, brd->rev, brd->irq); | ||
243 | |||
244 | /* | ||
245 | * allocate flip buffer for board. | ||
246 | * | ||
247 | * Okay to malloc with GFP_KERNEL, we are not at interrupt | ||
248 | * context, and there are no locks held. | ||
249 | */ | ||
250 | brd->flipbuf = kmalloc(MYFLIPLEN, GFP_KERNEL); | ||
251 | if (!brd->flipbuf) { | ||
252 | dev_err(&pdev->dev, "memory allocation for flipbuf failed\n"); | ||
253 | brd->state = BOARD_FAILED; | ||
254 | retval = -ENOMEM; | ||
255 | goto failed1; | ||
256 | } | ||
257 | memset(brd->flipbuf, 0, MYFLIPLEN); | ||
258 | |||
259 | jsm_create_driver_sysfiles(pdev->dev.driver); | ||
260 | |||
261 | wake_up_interruptible(&brd->state_wait); | ||
262 | return 0; | ||
263 | failed1: | ||
264 | free_irq(brd->irq, brd); | ||
265 | failed0: | ||
266 | kfree(brd); | ||
267 | iounmap(brd->re_map_membase); | ||
268 | return retval; | ||
269 | } | ||
270 | |||
271 | /* returns count (>= 0), or negative on error */ | ||
272 | static int jsm_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) | ||
273 | { | ||
274 | int rc; | ||
275 | |||
276 | rc = pci_enable_device(pdev); | ||
277 | if (rc) { | ||
278 | dev_err(&pdev->dev, "Device enable FAILED\n"); | ||
279 | return rc; | ||
280 | } | ||
281 | |||
282 | if ((rc = pci_request_regions(pdev, "jsm"))) { | ||
283 | dev_err(&pdev->dev, "pci_request_region FAILED\n"); | ||
284 | pci_disable_device(pdev); | ||
285 | return rc; | ||
286 | } | ||
287 | |||
288 | if ((rc = jsm_found_board(pdev, ent->driver_data))) { | ||
289 | dev_err(&pdev->dev, "jsm_found_board FAILED\n"); | ||
290 | pci_release_regions(pdev); | ||
291 | pci_disable_device(pdev); | ||
292 | return rc; | ||
293 | } | ||
294 | return rc; | ||
295 | } | ||
296 | |||
297 | |||
298 | /* | ||
299 | * jsm_cleanup_board() | ||
300 | * | ||
301 | * Free all the memory associated with a board | ||
302 | */ | ||
303 | static void jsm_cleanup_board(struct jsm_board *brd) | ||
304 | { | ||
305 | int i = 0; | ||
306 | |||
307 | free_irq(brd->irq, brd); | ||
308 | iounmap(brd->re_map_membase); | ||
309 | |||
310 | /* Free all allocated channels structs */ | ||
311 | for (i = 0; i < brd->maxports; i++) { | ||
312 | if (brd->channels[i]) { | ||
313 | if (brd->channels[i]->ch_rqueue) | ||
314 | kfree(brd->channels[i]->ch_rqueue); | ||
315 | if (brd->channels[i]->ch_equeue) | ||
316 | kfree(brd->channels[i]->ch_equeue); | ||
317 | if (brd->channels[i]->ch_wqueue) | ||
318 | kfree(brd->channels[i]->ch_wqueue); | ||
319 | kfree(brd->channels[i]); | ||
320 | } | ||
321 | } | ||
322 | |||
323 | pci_release_regions(brd->pci_dev); | ||
324 | pci_disable_device(brd->pci_dev); | ||
325 | kfree(brd->flipbuf); | ||
326 | kfree(brd); | ||
327 | } | ||
328 | |||
329 | static void jsm_remove_one(struct pci_dev *dev) | ||
330 | { | ||
331 | unsigned long lock_flags; | ||
332 | struct list_head *tmp; | ||
333 | struct jsm_board *brd; | ||
334 | |||
335 | spin_lock_irqsave(&jsm_board_head_lock, lock_flags); | ||
336 | list_for_each(tmp, &jsm_board_head) { | ||
337 | brd = list_entry(tmp, struct jsm_board, | ||
338 | jsm_board_entry); | ||
339 | if ( brd != NULL && brd->pci_dev == dev) { | ||
340 | jsm_remove_uart_port(brd); | ||
341 | jsm_cleanup_board(brd); | ||
342 | list_del(&brd->jsm_board_entry); | ||
343 | break; | ||
344 | } | ||
345 | } | ||
346 | spin_unlock_irqrestore(&jsm_board_head_lock, lock_flags); | ||
347 | return; | ||
348 | } | ||
349 | |||
350 | struct pci_driver jsm_driver = { | ||
351 | .name = "jsm", | ||
352 | .probe = jsm_init_one, | ||
353 | .id_table = jsm_pci_tbl, | ||
354 | .remove = __devexit_p(jsm_remove_one), | ||
355 | }; | ||
356 | |||
357 | /* | ||
358 | * jsm_init_module() | ||
359 | * | ||
360 | * Module load. This is where it all starts. | ||
361 | */ | ||
362 | static int __init jsm_init_module(void) | ||
363 | { | ||
364 | int rc = 0; | ||
365 | |||
366 | printk(KERN_INFO "%s, Digi International Part Number %s\n", | ||
367 | JSM_VERSION, JSM_VERSION); | ||
368 | |||
369 | /* | ||
370 | * Initialize global stuff | ||
371 | */ | ||
372 | |||
373 | rc = uart_register_driver(&jsm_uart_driver); | ||
374 | if (rc < 0) { | ||
375 | return rc; | ||
376 | } | ||
377 | |||
378 | rc = pci_register_driver(&jsm_driver); | ||
379 | if (rc < 0) { | ||
380 | uart_unregister_driver(&jsm_uart_driver); | ||
381 | return rc; | ||
382 | } | ||
383 | jsm_driver_state = DRIVER_READY; | ||
384 | |||
385 | return rc; | ||
386 | } | ||
387 | |||
388 | module_init(jsm_init_module); | ||
389 | |||
390 | /* | ||
391 | * jsm_exit_module() | ||
392 | * | ||
393 | * Module unload. This is where it all ends. | ||
394 | */ | ||
395 | static void __exit jsm_exit_module(void) | ||
396 | { | ||
397 | jsm_remove_driver_sysfiles(&jsm_driver.driver); | ||
398 | |||
399 | pci_unregister_driver(&jsm_driver); | ||
400 | |||
401 | uart_unregister_driver(&jsm_uart_driver); | ||
402 | } | ||
403 | module_exit(jsm_exit_module); | ||
404 | MODULE_LICENSE("GPL"); | ||