jsm_driver.c 7.3 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
/************************************************************************
 * Copyright 2003 Digi International (www.digi.com)
 *
 * Copyright (C) 2004 IBM Corporation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; without even the
 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
 * PURPOSE.  See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 * Temple Place - Suite 330, Boston,
 * MA  02111-1307, USA.
 *
 * Contact Information:
 * Scott H Kilau <Scott_Kilau@digi.com>
23
 * Wendy Xiong   <wendyx@us.ibm.com>
L
Linus Torvalds 已提交
24
 *
V
V. ANANDA KRISHNAN 已提交
25
 *
L
Linus Torvalds 已提交
26 27 28
 ***********************************************************************/
#include <linux/moduleparam.h>
#include <linux/pci.h>
29
#include <linux/slab.h>
L
Linus Torvalds 已提交
30 31 32 33

#include "jsm.h"

MODULE_AUTHOR("Digi International, http://www.digi.com");
34 35 36
MODULE_DESCRIPTION("Driver for the Digi International "
		   "Neo PCI based product line");
MODULE_LICENSE("GPL");
L
Linus Torvalds 已提交
37 38 39 40 41 42 43 44 45 46
MODULE_SUPPORTED_DEVICE("jsm");

#define JSM_DRIVER_NAME "jsm"
#define NR_PORTS	32
#define JSM_MINOR_START	0

struct uart_driver jsm_uart_driver = {
	.owner		= THIS_MODULE,
	.driver_name	= JSM_DRIVER_NAME,
	.dev_name	= "ttyn",
47
	.major		= 0,
L
Linus Torvalds 已提交
48 49 50 51
	.minor		= JSM_MINOR_START,
	.nr		= NR_PORTS,
};

B
Breno Leitao 已提交
52 53 54 55 56 57 58 59 60 61 62
static pci_ers_result_t jsm_io_error_detected(struct pci_dev *pdev,
                                       pci_channel_state_t state);
static pci_ers_result_t jsm_io_slot_reset(struct pci_dev *pdev);
static void jsm_io_resume(struct pci_dev *pdev);

static struct pci_error_handlers jsm_err_handler = {
	.error_detected = jsm_io_error_detected,
	.slot_reset = jsm_io_slot_reset,
	.resume = jsm_io_resume,
};

L
Linus Torvalds 已提交
63 64 65 66
int jsm_debug;
module_param(jsm_debug, int, 0);
MODULE_PARM_DESC(jsm_debug, "Driver debugging level");

67
static int __devinit jsm_probe_one(struct pci_dev *pdev, const struct pci_device_id *ent)
L
Linus Torvalds 已提交
68 69
{
	int rc = 0;
70 71
	struct jsm_board *brd;
	static int adapter_count = 0;
L
Linus Torvalds 已提交
72

73 74 75 76
	rc = pci_enable_device(pdev);
	if (rc) {
		dev_err(&pdev->dev, "Device enable FAILED\n");
		goto out;
L
Linus Torvalds 已提交
77 78
	}

79 80 81 82 83
	rc = pci_request_regions(pdev, "jsm");
	if (rc) {
		dev_err(&pdev->dev, "pci_request_region FAILED\n");
		goto out_disable_device;
	}
L
Linus Torvalds 已提交
84

85
	brd = kzalloc(sizeof(struct jsm_board), GFP_KERNEL);
L
Linus Torvalds 已提交
86
	if (!brd) {
87 88 89 90
		dev_err(&pdev->dev,
			"memory allocation for board structure failed\n");
		rc = -ENOMEM;
		goto out_release_regions;
L
Linus Torvalds 已提交
91 92 93
	}

	/* store the info for the board we've found */
94
	brd->boardnum = adapter_count++;
L
Linus Torvalds 已提交
95
	brd->pci_dev = pdev;
96 97
	if (pdev->device == PCIE_DEVICE_ID_NEO_4_IBM)
		brd->maxports = 4;
98 99
	else if (pdev->device == PCI_DEVICE_ID_DIGI_NEO_8)
		brd->maxports = 8;
100 101
	else
		brd->maxports = 2;
L
Linus Torvalds 已提交
102 103 104 105

	spin_lock_init(&brd->bd_intr_lock);

	/* store which revision we have */
106
	brd->rev = pdev->revision;
L
Linus Torvalds 已提交
107 108 109

	brd->irq = pdev->irq;

110 111
	jsm_printk(INIT, INFO, &brd->pci_dev,
		"jsm_found_board - NEO adapter\n");
L
Linus Torvalds 已提交
112

113 114 115
	/* get the PCI Base Address Registers */
	brd->membase	= pci_resource_start(pdev, 0);
	brd->membase_end = pci_resource_end(pdev, 0);
L
Linus Torvalds 已提交
116

117 118 119 120
	if (brd->membase & 1)
		brd->membase &= ~3;
	else
		brd->membase &= ~15;
L
Linus Torvalds 已提交
121

122 123
	/* Assign the board_ops struct */
	brd->bd_ops = &jsm_neo_ops;
L
Linus Torvalds 已提交
124

125 126
	brd->bd_uart_offset = 0x200;
	brd->bd_dividend = 921600;
L
Linus Torvalds 已提交
127

128 129 130 131 132 133 134
	brd->re_map_membase = ioremap(brd->membase, 0x1000);
	if (!brd->re_map_membase) {
		dev_err(&pdev->dev,
			"card has no PCI Memory resources, "
			"failing board.\n");
		rc = -ENOMEM;
		goto out_kfree_brd;
L
Linus Torvalds 已提交
135 136
	}

137
	rc = request_irq(brd->irq, brd->bd_ops->intr,
138
			IRQF_SHARED, "JSM", brd);
139 140 141
	if (rc) {
		printk(KERN_WARNING "Failed to hook IRQ %d\n",brd->irq);
		goto out_iounmap;
L
Linus Torvalds 已提交
142 143 144 145 146
	}

	rc = jsm_tty_init(brd);
	if (rc < 0) {
		dev_err(&pdev->dev, "Can't init tty devices (%d)\n", rc);
147
		rc = -ENXIO;
148
		goto out_free_irq;
L
Linus Torvalds 已提交
149 150 151 152
	}

	rc = jsm_uart_port_init(brd);
	if (rc < 0) {
153
		/* XXX: leaking all resources from jsm_tty_init here! */
L
Linus Torvalds 已提交
154
		dev_err(&pdev->dev, "Can't init uart port (%d)\n", rc);
155
		rc = -ENXIO;
156
		goto out_free_irq;
L
Linus Torvalds 已提交
157 158 159
	}

	/* Log the information about the board */
160 161
	dev_info(&pdev->dev, "board %d: Digi Neo (rev %d), irq %d\n",
			adapter_count, brd->rev, brd->irq);
L
Linus Torvalds 已提交
162 163 164 165 166 167 168

	/*
	 * allocate flip buffer for board.
	 *
	 * Okay to malloc with GFP_KERNEL, we are not at interrupt
	 * context, and there are no locks held.
	 */
169
	brd->flipbuf = kzalloc(MYFLIPLEN, GFP_KERNEL);
L
Linus Torvalds 已提交
170
	if (!brd->flipbuf) {
171 172
		/* XXX: leaking all resources from jsm_tty_init and
		 	jsm_uart_port_init here! */
L
Linus Torvalds 已提交
173
		dev_err(&pdev->dev, "memory allocation for flipbuf failed\n");
174
		rc = -ENOMEM;
B
Breno Leitao 已提交
175
		goto out_free_uart;
L
Linus Torvalds 已提交
176 177
	}

178
	pci_set_drvdata(pdev, brd);
B
Breno Leitao 已提交
179
	pci_save_state(pdev);
L
Linus Torvalds 已提交
180 181

	return 0;
B
Breno Leitao 已提交
182 183
 out_free_uart:
	jsm_remove_uart_port(brd);
184
 out_free_irq:
185
	jsm_remove_uart_port(brd);
L
Linus Torvalds 已提交
186
	free_irq(brd->irq, brd);
187
 out_iounmap:
L
Linus Torvalds 已提交
188
	iounmap(brd->re_map_membase);
189 190 191 192 193 194 195
 out_kfree_brd:
	kfree(brd);
 out_release_regions:
	pci_release_regions(pdev);
 out_disable_device:
	pci_disable_device(pdev);
 out:
L
Linus Torvalds 已提交
196 197 198
	return rc;
}

199
static void __devexit jsm_remove_one(struct pci_dev *pdev)
L
Linus Torvalds 已提交
200
{
201
	struct jsm_board *brd = pci_get_drvdata(pdev);
L
Linus Torvalds 已提交
202 203
	int i = 0;

204 205
	jsm_remove_uart_port(brd);

L
Linus Torvalds 已提交
206 207 208 209 210 211
	free_irq(brd->irq, brd);
	iounmap(brd->re_map_membase);

	/* Free all allocated channels structs */
	for (i = 0; i < brd->maxports; i++) {
		if (brd->channels[i]) {
212 213 214
			kfree(brd->channels[i]->ch_rqueue);
			kfree(brd->channels[i]->ch_equeue);
			kfree(brd->channels[i]->ch_wqueue);
L
Linus Torvalds 已提交
215 216 217 218
			kfree(brd->channels[i]);
		}
	}

219 220
	pci_release_regions(pdev);
	pci_disable_device(pdev);
L
Linus Torvalds 已提交
221 222 223 224
	kfree(brd->flipbuf);
	kfree(brd);
}

225 226 227 228 229
static struct pci_device_id jsm_pci_tbl[] = {
	{ PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_NEO_2DB9), 0, 0, 0 },
	{ PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_NEO_2DB9PRI), 0, 0, 1 },
	{ PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_NEO_2RJ45), 0, 0, 2 },
	{ PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_NEO_2RJ45PRI), 0, 0, 3 },
230
	{ PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCIE_DEVICE_ID_NEO_4_IBM), 0, 0, 4 },
231
	{ PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_NEO_8), 0, 0, 5 },
232 233 234
	{ 0, }
};
MODULE_DEVICE_TABLE(pci, jsm_pci_tbl);
L
Linus Torvalds 已提交
235

236
static struct pci_driver jsm_driver = {
L
Linus Torvalds 已提交
237 238
	.name		= "jsm",
	.id_table	= jsm_pci_tbl,
239
	.probe		= jsm_probe_one,
L
Linus Torvalds 已提交
240
	.remove		= __devexit_p(jsm_remove_one),
B
Breno Leitao 已提交
241
	.err_handler    = &jsm_err_handler,
L
Linus Torvalds 已提交
242 243
};

B
Breno Leitao 已提交
244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276
static pci_ers_result_t jsm_io_error_detected(struct pci_dev *pdev,
					pci_channel_state_t state)
{
	struct jsm_board *brd = pci_get_drvdata(pdev);

	jsm_remove_uart_port(brd);

	return PCI_ERS_RESULT_NEED_RESET;
}

static pci_ers_result_t jsm_io_slot_reset(struct pci_dev *pdev)
{
	int rc;

	rc = pci_enable_device(pdev);

	if (rc)
		return PCI_ERS_RESULT_DISCONNECT;

	pci_set_master(pdev);

	return PCI_ERS_RESULT_RECOVERED;
}

static void jsm_io_resume(struct pci_dev *pdev)
{
	struct jsm_board *brd = pci_get_drvdata(pdev);

	pci_restore_state(pdev);

	jsm_uart_port_init(brd);
}

L
Linus Torvalds 已提交
277 278
static int __init jsm_init_module(void)
{
279
	int rc;
L
Linus Torvalds 已提交
280 281

	rc = uart_register_driver(&jsm_uart_driver);
282 283 284 285
	if (!rc) {
		rc = pci_register_driver(&jsm_driver);
		if (rc)
			uart_unregister_driver(&jsm_uart_driver);
L
Linus Torvalds 已提交
286 287 288 289 290 291 292 293 294
	}
	return rc;
}

static void __exit jsm_exit_module(void)
{
	pci_unregister_driver(&jsm_driver);
	uart_unregister_driver(&jsm_uart_driver);
}
295 296

module_init(jsm_init_module);
L
Linus Torvalds 已提交
297
module_exit(jsm_exit_module);