jsm_driver.c 7.8 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
#include <linux/module.h>
L
Linus Torvalds 已提交
28
#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
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);

57
static const struct pci_error_handlers jsm_err_handler = {
B
Breno Leitao 已提交
58 59 60 61 62
	.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");

B
Bill Pemberton 已提交
67
static int 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 98 99 100 101 102 103 104 105 106 107 108 109 110

	switch (pdev->device) {

	case PCI_DEVICE_ID_NEO_2DB9:
	case PCI_DEVICE_ID_NEO_2DB9PRI:
	case PCI_DEVICE_ID_NEO_2RJ45:
	case PCI_DEVICE_ID_NEO_2RJ45PRI:
	case PCI_DEVICE_ID_NEO_2_422_485:
		brd->maxports = 2;
		break;

	case PCI_DEVICE_ID_NEO_4:
	case PCIE_DEVICE_ID_NEO_4:
	case PCIE_DEVICE_ID_NEO_4RJ45:
	case PCIE_DEVICE_ID_NEO_4_IBM:
111
		brd->maxports = 4;
112 113 114 115 116
		break;

	case PCI_DEVICE_ID_DIGI_NEO_8:
	case PCIE_DEVICE_ID_NEO_8:
	case PCIE_DEVICE_ID_NEO_8RJ45:
117
		brd->maxports = 8;
118 119 120 121 122 123
		break;

	default:
		brd->maxports = 1;
		break;
	}
L
Linus Torvalds 已提交
124 125 126 127

	spin_lock_init(&brd->bd_intr_lock);

	/* store which revision we have */
128
	brd->rev = pdev->revision;
L
Linus Torvalds 已提交
129 130 131

	brd->irq = pdev->irq;

132
	jsm_dbg(INIT, &brd->pci_dev, "jsm_found_board - NEO adapter\n");
L
Linus Torvalds 已提交
133

134 135 136
	/* get the PCI Base Address Registers */
	brd->membase	= pci_resource_start(pdev, 0);
	brd->membase_end = pci_resource_end(pdev, 0);
L
Linus Torvalds 已提交
137

138 139 140 141
	if (brd->membase & 1)
		brd->membase &= ~3;
	else
		brd->membase &= ~15;
L
Linus Torvalds 已提交
142

143 144
	/* Assign the board_ops struct */
	brd->bd_ops = &jsm_neo_ops;
L
Linus Torvalds 已提交
145

146 147
	brd->bd_uart_offset = 0x200;
	brd->bd_dividend = 921600;
L
Linus Torvalds 已提交
148

149
	brd->re_map_membase = ioremap(brd->membase, pci_resource_len(pdev, 0));
150 151 152 153 154 155
	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 已提交
156 157
	}

158
	rc = request_irq(brd->irq, brd->bd_ops->intr,
159
			IRQF_SHARED, "JSM", brd);
160 161 162
	if (rc) {
		printk(KERN_WARNING "Failed to hook IRQ %d\n",brd->irq);
		goto out_iounmap;
L
Linus Torvalds 已提交
163 164 165 166 167
	}

	rc = jsm_tty_init(brd);
	if (rc < 0) {
		dev_err(&pdev->dev, "Can't init tty devices (%d)\n", rc);
168
		rc = -ENXIO;
169
		goto out_free_irq;
L
Linus Torvalds 已提交
170 171 172 173
	}

	rc = jsm_uart_port_init(brd);
	if (rc < 0) {
174
		/* XXX: leaking all resources from jsm_tty_init here! */
L
Linus Torvalds 已提交
175
		dev_err(&pdev->dev, "Can't init uart port (%d)\n", rc);
176
		rc = -ENXIO;
177
		goto out_free_irq;
L
Linus Torvalds 已提交
178 179 180
	}

	/* Log the information about the board */
181 182
	dev_info(&pdev->dev, "board %d: Digi Neo (rev %d), irq %d\n",
			adapter_count, brd->rev, brd->irq);
L
Linus Torvalds 已提交
183

184
	pci_set_drvdata(pdev, brd);
B
Breno Leitao 已提交
185
	pci_save_state(pdev);
L
Linus Torvalds 已提交
186 187

	return 0;
188
 out_free_irq:
189
	jsm_remove_uart_port(brd);
L
Linus Torvalds 已提交
190
	free_irq(brd->irq, brd);
191
 out_iounmap:
L
Linus Torvalds 已提交
192
	iounmap(brd->re_map_membase);
193 194 195 196 197 198 199
 out_kfree_brd:
	kfree(brd);
 out_release_regions:
	pci_release_regions(pdev);
 out_disable_device:
	pci_disable_device(pdev);
 out:
L
Linus Torvalds 已提交
200 201 202
	return rc;
}

B
Bill Pemberton 已提交
203
static void jsm_remove_one(struct pci_dev *pdev)
L
Linus Torvalds 已提交
204
{
205
	struct jsm_board *brd = pci_get_drvdata(pdev);
L
Linus Torvalds 已提交
206 207
	int i = 0;

208 209
	jsm_remove_uart_port(brd);

L
Linus Torvalds 已提交
210 211 212 213 214 215
	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]) {
216 217
			kfree(brd->channels[i]->ch_rqueue);
			kfree(brd->channels[i]->ch_equeue);
L
Linus Torvalds 已提交
218 219 220 221
			kfree(brd->channels[i]);
		}
	}

222 223
	pci_release_regions(pdev);
	pci_disable_device(pdev);
L
Linus Torvalds 已提交
224 225 226
	kfree(brd);
}

227 228 229 230 231
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 },
232
	{ PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCIE_DEVICE_ID_NEO_4_IBM), 0, 0, 4 },
233
	{ PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_NEO_8), 0, 0, 5 },
234 235 236 237 238 239 240 241
	{ PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_NEO_4), 0, 0, 6 },
	{ PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_NEO_1_422), 0, 0, 7 },
	{ PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_NEO_1_422_485), 0, 0, 8 },
	{ PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_NEO_2_422_485), 0, 0, 9 },
	{ PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCIE_DEVICE_ID_NEO_8), 0, 0, 10 },
	{ PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCIE_DEVICE_ID_NEO_4), 0, 0, 11 },
	{ PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCIE_DEVICE_ID_NEO_4RJ45), 0, 0, 12 },
	{ PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCIE_DEVICE_ID_NEO_8RJ45), 0, 0, 13 },
242 243 244
	{ 0, }
};
MODULE_DEVICE_TABLE(pci, jsm_pci_tbl);
L
Linus Torvalds 已提交
245

246
static struct pci_driver jsm_driver = {
L
Linus Torvalds 已提交
247 248
	.name		= "jsm",
	.id_table	= jsm_pci_tbl,
249
	.probe		= jsm_probe_one,
250
	.remove		= jsm_remove_one,
B
Breno Leitao 已提交
251
	.err_handler    = &jsm_err_handler,
L
Linus Torvalds 已提交
252 253
};

B
Breno Leitao 已提交
254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282
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);
283
	pci_save_state(pdev);
B
Breno Leitao 已提交
284 285 286 287

	jsm_uart_port_init(brd);
}

L
Linus Torvalds 已提交
288 289
static int __init jsm_init_module(void)
{
290
	int rc;
L
Linus Torvalds 已提交
291 292

	rc = uart_register_driver(&jsm_uart_driver);
293 294 295 296
	if (!rc) {
		rc = pci_register_driver(&jsm_driver);
		if (rc)
			uart_unregister_driver(&jsm_uart_driver);
L
Linus Torvalds 已提交
297 298 299 300 301 302 303 304 305
	}
	return rc;
}

static void __exit jsm_exit_module(void)
{
	pci_unregister_driver(&jsm_driver);
	uart_unregister_driver(&jsm_uart_driver);
}
306 307

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