moxa.c 52.9 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1
2
3
4
/*****************************************************************************/
/*
 *           moxa.c  -- MOXA Intellio family multiport serial driver.
 *
Jiri Slaby's avatar
Jiri Slaby committed
5
6
 *      Copyright (C) 1999-2000  Moxa Technologies (support@moxa.com).
 *      Copyright (c) 2007 Jiri Slaby <jirislaby@gmail.com>
Linus Torvalds's avatar
Linus Torvalds committed
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
 *
 *      This code is loosely based on the Linux serial driver, written by
 *      Linus Torvalds, Theodore T'so and others.
 *
 *      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 of the License, or
 *      (at your option) any later version.
 */

/*
 *    MOXA Intellio Series Driver
 *      for             : LINUX
 *      date            : 1999/1/7
 *      version         : 5.1
 */

#include <linux/module.h>
#include <linux/types.h>
#include <linux/mm.h>
#include <linux/ioport.h>
#include <linux/errno.h>
Jiri Slaby's avatar
Jiri Slaby committed
29
#include <linux/firmware.h>
Linus Torvalds's avatar
Linus Torvalds committed
30
31
32
33
34
35
36
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/interrupt.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/major.h>
Alexey Dobriyan's avatar
Alexey Dobriyan committed
37
#include <linux/smp_lock.h>
Linus Torvalds's avatar
Linus Torvalds committed
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include <linux/string.h>
#include <linux/fcntl.h>
#include <linux/ptrace.h>
#include <linux/serial.h>
#include <linux/tty_driver.h>
#include <linux/delay.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/bitops.h>

#include <asm/system.h>
#include <asm/io.h>
#include <asm/uaccess.h>

Jiri Slaby's avatar
Jiri Slaby committed
52
53
#include "moxa.h"

Jiri Slaby's avatar
Jiri Slaby committed
54
#define MOXA_VERSION		"6.0k"
Linus Torvalds's avatar
Linus Torvalds committed
55

Jiri Slaby's avatar
Jiri Slaby committed
56
57
#define MOXA_FW_HDRLEN		32

58
#define MOXAMAJOR		172
Linus Torvalds's avatar
Linus Torvalds committed
59

60
#define MAX_BOARDS		4	/* Don't change this value */
Linus Torvalds's avatar
Linus Torvalds committed
61
#define MAX_PORTS_PER_BOARD	32	/* Don't change this value */
62
#define MAX_PORTS		(MAX_BOARDS * MAX_PORTS_PER_BOARD)
Linus Torvalds's avatar
Linus Torvalds committed
63

64
65
66
#define MOXA_IS_320(brd) ((brd)->boardType == MOXA_BOARD_C320_ISA || \
		(brd)->boardType == MOXA_BOARD_C320_PCI)

Linus Torvalds's avatar
Linus Torvalds committed
67
68
69
/*
 *    Define the Moxa PCI vendor and device IDs.
 */
70
71
#define MOXA_BUS_TYPE_ISA	0
#define MOXA_BUS_TYPE_PCI	1
Linus Torvalds's avatar
Linus Torvalds committed
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91

enum {
	MOXA_BOARD_C218_PCI = 1,
	MOXA_BOARD_C218_ISA,
	MOXA_BOARD_C320_PCI,
	MOXA_BOARD_C320_ISA,
	MOXA_BOARD_CP204J,
};

static char *moxa_brdname[] =
{
	"C218 Turbo PCI series",
	"C218 Turbo ISA series",
	"C320 Turbo PCI series",
	"C320 Turbo ISA series",
	"CP-204J series",
};

#ifdef CONFIG_PCI
static struct pci_device_id moxa_pcibrds[] = {
92
93
94
95
96
97
	{ PCI_DEVICE(PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_C218),
		.driver_data = MOXA_BOARD_C218_PCI },
	{ PCI_DEVICE(PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_C320),
		.driver_data = MOXA_BOARD_C320_PCI },
	{ PCI_DEVICE(PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_CP204J),
		.driver_data = MOXA_BOARD_CP204J },
Linus Torvalds's avatar
Linus Torvalds committed
98
99
100
101
102
	{ 0 }
};
MODULE_DEVICE_TABLE(pci, moxa_pcibrds);
#endif /* CONFIG_PCI */

Jiri Slaby's avatar
Jiri Slaby committed
103
104
struct moxa_port;

105
static struct moxa_board_conf {
Linus Torvalds's avatar
Linus Torvalds committed
106
107
108
	int boardType;
	int numPorts;
	int busType;
109

110
	unsigned int ready;
111

Jiri Slaby's avatar
Jiri Slaby committed
112
113
	struct moxa_port *ports;

114
115
116
117
118
119
120
121
122
123
124
125
	void __iomem *basemem;
	void __iomem *intNdx;
	void __iomem *intPend;
	void __iomem *intTable;
} moxa_boards[MAX_BOARDS];

struct mxser_mstatus {
	tcflag_t cflag;
	int cts;
	int dsr;
	int ri;
	int dcd;
126
};
Linus Torvalds's avatar
Linus Torvalds committed
127

128
129
130
131
struct moxaq_str {
	int inq;
	int outq;
};
Linus Torvalds's avatar
Linus Torvalds committed
132

133
struct moxa_port {
Alan Cox's avatar
Alan Cox committed
134
	struct tty_port port;
Jiri Slaby's avatar
Jiri Slaby committed
135
	struct moxa_board_conf *board;
136
137
	void __iomem *tableAddr;

Linus Torvalds's avatar
Linus Torvalds committed
138
139
	int type;
	int cflag;
140
	unsigned long statusflags;
Linus Torvalds's avatar
Linus Torvalds committed
141

142
143
144
	u8 DCDState;
	u8 lineCtrl;
	u8 lowChkFlag;
145
};
Linus Torvalds's avatar
Linus Torvalds committed
146

Jiri Slaby's avatar
Jiri Slaby committed
147
148
149
150
151
152
struct mon_str {
	int tick;
	int rxcnt[MAX_PORTS];
	int txcnt[MAX_PORTS];
};

Linus Torvalds's avatar
Linus Torvalds committed
153
/* statusflags */
Alan Cox's avatar
Alan Cox committed
154
155
156
157
#define TXSTOPPED	1
#define LOWWAIT 	2
#define EMPTYWAIT	3
#define THROTTLE	4
Linus Torvalds's avatar
Linus Torvalds committed
158
159
160
161
162
163

#define SERIAL_DO_RESTART

#define WAKEUP_CHARS		256

static int ttymajor = MOXAMAJOR;
Jiri Slaby's avatar
Jiri Slaby committed
164
165
static struct mon_str moxaLog;
static unsigned int moxaFuncTout = HZ / 2;
166
static unsigned int moxaLowWaterChk;
Jiri Slaby's avatar
Jiri Slaby committed
167
static DEFINE_MUTEX(moxa_openlock);
Linus Torvalds's avatar
Linus Torvalds committed
168
169
/* Variables for insmod */
#ifdef MODULE
170
171
172
static unsigned long baseaddr[MAX_BOARDS];
static unsigned int type[MAX_BOARDS];
static unsigned int numports[MAX_BOARDS];
Linus Torvalds's avatar
Linus Torvalds committed
173
174
175
176
177
178
#endif

MODULE_AUTHOR("William Chen");
MODULE_DESCRIPTION("MOXA Intellio Family Multiport Board Device Driver");
MODULE_LICENSE("GPL");
#ifdef MODULE
179
180
181
182
183
184
module_param_array(type, uint, NULL, 0);
MODULE_PARM_DESC(type, "card type: C218=2, C320=4");
module_param_array(baseaddr, ulong, NULL, 0);
MODULE_PARM_DESC(baseaddr, "base address");
module_param_array(numports, uint, NULL, 0);
MODULE_PARM_DESC(numports, "numports (ignored for C218)");
Linus Torvalds's avatar
Linus Torvalds committed
185
186
187
188
189
190
191
192
193
194
195
196
197
198
#endif
module_param(ttymajor, int, 0);

/*
 * static functions:
 */
static int moxa_open(struct tty_struct *, struct file *);
static void moxa_close(struct tty_struct *, struct file *);
static int moxa_write(struct tty_struct *, const unsigned char *, int);
static int moxa_write_room(struct tty_struct *);
static void moxa_flush_buffer(struct tty_struct *);
static int moxa_chars_in_buffer(struct tty_struct *);
static void moxa_throttle(struct tty_struct *);
static void moxa_unthrottle(struct tty_struct *);
Alan Cox's avatar
Alan Cox committed
199
static void moxa_set_termios(struct tty_struct *, struct ktermios *);
Linus Torvalds's avatar
Linus Torvalds committed
200
201
202
203
204
205
206
static void moxa_stop(struct tty_struct *);
static void moxa_start(struct tty_struct *);
static void moxa_hangup(struct tty_struct *);
static int moxa_tiocmget(struct tty_struct *tty, struct file *file);
static int moxa_tiocmset(struct tty_struct *tty, struct file *file,
			 unsigned int set, unsigned int clear);
static void moxa_poll(unsigned long);
207
static void moxa_set_tty_param(struct tty_struct *, struct ktermios *);
208
static void moxa_setup_empty_event(struct tty_struct *);
Alan Cox's avatar
Alan Cox committed
209
static void moxa_shutdown(struct tty_port *);
210
static int moxa_carrier_raised(struct tty_port *);
Alan Cox's avatar
Alan Cox committed
211
static void moxa_dtr_rts(struct tty_port *, int);
Linus Torvalds's avatar
Linus Torvalds committed
212
213
214
/*
 * moxa board interface functions:
 */
Jiri Slaby's avatar
Jiri Slaby committed
215
216
217
218
219
220
221
222
static void MoxaPortEnable(struct moxa_port *);
static void MoxaPortDisable(struct moxa_port *);
static int MoxaPortSetTermio(struct moxa_port *, struct ktermios *, speed_t);
static int MoxaPortGetLineOut(struct moxa_port *, int *, int *);
static void MoxaPortLineCtrl(struct moxa_port *, int, int);
static void MoxaPortFlowCtrl(struct moxa_port *, int, int, int, int, int);
static int MoxaPortLineStatus(struct moxa_port *);
static void MoxaPortFlushData(struct moxa_port *, int);
Alan Cox's avatar
Alan Cox committed
223
static int MoxaPortWriteData(struct tty_struct *, const unsigned char *, int);
224
static int MoxaPortReadData(struct moxa_port *);
Jiri Slaby's avatar
Jiri Slaby committed
225
226
227
228
229
static int MoxaPortTxQueue(struct moxa_port *);
static int MoxaPortRxQueue(struct moxa_port *);
static int MoxaPortTxFree(struct moxa_port *);
static void MoxaPortTxDisable(struct moxa_port *);
static void MoxaPortTxEnable(struct moxa_port *);
230
231
static int moxa_get_serial_info(struct moxa_port *, struct serial_struct __user *);
static int moxa_set_serial_info(struct moxa_port *, struct serial_struct __user *);
Jiri Slaby's avatar
Jiri Slaby committed
232
static void MoxaSetFifo(struct moxa_port *port, int enable);
Linus Torvalds's avatar
Linus Torvalds committed
233

Jiri Slaby's avatar
Jiri Slaby committed
234
235
236
237
/*
 * I/O functions
 */

Alan Cox's avatar
Alan Cox committed
238
239
static DEFINE_SPINLOCK(moxafunc_lock);

Jiri Slaby's avatar
Jiri Slaby committed
240
241
242
243
244
245
246
247
248
249
250
static void moxa_wait_finish(void __iomem *ofsAddr)
{
	unsigned long end = jiffies + moxaFuncTout;

	while (readw(ofsAddr + FuncCode) != 0)
		if (time_after(jiffies, end))
			return;
	if (readw(ofsAddr + FuncCode) != 0 && printk_ratelimit())
		printk(KERN_WARNING "moxa function expired\n");
}

Jiri Slaby's avatar
Jiri Slaby committed
251
static void moxafunc(void __iomem *ofsAddr, u16 cmd, u16 arg)
Jiri Slaby's avatar
Jiri Slaby committed
252
{
Alan Cox's avatar
Alan Cox committed
253
254
        unsigned long flags;
        spin_lock_irqsave(&moxafunc_lock, flags);
Jiri Slaby's avatar
Jiri Slaby committed
255
256
257
	writew(arg, ofsAddr + FuncArg);
	writew(cmd, ofsAddr + FuncCode);
	moxa_wait_finish(ofsAddr);
Alan Cox's avatar
Alan Cox committed
258
259
260
261
262
263
264
265
266
267
268
269
270
271
	spin_unlock_irqrestore(&moxafunc_lock, flags);
}

static int moxafuncret(void __iomem *ofsAddr, u16 cmd, u16 arg)
{
        unsigned long flags;
        u16 ret;
        spin_lock_irqsave(&moxafunc_lock, flags);
	writew(arg, ofsAddr + FuncArg);
	writew(cmd, ofsAddr + FuncCode);
	moxa_wait_finish(ofsAddr);
	ret = readw(ofsAddr + FuncArg);
	spin_unlock_irqrestore(&moxafunc_lock, flags);
	return ret;
Jiri Slaby's avatar
Jiri Slaby committed
272
273
}

274
275
276
277
278
279
280
281
282
283
284
285
286
287
static void moxa_low_water_check(void __iomem *ofsAddr)
{
	u16 rptr, wptr, mask, len;

	if (readb(ofsAddr + FlagStat) & Xoff_state) {
		rptr = readw(ofsAddr + RXrptr);
		wptr = readw(ofsAddr + RXwptr);
		mask = readw(ofsAddr + RX_mask);
		len = (wptr - rptr) & mask;
		if (len <= Low_water)
			moxafunc(ofsAddr, FC_SendXon, 0);
	}
}

Jiri Slaby's avatar
Jiri Slaby committed
288
289
290
291
292
293
294
295
296
/*
 * TTY operations
 */

static int moxa_ioctl(struct tty_struct *tty, struct file *file,
		      unsigned int cmd, unsigned long arg)
{
	struct moxa_port *ch = tty->driver_data;
	void __user *argp = (void __user *)arg;
Jiri Slaby's avatar
Jiri Slaby committed
297
	int status, ret = 0;
Jiri Slaby's avatar
Jiri Slaby committed
298
299
300
301
302
303
304
305
306
307
308

	if (tty->index == MAX_PORTS) {
		if (cmd != MOXA_GETDATACOUNT && cmd != MOXA_GET_IOQUEUE &&
				cmd != MOXA_GETMSTATUS)
			return -EINVAL;
	} else if (!ch)
		return -ENODEV;

	switch (cmd) {
	case MOXA_GETDATACOUNT:
		moxaLog.tick = jiffies;
Jiri Slaby's avatar
Jiri Slaby committed
309
310
311
		if (copy_to_user(argp, &moxaLog, sizeof(moxaLog)))
			ret = -EFAULT;
		break;
Jiri Slaby's avatar
Jiri Slaby committed
312
313
	case MOXA_FLUSH_QUEUE:
		MoxaPortFlushData(ch, arg);
Jiri Slaby's avatar
Jiri Slaby committed
314
		break;
Jiri Slaby's avatar
Jiri Slaby committed
315
316
317
318
319
320
	case MOXA_GET_IOQUEUE: {
		struct moxaq_str __user *argm = argp;
		struct moxaq_str tmp;
		struct moxa_port *p;
		unsigned int i, j;

Jiri Slaby's avatar
Jiri Slaby committed
321
		mutex_lock(&moxa_openlock);
Jiri Slaby's avatar
Jiri Slaby committed
322
323
324
325
326
327
328
329
		for (i = 0; i < MAX_BOARDS; i++) {
			p = moxa_boards[i].ports;
			for (j = 0; j < MAX_PORTS_PER_BOARD; j++, p++, argm++) {
				memset(&tmp, 0, sizeof(tmp));
				if (moxa_boards[i].ready) {
					tmp.inq = MoxaPortRxQueue(p);
					tmp.outq = MoxaPortTxQueue(p);
				}
Jiri Slaby's avatar
Jiri Slaby committed
330
331
				if (copy_to_user(argm, &tmp, sizeof(tmp))) {
					mutex_unlock(&moxa_openlock);
Jiri Slaby's avatar
Jiri Slaby committed
332
					return -EFAULT;
Jiri Slaby's avatar
Jiri Slaby committed
333
				}
Jiri Slaby's avatar
Jiri Slaby committed
334
335
			}
		}
Jiri Slaby's avatar
Jiri Slaby committed
336
337
		mutex_unlock(&moxa_openlock);
		break;
Jiri Slaby's avatar
Jiri Slaby committed
338
339
	} case MOXA_GET_OQUEUE:
		status = MoxaPortTxQueue(ch);
Jiri Slaby's avatar
Jiri Slaby committed
340
341
		ret = put_user(status, (unsigned long __user *)argp);
		break;
Jiri Slaby's avatar
Jiri Slaby committed
342
343
	case MOXA_GET_IQUEUE:
		status = MoxaPortRxQueue(ch);
Jiri Slaby's avatar
Jiri Slaby committed
344
345
		ret = put_user(status, (unsigned long __user *)argp);
		break;
Jiri Slaby's avatar
Jiri Slaby committed
346
347
348
349
350
351
	case MOXA_GETMSTATUS: {
		struct mxser_mstatus __user *argm = argp;
		struct mxser_mstatus tmp;
		struct moxa_port *p;
		unsigned int i, j;

Jiri Slaby's avatar
Jiri Slaby committed
352
		mutex_lock(&moxa_openlock);
Jiri Slaby's avatar
Jiri Slaby committed
353
354
355
		for (i = 0; i < MAX_BOARDS; i++) {
			p = moxa_boards[i].ports;
			for (j = 0; j < MAX_PORTS_PER_BOARD; j++, p++, argm++) {
Alan Cox's avatar
Alan Cox committed
356
				struct tty_struct *ttyp;
Jiri Slaby's avatar
Jiri Slaby committed
357
358
359
360
361
362
363
364
365
366
367
368
				memset(&tmp, 0, sizeof(tmp));
				if (!moxa_boards[i].ready)
					goto copy;

				status = MoxaPortLineStatus(p);
				if (status & 1)
					tmp.cts = 1;
				if (status & 2)
					tmp.dsr = 1;
				if (status & 4)
					tmp.dcd = 1;

Alan Cox's avatar
Alan Cox committed
369
370
				ttyp = tty_port_tty_get(&p->port);
				if (!ttyp || !ttyp->termios)
Jiri Slaby's avatar
Jiri Slaby committed
371
372
					tmp.cflag = p->cflag;
				else
Alan Cox's avatar
Alan Cox committed
373
374
					tmp.cflag = ttyp->termios->c_cflag;
				tty_kref_put(tty);
Jiri Slaby's avatar
Jiri Slaby committed
375
copy:
Jiri Slaby's avatar
Jiri Slaby committed
376
377
				if (copy_to_user(argm, &tmp, sizeof(tmp))) {
					mutex_unlock(&moxa_openlock);
Jiri Slaby's avatar
Jiri Slaby committed
378
					return -EFAULT;
Jiri Slaby's avatar
Jiri Slaby committed
379
				}
Jiri Slaby's avatar
Jiri Slaby committed
380
381
			}
		}
Jiri Slaby's avatar
Jiri Slaby committed
382
383
		mutex_unlock(&moxa_openlock);
		break;
Jiri Slaby's avatar
Jiri Slaby committed
384
385
	}
	case TIOCGSERIAL:
Alan Cox's avatar
Alan Cox committed
386
	        mutex_lock(&ch->port.mutex);
Jiri Slaby's avatar
Jiri Slaby committed
387
		ret = moxa_get_serial_info(ch, argp);
Alan Cox's avatar
Alan Cox committed
388
		mutex_unlock(&ch->port.mutex);
Jiri Slaby's avatar
Jiri Slaby committed
389
		break;
Jiri Slaby's avatar
Jiri Slaby committed
390
	case TIOCSSERIAL:
Alan Cox's avatar
Alan Cox committed
391
	        mutex_lock(&ch->port.mutex);
Jiri Slaby's avatar
Jiri Slaby committed
392
		ret = moxa_set_serial_info(ch, argp);
Alan Cox's avatar
Alan Cox committed
393
		mutex_unlock(&ch->port.mutex);
Jiri Slaby's avatar
Jiri Slaby committed
394
395
396
		break;
	default:
		ret = -ENOIOCTLCMD;
Jiri Slaby's avatar
Jiri Slaby committed
397
	}
Jiri Slaby's avatar
Jiri Slaby committed
398
	return ret;
Jiri Slaby's avatar
Jiri Slaby committed
399
400
}

Alan Cox's avatar
Alan Cox committed
401
static int moxa_break_ctl(struct tty_struct *tty, int state)
Jiri Slaby's avatar
Jiri Slaby committed
402
403
404
405
406
{
	struct moxa_port *port = tty->driver_data;

	moxafunc(port->tableAddr, state ? FC_SendBreak : FC_StopBreak,
			Magic_code);
Alan Cox's avatar
Alan Cox committed
407
	return 0;
Jiri Slaby's avatar
Jiri Slaby committed
408
409
}

410
static const struct tty_operations moxa_ops = {
Linus Torvalds's avatar
Linus Torvalds committed
411
412
413
414
415
416
417
418
419
420
421
422
423
	.open = moxa_open,
	.close = moxa_close,
	.write = moxa_write,
	.write_room = moxa_write_room,
	.flush_buffer = moxa_flush_buffer,
	.chars_in_buffer = moxa_chars_in_buffer,
	.ioctl = moxa_ioctl,
	.throttle = moxa_throttle,
	.unthrottle = moxa_unthrottle,
	.set_termios = moxa_set_termios,
	.stop = moxa_stop,
	.start = moxa_start,
	.hangup = moxa_hangup,
Jiri Slaby's avatar
Jiri Slaby committed
424
	.break_ctl = moxa_break_ctl,
Linus Torvalds's avatar
Linus Torvalds committed
425
426
427
428
	.tiocmget = moxa_tiocmget,
	.tiocmset = moxa_tiocmset,
};

429
430
static const struct tty_port_operations moxa_port_ops = {
	.carrier_raised = moxa_carrier_raised,
Alan Cox's avatar
Alan Cox committed
431
432
	.dtr_rts = moxa_dtr_rts,
	.shutdown = moxa_shutdown,
433
434
};

435
436
static struct tty_driver *moxaDriver;
static DEFINE_TIMER(moxaTimer, moxa_poll, 0, 0);
437
static DEFINE_SPINLOCK(moxa_lock);
Alan Cox's avatar
Alan Cox committed
438

Jiri Slaby's avatar
Jiri Slaby committed
439
440
441
442
/*
 * HW init
 */

Jiri Slaby's avatar
Jiri Slaby committed
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
static int moxa_check_fw_model(struct moxa_board_conf *brd, u8 model)
{
	switch (brd->boardType) {
	case MOXA_BOARD_C218_ISA:
	case MOXA_BOARD_C218_PCI:
		if (model != 1)
			goto err;
		break;
	case MOXA_BOARD_CP204J:
		if (model != 3)
			goto err;
		break;
	default:
		if (model != 2)
			goto err;
		break;
	}
	return 0;
err:
	return -EINVAL;
}

static int moxa_check_fw(const void *ptr)
{
	const __le16 *lptr = ptr;

	if (*lptr != cpu_to_le16(0x7980))
		return -EINVAL;

	return 0;
}

static int moxa_load_bios(struct moxa_board_conf *brd, const u8 *buf,
		size_t len)
{
	void __iomem *baseAddr = brd->basemem;
	u16 tmp;

	writeb(HW_reset, baseAddr + Control_reg);	/* reset */
	msleep(10);
	memset_io(baseAddr, 0, 4096);
	memcpy_toio(baseAddr, buf, len);	/* download BIOS */
	writeb(0, baseAddr + Control_reg);	/* restart */

	msleep(2000);

	switch (brd->boardType) {
	case MOXA_BOARD_C218_ISA:
	case MOXA_BOARD_C218_PCI:
		tmp = readw(baseAddr + C218_key);
		if (tmp != C218_KeyCode)
			goto err;
		break;
	case MOXA_BOARD_CP204J:
		tmp = readw(baseAddr + C218_key);
		if (tmp != CP204J_KeyCode)
			goto err;
		break;
	default:
		tmp = readw(baseAddr + C320_key);
		if (tmp != C320_KeyCode)
			goto err;
		tmp = readw(baseAddr + C320_status);
		if (tmp != STS_init) {
Jiri Slaby's avatar
Jiri Slaby committed
507
			printk(KERN_ERR "MOXA: bios upload failed -- CPU/Basic "
Jiri Slaby's avatar
Jiri Slaby committed
508
509
510
511
512
513
514
515
					"module not found\n");
			return -EIO;
		}
		break;
	}

	return 0;
err:
Jiri Slaby's avatar
Jiri Slaby committed
516
	printk(KERN_ERR "MOXA: bios upload failed -- board not found\n");
Jiri Slaby's avatar
Jiri Slaby committed
517
518
519
520
521
522
523
524
525
	return -EIO;
}

static int moxa_load_320b(struct moxa_board_conf *brd, const u8 *ptr,
		size_t len)
{
	void __iomem *baseAddr = brd->basemem;

	if (len < 7168) {
Jiri Slaby's avatar
Jiri Slaby committed
526
		printk(KERN_ERR "MOXA: invalid 320 bios -- too short\n");
Jiri Slaby's avatar
Jiri Slaby committed
527
528
529
530
531
532
533
534
535
536
537
538
		return -EINVAL;
	}

	writew(len - 7168 - 2, baseAddr + C320bapi_len);
	writeb(1, baseAddr + Control_reg);	/* Select Page 1 */
	memcpy_toio(baseAddr + DynPage_addr, ptr, 7168);
	writeb(2, baseAddr + Control_reg);	/* Select Page 2 */
	memcpy_toio(baseAddr + DynPage_addr, ptr + 7168, len - 7168);

	return 0;
}

539
static int moxa_real_load_code(struct moxa_board_conf *brd, const void *ptr,
Jiri Slaby's avatar
Jiri Slaby committed
540
541
542
		size_t len)
{
	void __iomem *baseAddr = brd->basemem;
543
	const __le16 *uptr = ptr;
Jiri Slaby's avatar
Jiri Slaby committed
544
	size_t wlen, len2, j;
545
	unsigned long key, loadbuf, loadlen, checksum, checksum_ok;
546
	unsigned int i, retry;
Jiri Slaby's avatar
Jiri Slaby committed
547
548
	u16 usum, keycode;

549
550
	keycode = (brd->boardType == MOXA_BOARD_CP204J) ? CP204J_KeyCode :
				C218_KeyCode;
Jiri Slaby's avatar
Jiri Slaby committed
551

552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
	switch (brd->boardType) {
	case MOXA_BOARD_CP204J:
	case MOXA_BOARD_C218_ISA:
	case MOXA_BOARD_C218_PCI:
		key = C218_key;
		loadbuf = C218_LoadBuf;
		loadlen = C218DLoad_len;
		checksum = C218check_sum;
		checksum_ok = C218chksum_ok;
		break;
	default:
		key = C320_key;
		keycode = C320_KeyCode;
		loadbuf = C320_LoadBuf;
		loadlen = C320DLoad_len;
		checksum = C320check_sum;
		checksum_ok = C320chksum_ok;
		break;
Jiri Slaby's avatar
Jiri Slaby committed
570
571
572
573
574
575
576
577
578
579
580
581
582
	}

	usum = 0;
	wlen = len >> 1;
	for (i = 0; i < wlen; i++)
		usum += le16_to_cpu(uptr[i]);
	retry = 0;
	do {
		wlen = len >> 1;
		j = 0;
		while (wlen) {
			len2 = (wlen > 2048) ? 2048 : wlen;
			wlen -= len2;
583
			memcpy_toio(baseAddr + loadbuf, ptr + j, len2 << 1);
Jiri Slaby's avatar
Jiri Slaby committed
584
			j += len2 << 1;
585
586
587
588
589

			writew(len2, baseAddr + loadlen);
			writew(0, baseAddr + key);
			for (i = 0; i < 100; i++) {
				if (readw(baseAddr + key) == keycode)
Jiri Slaby's avatar
Jiri Slaby committed
590
591
592
					break;
				msleep(10);
			}
593
			if (readw(baseAddr + key) != keycode)
Jiri Slaby's avatar
Jiri Slaby committed
594
595
				return -EIO;
		}
596
597
598
599
600
		writew(0, baseAddr + loadlen);
		writew(usum, baseAddr + checksum);
		writew(0, baseAddr + key);
		for (i = 0; i < 100; i++) {
			if (readw(baseAddr + key) == keycode)
Jiri Slaby's avatar
Jiri Slaby committed
601
602
603
604
				break;
			msleep(10);
		}
		retry++;
605
606
	} while ((readb(baseAddr + checksum_ok) != 1) && (retry < 3));
	if (readb(baseAddr + checksum_ok) != 1)
Jiri Slaby's avatar
Jiri Slaby committed
607
608
		return -EIO;

609
	writew(0, baseAddr + key);
Jiri Slaby's avatar
Jiri Slaby committed
610
611
612
613
614
615
616
617
	for (i = 0; i < 600; i++) {
		if (readw(baseAddr + Magic_no) == Magic_code)
			break;
		msleep(10);
	}
	if (readw(baseAddr + Magic_no) != Magic_code)
		return -EIO;

618
	if (MOXA_IS_320(brd)) {
619
620
621
622
623
624
625
626
627
		if (brd->busType == MOXA_BUS_TYPE_PCI) {	/* ASIC board */
			writew(0x3800, baseAddr + TMS320_PORT1);
			writew(0x3900, baseAddr + TMS320_PORT2);
			writew(28499, baseAddr + TMS320_CLOCK);
		} else {
			writew(0x3200, baseAddr + TMS320_PORT1);
			writew(0x3400, baseAddr + TMS320_PORT2);
			writew(19999, baseAddr + TMS320_CLOCK);
		}
Jiri Slaby's avatar
Jiri Slaby committed
628
629
630
631
632
633
634
635
636
637
638
	}
	writew(1, baseAddr + Disable_IRQ);
	writew(0, baseAddr + Magic_no);
	for (i = 0; i < 500; i++) {
		if (readw(baseAddr + Magic_no) == Magic_code)
			break;
		msleep(10);
	}
	if (readw(baseAddr + Magic_no) != Magic_code)
		return -EIO;

639
	if (MOXA_IS_320(brd)) {
640
641
642
643
644
645
646
647
648
649
650
651
652
		j = readw(baseAddr + Module_cnt);
		if (j <= 0)
			return -EIO;
		brd->numPorts = j * 8;
		writew(j, baseAddr + Module_no);
		writew(0, baseAddr + Magic_no);
		for (i = 0; i < 600; i++) {
			if (readw(baseAddr + Magic_no) == Magic_code)
				break;
			msleep(10);
		}
		if (readw(baseAddr + Magic_no) != Magic_code)
			return -EIO;
Jiri Slaby's avatar
Jiri Slaby committed
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
	}
	brd->intNdx = baseAddr + IRQindex;
	brd->intPend = baseAddr + IRQpending;
	brd->intTable = baseAddr + IRQtable;

	return 0;
}

static int moxa_load_code(struct moxa_board_conf *brd, const void *ptr,
		size_t len)
{
	void __iomem *ofsAddr, *baseAddr = brd->basemem;
	struct moxa_port *port;
	int retval, i;

	if (len % 2) {
Jiri Slaby's avatar
Jiri Slaby committed
669
		printk(KERN_ERR "MOXA: bios length is not even\n");
Jiri Slaby's avatar
Jiri Slaby committed
670
671
672
		return -EINVAL;
	}

673
674
675
676
	retval = moxa_real_load_code(brd, ptr, len); /* may change numPorts */
	if (retval)
		return retval;

Jiri Slaby's avatar
Jiri Slaby committed
677
678
679
680
681
682
	switch (brd->boardType) {
	case MOXA_BOARD_C218_ISA:
	case MOXA_BOARD_C218_PCI:
	case MOXA_BOARD_CP204J:
		port = brd->ports;
		for (i = 0; i < brd->numPorts; i++, port++) {
Jiri Slaby's avatar
Jiri Slaby committed
683
			port->board = brd;
Jiri Slaby's avatar
Jiri Slaby committed
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
			port->DCDState = 0;
			port->tableAddr = baseAddr + Extern_table +
					Extern_size * i;
			ofsAddr = port->tableAddr;
			writew(C218rx_mask, ofsAddr + RX_mask);
			writew(C218tx_mask, ofsAddr + TX_mask);
			writew(C218rx_spage + i * C218buf_pageno, ofsAddr + Page_rxb);
			writew(readw(ofsAddr + Page_rxb) + C218rx_pageno, ofsAddr + EndPage_rxb);

			writew(C218tx_spage + i * C218buf_pageno, ofsAddr + Page_txb);
			writew(readw(ofsAddr + Page_txb) + C218tx_pageno, ofsAddr + EndPage_txb);

		}
		break;
	default:
		port = brd->ports;
		for (i = 0; i < brd->numPorts; i++, port++) {
Jiri Slaby's avatar
Jiri Slaby committed
701
			port->board = brd;
Jiri Slaby's avatar
Jiri Slaby committed
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
			port->DCDState = 0;
			port->tableAddr = baseAddr + Extern_table +
					Extern_size * i;
			ofsAddr = port->tableAddr;
			switch (brd->numPorts) {
			case 8:
				writew(C320p8rx_mask, ofsAddr + RX_mask);
				writew(C320p8tx_mask, ofsAddr + TX_mask);
				writew(C320p8rx_spage + i * C320p8buf_pgno, ofsAddr + Page_rxb);
				writew(readw(ofsAddr + Page_rxb) + C320p8rx_pgno, ofsAddr + EndPage_rxb);
				writew(C320p8tx_spage + i * C320p8buf_pgno, ofsAddr + Page_txb);
				writew(readw(ofsAddr + Page_txb) + C320p8tx_pgno, ofsAddr + EndPage_txb);

				break;
			case 16:
				writew(C320p16rx_mask, ofsAddr + RX_mask);
				writew(C320p16tx_mask, ofsAddr + TX_mask);
				writew(C320p16rx_spage + i * C320p16buf_pgno, ofsAddr + Page_rxb);
				writew(readw(ofsAddr + Page_rxb) + C320p16rx_pgno, ofsAddr + EndPage_rxb);
				writew(C320p16tx_spage + i * C320p16buf_pgno, ofsAddr + Page_txb);
				writew(readw(ofsAddr + Page_txb) + C320p16tx_pgno, ofsAddr + EndPage_txb);
				break;

			case 24:
				writew(C320p24rx_mask, ofsAddr + RX_mask);
				writew(C320p24tx_mask, ofsAddr + TX_mask);
				writew(C320p24rx_spage + i * C320p24buf_pgno, ofsAddr + Page_rxb);
				writew(readw(ofsAddr + Page_rxb) + C320p24rx_pgno, ofsAddr + EndPage_rxb);
				writew(C320p24tx_spage + i * C320p24buf_pgno, ofsAddr + Page_txb);
				writew(readw(ofsAddr + Page_txb), ofsAddr + EndPage_txb);
				break;
			case 32:
				writew(C320p32rx_mask, ofsAddr + RX_mask);
				writew(C320p32tx_mask, ofsAddr + TX_mask);
				writew(C320p32tx_ofs, ofsAddr + Ofs_txb);
				writew(C320p32rx_spage + i * C320p32buf_pgno, ofsAddr + Page_rxb);
				writew(readb(ofsAddr + Page_rxb), ofsAddr + EndPage_rxb);
				writew(C320p32tx_spage + i * C320p32buf_pgno, ofsAddr + Page_txb);
				writew(readw(ofsAddr + Page_txb), ofsAddr + EndPage_txb);
				break;
			}
		}
		break;
	}
	return 0;
}

static int moxa_load_fw(struct moxa_board_conf *brd, const struct firmware *fw)
{
751
	const void *ptr = fw->data;
Jiri Slaby's avatar
Jiri Slaby committed
752
753
754
755
756
757
758
759
760
761
762
763
	char rsn[64];
	u16 lens[5];
	size_t len;
	unsigned int a, lenp, lencnt;
	int ret = -EINVAL;
	struct {
		__le32 magic;	/* 0x34303430 */
		u8 reserved1[2];
		u8 type;	/* UNIX = 3 */
		u8 model;	/* C218T=1, C320T=2, CP204=3 */
		u8 reserved2[8];
		__le16 len[5];
764
	} const *hdr = ptr;
Jiri Slaby's avatar
Jiri Slaby committed
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790

	BUILD_BUG_ON(ARRAY_SIZE(hdr->len) != ARRAY_SIZE(lens));

	if (fw->size < MOXA_FW_HDRLEN) {
		strcpy(rsn, "too short (even header won't fit)");
		goto err;
	}
	if (hdr->magic != cpu_to_le32(0x30343034)) {
		sprintf(rsn, "bad magic: %.8x", le32_to_cpu(hdr->magic));
		goto err;
	}
	if (hdr->type != 3) {
		sprintf(rsn, "not for linux, type is %u", hdr->type);
		goto err;
	}
	if (moxa_check_fw_model(brd, hdr->model)) {
		sprintf(rsn, "not for this card, model is %u", hdr->model);
		goto err;
	}

	len = MOXA_FW_HDRLEN;
	lencnt = hdr->model == 2 ? 5 : 3;
	for (a = 0; a < ARRAY_SIZE(lens); a++) {
		lens[a] = le16_to_cpu(hdr->len[a]);
		if (lens[a] && len + lens[a] <= fw->size &&
				moxa_check_fw(&fw->data[len]))
Jiri Slaby's avatar
Jiri Slaby committed
791
			printk(KERN_WARNING "MOXA firmware: unexpected input "
Jiri Slaby's avatar
Jiri Slaby committed
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
				"at offset %u, but going on\n", (u32)len);
		if (!lens[a] && a < lencnt) {
			sprintf(rsn, "too few entries in fw file");
			goto err;
		}
		len += lens[a];
	}

	if (len != fw->size) {
		sprintf(rsn, "bad length: %u (should be %u)", (u32)fw->size,
				(u32)len);
		goto err;
	}

	ptr += MOXA_FW_HDRLEN;
	lenp = 0; /* bios */

	strcpy(rsn, "read above");

	ret = moxa_load_bios(brd, ptr, lens[lenp]);
	if (ret)
		goto err;

	/* we skip the tty section (lens[1]), since we don't need it */
	ptr += lens[lenp] + lens[lenp + 1];
	lenp += 2; /* comm */

	if (hdr->model == 2) {
		ret = moxa_load_320b(brd, ptr, lens[lenp]);
		if (ret)
			goto err;
		/* skip another tty */
		ptr += lens[lenp] + lens[lenp + 1];
		lenp += 2;
	}

	ret = moxa_load_code(brd, ptr, lens[lenp]);
	if (ret)
		goto err;

	return 0;
err:
	printk(KERN_ERR "firmware failed to load, reason: %s\n", rsn);
	return ret;
}

static int moxa_init_board(struct moxa_board_conf *brd, struct device *dev)
{
	const struct firmware *fw;
	const char *file;
842
843
	struct moxa_port *p;
	unsigned int i;
Jiri Slaby's avatar
Jiri Slaby committed
844
845
	int ret;

846
847
848
849
850
851
852
853
854
	brd->ports = kcalloc(MAX_PORTS_PER_BOARD, sizeof(*brd->ports),
			GFP_KERNEL);
	if (brd->ports == NULL) {
		printk(KERN_ERR "cannot allocate memory for ports\n");
		ret = -ENOMEM;
		goto err;
	}

	for (i = 0, p = brd->ports; i < MAX_PORTS_PER_BOARD; i++, p++) {
Alan Cox's avatar
Alan Cox committed
855
		tty_port_init(&p->port);
856
		p->port.ops = &moxa_port_ops;
857
858
859
860
		p->type = PORT_16550A;
		p->cflag = B9600 | CS8 | CREAD | CLOCAL | HUPCL;
	}

Jiri Slaby's avatar
Jiri Slaby committed
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
	switch (brd->boardType) {
	case MOXA_BOARD_C218_ISA:
	case MOXA_BOARD_C218_PCI:
		file = "c218tunx.cod";
		break;
	case MOXA_BOARD_CP204J:
		file = "cp204unx.cod";
		break;
	default:
		file = "c320tunx.cod";
		break;
	}

	ret = request_firmware(&fw, file, dev);
	if (ret) {
876
877
878
879
		printk(KERN_ERR "MOXA: request_firmware failed. Make sure "
				"you've placed '%s' file into your firmware "
				"loader directory (e.g. /lib/firmware)\n",
				file);
880
		goto err_free;
Jiri Slaby's avatar
Jiri Slaby committed
881
882
883
884
885
	}

	ret = moxa_load_fw(brd, fw);

	release_firmware(fw);
886
887
888
889

	if (ret)
		goto err_free;

Jiri Slaby's avatar
Jiri Slaby committed
890
	spin_lock_bh(&moxa_lock);
891
	brd->ready = 1;
Jiri Slaby's avatar
Jiri Slaby committed
892
893
	if (!timer_pending(&moxaTimer))
		mod_timer(&moxaTimer, jiffies + HZ / 50);
Jiri Slaby's avatar
Jiri Slaby committed
894
	spin_unlock_bh(&moxa_lock);
Jiri Slaby's avatar
Jiri Slaby committed
895

896
897
898
899
	return 0;
err_free:
	kfree(brd->ports);
err:
Jiri Slaby's avatar
Jiri Slaby committed
900
901
902
	return ret;
}

903
904
static void moxa_board_deinit(struct moxa_board_conf *brd)
{
Jiri Slaby's avatar
Jiri Slaby committed
905
906
907
	unsigned int a, opened;

	mutex_lock(&moxa_openlock);
908
	spin_lock_bh(&moxa_lock);
909
	brd->ready = 0;
910
	spin_unlock_bh(&moxa_lock);
Jiri Slaby's avatar
Jiri Slaby committed
911
912
913

	/* pci hot-un-plug support */
	for (a = 0; a < brd->numPorts; a++)
Alan Cox's avatar
Alan Cox committed
914
915
916
917
918
919
920
921
		if (brd->ports[a].port.flags & ASYNC_INITIALIZED) {
			struct tty_struct *tty = tty_port_tty_get(
						&brd->ports[a].port);
			if (tty) {
				tty_hangup(tty);
				tty_kref_put(tty);
			}
		}
Jiri Slaby's avatar
Jiri Slaby committed
922
923
924
	while (1) {
		opened = 0;
		for (a = 0; a < brd->numPorts; a++)
Alan Cox's avatar
Alan Cox committed
925
			if (brd->ports[a].port.flags & ASYNC_INITIALIZED)
Jiri Slaby's avatar
Jiri Slaby committed
926
927
928
929
930
931
932
933
				opened++;
		mutex_unlock(&moxa_openlock);
		if (!opened)
			break;
		msleep(50);
		mutex_lock(&moxa_openlock);
	}

934
935
936
937
938
	iounmap(brd->basemem);
	brd->basemem = NULL;
	kfree(brd->ports);
}

Linus Torvalds's avatar
Linus Torvalds committed
939
#ifdef CONFIG_PCI
940
941
static int __devinit moxa_pci_probe(struct pci_dev *pdev,
		const struct pci_device_id *ent)
Linus Torvalds's avatar
Linus Torvalds committed
942
{
943
944
945
946
947
948
	struct moxa_board_conf *board;
	unsigned int i;
	int board_type = ent->driver_data;
	int retval;

	retval = pci_enable_device(pdev);
Jiri Slaby's avatar
Jiri Slaby committed
949
950
	if (retval) {
		dev_err(&pdev->dev, "can't enable pci device\n");
951
		goto err;
Jiri Slaby's avatar
Jiri Slaby committed
952
	}
953
954
955
956
957
958
959

	for (i = 0; i < MAX_BOARDS; i++)
		if (moxa_boards[i].basemem == NULL)
			break;

	retval = -ENODEV;
	if (i >= MAX_BOARDS) {
Jiri Slaby's avatar
Jiri Slaby committed
960
		dev_warn(&pdev->dev, "more than %u MOXA Intellio family boards "
961
962
963
964
965
				"found. Board is ignored.\n", MAX_BOARDS);
		goto err;
	}

	board = &moxa_boards[i];
Jiri Slaby's avatar
Jiri Slaby committed
966
967
968
969
970
971
972

	retval = pci_request_region(pdev, 2, "moxa-base");
	if (retval) {
		dev_err(&pdev->dev, "can't request pci region 2\n");
		goto err;
	}

973
	board->basemem = ioremap_nocache(pci_resource_start(pdev, 2), 0x4000);
Jiri Slaby's avatar
Jiri Slaby committed
974
975
	if (board->basemem == NULL) {
		dev_err(&pdev->dev, "can't remap io space 2\n");
Jiri Slaby's avatar
Jiri Slaby committed
976
		goto err_reg;
Jiri Slaby's avatar
Jiri Slaby committed
977
	}
978

Linus Torvalds's avatar
Linus Torvalds committed
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
	board->boardType = board_type;
	switch (board_type) {
	case MOXA_BOARD_C218_ISA:
	case MOXA_BOARD_C218_PCI:
		board->numPorts = 8;
		break;

	case MOXA_BOARD_CP204J:
		board->numPorts = 4;
		break;
	default:
		board->numPorts = 0;
		break;
	}
	board->busType = MOXA_BUS_TYPE_PCI;
Jiri Slaby's avatar
Jiri Slaby committed
994

Jiri Slaby's avatar
Jiri Slaby committed
995
996
997
998
	retval = moxa_init_board(board, &pdev->dev);
	if (retval)
		goto err_base;

999
	pci_set_drvdata(pdev, board);
Linus Torvalds's avatar
Linus Torvalds committed
1000

1001
1002
1003
	dev_info(&pdev->dev, "board '%s' ready (%u ports, firmware loaded)\n",
			moxa_brdname[board_type - 1], board->numPorts);

Jiri Slaby's avatar
Jiri Slaby committed
1004
	return 0;
Jiri Slaby's avatar
Jiri Slaby committed
1005
1006
1007
err_base:
	iounmap(board->basemem);
	board->basemem = NULL;
Jiri Slaby's avatar
Jiri Slaby committed
1008
1009
err_reg:
	pci_release_region(pdev, 2);
1010
1011
1012
1013
1014
1015
1016
1017
err:
	return retval;
}

static void __devexit moxa_pci_remove(struct pci_dev *pdev)
{
	struct moxa_board_conf *brd = pci_get_drvdata(pdev);

1018
1019
	moxa_board_deinit(brd);

Jiri Slaby's avatar
Jiri Slaby committed
1020
	pci_release_region(pdev, 2);
Linus Torvalds's avatar
Linus Torvalds committed
1021
}
Jiri Slaby's avatar
Jiri Slaby committed
1022
1023
1024
1025
1026
1027
1028

static struct pci_driver moxa_pci_driver = {
	.name = "moxa",
	.id_table = moxa_pcibrds,
	.probe = moxa_pci_probe,
	.remove = __devexit_p(moxa_pci_remove)
};
Linus Torvalds's avatar
Linus Torvalds committed
1029
1030
1031
1032
#endif /* CONFIG_PCI */

static int __init moxa_init(void)
{
1033
	unsigned int isabrds = 0;
1034
	int retval = 0;
Linus Torvalds's avatar
Linus Torvalds committed
1035

Jiri Slaby's avatar
Jiri Slaby committed
1036
1037
	printk(KERN_INFO "MOXA Intellio family driver version %s\n",
			MOXA_VERSION);
Linus Torvalds's avatar
Linus Torvalds committed
1038
1039
1040
1041
1042
	moxaDriver = alloc_tty_driver(MAX_PORTS + 1);
	if (!moxaDriver)
		return -ENOMEM;

	moxaDriver->owner = THIS_MODULE;
1043
	moxaDriver->name = "ttyMX";
Linus Torvalds's avatar
Linus Torvalds committed
1044
1045
1046
1047
1048
1049
	moxaDriver->major = ttymajor;
	moxaDriver->minor_start = 0;
	moxaDriver->type = TTY_DRIVER_TYPE_SERIAL;
	moxaDriver->subtype = SERIAL_TYPE_NORMAL;
	moxaDriver->init_termios = tty_std_termios;
	moxaDriver->init_termios.c_cflag = B9600 | CS8 | CREAD | CLOCAL | HUPCL;
Alan Cox's avatar
Alan Cox committed
1050
1051
	moxaDriver->init_termios.c_ispeed = 9600;
	moxaDriver->init_termios.c_ospeed = 9600;
Linus Torvalds's avatar
Linus Torvalds committed
1052
1053
1054
1055
	moxaDriver->flags = TTY_DRIVER_REAL_RAW;
	tty_set_operations(moxaDriver, &moxa_ops);

	if (tty_register_driver(moxaDriver)) {
Jiri Slaby's avatar
Jiri Slaby committed
1056
		printk(KERN_ERR "can't register MOXA Smartio tty driver!\n");
Linus Torvalds's avatar
Linus Torvalds committed
1057
1058
1059
1060
		put_tty_driver(moxaDriver);
		return -1;
	}

1061
	/* Find the boards defined from module args. */
Linus Torvalds's avatar
Linus Torvalds committed
1062
#ifdef MODULE
1063
1064
	{
	struct moxa_board_conf *brd = moxa_boards;
1065
	unsigned int i;
Linus Torvalds's avatar
Linus Torvalds committed
1066
	for (i = 0; i < MAX_BOARDS; i++) {
1067
1068
1069
1070
		if (!baseaddr[i])
			break;
		if (type[i] == MOXA_BOARD_C218_ISA ||
				type[i] == MOXA_BOARD_C320_ISA) {
Jiri Slaby's avatar
Jiri Slaby committed
1071
			pr_debug("Moxa board %2d: %s board(baseAddr=%lx)\n",
1072
1073
1074
1075
1076
1077
					isabrds + 1, moxa_brdname[type[i] - 1],
					baseaddr[i]);
			brd->boardType = type[i];
			brd->numPorts = type[i] == MOXA_BOARD_C218_ISA ? 8 :
					numports[i];
			brd->busType = MOXA_BUS_TYPE_ISA;
1078
			brd->basemem = ioremap_nocache(baseaddr[i], 0x4000);
1079
			if (!brd->basemem) {
Jiri Slaby's avatar
Jiri Slaby committed
1080
				printk(KERN_ERR "MOXA: can't remap %lx\n",
1081
						baseaddr[i]);
Linus Torvalds's avatar
Linus Torvalds committed
1082
1083
				continue;
			}
Jiri Slaby's avatar
Jiri Slaby committed
1084
1085
1086
1087
1088
			if (moxa_init_board(brd, NULL)) {
				iounmap(brd->basemem);
				brd->basemem = NULL;
				continue;
			}
1089

1090
1091
1092
1093
			printk(KERN_INFO "MOXA isa board found at 0x%.8lu and "
					"ready (%u ports, firmware loaded)\n",
					baseaddr[i], brd->numPorts);

1094
1095
			brd++;
			isabrds++;
Linus Torvalds's avatar
Linus Torvalds committed
1096
1097
		}
	}
1098
	}
Linus Torvalds's avatar
Linus Torvalds committed
1099
#endif
Jiri Slaby's avatar
Jiri Slaby committed
1100

Linus Torvalds's avatar
Linus Torvalds committed
1101
#ifdef CONFIG_PCI
Jiri Slaby's avatar
Jiri Slaby committed
1102
1103
	retval = pci_register_driver(&moxa_pci_driver);
	if (retval) {
Jiri Slaby's avatar
Jiri Slaby committed
1104
		printk(KERN_ERR "Can't register MOXA pci driver!\n");
1105
		if (isabrds)
Jiri Slaby's avatar
Jiri Slaby committed
1106
			retval = 0;
Linus Torvalds's avatar
Linus Torvalds committed
1107
1108
	}
#endif
Jiri Slaby's avatar
Jiri Slaby committed
1109
1110

	return retval;
Linus Torvalds's avatar
Linus Torvalds committed
1111
1112
1113
1114
}

static void __exit moxa_exit(void)
{
Jiri Slaby's avatar
Jiri Slaby committed
1115
	unsigned int i;
Linus Torvalds's avatar
Linus Torvalds committed
1116

1117
#ifdef CONFIG_PCI
Jiri Slaby's avatar
Jiri Slaby committed
1118
	pci_unregister_driver(&moxa_pci_driver);
1119
#endif
Jiri Slaby's avatar
Jiri Slaby committed
1120

1121
1122
1123
	for (i = 0; i < MAX_BOARDS; i++) /* ISA boards */
		if (moxa_boards[i].ready)
			moxa_board_deinit(&moxa_boards[i]);
Jiri Slaby's avatar
Jiri Slaby committed
1124
1125
1126
1127
1128
1129
1130

	del_timer_sync(&moxaTimer);

	if (tty_unregister_driver(moxaDriver))
		printk(KERN_ERR "Couldn't unregister MOXA Intellio family "
				"serial driver\n");
	put_tty_driver(moxaDriver);
Linus Torvalds's avatar
Linus Torvalds committed
1131
1132
1133
1134
1135
}

module_init(moxa_init);
module_exit(moxa_exit);

Alan Cox's avatar
Alan Cox committed
1136
static void moxa_shutdown(struct tty_port *port)
Jiri Slaby's avatar
Jiri Slaby committed
1137
{
Alan Cox's avatar
Alan Cox committed
1138
1139
	struct moxa_port *ch = container_of(port, struct moxa_port, port);
        MoxaPortDisable(ch);
Jiri Slaby's avatar
Jiri Slaby committed
1140
	MoxaPortFlushData(ch, 2);
Alan Cox's avatar
Alan Cox committed
1141
	clear_bit(ASYNCB_NORMAL_ACTIVE, &port->flags);
Jiri Slaby's avatar
Jiri Slaby committed
1142
1143
}

1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
static int moxa_carrier_raised(struct tty_port *port)
{
	struct moxa_port *ch = container_of(port, struct moxa_port, port);
	int dcd;

	spin_lock_bh(&moxa_lock);
	dcd = ch->DCDState;
	spin_unlock_bh(&moxa_lock);
	return dcd;
}

Alan Cox's avatar
Alan Cox committed
1155
static void moxa_dtr_rts(struct tty_port *port, int onoff)
Jiri Slaby's avatar
Jiri Slaby committed
1156
{
Alan Cox's avatar
Alan Cox committed
1157
1158
	struct moxa_port *ch = container_of(port, struct moxa_port, port);
	MoxaPortLineCtrl(ch, onoff, onoff);
Jiri Slaby's avatar
Jiri Slaby committed
1159
1160
}

Alan Cox's avatar
Alan Cox committed
1161

Linus Torvalds's avatar
Linus Torvalds committed
1162
1163
static int moxa_open(struct tty_struct *tty, struct file *filp)
{
1164
	struct moxa_board_conf *brd;
1165
	struct moxa_port *ch;
Linus Torvalds's avatar
Linus Torvalds committed
1166
1167
1168
	int port;
	int retval;

1169
	port = tty->index;
Linus Torvalds's avatar
Linus Torvalds committed
1170
	if (port == MAX_PORTS) {
Jiri Slaby's avatar
Jiri Slaby committed
1171
		return capable(CAP_SYS_ADMIN) ? 0 : -EPERM;
Linus Torvalds's avatar
Linus Torvalds committed
1172
	}
Jiri Slaby's avatar
Jiri Slaby committed
1173
1174
	if (mutex_lock_interruptible(&moxa_openlock))
		return -ERESTARTSYS;
1175
	brd = &moxa_boards[port / MAX_PORTS_PER_BOARD];
Jiri Slaby's avatar
Jiri Slaby committed
1176
1177
	if (!brd->ready) {
		mutex_unlock(&moxa_openlock);
1178
		return -ENODEV;
Jiri Slaby's avatar
Jiri Slaby committed
1179
	}
Linus Torvalds's avatar
Linus Torvalds committed
1180

1181
1182
1183
1184
1185
	if (port % MAX_PORTS_PER_BOARD >= brd->numPorts) {
		mutex_unlock(&moxa_openlock);
		return -ENODEV;
	}

1186
	ch = &brd->ports[port % MAX_PORTS_PER_BOARD];
Alan Cox's avatar
Alan Cox committed
1187
	ch->port.count++;
Linus Torvalds's avatar
Linus Torvalds committed
1188
	tty->driver_data = ch;
Alan Cox's avatar
Alan Cox committed
1189
	tty_port_tty_set(&ch->port, tty);
Alan Cox's avatar
Alan Cox committed
1190
	mutex_lock(&ch->port.mutex);
Alan Cox's avatar
Alan Cox committed
1191
	if (!(ch->port.flags & ASYNC_INITIALIZED)) {
Linus Torvalds's avatar
Linus Torvalds committed
1192
		ch->statusflags = 0;
1193
		moxa_set_tty_param(tty, tty->termios);
Jiri Slaby's avatar
Jiri Slaby committed
1194
1195
		MoxaPortLineCtrl(ch, 1, 1);
		MoxaPortEnable(ch);
Jiri Slaby's avatar
Jiri Slaby committed
1196
		MoxaSetFifo(ch, ch->type == PORT_16550A);
Alan Cox's avatar
Alan Cox committed
1197
		ch->port.flags |= ASYNC_INITIALIZED;
Linus Torvalds's avatar
Linus Torvalds committed
1198
	}
Alan Cox's avatar
Alan Cox committed
1199
	mutex_unlock(&ch->port.mutex);
Jiri Slaby's avatar
Jiri Slaby committed
1200
	mutex_unlock(&moxa_openlock);
Linus Torvalds's avatar
Linus Torvalds committed
1201

Alan Cox's avatar
Alan Cox committed
1202
1203
1204
	retval = tty_port_block_til_ready(&ch->port, tty, filp);
	if (retval == 0)
	        set_bit(ASYNCB_NORMAL_ACTIVE, &ch->port.flags);
Jiri Slaby's avatar
Jiri Slaby committed
1205
	return retval;
Linus Torvalds's avatar
Linus Torvalds committed
1206
1207
1208
1209
}

static void moxa_close(struct tty_struct *tty, struct file *filp)
{
Alan Cox's avatar
Alan Cox committed
1210
	struct moxa_port *ch = tty->driver_data;
Linus Torvalds's avatar
Linus Torvalds committed
1211
	ch->cflag = tty->termios->c_cflag;
Alan Cox's avatar
Alan Cox committed
1212
	tty_port_close(&ch->port, tty, filp);
Linus Torvalds's avatar
Linus Torvalds committed
1213
1214
1215
1216
1217
}

static int moxa_write(struct tty_struct *tty,
		      const unsigned char *buf, int count)
{
Jiri Slaby's avatar
Jiri Slaby committed
1218
1219
	struct moxa_port *ch = tty->driver_data;
	int len;
Linus Torvalds's avatar
Linus Torvalds committed
1220
1221

	if (ch == NULL)
Jiri Slaby's avatar
Jiri Slaby committed
1222
		return 0;
Alan Cox's avatar
Alan Cox committed
1223

1224
	spin_lock_bh(&moxa_lock);
Alan Cox's avatar
Alan Cox committed
1225
	len = MoxaPortWriteData(tty, buf, count);
1226
	spin_unlock_bh(&moxa_lock);
Linus Torvalds's avatar
Linus Torvalds committed
1227

Alan Cox's avatar
Alan Cox committed
1228
	set_bit(LOWWAIT, &ch->statusflags);
Jiri Slaby's avatar
Jiri Slaby committed
1229
	return len;
Linus Torvalds's avatar
Linus Torvalds committed
1230
1231
1232
1233
}

static int moxa_write_room(struct tty_struct *tty)
{
1234
	struct moxa_port *ch;
Linus Torvalds's avatar
Linus Torvalds committed
1235
1236

	if (tty->stopped)
Jiri Slaby's avatar
Jiri Slaby committed
1237
		return 0;
Jiri Slaby's avatar
Jiri Slaby committed
1238
	ch = tty->driver_data;
Linus Torvalds's avatar
Linus Torvalds committed
1239
	if (ch == NULL)
Jiri Slaby's avatar
Jiri Slaby committed
1240
		return 0;
Jiri Slaby's avatar
Jiri Slaby committed
1241
	return MoxaPortTxFree(ch);
Linus Torvalds's avatar
Linus Torvalds committed
1242
1243
1244
1245
}

static void moxa_flush_buffer(struct tty_struct *tty)
{
Jiri Slaby's avatar
Jiri Slaby committed
1246
	struct moxa_port *ch = tty->driver_data;
Linus Torvalds's avatar
Linus Torvalds committed
1247
1248
1249

	if (ch == NULL)
		return;
Jiri Slaby's avatar
Jiri Slaby committed
1250
	MoxaPortFlushData(ch, 1);
Linus Torvalds's avatar
Linus Torvalds committed
1251
1252
1253
1254
1255
	tty_wakeup(tty);
}

static int moxa_chars_in_buffer(struct tty_struct *tty)
{
Jiri Slaby's avatar
Jiri Slaby committed
1256
	struct moxa_port *ch = tty->driver_data;
Linus Torvalds's avatar
Linus Torvalds committed
1257
1258
	int chars;

1259
	lock_kernel();
Jiri Slaby's avatar
Jiri Slaby committed
1260
	chars = MoxaPortTxQueue(ch);
Linus Torvalds's avatar
Linus Torvalds committed
1261
1262
1263
1264
1265
	if (chars) {
		/*
		 * Make it possible to wakeup anything waiting for output
		 * in tty_ioctl.c, etc.
		 */
Alan Cox's avatar
Alan Cox committed
1266
		if (!test_bit(EMPTYWAIT, &ch->statusflags))
1267
			moxa_setup_empty_event(tty);
Linus Torvalds's avatar
Linus Torvalds committed
1268
	}
1269
	unlock_kernel();
Jiri Slaby's avatar
Jiri Slaby committed
1270
	return chars;
Linus Torvalds's avatar
Linus Torvalds committed
1271
1272
1273
1274
}

static int moxa_tiocmget(struct tty_struct *tty, struct file *file)
{
Jiri Slaby's avatar
Jiri Slaby committed
1275
	struct moxa_port *ch;
Linus Torvalds's avatar
Linus Torvalds committed
1276
1277
	int flag = 0, dtr, rts;

Jiri Slaby's avatar
Jiri Slaby committed
1278
1279
1280
1281
	mutex_lock(&moxa_openlock);
	ch = tty->driver_data;
	if (!ch) {
		mutex_unlock(&moxa_openlock);
Jiri Slaby's avatar
Jiri Slaby committed
1282
		return -EINVAL;
Jiri Slaby's avatar
Jiri Slaby committed
1283
	}
Linus Torvalds's avatar
Linus Torvalds committed
1284

Jiri Slaby's avatar
Jiri Slaby committed
1285
	MoxaPortGetLineOut(ch, &dtr, &rts);
Linus Torvalds's avatar
Linus Torvalds committed
1286
1287
1288
1289
	if (dtr)
		flag |= TIOCM_DTR;
	if (rts)
		flag |= TIOCM_RTS;
Jiri Slaby's avatar
Jiri Slaby committed
1290
	dtr = MoxaPortLineStatus(ch);
Linus Torvalds's avatar
Linus Torvalds committed
1291
1292
1293
1294
1295
1296
	if (dtr & 1)
		flag |= TIOCM_CTS;
	if (dtr & 2)
		flag |= TIOCM_DSR;
	if (dtr & 4)
		flag |= TIOCM_CD;
Jiri Slaby's avatar
Jiri Slaby committed
1297
	mutex_unlock(&moxa_openlock);
Linus Torvalds's avatar
Linus Torvalds committed
1298
1299
1300
1301
1302
1303
	return flag;
}

static int moxa_tiocmset(struct tty_struct *tty, struct file *file,
			 unsigned int set, unsigned int clear)
{
Jiri Slaby's avatar
Jiri Slaby committed
1304
	struct moxa_port *ch;
Linus Torvalds's avatar
Linus Torvalds committed
1305
1306
1307
	int port;
	int dtr, rts;

1308
	port = tty->index;
Jiri Slaby's avatar
Jiri Slaby committed
1309
1310
1311
1312
	mutex_lock(&moxa_openlock);
	ch = tty->driver_data;
	if (!ch) {
		mutex_unlock(&moxa_openlock);
Jiri Slaby's avatar
Jiri Slaby committed
1313
		return -EINVAL;
Jiri Slaby's avatar
Jiri Slaby committed
1314
	}
Linus Torvalds's avatar
Linus Torvalds committed
1315

Jiri Slaby's avatar
Jiri Slaby committed
1316
	MoxaPortGetLineOut(ch, &