Re: Coming? support for 11c1:048c and f modems

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi guys,

I'm trying to make this modem work too, but I've the drivers that come on a CD from agere, for Suse 9.1, Suse 9.2, Fedora 3, Fedora 4, Mandrake 10.1, Redhat 8 and Redhat 9. I did a lot of tests, the only driver that works is for Suse 9.1, but it worked on kernels from 2.6.8 to 2.6.12. I'm trying with kernel 2.6.15, but its not working yet. The error that I receive is: Loading module Agere Modem Controller driver version 2.1.40.0.SuSE9.1 (2005-12-16)
uart_add_one_port error code = -22
serial8250_register_ports unable to register port
serial8250_init: serial8250_register_ports returns err code -22
agrserial - serial8250_init failed

when I change the port at the serial26.c source code to 3 instead of 14, it does not give me this error, but freezes the system completly when I try to use it with wvdial.

I can do tests from 8am to 6pm here on Brazil, if anyone want to help, I'm offering my help to test it!

as the file serial26.c from Suse9.1 version has a GPL header, I'm attaching it here, because it diffears from the agrsm-alpha, and from the versions to Suse 9.2 and all others.

sorry for my english,

Best Regards,

Pablo Marcondes



Jacques Goldberg wrote:
There is one thing which I can contribute, if it helps.
Build a list of e-mail addresses which called for help on that stuff.

Jacques

Marvin Stodolsky wrote:
Alexei Chentsov has spotted a code release from AgereSystems hopefully
extending soft modem support.  The package is agrsm-alpha.tar.bz2
which can be downloaded from
http://phep2.technion.ac.il/linmodems/packages/ltmodem/sv92/
Alexei suggests that the more expert pool skills and make the code
really useful.
While he has two cogent modems, he lacks time to do all the needed
fixes himself.

The code supports three softmodems previously not supported under Linux.
  { 0x11C1, 0x048C, 0x0000, 0x0000 }, are  PCI card softmodems
  { 0x11C1, 0x048F, 0x0000, 0x0000 },   "                     "
which has fairly broad spread usage in Latin Americaa
  { 0x11C1, 0x0620, 0x0000, 0x0000 },  is an SV2PP type
which we have encountered a few times on this List.

The remainder are softmodems with  AC'97/MC97  controllers, which
already have alternate support through the ALSA snd-intel8x0m driver
plus the  slmodemd maintained by Sasha Khapyorsky.
  { 0x8086, 0x2416, 0x0000, 0x0000 },
  { 0x8086, 0x2426, 0x0000, 0x0000 },   "
  { 0x8086, 0x2446, 0x0000, 0x0000 },
  { 0x8086, 0x7196, 0x0000, 0x0000 },
  { 0x8086, 0x2486, 0x0000, 0x0000 },
  { 0x8086, 0x24C6, 0x0000, 0x0000 },
  { 0x8086, 0x24D6, 0x0000, 0x0000 },
  { 0x8086, 0x266D, 0x0000, 0x0000 },

Alexei did compiles under some Fedora kernel (i presume)  and my
compile below is under a lean 2.6.17.14-marv3 kernel derived from
Ubuntu 2.6.17-10-generic sources.  Sadly, the compile failed for the
stock 2.6.17-10-generic  resources for reasons to be clarified.

$ make clean
is broken, so the package should only be used once. Just unpack the
tarball for a clean copu until "make clean" is fixed.  The alpha
development at AgereSystems was last with kernel 2.6.14 sources.  It
would be nice to clean out all those "deprecated" warnings below
through appropriate update

See Alexei's comments below.

MarvS

==============
make -C /lib/modules/`uname -r`/build SUBDIRS=/usr/src/modules/agrsm modules
make[1]: Entering directory `/usr/src/linux-headers-2.6.17.14-marv3'
  CC [M]  /usr/src/modules/agrsm/agrsoftmodem.o
/usr/src/modules/agrsm/agrsoftmodem.c: In function 'SetAgrModemInterface':
/usr/src/modules/agrsm/agrsoftmodem.c:106: warning: 'inter_module_put'
is deprecated (declared at include/linux/module.h:566)
/usr/src/modules/agrsm/agrsoftmodem.c: In function 'modem_init_module':
/usr/src/modules/agrsm/agrsoftmodem.c:136: warning:
'inter_module_register' is deprecated (declared at
include/linux/module.h:562)
/usr/src/modules/agrsm/agrsoftmodem.c:137: warning:
'inter_module_register' is deprecated (declared at
include/linux/module.h:562)
/usr/src/modules/agrsm/agrsoftmodem.c: In function 'modem_cleanup_module':
/usr/src/modules/agrsm/agrsoftmodem.c:148: warning:
'inter_module_unregister' is deprecated (declared at
include/linux/module.h:563)
/usr/src/modules/agrsm/agrsoftmodem.c:149: warning:
'inter_module_unregister' is deprecated (declared at
include/linux/module.h:563)
  CC [M]  /usr/src/modules/agrsm/serial26.o
/usr/src/modules/agrsm/serial26.c: In function 'serial8250_config_port':
/usr/src/modules/agrsm/serial26.c:1998: warning: unused variable 'ret'
/usr/src/modules/agrsm/serial26.c: At top level:
/usr/src/modules/agrsm/serial26.c:2090: warning: initialization from
incompatible pointer type
/usr/src/modules/agrsm/serial26.c:2091: warning: initialization from
incompatible pointer type
/usr/src/modules/agrsm/serial26.c: In function 'serial8250_exit':
/usr/src/modules/agrsm/serial26.c:2523: warning:
'inter_module_unregister' is deprecated (declared at
include/linux/module.h:563)
/usr/src/modules/agrsm/serial26.c:2524: warning: 'inter_module_put' is
deprecated (declared at include/linux/module.h:566)
/usr/src/modules/agrsm/serial26.c:2525: warning: 'inter_module_put' is
deprecated (declared at include/linux/module.h:566)
/usr/src/modules/agrsm/serial26.c: In function 'agr_init_module':
/usr/src/modules/agrsm/serial26.c:2555: warning: 'inter_module_put' is
deprecated (declared at include/linux/module.h:566)
/usr/src/modules/agrsm/serial26.c:2561: warning:
'inter_module_register' is deprecated (declared at
include/linux/module.h:562)
/usr/src/modules/agrsm/serial26.c: At top level:
/usr/src/modules/agrsm/serial26.c:1881: warning:
'serial8250_request_rsa_resource' defined but not used
  LD [M]  /usr/src/modules/agrsm/agrmodem.o
  LD [M]  /usr/src/modules/agrsm/agrserial.o
  Building modules, stage 2.
  MODPOST
WARNING: could not find /usr/src/modules/agrsm/.agrmodemlib.o.cmd for
/usr/src/modules/agrsm/agrmodemlib.o
WARNING: "uart_write_wakeup" [/usr/src/modules/agrsm/agrserial.ko] undefined! WARNING: "uart_add_one_port" [/usr/src/modules/agrsm/agrserial.ko] undefined! WARNING: "uart_get_divisor" [/usr/src/modules/agrsm/agrserial.ko] undefined!
WARNING: "uart_unregister_driver"
[/usr/src/modules/agrsm/agrserial.ko] undefined!
WARNING: "uart_update_timeout" [/usr/src/modules/agrsm/agrserial.ko] undefined! WARNING: "uart_remove_one_port" [/usr/src/modules/agrsm/agrserial.ko] undefined! WARNING: "uart_suspend_port" [/usr/src/modules/agrsm/agrserial.ko] undefined! WARNING: "uart_register_driver" [/usr/src/modules/agrsm/agrserial.ko] undefined! WARNING: "uart_resume_port" [/usr/src/modules/agrsm/agrserial.ko] undefined! WARNING: "uart_get_baud_rate" [/usr/src/modules/agrsm/agrserial.ko] undefined!
  CC      /usr/src/modules/agrsm/agrmodem.mod.o
  LD [M]  /usr/src/modules/agrsm/agrmodem.ko
  CC      /usr/src/modules/agrsm/agrserial.mod.o
  LD [M]  /usr/src/modules/agrsm/agrserial.ko
make[1]: Leaving directory `/usr/src/linux-headers-2.6.17.14-marv3'
marv@marvlap:/usr/src/modules/agrsm$


On 4/9/07, Marvin Stodolsky <marvin.stodolsky@xxxxxxxxx> wrote:
> Alexei
>
> Much thanks. There 11c1.048? Lucetn SV92 modems have been broadly sold
> in S. America.  Isomar can probably provide us with numerous testers
>
> I'll do an announcement to the List this evening.
>
> Marv
>
> ---a------ orwarded message ----------
> From: Marvin Stodolsky <marvin.stodolsky@xxxxxxxxx>
> Date: Apr 9, 2007 11:33 AM
> Subject: Fwd: sv92 update
> To: Jacques Goldberg <jacques.goldberg@xxxxxxxxx>
>
>
> Jacques,
>
> Please make a new folder under Lucent support called sv92 and put
> Alex's code therein.  I'll do an announcement to the List this
> evening.
>
> Marv
>
> ---------- Forwarded message ----------
> From: A.Chentsov <chentsov@xxxxxxx>
> Date: Apr 8, 2007 4:38 PM
> Subject: sv92 update
> To: Marvin Stodolsky <marvin.stodolsky@xxxxxxxxx>
>
> Hello Marvin.
>
> It turned out that Agere released some code for sv92. There's linspire > project to incorporate some closed projects into linux. It includes the
> code in question. They call it ageremodem-source.
>
> I took the code and adjusted for kernel variety. The source is attached.
>
> Tested 11c1:48c on two machines without success. One never detected the
> device. The other one detected but gave a panic on device open call.
>
> We can announce a testing or refer people to the linspire project.
>
> Regards,
> A. Chentsov.
>



/*
 *  linux/drivers/char/8250.c
 *
 *  Driver for 8250/16550-type serial ports
 *
 *  Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
 *
 *  Copyright (C) 2001 Russell King.
 *
 * 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.
 *
 *  $Id: 8250.c,v 1.90 2002/07/28 10:03:27 rmk Exp $
 *
 * A note about mapbase / membase
 *
 *  mapbase is the physical address of the IO port.  Currently, we don't
 *  support this very well, and it may well be dropped from this driver
 *  in future.  As such, mapbase should be NULL.
 *
 *  membase is an 'ioremapped' cookie.  This is compatible with the old
 *  serial.c driver, and is currently the preferred form.
 */
/*
 * Serial Interface for Modem Driver
 *
 * This module is adapted to be a serial interface driver for modem
 * controller drivers.  It provides a generic serial driver like
 * interface to the operating system.  It can work in conjunction
 * with a modem controller driver which supports the required
 * interface. The modem controller driver is expected to perform
 * core modem functions and interact with the modem hardware.
 * - design done by Soumyendu Sarkar of Agere Systems.
 *
 * Adapted to work with Agere Soft Modem driver module by
 * Soumyendu Sarkar of Agere Systems on Dec 2002.
 *
 * Copyright (C) 2002, 2003, 2004, 2005 Agere Systems Inc.
 * 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. This program
 * is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; 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., 675 Mass Ave, Cambridge,
 * MA 02139, USA.
 *
 * Migrated base code from serial.c for Kernel 2.4.x to the UART specific
 * 8250.c for Kernel 2.6.x by Soumyendu Sarkar on March 01, 2004. The 
 * serial_core.c for Kernel 2.6.x does not directly support UART specific
 * interface which is exposed by the Agere Modem Controller module.
 *
 *   Soumyendu Sarkar       11/16/2005    Adapted to kernel 2.6.14 with PCI PP modem support
 */
 
#define LUCENT_MODEM // modifications to the 8250.c code for Lucent controllerless modem 
#define AGERE_SOFT_MODEM // additional modifications for Agere Softmodem to the 8250.c code for Lucent controllerless modem
#ifndef __KERNEL__
#define __KERNEL__
#endif

#ifdef LUCENT_MODEM
static char *serialif_version = "2.1.40.0.SuSE9.1";
static char *serialif_revdate = "2005-12-16";
static char *serialif_name = "Agere Modem Interface driver";
#endif

#include <linux/config.h>
#include <linux/module.h>
#include <linux/tty.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/console.h>
#include <linux/sysrq.h>
#include <linux/serial_reg.h>
#include <linux/serial.h>
#include <linux/serialP.h>
#include <linux/delay.h>

#include <asm/io.h>
#include <asm/irq.h>

#if defined(CONFIG_SERIAL_8250_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
#define SUPPORT_SYSRQ
#endif

#include <linux/serial_core.h>
#ifdef LUCENT_MODEM
#include <../drivers/serial/8250.h>
#else
#include "8250.h"
#endif

/*
 * Configuration:
 *   share_irqs - whether we pass SA_SHIRQ to request_irq().  This option
 *                is unsafe when used on edge-triggered interrupts.
 */
unsigned int share_irqs = SERIAL8250_SHARE_IRQS;

/*
 * Debugging.
 */
#if 0
#define DEBUG_AUTOCONF(fmt...)	printk(fmt)
#else
#define DEBUG_AUTOCONF(fmt...)	do { } while (0)
#endif

#if 0
#define DEBUG_INTR(fmt...)	printk(fmt)
#else
#define DEBUG_INTR(fmt...)	do { } while (0)
#endif

#define PASS_LIMIT	256

/*
 * We default to IRQ0 for the "no irq" hack.   Some
 * machine types want others as well - they're free
 * to redefine this in their header file.
 */
#define is_real_interrupt(irq)	((irq) != 0)

/*
 * This converts from our new CONFIG_ symbols to the symbols
 * that asm/serial.h expects.  You _NEED_ to comment out the
 * linux/config.h include contained inside asm/serial.h for
 * this to work.
 */
#undef CONFIG_SERIAL_MANY_PORTS
#undef CONFIG_SERIAL_DETECT_IRQ
#undef CONFIG_SERIAL_MULTIPORT
#undef CONFIG_HUB6

#ifdef CONFIG_SERIAL_8250_DETECT_IRQ
#define CONFIG_SERIAL_DETECT_IRQ 1
#endif
#ifdef CONFIG_SERIAL_8250_MULTIPORT
#define CONFIG_SERIAL_MULTIPORT 1
#endif
#ifdef CONFIG_SERIAL_8250_MANY_PORTS
#define CONFIG_SERIAL_MANY_PORTS 1
#endif

#ifdef LUCENT_MODEM
#undef CONFIG_SERIAL_SHARE_IRQ
#define IRQ_T(state) ((state->flags & ASYNC_SHARE_IRQ) ? SA_SHIRQ : SA_INTERRUPT)
int int_hooked = 0;
int (*GetAgrModemInterface)(void *mdmdata) = NULL;
int (*SetAgrModemInterface)(int state) = NULL;
#endif

/*
 * HUB6 is always on.  This will be removed once the header
 * files have been cleaned.
 */
#define CONFIG_HUB6 1

#include <asm/serial.h>

static struct old_serial_port old_serial_port[] = {
	SERIAL_PORT_DFNS /* defined in asm/serial.h */
};

#define UART_NR	ARRAY_SIZE(old_serial_port)

#if defined(CONFIG_SERIAL_8250_RSA) && defined(MODULE)

#define PORT_RSA_MAX 4
static int probe_rsa[PORT_RSA_MAX];
static int force_rsa[PORT_RSA_MAX];
#endif /* CONFIG_SERIAL_8250_RSA  */

struct uart_8250_port {
	struct uart_port	port;
	struct timer_list	timer;		/* "no irq" timer */
	struct list_head	list;		/* ports on this IRQ */
	unsigned int		capabilities;	/* port capabilities */
	unsigned short		rev;
	unsigned char		acr;
	unsigned char		ier;
	unsigned char		lcr;
	unsigned char		mcr_mask;	/* mask of user bits */
	unsigned char		mcr_force;	/* mask of forced bits */
	unsigned char		lsr_break_flag;

	/*
	 * We provide a per-port pm hook.
	 */
	void			(*pm)(struct uart_port *port,
				      unsigned int state, unsigned int old);
};

struct irq_info {
	spinlock_t		lock;
	struct list_head	*head;
};

#ifndef LUCENT_MODEM
static struct irq_info irq_lists[NR_IRQS];
#endif

/*
 * Here we define the default xmit fifo size used for each type of UART.
 */
#ifdef LUCENT_MODEM
static const struct serial_uart_config uart_config[] = {
#else
static const struct serial_uart_config uart_config[PORT_MAX_8250+1] = {
#endif
  { "unknown",	1,	0 },
	{ "8250",	1,	0 },
	{ "16450",	1,	0 },
	{ "16550",	1,	0 },
#ifndef LUCENT_MODEM
	{ "16550A",	16,	UART_CLEAR_FIFO | UART_USE_FIFO },
#else
	{ "AgereModem", 16, 0 },
#endif
	{ "Cirrus",	1, 	0 },
	{ "ST16650",	1,	UART_CLEAR_FIFO | UART_STARTECH },
	{ "ST16650V2",	32,	UART_CLEAR_FIFO | UART_USE_FIFO | UART_STARTECH },
	{ "TI16750",	64,	UART_CLEAR_FIFO | UART_USE_FIFO },
	{ "Startech",	1,	0 },
	{ "16C950/954",	128,	UART_CLEAR_FIFO | UART_USE_FIFO },
	{ "ST16654",	64,	UART_CLEAR_FIFO | UART_USE_FIFO | UART_STARTECH },
	{ "XR16850",	128,	UART_CLEAR_FIFO | UART_USE_FIFO | UART_STARTECH },
	{ "RSA",	2048,	UART_CLEAR_FIFO | UART_USE_FIFO },
	{ "NS16550A",	16,	UART_CLEAR_FIFO | UART_USE_FIFO | UART_NATSEMI }
};

#ifdef LUCENT_MODEM
// ensure that this remains after <linux/serial.h> for PORT_MAX def
typedef unsigned char byte ;
typedef unsigned short word ;
typedef unsigned long dword ;
typedef int BOOL;

struct ltmodem_ops
{
	int (*detect_modem)(void *pltmodem_res);
	void (*init_modem)(void);
	int (*PortOpen)(void);
	void (*PortClose)(void);
	int (*read_vuart_register)(int offset);
	void (*write_vuart_register)(int offset, int value);
	int (*app_ioctl_handler)(unsigned int cmd, unsigned long arg);
	byte(*dsp_isr)(void);
	word (*read_buffer)( byte *data, word size );
};

struct ltmodem_res
{
	word BaseAddress;
	word BaseAddress2;
	byte Irq;
};

#define IOCTL_MODEM_APP_1	_IOR(62,41,char *)
#define IOCTL_MODEM_APP_2	_IOR(62,42,char *)
#define IOCTL_MODEM_APP_3	_IOR(62,43,char *)
#define IOCTL_MODEM_APP_4	_IOR(62,44,char *)
#define IOCTL_MODEM_APP_5	_IOR(62,45,char *)
#define IOCTL_MODEM_APP_6	_IOR(62,46,char *)
#define IOCTL_MODEM_APP_7	_IOR(62,47,char *)
#define IOCTL_MODEM_APP_8	_IOR(62,48,char *)

#ifdef PORT_MAX_8250
#undef PORT_MAX_8250
#endif

#define PORT_LTMODEM      PORT_16550A
#define PORT_LT_MODEM_IF  14
#define PORT_MAX_8250     15

#ifdef CONFIG_SERIAL_8250_CONSOLE
#undef CONFIG_SERIAL_8250_CONSOLE
#endif

struct ltmodem_ops lt_modem_ops;
struct ltmodem_res lt_modem_res;
static void agr_rs_interrupt (void);
static int intf_flag = 0;
static int uart_flag = 0;
static int tx_empty_flag = 0;
static struct uart_8250_port serial8250_ports[UART_NR];

#endif  //LUCENT_MODEM

static _INLINE_ unsigned int serial_in(struct uart_8250_port *up, int offset)
{
#ifdef LUCENT_MODEM
  return(lt_modem_ops.read_vuart_register( offset ));   //only have support fpr single modem so we can just use offsets.
#else
	offset <<= up->port.regshift;

	switch (up->port.iotype) {
	case SERIAL_IO_HUB6:
		outb(up->port.hub6 - 1 + offset, up->port.iobase);
		return inb(up->port.iobase + 1);

	case SERIAL_IO_MEM:
		return readb(up->port.membase + offset);

	default:
		return inb(up->port.iobase + offset);
	}
#endif
}

static _INLINE_ void
serial_out(struct uart_8250_port *up, int offset, int value)
{
#ifdef LUCENT_MODEM
    lt_modem_ops.write_vuart_register(offset, value);
#else
	offset <<= up->port.regshift;

	switch (up->port.iotype) {
	case SERIAL_IO_HUB6:
		outb(up->port.hub6 - 1 + offset, up->port.iobase);
		outb(value, up->port.iobase + 1);
		break;

	case SERIAL_IO_MEM:
		writeb(value, up->port.membase + offset);
		break;

	default:
		outb(value, up->port.iobase + offset);
	}
#endif
}

/*
 * We used to support using pause I/O for certain machines.  We
 * haven't supported this for a while, but just in case it's badly
 * needed for certain old 386 machines, I've left these #define's
 * in....
 */
#define serial_inp(up, offset)		serial_in(up, offset)
#define serial_outp(up, offset, value)	serial_out(up, offset, value)


/*
 * For the 16C950
 */
static void serial_icr_write(struct uart_8250_port *up, int offset, int value)
{
	serial_out(up, UART_SCR, offset);
	serial_out(up, UART_ICR, value);
}

static unsigned int serial_icr_read(struct uart_8250_port *up, int offset)
{
	unsigned int value;

	serial_icr_write(up, UART_ACR, up->acr | UART_ACR_ICRRD);
	serial_out(up, UART_SCR, offset);
	value = serial_in(up, UART_ICR);
	serial_icr_write(up, UART_ACR, up->acr);

	return value;
}

#ifdef CONFIG_SERIAL_8250_RSA
/*
 * Attempts to turn on the RSA FIFO.  Returns zero on failure.
 * We set the port uart clock rate if we succeed.
 */
static int __enable_rsa(struct uart_8250_port *up)
{
	unsigned char mode;
	int result;

	mode = serial_inp(up, UART_RSA_MSR);
	result = mode & UART_RSA_MSR_FIFO;

	if (!result) {
		serial_outp(up, UART_RSA_MSR, mode | UART_RSA_MSR_FIFO);
		mode = serial_inp(up, UART_RSA_MSR);
		result = mode & UART_RSA_MSR_FIFO;
	}

	if (result)
		up->port.uartclk = SERIAL_RSA_BAUD_BASE * 16;

	return result;
}

static void enable_rsa(struct uart_8250_port *up)
{
	if (up->port.type == PORT_RSA) {
		if (up->port.uartclk != SERIAL_RSA_BAUD_BASE * 16) {
			spin_lock_irq(&up->port.lock);
			__enable_rsa(up);
			spin_unlock_irq(&up->port.lock);
		}
		if (up->port.uartclk == SERIAL_RSA_BAUD_BASE * 16)
			serial_outp(up, UART_RSA_FRR, 0);
	}
}

/*
 * Attempts to turn off the RSA FIFO.  Returns zero on failure.
 * It is unknown why interrupts were disabled in here.  However,
 * the caller is expected to preserve this behaviour by grabbing
 * the spinlock before calling this function.
 */
static void disable_rsa(struct uart_8250_port *up)
{
	unsigned char mode;
	int result;

	if (up->port.type == PORT_RSA &&
	    up->port.uartclk == SERIAL_RSA_BAUD_BASE * 16) {
		spin_lock_irq(&up->port.lock);

		mode = serial_inp(up, UART_RSA_MSR);
		result = !(mode & UART_RSA_MSR_FIFO);

		if (!result) {
			serial_outp(up, UART_RSA_MSR, mode & ~UART_RSA_MSR_FIFO);
			mode = serial_inp(up, UART_RSA_MSR);
			result = !(mode & UART_RSA_MSR_FIFO);
		}

		if (result)
			up->port.uartclk = SERIAL_RSA_BAUD_BASE_LO * 16;
		spin_unlock_irq(&up->port.lock);
	}
}
#endif /* CONFIG_SERIAL_8250_RSA */

/*
 * This is a quickie test to see how big the FIFO is.
 * It doesn't work at all the time, more's the pity.
 */
static int size_fifo(struct uart_8250_port *up)
{
	unsigned char old_fcr, old_mcr, old_dll, old_dlm;
	int count;

	old_fcr = serial_inp(up, UART_FCR);
	old_mcr = serial_inp(up, UART_MCR);
	serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO |
		    UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
	serial_outp(up, UART_MCR, UART_MCR_LOOP);
	serial_outp(up, UART_LCR, UART_LCR_DLAB);
	old_dll = serial_inp(up, UART_DLL);
	old_dlm = serial_inp(up, UART_DLM);
	serial_outp(up, UART_DLL, 0x01);
	serial_outp(up, UART_DLM, 0x00);
	serial_outp(up, UART_LCR, 0x03);
	for (count = 0; count < 256; count++)
		serial_outp(up, UART_TX, count);
	mdelay(20);/* FIXME - schedule_timeout */
	for (count = 0; (serial_inp(up, UART_LSR) & UART_LSR_DR) &&
	     (count < 256); count++)
		serial_inp(up, UART_RX);
	serial_outp(up, UART_FCR, old_fcr);
	serial_outp(up, UART_MCR, old_mcr);
	serial_outp(up, UART_LCR, UART_LCR_DLAB);
	serial_outp(up, UART_DLL, old_dll);
	serial_outp(up, UART_DLM, old_dlm);

	return count;
}

/*
 * This is a helper routine to autodetect StarTech/Exar/Oxsemi UART's.
 * When this function is called we know it is at least a StarTech
 * 16650 V2, but it might be one of several StarTech UARTs, or one of
 * its clones.  (We treat the broken original StarTech 16650 V1 as a
 * 16550, and why not?  Startech doesn't seem to even acknowledge its
 * existence.)
 * 
 * What evil have men's minds wrought...
 */
static void autoconfig_has_efr(struct uart_8250_port *up)
{
	unsigned char id1, id2, id3, rev, saved_dll, saved_dlm;

	/*
	 * First we check to see if it's an Oxford Semiconductor UART.
	 *
	 * If we have to do this here because some non-National
	 * Semiconductor clone chips lock up if you try writing to the
	 * LSR register (which serial_icr_read does)
	 */

	/*
	 * Check for Oxford Semiconductor 16C950.
	 *
	 * EFR [4] must be set else this test fails.
	 *
	 * This shouldn't be necessary, but Mike Hudson (Exoray@xxxxxxx)
	 * claims that it's needed for 952 dual UART's (which are not
	 * recommended for new designs).
	 */
	up->acr = 0;
	serial_out(up, UART_LCR, 0xBF);
	serial_out(up, UART_EFR, UART_EFR_ECB);
	serial_out(up, UART_LCR, 0x00);
	id1 = serial_icr_read(up, UART_ID1);
	id2 = serial_icr_read(up, UART_ID2);
	id3 = serial_icr_read(up, UART_ID3);
	rev = serial_icr_read(up, UART_REV);

	DEBUG_AUTOCONF("950id=%02x:%02x:%02x:%02x ", id1, id2, id3, rev);

	if (id1 == 0x16 && id2 == 0xC9 &&
	    (id3 == 0x50 || id3 == 0x52 || id3 == 0x54)) {
		up->port.type = PORT_16C950;
		up->rev = rev | (id3 << 8);
		return;
	}
	
	/*
	 * We check for a XR16C850 by setting DLL and DLM to 0, and then
	 * reading back DLL and DLM.  The chip type depends on the DLM
	 * value read back:
	 *  0x10 - XR16C850 and the DLL contains the chip revision.
	 *  0x12 - XR16C2850.
	 *  0x14 - XR16C854.
	 */
	serial_outp(up, UART_LCR, UART_LCR_DLAB);
	saved_dll = serial_inp(up, UART_DLL);
	saved_dlm = serial_inp(up, UART_DLM);
	serial_outp(up, UART_DLL, 0);
	serial_outp(up, UART_DLM, 0);
	id2 = serial_inp(up, UART_DLL);
	id1 = serial_inp(up, UART_DLM);
	serial_outp(up, UART_DLL, saved_dll);
	serial_outp(up, UART_DLM, saved_dlm);

	DEBUG_AUTOCONF("850id=%02x:%02x ", id1, id2);

	if (id1 == 0x10 || id1 == 0x12 || id1 == 0x14) {
		if (id1 == 0x10)
			up->rev = id2;
		up->port.type = PORT_16850;
		return;
	}

	/*
	 * It wasn't an XR16C850.
	 *
	 * We distinguish between the '654 and the '650 by counting
	 * how many bytes are in the FIFO.  I'm using this for now,
	 * since that's the technique that was sent to me in the
	 * serial driver update, but I'm not convinced this works.
	 * I've had problems doing this in the past.  -TYT
	 */
	if (size_fifo(up) == 64)
		up->port.type = PORT_16654;
	else
		up->port.type = PORT_16650V2;
}

/*
 * We detected a chip without a FIFO.  Only two fall into
 * this category - the original 8250 and the 16450.  The
 * 16450 has a scratch register (accessible with LCR=0)
 */
static void autoconfig_8250(struct uart_8250_port *up)
{
	unsigned char scratch, status1, status2;

	up->port.type = PORT_8250;

	scratch = serial_in(up, UART_SCR);
	serial_outp(up, UART_SCR, 0xa5);
	status1 = serial_in(up, UART_SCR);
	serial_outp(up, UART_SCR, 0x5a);
	status2 = serial_in(up, UART_SCR);
	serial_outp(up, UART_SCR, scratch);

	if (status1 == 0xa5 && status2 == 0x5a)
		up->port.type = PORT_16450;
}

/*
 * We know that the chip has FIFOs.  Does it have an EFR?  The
 * EFR is located in the same register position as the IIR and
 * we know the top two bits of the IIR are currently set.  The
 * EFR should contain zero.  Try to read the EFR.
 */
static void autoconfig_16550a(struct uart_8250_port *up)
{
	unsigned char status1, status2;

	up->port.type = PORT_16550A;

	/*
	 * Check for presence of the EFR when DLAB is set.
	 * Only ST16C650V1 UARTs pass this test.
	 */
	serial_outp(up, UART_LCR, UART_LCR_DLAB);
	if (serial_in(up, UART_EFR) == 0) {
		DEBUG_AUTOCONF("EFRv1 ");
		up->port.type = PORT_16650;
		return;
	}

	/*
	 * Maybe it requires 0xbf to be written to the LCR.
	 * (other ST16C650V2 UARTs, TI16C752A, etc)
	 */
	serial_outp(up, UART_LCR, 0xBF);
	if (serial_in(up, UART_EFR) == 0) {
		DEBUG_AUTOCONF("EFRv2 ");
		autoconfig_has_efr(up);
		return;
	}

	/*
	 * Check for a National Semiconductor SuperIO chip.
	 * Attempt to switch to bank 2, read the value of the LOOP bit
	 * from EXCR1. Switch back to bank 0, change it in MCR. Then
	 * switch back to bank 2, read it from EXCR1 again and check
	 * it's changed. If so, set baud_base in EXCR2 to 921600.
	 */
	serial_outp(up, UART_LCR, 0);
	status1 = serial_in(up, UART_MCR);
	serial_outp(up, UART_LCR, 0xE0);
	status2 = serial_in(up, 0x02); /* EXCR1 */

	if (!((status2 ^ status1) & UART_MCR_LOOP)) {
		serial_outp(up, UART_LCR, 0);
		serial_outp(up, UART_MCR, status1 ^ UART_MCR_LOOP);
		serial_outp(up, UART_LCR, 0xE0);
		status2 = serial_in(up, 0x02); /* EXCR1 */
		serial_outp(up, UART_LCR, 0);
		serial_outp(up, UART_MCR, status1);

		if ((status2 ^ status1) & UART_MCR_LOOP) {
			serial_outp(up, UART_LCR, 0xE0);
			status1 = serial_in(up, 0x04); /* EXCR1 */
			status1 &= ~0xB0; /* Disable LOCK, mask out PRESL[01] */
			status1 |= 0x10;  /* 1.625 divisor for baud_base --> 921600 */
			serial_outp(up, 0x04, status1);
			serial_outp(up, UART_LCR, 0);

			up->port.type = PORT_NS16550A;
			up->port.uartclk = 921600*16;
			return;
		}
	}

	/*
	 * No EFR.  Try to detect a TI16750, which only sets bit 5 of
	 * the IIR when 64 byte FIFO mode is enabled when DLAB is set.
	 * Try setting it with and without DLAB set.  Cheap clones
	 * set bit 5 without DLAB set.
	 */
	serial_outp(up, UART_LCR, 0);
	serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE);
	status1 = serial_in(up, UART_IIR) >> 5;
	serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO);
	serial_outp(up, UART_LCR, UART_LCR_DLAB);
	serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE);
	status2 = serial_in(up, UART_IIR) >> 5;
	serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO);

	DEBUG_AUTOCONF("iir1=%d iir2=%d ", status1, status2);

	if (status1 == 6 && status2 == 7) {
		up->port.type = PORT_16750;
		return;
	}
}

/*
 * This routine is called by rs_init() to initialize a specific serial
 * port.  It determines what type of UART chip this serial port is
 * using: 8250, 16450, 16550, 16550A.  The important question is
 * whether or not this UART is a 16550A or not, since this will
 * determine whether or not we can use its FIFO features or not.
 */
static void autoconfig(struct uart_8250_port *up, unsigned int probeflags)
{
	unsigned char status1, scratch, scratch2, scratch3;
	unsigned char save_lcr, save_mcr;
	unsigned long flags;

#ifdef LUCENT_MODEM
	up->port.type     = PORT_16550A;
  up->port.iobase   = lt_modem_res.BaseAddress;
	up->port.uartclk  = BASE_BAUD * 16;
	up->port.iotype   = UPIO_PORT;
	up->port.fifosize = 64;
	up->port.flags    = 0;
#ifndef AGERE_SOFT_MODEM
	request_region(up->port.iobase, 256, uart_config[up->port.type].name);
#endif
	return;
#endif

  if (!up->port.iobase && !up->port.mapbase && !up->port.membase)
		return;

	DEBUG_AUTOCONF("ttyS%d: autoconf (0x%04x, 0x%08lx): ",
			up->port.line, up->port.iobase, up->port.membase);

	/*
	 * We really do need global IRQs disabled here - we're going to
	 * be frobbing the chips IRQ enable register to see if it exists.
	 */
	spin_lock_irqsave(&up->port.lock, flags);
//	save_flags(flags); cli();

	if (!(up->port.flags & UPF_BUGGY_UART)) {
		/*
		 * Do a simple existence test first; if we fail this,
		 * there's no point trying anything else.
		 * 
		 * 0x80 is used as a nonsense port to prevent against
		 * false positives due to ISA bus float.  The
		 * assumption is that 0x80 is a non-existent port;
		 * which should be safe since include/asm/io.h also
		 * makes this assumption.
		 *
		 * Note: this is safe as long as MCR bit 4 is clear
		 * and the device is in "PC" mode.
		 */
		scratch = serial_inp(up, UART_IER);
		serial_outp(up, UART_IER, 0);
#ifdef __i386__
		outb(0xff, 0x080);
#endif
		scratch2 = serial_inp(up, UART_IER);
		serial_outp(up, UART_IER, 0x0F);
#ifdef __i386__
		outb(0, 0x080);
#endif
		scratch3 = serial_inp(up, UART_IER);
		serial_outp(up, UART_IER, scratch);
		if (scratch2 != 0 || scratch3 != 0x0F) {
			/*
			 * We failed; there's nothing here
			 */
			DEBUG_AUTOCONF("IER test failed (%02x, %02x) ",
				       scratch2, scratch3);
			goto out;
		}
	}

	save_mcr = serial_in(up, UART_MCR);
	save_lcr = serial_in(up, UART_LCR);

	/* 
	 * Check to see if a UART is really there.  Certain broken
	 * internal modems based on the Rockwell chipset fail this
	 * test, because they apparently don't implement the loopback
	 * test mode.  So this test is skipped on the COM 1 through
	 * COM 4 ports.  This *should* be safe, since no board
	 * manufacturer would be stupid enough to design a board
	 * that conflicts with COM 1-4 --- we hope!
	 */
	if (!(up->port.flags & UPF_SKIP_TEST)) {
		serial_outp(up, UART_MCR, UART_MCR_LOOP | 0x0A);
		status1 = serial_inp(up, UART_MSR) & 0xF0;
		serial_outp(up, UART_MCR, save_mcr);
		if (status1 != 0x90) {
			DEBUG_AUTOCONF("LOOP test failed (%02x) ",
				       status1);
			goto out;
		}
	}

	/*
	 * We're pretty sure there's a port here.  Lets find out what
	 * type of port it is.  The IIR top two bits allows us to find
	 * out if its 8250 or 16450, 16550, 16550A or later.  This
	 * determines what we test for next.
	 *
	 * We also initialise the EFR (if any) to zero for later.  The
	 * EFR occupies the same register location as the FCR and IIR.
	 */
	serial_outp(up, UART_LCR, 0xBF);
	serial_outp(up, UART_EFR, 0);
	serial_outp(up, UART_LCR, 0);

	serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO);
	scratch = serial_in(up, UART_IIR) >> 6;

	DEBUG_AUTOCONF("iir=%d ", scratch);

	switch (scratch) {
	case 0:
		autoconfig_8250(up);
		break;
	case 1:
		up->port.type = PORT_UNKNOWN;
		break;
	case 2:
		up->port.type = PORT_16550;
		break;
	case 3:
		autoconfig_16550a(up);
		break;
	}

#if defined(CONFIG_SERIAL_8250_RSA) && defined(MODULE)
	/*
	 * Only probe for RSA ports if we got the region.
	 */
	if (up->port.type == PORT_16550A && probeflags & PROBE_RSA) {
		int i;

		for (i = 0 ; i < PORT_RSA_MAX ; ++i) {
			if (!probe_rsa[i] && !force_rsa[i])
				break;
#ifndef LT_KER_26
      if (((probe_rsa[i] != up->port.iobase) ||
			     check_region(up->port.iobase + UART_RSA_BASE, 16)) &&
			    (force_rsa[i] != up->port.iobase))
#else
      if (((probe_rsa[i] != up->port.iobase) ||
			     __check_region(&ioport_resource, up->port.iobase + UART_RSA_BASE, 16)) &&
			    (force_rsa[i] != up->port.iobase))
#endif
				continue;
			if (__enable_rsa(up)) {
				up->port.type = PORT_RSA;
				break;
			}
		}
	}
#endif
	serial_outp(up, UART_LCR, save_lcr);

	up->port.fifosize = uart_config[up->port.type].dfl_xmit_fifo_size;
	up->capabilities = uart_config[up->port.type].flags;

	if (up->port.type == PORT_UNKNOWN)
		goto out;

	/*
	 * Reset the UART.
	 */
#ifdef CONFIG_SERIAL_8250_RSA
	if (up->port.type == PORT_RSA)
		serial_outp(up, UART_RSA_FRR, 0);
#endif
	serial_outp(up, UART_MCR, save_mcr);
	serial_outp(up, UART_FCR, (UART_FCR_ENABLE_FIFO |
				     UART_FCR_CLEAR_RCVR |
				     UART_FCR_CLEAR_XMIT));
	serial_outp(up, UART_FCR, 0);
	(void)serial_in(up, UART_RX);
	serial_outp(up, UART_IER, 0);

 out:	
	spin_unlock_irqrestore(&up->port.lock, flags);
//	restore_flags(flags);
#ifdef CONFIG_SERIAL_8250_RSA
	if (up->port.iobase && up->port.type == PORT_RSA) {
		release_region(up->port.iobase, 8);
		request_region(up->port.iobase + UART_RSA_BASE, 16,
			       "serial_rsa");
	}
#endif
	DEBUG_AUTOCONF("type=%s\n", uart_config[up->port.type].name);
}

static void autoconfig_irq(struct uart_8250_port *up)
{
	unsigned char save_mcr, save_ier;
	unsigned char save_ICP = 0;
	unsigned int ICP = 0;
	unsigned long irqs;
	int irq;

	if (up->port.flags & UPF_FOURPORT) {
		ICP = (up->port.iobase & 0xfe0) | 0x1f;
		save_ICP = inb_p(ICP);
		outb_p(0x80, ICP);
		(void) inb_p(ICP);
	}

	/* forget possible initially masked and pending IRQ */
	probe_irq_off(probe_irq_on());
	save_mcr = serial_inp(up, UART_MCR);
	save_ier = serial_inp(up, UART_IER);
	serial_outp(up, UART_MCR, UART_MCR_OUT1 | UART_MCR_OUT2);
	
	irqs = probe_irq_on();
	serial_outp(up, UART_MCR, 0);
	udelay (10);
	if (up->port.flags & UPF_FOURPORT)  {
		serial_outp(up, UART_MCR,
			    UART_MCR_DTR | UART_MCR_RTS);
	} else {
		serial_outp(up, UART_MCR,
			    UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2);
	}
	serial_outp(up, UART_IER, 0x0f);	/* enable all intrs */
	(void)serial_inp(up, UART_LSR);
	(void)serial_inp(up, UART_RX);
	(void)serial_inp(up, UART_IIR);
	(void)serial_inp(up, UART_MSR);
	serial_outp(up, UART_TX, 0xFF);
	udelay (20);
	irq = probe_irq_off(irqs);

	serial_outp(up, UART_MCR, save_mcr);
	serial_outp(up, UART_IER, save_ier);

	if (up->port.flags & UPF_FOURPORT)
		outb_p(save_ICP, ICP);

	up->port.irq = (irq > 0) ? irq : 0;
}

static void serial8250_stop_tx(struct uart_port *port, unsigned int tty_stop)
{
	struct uart_8250_port *up = (struct uart_8250_port *)port;

	if (up->ier & UART_IER_THRI) {
		up->ier &= ~UART_IER_THRI;
		serial_out(up, UART_IER, up->ier);
	}
	if (up->port.type == PORT_16C950 && tty_stop) {
		up->acr |= UART_ACR_TXDIS;
		serial_icr_write(up, UART_ACR, up->acr);
	}
}

static void serial8250_start_tx(struct uart_port *port, unsigned int tty_start)
{
	struct uart_8250_port *up = (struct uart_8250_port *)port;

	if (!(up->ier & UART_IER_THRI)) {
		up->ier |= UART_IER_THRI;
		serial_out(up, UART_IER, up->ier);
	}
	/*
	 * We only do this from uart_start
	 */
	if (tty_start && up->port.type == PORT_16C950) {
		up->acr &= ~UART_ACR_TXDIS;
		serial_icr_write(up, UART_ACR, up->acr);
	}
}

static void serial8250_stop_rx(struct uart_port *port)
{
	struct uart_8250_port *up = (struct uart_8250_port *)port;

	up->ier &= ~UART_IER_RLSI;
	up->port.read_status_mask &= ~UART_LSR_DR;
	serial_out(up, UART_IER, up->ier);
}

static void serial8250_enable_ms(struct uart_port *port)
{
	struct uart_8250_port *up = (struct uart_8250_port *)port;

	up->ier |= UART_IER_MSI;
	serial_out(up, UART_IER, up->ier);
}

static _INLINE_ void
receive_chars(struct uart_8250_port *up, int *status, struct pt_regs *regs)
{
	struct tty_struct *tty = up->port.info->tty;
	unsigned char ch;
	int max_count = 256;

	do {
		if (unlikely(tty->flip.count >= TTY_FLIPBUF_SIZE)) {
			tty->flip.work.func((void *)tty);
			if (tty->flip.count >= TTY_FLIPBUF_SIZE)
				return; // if TTY_DONT_FLIP is set
		}
		ch = serial_inp(up, UART_RX);
		*tty->flip.char_buf_ptr = ch;
		*tty->flip.flag_buf_ptr = TTY_NORMAL;
		up->port.icount.rx++;

		if (unlikely(*status & (UART_LSR_BI | UART_LSR_PE |
				       UART_LSR_FE | UART_LSR_OE))) {
			/*
			 * For statistics only
			 */
			if (*status & UART_LSR_BI) {
				*status &= ~(UART_LSR_FE | UART_LSR_PE);
				up->port.icount.brk++;
				/*
				 * We do the SysRQ and SAK checking
				 * here because otherwise the break
				 * may get masked by ignore_status_mask
				 * or read_status_mask.
				 */
				if (uart_handle_break(&up->port))
					goto ignore_char;
			} else if (*status & UART_LSR_PE)
				up->port.icount.parity++;
			else if (*status & UART_LSR_FE)
				up->port.icount.frame++;
			if (*status & UART_LSR_OE)
				up->port.icount.overrun++;

			/*
			 * Mask off conditions which should be ingored.
			 */
			*status &= up->port.read_status_mask;

#ifdef CONFIG_SERIAL_8250_CONSOLE
			if (up->port.line == up->port.cons->index) {
				/* Recover the break flag from console xmit */
				*status |= up->lsr_break_flag;
				up->lsr_break_flag = 0;
			}
#endif
			if (*status & UART_LSR_BI) {
				DEBUG_INTR("handling break....");
				*tty->flip.flag_buf_ptr = TTY_BREAK;
			} else if (*status & UART_LSR_PE)
				*tty->flip.flag_buf_ptr = TTY_PARITY;
			else if (*status & UART_LSR_FE)
				*tty->flip.flag_buf_ptr = TTY_FRAME;
		}
		if (uart_handle_sysrq_char(&up->port, ch, regs))
			goto ignore_char;
		if ((*status & up->port.ignore_status_mask) == 0) {
			tty->flip.flag_buf_ptr++;
			tty->flip.char_buf_ptr++;
			tty->flip.count++;
		}
		if ((*status & UART_LSR_OE) &&
		    tty->flip.count < TTY_FLIPBUF_SIZE) {
			/*
			 * Overrun is special, since it's reported
			 * immediately, and doesn't affect the current
			 * character.
			 */
			*tty->flip.flag_buf_ptr = TTY_OVERRUN;
			tty->flip.flag_buf_ptr++;
			tty->flip.char_buf_ptr++;
			tty->flip.count++;
		}
	ignore_char:
		*status = serial_inp(up, UART_LSR);
	} while ((*status & UART_LSR_DR) && (max_count-- > 0));
	tty_flip_buffer_push(tty);
}

static _INLINE_ void transmit_chars(struct uart_8250_port *up)
{
	struct circ_buf *xmit = &up->port.info->xmit;
	int count;

  if (up->port.x_char) {
		serial_outp(up, UART_TX, up->port.x_char);
		up->port.icount.tx++;
		up->port.x_char = 0;
		return;
	}
	if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) {
		serial8250_stop_tx(&up->port, 0);
		return;
	}

	count = up->port.fifosize;
	do {
		serial_out(up, UART_TX, xmit->buf[xmit->tail]);
		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
		up->port.icount.tx++;
		if (uart_circ_empty(xmit))
			break;
	} while (--count > 0);

	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
		uart_write_wakeup(&up->port);

	DEBUG_INTR("THRE...");

	if (uart_circ_empty(xmit))
		serial8250_stop_tx(&up->port, 0);
}

static _INLINE_ void check_modem_status(struct uart_8250_port *up)
{
	int status;

	status = serial_in(up, UART_MSR);

	if ((status & UART_MSR_ANY_DELTA) == 0)
		return;

	if (status & UART_MSR_TERI)
		up->port.icount.rng++;
	if (status & UART_MSR_DDSR)
		up->port.icount.dsr++;
	if (status & UART_MSR_DDCD)
		uart_handle_dcd_change(&up->port, status & UART_MSR_DCD);
	if (status & UART_MSR_DCTS)
		uart_handle_cts_change(&up->port, status & UART_MSR_CTS);

	wake_up_interruptible(&up->port.info->delta_msr_wait);
}

/*
 * This handles the interrupt from one port.
 */
static inline void
serial8250_handle_port(struct uart_8250_port *up, struct pt_regs *regs)
{
	unsigned int status = serial_inp(up, UART_LSR);

	DEBUG_INTR("status = %x...", status);

  if (status & UART_LSR_DR)
		receive_chars(up, &status, regs);
	check_modem_status(up);
	if (status & UART_LSR_THRE)
		transmit_chars(up);
}

/*
 * This is the serial driver's interrupt routine.
 *
 * Arjan thinks the old way was overly complex, so it got simplified.
 * Alan disagrees, saying that need the complexity to handle the weird
 * nature of ISA shared interrupts.  (This is a special exception.)
 *
 * In order to handle ISA shared interrupts properly, we need to check
 * that all ports have been serviced, and therefore the ISA interrupt
 * line has been de-asserted.
 *
 * This means we need to loop through all ports. checking that they
 * don't have an interrupt pending.
 */
static irqreturn_t serial8250_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
#ifndef LUCENT_MODEM
	struct irq_info *i = dev_id;
	struct list_head *l, *end = NULL;
	int pass_counter = 0;

	DEBUG_INTR("serial8250_interrupt(%d)...", irq);

	spin_lock(&i->lock);

	l = i->head;
	do {
		struct uart_8250_port *up;
		unsigned int iir;

		up = list_entry(l, struct uart_8250_port, list);

		iir = serial_in(up, UART_IIR);
		if (!(iir & UART_IIR_NO_INT)) {
			spin_lock(&up->port.lock);
			serial8250_handle_port(up, regs);
			spin_unlock(&up->port.lock);

			end = NULL;
		} else if (end == NULL)
			end = l;

		l = l->next;

		if (l == i->head && pass_counter++ > PASS_LIMIT) {
			/* If we hit this, we're dead. */
			printk(KERN_ERR "serial8250: too much work for "
				"irq%d\n", irq);
			break;
		}
	} while (l != end);

	spin_unlock(&i->lock);

	DEBUG_INTR("end.\n");
#else
    struct uart_8250_port *up = &serial8250_ports[PORT_LT_MODEM_IF];
		unsigned int iir;
		iir = serial_in(up, UART_IIR);
    if (!(iir & UART_IIR_NO_INT))
			serial8250_handle_port(up, regs);
#endif	//LUCENT_MODEM
	/* FIXME! Was it really ours? */
	return IRQ_HANDLED;
}

#ifdef LUCENT_MODEM
static void agr_rs_interrupt (void)
{
  serial8250_interrupt(lt_modem_res.Irq,NULL,NULL);
}
#endif	//LUCENT_MODEM

#if defined(LUCENT_MODEM) && !defined(AGERE_SOFT_MODEM)
/* DSP interrupt service routine */
static irqreturn_t VMODEM_Hw_Int_Proc (int irq, void *dev_id, struct pt_regs * regs)
{
  unsigned long flags;

  save_flags(flags);
  lt_modem_ops.dsp_isr();
  restore_flags(flags);
	return IRQ_HANDLED;
}
#endif
/*
 * To support ISA shared interrupts, we need to have one interrupt
 * handler that ensures that the IRQ line has been deasserted
 * before returning.  Failing to do this will result in the IRQ
 * line being stuck active, and, since ISA irqs are edge triggered,
 * no more IRQs will be seen.
 */
#ifndef LUCENT_MODEM
static void serial_do_unlink(struct irq_info *i, struct uart_8250_port *up)
{
	spin_lock_irq(&i->lock);

	if (!list_empty(i->head)) {
		if (i->head == &up->list)
			i->head = i->head->next;
		list_del(&up->list);
	} else {
		BUG_ON(i->head != &up->list);
		i->head = NULL;
	}

	spin_unlock_irq(&i->lock);
}
#endif

#ifndef AGERE_SOFT_MODEM
static int serial_link_irq_chain(struct uart_8250_port *up)
{
#ifdef LUCENT_MODEM
	int ret = 0;
#ifndef AGERE_SOFT_MODEM
	void (*handler)(int, void *, struct pt_regs *);

	    handler = VMODEM_Hw_Int_Proc;

	    ret = request_irq(up->port.irq, handler, SA_INTERRUPT | SA_SHIRQ,
				 uart_config[up->port.type].name, &lt_modem_res.BaseAddress);
	    if (ret) {
	      int_hooked--;
	    }
#endif
#else
	struct irq_info *i = irq_lists + up->port.irq;
	int ret, irq_flags = up->port.flags & UPF_SHARE_IRQ ? SA_SHIRQ : 0;
	void (*handler)(int, void *, struct pt_regs *);

	spin_lock_irq(&i->lock);

	if (i->head) {
		list_add(&up->list, i->head);
		spin_unlock_irq(&i->lock);

		ret = 0;
	} else {
		INIT_LIST_HEAD(&up->list);
		i->head = &up->list;
		spin_unlock_irq(&i->lock);

		ret = request_irq(up->port.irq, serial8250_interrupt,
				  irq_flags, "serial", i);

		if (ret < 0)
			serial_do_unlink(i, up);
	}
#endif
	return ret;
}
#endif

static void serial_unlink_irq_chain(struct uart_8250_port *up)
{
#ifdef LUCENT_MODEM
#ifndef AGERE_SOFT_MODEM
  if ( int_hooked )
    free_irq(up->port.irq, &lt_modem_res.BaseAddress);
#endif
#else
	struct irq_info *i = irq_lists + up->port.irq;

	BUG_ON(i->head == NULL);

	if (list_empty(i->head))
		free_irq(up->port.irq, i);

	serial_do_unlink(i, up);
#endif
}

/*
 * This function is used to handle ports that do not have an
 * interrupt.  This doesn't work very well for 16450's, but gives
 * barely passable results for a 16550A.  (Although at the expense
 * of much CPU overhead).
 */
#ifndef LUCENT_MODEM
static void serial8250_timeout(unsigned long data)
{
	struct uart_8250_port *up = (struct uart_8250_port *)data;
	unsigned int timeout;
	unsigned int iir;

	iir = serial_in(up, UART_IIR);
	if (!(iir & UART_IIR_NO_INT)) {
		spin_lock(&up->port.lock);
		serial8250_handle_port(up, NULL);
		spin_unlock(&up->port.lock);
	}

	timeout = up->port.timeout;
	timeout = timeout > 6 ? (timeout / 2 - 2) : 1;
	mod_timer(&up->timer, jiffies + timeout);
}
#endif

static unsigned int serial8250_tx_empty(struct uart_port *port)
{
#ifdef LUCENT_MODEM
  tx_empty_flag = 1;
  return 0;
#else
	struct uart_8250_port *up = (struct uart_8250_port *)port;
	unsigned long flags;
	unsigned int ret;

	spin_lock_irqsave(&up->port.lock, flags);
	ret = serial_in(up, UART_LSR) & UART_LSR_TEMT ? TIOCSER_TEMT : 0;
	spin_unlock_irqrestore(&up->port.lock, flags);
	return ret;
#endif
}

static unsigned int serial8250_get_mctrl(struct uart_port *port)
{
	struct uart_8250_port *up = (struct uart_8250_port *)port;
	unsigned long flags;
	unsigned char status;
	unsigned int ret = 0;

	spin_lock_irqsave(&up->port.lock, flags);
	status = serial_in(up, UART_MSR);
	spin_unlock_irqrestore(&up->port.lock, flags);

#ifdef LUCENT_MODEM
  if (tx_empty_flag == 1)  {
  tx_empty_flag = 0;
  ret = TIOCM_CAR | TIOCM_DSR | TIOCM_CTS;
  }
  else  {
#endif
	if (status & UART_MSR_DCD)
		ret |= TIOCM_CAR;
	if (status & UART_MSR_RI)
		ret |= TIOCM_RNG;
	if (status & UART_MSR_DSR)
		ret |= TIOCM_DSR;
	if (status & UART_MSR_CTS)
		ret |= TIOCM_CTS;
#ifdef LUCENT_MODEM
  }
#endif
	return ret;
}

static void serial8250_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
	struct uart_8250_port *up = (struct uart_8250_port *)port;
	unsigned char mcr = 0;

	if (mctrl & TIOCM_RTS)
		mcr |= UART_MCR_RTS;
	if (mctrl & TIOCM_DTR)
		mcr |= UART_MCR_DTR;
	if (mctrl & TIOCM_OUT1)
		mcr |= UART_MCR_OUT1;
	if (mctrl & TIOCM_OUT2)
		mcr |= UART_MCR_OUT2;
	if (mctrl & TIOCM_LOOP)
		mcr |= UART_MCR_LOOP;

	mcr = (mcr & up->mcr_mask) | up->mcr_force;

	serial_out(up, UART_MCR, mcr);
}

static void serial8250_break_ctl(struct uart_port *port, int break_state)
{
	struct uart_8250_port *up = (struct uart_8250_port *)port;
	unsigned long flags;

	spin_lock_irqsave(&up->port.lock, flags);
	if (break_state == -1)
		up->lcr |= UART_LCR_SBC;
	else
		up->lcr &= ~UART_LCR_SBC;
	serial_out(up, UART_LCR, up->lcr);
	spin_unlock_irqrestore(&up->port.lock, flags);
}

static int serial8250_startup(struct uart_port *port)
{
	struct uart_8250_port *up = (struct uart_8250_port *)port;
	unsigned long flags;
#ifndef AGERE_SOFT_MODEM
	int retval;
#endif
#ifdef LUCENT_MODEM
	if (intf_flag == 0)
	{
    if (SetAgrModemInterface != NULL)
		  SetAgrModemInterface(1);
		intf_flag = 1;
	}

  if (!int_hooked)
	  {
	    int_hooked++;
#ifndef AGERE_SOFT_MODEM
 if (!is_real_interrupt(up->port.irq)) {
		unsigned int timeout = up->port.timeout;

		timeout = timeout > 6 ? (timeout / 2 - 2) : 1;

		up->timer.data = (unsigned long)up;
		mod_timer(&up->timer, jiffies + timeout);
	} else {
		retval = serial_link_irq_chain(up);
		if (retval)
			return retval;
	}
#endif
	    lt_modem_ops.PortOpen();
	  }
#endif
  
	up->capabilities = uart_config[up->port.type].flags;

	if (up->port.type == PORT_16C950) {
		/* Wake up and initialize UART */
		up->acr = 0;
		serial_outp(up, UART_LCR, 0xBF);
		serial_outp(up, UART_EFR, UART_EFR_ECB);
		serial_outp(up, UART_IER, 0);
		serial_outp(up, UART_LCR, 0);
		serial_icr_write(up, UART_CSR, 0); /* Reset the UART */
		serial_outp(up, UART_LCR, 0xBF);
		serial_outp(up, UART_EFR, UART_EFR_ECB);
		serial_outp(up, UART_LCR, 0);
	}

#ifdef CONFIG_SERIAL_8250_RSA
	/*
	 * If this is an RSA port, see if we can kick it up to the
	 * higher speed clock.
	 */
	enable_rsa(up);
#endif

	/*
	 * Clear the FIFO buffers and disable them.
	 * (they will be reeanbled in set_termios())
	 */
	if (up->capabilities & UART_CLEAR_FIFO) {
		serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO);
		serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO |
				UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
		serial_outp(up, UART_FCR, 0);
	}

	/*
	 * Clear the interrupt registers.
	 */
	(void) serial_inp(up, UART_LSR);
	(void) serial_inp(up, UART_RX);
	(void) serial_inp(up, UART_IIR);
	(void) serial_inp(up, UART_MSR);

	/*
	 * At this point, there's no way the LSR could still be 0xff;
	 * if it is, then bail out, because there's likely no UART
	 * here.
	 */
	if (!(up->port.flags & UPF_BUGGY_UART) &&
	    (serial_inp(up, UART_LSR) == 0xff)) {
		printk("ttyS%d: LSR safety check engaged!\n", up->port.line);
		return -ENODEV;
	}

	/*
	 * If the "interrupt" for this port doesn't correspond with any
	 * hardware interrupt, we use a timer-based system.  The original
	 * driver used to do this with IRQ0.
	 */
#ifndef LUCENT_MODEM
 if (!is_real_interrupt(up->port.irq)) {
		unsigned int timeout = up->port.timeout;

		timeout = timeout > 6 ? (timeout / 2 - 2) : 1;

		up->timer.data = (unsigned long)up;
		mod_timer(&up->timer, jiffies + timeout);
	} else {
		retval = serial_link_irq_chain(up);
		if (retval)
			return retval;
	}
#endif

	/*
	 * Now, initialize the UART
	 */
	serial_outp(up, UART_LCR, UART_LCR_WLEN8);

	spin_lock_irqsave(&up->port.lock, flags);
	if (up->port.flags & UPF_FOURPORT) {
		if (!is_real_interrupt(up->port.irq))
			up->port.mctrl |= TIOCM_OUT1;
	} else
		/*
		 * Most PC uarts need OUT2 raised to enable interrupts.
		 */
		if (is_real_interrupt(up->port.irq))
			up->port.mctrl |= TIOCM_OUT2;

	serial8250_set_mctrl(&up->port, up->port.mctrl);
	spin_unlock_irqrestore(&up->port.lock, flags);

	/*
	 * Finally, enable interrupts.  Note: Modem status interrupts
	 * are set via set_termios(), which will be occurring imminently
	 * anyway, so we don't enable them here.
	 */
	up->ier = UART_IER_RLSI | UART_IER_RDI;
	serial_outp(up, UART_IER, up->ier);

	if (up->port.flags & UPF_FOURPORT) {
		unsigned int icp;
		/*
		 * Enable interrupts on the AST Fourport board
		 */
		icp = (up->port.iobase & 0xfe0) | 0x01f;
		outb_p(0x80, icp);
		(void) inb_p(icp);
	}

	/*
	 * And clear the interrupt registers again for luck.
	 */
	(void) serial_inp(up, UART_LSR);
	(void) serial_inp(up, UART_RX);
	(void) serial_inp(up, UART_IIR);
	(void) serial_inp(up, UART_MSR);

	return 0;
}

static void serial8250_shutdown(struct uart_port *port)
{
	struct uart_8250_port *up = (struct uart_8250_port *)port;
	unsigned long flags;

	/*
	 * Disable interrupts from this port
	 */
	up->ier = 0;
	serial_outp(up, UART_IER, 0);

	spin_lock_irqsave(&up->port.lock, flags);
	if (up->port.flags & UPF_FOURPORT) {
		/* reset interrupts on the AST Fourport board */
		inb((up->port.iobase & 0xfe0) | 0x1f);
		up->port.mctrl |= TIOCM_OUT1;
	} else
		up->port.mctrl &= ~TIOCM_OUT2;

	serial8250_set_mctrl(&up->port, up->port.mctrl);
	spin_unlock_irqrestore(&up->port.lock, flags);

	/*
	 * Disable break condition and FIFOs
	 */
	serial_out(up, UART_LCR, serial_inp(up, UART_LCR) & ~UART_LCR_SBC);
	serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO |
				  UART_FCR_CLEAR_RCVR |
				  UART_FCR_CLEAR_XMIT);
	serial_outp(up, UART_FCR, 0);

#ifdef CONFIG_SERIAL_8250_RSA
	/*
	 * Reset the RSA board back to 115kbps compat mode.
	 */
	disable_rsa(up);
#endif

	/*
	 * Read data port to reset things, and then unlink from
	 * the IRQ chain.
	 */
	(void) serial_in(up, UART_RX);

#ifdef LUCENT_MODEM
	lt_modem_ops.PortClose () ;
	if (intf_flag == 1)
		intf_flag = 0;
#endif
#ifndef LUCENT_MODEM
	if (!is_real_interrupt(up->port.irq))
		del_timer_sync(&up->timer);
	else
#endif
		serial_unlink_irq_chain(up);
		int_hooked--;
}

static unsigned int serial8250_get_divisor(struct uart_port *port, unsigned int baud)
{
	unsigned int quot;

	/*
	 * Handle magic divisors for baud rates above baud_base on
	 * SMSC SuperIO chips.
	 */
	if ((port->flags & UPF_MAGIC_MULTIPLIER) &&
	    baud == (port->uartclk/4))
		quot = 0x8001;
	else if ((port->flags & UPF_MAGIC_MULTIPLIER) &&
		 baud == (port->uartclk/8))
		quot = 0x8002;
	else
		quot = uart_get_divisor(port, baud);

	return quot;
}

static void
serial8250_set_termios(struct uart_port *port, struct termios *termios,
		       struct termios *old)
{
	struct uart_8250_port *up = (struct uart_8250_port *)port;
	unsigned char cval, fcr = 0;
	unsigned long flags;
	unsigned int baud, quot;

	switch (termios->c_cflag & CSIZE) {
	case CS5:
		cval = 0x00;
		break;
	case CS6:
		cval = 0x01;
		break;
	case CS7:
		cval = 0x02;
		break;
	default:
	case CS8:
		cval = 0x03;
		break;
	}

	if (termios->c_cflag & CSTOPB)
		cval |= 0x04;
	if (termios->c_cflag & PARENB)
		cval |= UART_LCR_PARITY;
	if (!(termios->c_cflag & PARODD))
		cval |= UART_LCR_EPAR;
#ifdef CMSPAR
	if (termios->c_cflag & CMSPAR)
		cval |= UART_LCR_SPAR;
#endif

	/*
	 * Ask the core to calculate the divisor for us.
	 */
	baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); 
	quot = serial8250_get_divisor(port, baud);

	/*
	 * Work around a bug in the Oxford Semiconductor 952 rev B
	 * chip which causes it to seriously miscalculate baud rates
	 * when DLL is 0.
	 */
	if ((quot & 0xff) == 0 && up->port.type == PORT_16C950 &&
	    up->rev == 0x5201)
		quot ++;

	if (up->capabilities & UART_USE_FIFO) {
		if (baud < 2400)
			fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1;
#ifdef CONFIG_SERIAL_8250_RSA
		else if (up->port.type == PORT_RSA)
			fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_14;
#endif
		else
			fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_8;
	}
	if (up->port.type == PORT_16750)
		fcr |= UART_FCR7_64BYTE;

	/*
	 * Ok, we're now changing the port state.  Do it with
	 * interrupts disabled.
	 */
	spin_lock_irqsave(&up->port.lock, flags);

	/*
	 * Update the per-port timeout.
	 */
	uart_update_timeout(port, termios->c_cflag, baud);

	up->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR;
	if (termios->c_iflag & INPCK)
		up->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE;
	if (termios->c_iflag & (BRKINT | PARMRK))
		up->port.read_status_mask |= UART_LSR_BI;

	/*
	 * Characteres to ignore
	 */
	up->port.ignore_status_mask = 0;
	if (termios->c_iflag & IGNPAR)
		up->port.ignore_status_mask |= UART_LSR_PE | UART_LSR_FE;
	if (termios->c_iflag & IGNBRK) {
		up->port.ignore_status_mask |= UART_LSR_BI;
		/*
		 * If we're ignoring parity and break indicators,
		 * ignore overruns too (for real raw support).
		 */
		if (termios->c_iflag & IGNPAR)
			up->port.ignore_status_mask |= UART_LSR_OE;
	}

	/*
	 * ignore all characters if CREAD is not set
	 */
	if ((termios->c_cflag & CREAD) == 0)
		up->port.ignore_status_mask |= UART_LSR_DR;

	/*
	 * CTS flow control flag and modem status interrupts
	 */
	up->ier &= ~UART_IER_MSI;
	if (UART_ENABLE_MS(&up->port, termios->c_cflag))
		up->ier |= UART_IER_MSI;

	serial_out(up, UART_IER, up->ier);

	if (up->capabilities & UART_STARTECH) {
		serial_outp(up, UART_LCR, 0xBF);
		serial_outp(up, UART_EFR,
			    termios->c_cflag & CRTSCTS ? UART_EFR_CTS :0);
	}

	if (up->capabilities & UART_NATSEMI) {
		/* Switch to bank 2 not bank 1, to avoid resetting EXCR2 */
		serial_outp(up, UART_LCR, 0xe0);
	} else {
		serial_outp(up, UART_LCR, cval | UART_LCR_DLAB);/* set DLAB */
	}
	serial_outp(up, UART_DLL, quot & 0xff);		/* LS of divisor */
	serial_outp(up, UART_DLM, quot >> 8);		/* MS of divisor */
	if (up->port.type == PORT_16750)
		serial_outp(up, UART_FCR, fcr);		/* set fcr */
	serial_outp(up, UART_LCR, cval);		/* reset DLAB */
	up->lcr = cval;					/* Save LCR */
	if (up->port.type != PORT_16750) {
		if (fcr & UART_FCR_ENABLE_FIFO) {
			/* emulated UARTs (Lucent Venus 167x) need two steps */
			serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO);
		}
		serial_outp(up, UART_FCR, fcr);		/* set fcr */
	}
	spin_unlock_irqrestore(&up->port.lock, flags);
}

static void
serial8250_pm(struct uart_port *port, unsigned int state,
	      unsigned int oldstate)
{
	struct uart_8250_port *up = (struct uart_8250_port *)port;
	if (state) {
		/* sleep */
		if (up->capabilities & UART_STARTECH) {
			/* Arrange to enter sleep mode */
			serial_outp(up, UART_LCR, 0xBF);
			serial_outp(up, UART_EFR, UART_EFR_ECB);
			serial_outp(up, UART_LCR, 0);
			serial_outp(up, UART_IER, UART_IERX_SLEEP);
			serial_outp(up, UART_LCR, 0xBF);
			serial_outp(up, UART_EFR, 0);
			serial_outp(up, UART_LCR, 0);
		}
		if (up->port.type == PORT_16750) {
			/* Arrange to enter sleep mode */
			serial_outp(up, UART_IER, UART_IERX_SLEEP);
		}

		if (up->pm)
			up->pm(port, state, oldstate);
	} else {
		/* wake */
		if (up->capabilities & UART_STARTECH) {
			/* Wake up UART */
			serial_outp(up, UART_LCR, 0xBF);
			serial_outp(up, UART_EFR, UART_EFR_ECB);
			/*
			 * Turn off LCR == 0xBF so we actually set the IER
			 * register on the XR16C850
			 */
			serial_outp(up, UART_LCR, 0);
			serial_outp(up, UART_IER, 0);
			/*
			 * Now reset LCR so we can turn off the ECB bit
			 */
			serial_outp(up, UART_LCR, 0xBF);
			serial_outp(up, UART_EFR, 0);
			/*
			 * For a XR16C850, we need to set the trigger levels
			 */
			if (up->port.type == PORT_16850) {
				unsigned char fctr;

				fctr = serial_inp(up, UART_FCTR) &
					 ~(UART_FCTR_RX | UART_FCTR_TX);
				serial_outp(up, UART_FCTR, fctr |
						UART_FCTR_TRGD |
						UART_FCTR_RX);
				serial_outp(up, UART_TRG, UART_TRG_96);
				serial_outp(up, UART_FCTR, fctr |
						UART_FCTR_TRGD |
						UART_FCTR_TX);
				serial_outp(up, UART_TRG, UART_TRG_96);
			}
			serial_outp(up, UART_LCR, 0);
		}

		if (up->port.type == PORT_16750) {
			/* Wake up UART */
			serial_outp(up, UART_IER, 0);
		}

		if (up->pm)
			up->pm(port, state, oldstate);
	}
}

/*
 * Resource handling.  This is complicated by the fact that resources
 * depend on the port type.  Maybe we should be claiming the standard
 * 8250 ports, and then trying to get other resources as necessary?
 */
static int
serial8250_request_std_resource(struct uart_8250_port *up, struct resource **res)
{
	unsigned int size = 8 << up->port.regshift;
	int ret = 0;

	switch (up->port.iotype) {
	case SERIAL_IO_MEM:
		if (up->port.mapbase) {
			*res = request_mem_region(up->port.mapbase, size, "serial");
			if (!*res)
				ret = -EBUSY;
		}
		break;

	case SERIAL_IO_HUB6:
	case SERIAL_IO_PORT:
		*res = request_region(up->port.iobase, size, "serial");
		if (!*res)
			ret = -EBUSY;
		break;
	}
	return ret;
}

static int
serial8250_request_rsa_resource(struct uart_8250_port *up, struct resource **res)
{
	unsigned int size = 8 << up->port.regshift;
	unsigned long start;
	int ret = 0;

	switch (up->port.iotype) {
	case SERIAL_IO_MEM:
		if (up->port.mapbase) {
			start = up->port.mapbase;
			start += UART_RSA_BASE << up->port.regshift;
			*res = request_mem_region(start, size, "serial-rsa");
			if (!*res)
				ret = -EBUSY;
		}
		break;

	case SERIAL_IO_HUB6:
	case SERIAL_IO_PORT:
		start = up->port.iobase;
		start += UART_RSA_BASE << up->port.regshift;
		*res = request_region(start, size, "serial-rsa");
		if (!*res)
			ret = -EBUSY;
		break;
	}

	return ret;
}

static void serial8250_release_port(struct uart_port *port)
{
	struct uart_8250_port *up = (struct uart_8250_port *)port;
	unsigned long start, offset = 0, size = 0;

	if (up->port.type == PORT_RSA) {
		offset = UART_RSA_BASE << up->port.regshift;
		size = 8;
	}

	size <<= up->port.regshift;

	switch (up->port.iotype) {
	case SERIAL_IO_MEM:
		if (up->port.mapbase) {
			/*
			 * Unmap the area.
			 */
			iounmap(up->port.membase);
			up->port.membase = NULL;

			start = up->port.mapbase;

			if (size)
				release_mem_region(start + offset, size);
			release_mem_region(start, 8 << up->port.regshift);
		}
		break;

	case SERIAL_IO_HUB6:
	case SERIAL_IO_PORT:
#ifndef AGERE_SOFT_MODEM
		start = up->port.iobase;
		if (size)
			release_region(start + offset, size);
		release_region(start + offset, 8 << up->port.regshift);
#endif
		break;

	default:
		break;
	}
}

static int serial8250_request_port(struct uart_port *port)
{
	struct uart_8250_port *up = (struct uart_8250_port *)port;
	struct resource *res = NULL, *res_rsa = NULL;
	int ret = 0;

	if (up->port.flags & UPF_RESOURCES) {
		if (up->port.type == PORT_RSA) {
			ret = serial8250_request_rsa_resource(up, &res_rsa);
			if (ret < 0)
				return ret;
		}

		ret = serial8250_request_std_resource(up, &res);
	}

	/*
	 * If we have a mapbase, then request that as well.
	 */
	if (ret == 0 && up->port.flags & UPF_IOREMAP) {
		int size = res->end - res->start + 1;

		up->port.membase = ioremap(up->port.mapbase, size);
		if (!up->port.membase)
			ret = -ENOMEM;
	}

	if (ret < 0) {
		if (res_rsa)
			release_resource(res_rsa);
		if (res)
			release_resource(res);
	}
	return ret;
}

static void serial8250_config_port(struct uart_port *port, int flags)
{
	struct uart_8250_port *up = (struct uart_8250_port *)port;
	struct resource *res_std = NULL, *res_rsa = NULL;
	int probeflags = PROBE_ANY;
	int ret;

#ifdef CONFIG_MCA
	/*
	 * Don't probe for MCA ports on non-MCA machines.
	 */
	if (up->port.flags & UPF_BOOT_ONLYMCA && !MCA_bus)
		return;
#endif

	/*
	 * Find the region that we can probe for.  This in turn
	 * tells us whether we can probe for the type of port.
	 */
	if (up->port.flags & UPF_RESOURCES) {
		ret = serial8250_request_std_resource(up, &res_std);
		if (ret < 0)
			return;

		ret = serial8250_request_rsa_resource(up, &res_rsa);
		if (ret < 0)
			probeflags &= ~PROBE_RSA;
	} else {
		probeflags &= ~PROBE_RSA;
	}

	if (flags & UART_CONFIG_TYPE)
		autoconfig(up, probeflags);
	if (up->port.type != PORT_UNKNOWN && flags & UART_CONFIG_IRQ)
		autoconfig_irq(up);

	/*
	 * If the port wasn't an RSA port, release the resource.
	 */
	if (up->port.type != PORT_RSA && res_rsa)
		release_resource(res_rsa);

	if (up->port.type == PORT_UNKNOWN && res_std)
		release_resource(res_std);
}

static int
serial8250_verify_port(struct uart_port *port, struct serial_struct *ser)
{
	if (ser->irq >= NR_IRQS || ser->irq < 0 ||
	    ser->baud_base < 9600 || ser->type < PORT_UNKNOWN ||
#ifdef LUCENT_MODEM
	    ser->type > (PORT_MAX_8250 + 1) ||
#else
	    ser->type > PORT_MAX_8250 ||
#endif
      ser->type == PORT_CIRRUS ||
	    ser->type == PORT_STARTECH)
		return -EINVAL;
	return 0;
}

static const char *
serial8250_type(struct uart_port *port)
{
	int type = port->type;

	if (type >= ARRAY_SIZE(uart_config))
		type = 0;
	return uart_config[type].name;
}

static
int	serial8250_ioctl(struct uart_port *port, unsigned int cmd, unsigned long arg)
{
	switch (cmd) {
		case IOCTL_MODEM_APP_1:
		case IOCTL_MODEM_APP_2:
		case IOCTL_MODEM_APP_3:
		case IOCTL_MODEM_APP_4:
		case IOCTL_MODEM_APP_5:
		case IOCTL_MODEM_APP_6:
		case IOCTL_MODEM_APP_7:
		case IOCTL_MODEM_APP_8:
			lt_modem_ops.app_ioctl_handler(cmd,arg);
			return 0;
	}
	return -ENOIOCTLCMD;
}

static struct uart_ops serial8250_pops = {
	.tx_empty	= serial8250_tx_empty,
	.set_mctrl	= serial8250_set_mctrl,
	.get_mctrl	= serial8250_get_mctrl,
	.stop_tx	= serial8250_stop_tx,
	.start_tx	= serial8250_start_tx,
	.stop_rx	= serial8250_stop_rx,
	.enable_ms	= serial8250_enable_ms,
	.break_ctl	= serial8250_break_ctl,
	.startup	= serial8250_startup,
	.shutdown	= serial8250_shutdown,
	.ioctl		= serial8250_ioctl,
	.set_termios	= serial8250_set_termios,
	.pm		= serial8250_pm,
	.type		= serial8250_type,
	.release_port	= serial8250_release_port,
	.request_port	= serial8250_request_port,
	.config_port	= serial8250_config_port,
	.verify_port	= serial8250_verify_port,
};

#ifndef LUCENT_MODEM
static struct uart_8250_port serial8250_ports[UART_NR];

static void __init serial8250_isa_init_ports(void)
{
	struct uart_8250_port *up;
	static int first = 1;
	int i;

	if (!first)
		return;
	first = 0;

	for (i = 0, up = serial8250_ports; i < ARRAY_SIZE(old_serial_port);
	     i++, up++) {
		up->port.iobase   = old_serial_port[i].port;
		up->port.irq      = irq_canonicalize(old_serial_port[i].irq);
		up->port.uartclk  = old_serial_port[i].baud_base * 16;
		up->port.flags    = old_serial_port[i].flags |
				    UPF_RESOURCES;
		up->port.hub6     = old_serial_port[i].hub6;
		up->port.membase  = old_serial_port[i].iomem_base;
		up->port.iotype   = old_serial_port[i].io_type;
		up->port.regshift = old_serial_port[i].iomem_reg_shift;
		up->port.ops      = &serial8250_pops;
		if (share_irqs)
			up->port.flags |= UPF_SHARE_IRQ;
	}
}
#endif

#ifndef LUCENT_MODEM
static void __init serial8250_register_ports(struct uart_driver *drv)
#else
static int serial8250_register_ports(struct uart_driver *drv)
#endif
{
#ifndef LUCENT_MODEM
	int i;
	serial8250_isa_init_ports();

	for (i = 0; i < UART_NR; i++) {
		struct uart_8250_port *up = &serial8250_ports[i];

		up->port.line = i;
		up->port.ops = &serial8250_pops;
		init_timer(&up->timer);
		up->timer.function = serial8250_timeout;

		/*
		 * ALPHA_KLUDGE_MCR needs to be killed.
		 */
		up->mcr_mask = ~ALPHA_KLUDGE_MCR;
		up->mcr_force = ALPHA_KLUDGE_MCR;

		uart_add_one_port(drv, &up->port);
	}
#else
		struct uart_8250_port *up = &serial8250_ports[PORT_LT_MODEM_IF];
    int ret_val = 0;

    up->port.type     = PORT_LTMODEM;
    up->port.iobase   = lt_modem_res.BaseAddress;
    up->port.irq	    = irq_canonicalize(lt_modem_res.Irq);
    up->port.uartclk  = BASE_BAUD * 16;
    up->port.iotype   = UPIO_PORT;
    up->port.fifosize = uart_config[up->port.type].dfl_xmit_fifo_size;
    up->port.flags    = uart_config[up->port.type].flags;
    up->port.line     = PORT_LT_MODEM_IF;
    up->port.ops      = &serial8250_pops;

		/*
		 * ALPHA_KLUDGE_MCR needs to be killed.
		 */
		up->mcr_mask  = ~ALPHA_KLUDGE_MCR;
		up->mcr_force = ALPHA_KLUDGE_MCR;

	if ((ret_val = uart_add_one_port(drv, &up->port)) < 0)
  {
		printk(KERN_ERR"uart_add_one_port error code = %d\n", ret_val);
		printk(KERN_ERR"serial8250_register_ports unable to register port\n");
	}
  else
    uart_flag = 1;

  return (ret_val);
#endif
}

#ifdef CONFIG_SERIAL_8250_CONSOLE

#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE)

/*
 *	Wait for transmitter & holding register to empty
 */
static inline void wait_for_xmitr(struct uart_8250_port *up)
{
	unsigned int status, tmout = 10000;

	/* Wait up to 10ms for the character(s) to be sent. */
	do {
		status = serial_in(up, UART_LSR);

		if (status & UART_LSR_BI)
			up->lsr_break_flag = UART_LSR_BI;

		if (--tmout == 0)
			break;
		udelay(1);
	} while ((status & BOTH_EMPTY) != BOTH_EMPTY);

	/* Wait up to 1s for flow control if necessary */
	if (up->port.flags & UPF_CONS_FLOW) {
		tmout = 1000000;
		while (--tmout &&
		       ((serial_in(up, UART_MSR) & UART_MSR_CTS) == 0))
			udelay(1);
	}
}

/*
 *	Print a string to the serial port trying not to disturb
 *	any possible real use of the port...
 *
 *	The console_lock must be held when we get here.
 */
static void
serial8250_console_write(struct console *co, const char *s, unsigned int count)
{
	struct uart_8250_port *up = &serial8250_ports[co->index];
	unsigned int ier;
	int i;

	/*
	 *	First save the UER then disable the interrupts
	 */
	ier = serial_in(up, UART_IER);
	serial_out(up, UART_IER, 0);

	/*
	 *	Now, do each character
	 */
	for (i = 0; i < count; i++, s++) {
		wait_for_xmitr(up);

		/*
		 *	Send the character out.
		 *	If a LF, also do CR...
		 */
		serial_out(up, UART_TX, *s);
		if (*s == 10) {
			wait_for_xmitr(up);
			serial_out(up, UART_TX, 13);
		}
	}

	/*
	 *	Finally, wait for transmitter to become empty
	 *	and restore the IER
	 */
	wait_for_xmitr(up);
	serial_out(up, UART_IER, ier);
}

static int __init serial8250_console_setup(struct console *co, char *options)
{
	struct uart_port *port;
	int baud = 9600;
	int bits = 8;
	int parity = 'n';
	int flow = 'n';

	/*
	 * Check whether an invalid uart number has been specified, and
	 * if so, search for the first available port that does have
	 * console support.
	 */
	if (co->index >= UART_NR)
		co->index = 0;
	port = &serial8250_ports[co->index].port;

	/*
	 * Temporary fix.
	 */
	spin_lock_init(&port->lock);

	if (options)
		uart_parse_options(options, &baud, &parity, &bits, &flow);

	return uart_set_options(port, co, baud, parity, bits, flow);
}

extern struct uart_driver serial8250_reg;
static struct console serial8250_console = {
	.name		= "ttyS",
	.write		= serial8250_console_write,
	.device		= uart_console_device,
	.setup		= serial8250_console_setup,
	.flags		= CON_PRINTBUFFER,
	.index		= -1,
	.data		= &serial8250_reg,
};

static int __init serial8250_console_init(void)
{
	serial8250_isa_init_ports();
	register_console(&serial8250_console);
	return 0;
}
console_initcall(serial8250_console_init);

#define SERIAL8250_CONSOLE	&serial8250_console
#else
#define SERIAL8250_CONSOLE	NULL
#endif

static struct uart_driver serial8250_reg = {
//	.owner			= THIS_MODULE,
//	.driver_name		= "agserial",
#ifndef LUCENT_MODEM
	.owner			= THIS_MODULE,
	.driver_name		= "serial",
	.devfs_name		= "tts/",
	.dev_name		= "ttyS",
	.major			= TTY_MAJOR,
	.minor			= 64,
	.nr			= UART_NR,
	.cons			= SERIAL8250_CONSOLE,
#else
//	.devfs_name		= "tts/14",
//	.dev_name		= "ttyS14",
//	.major			= 62,
//	.minor			= 64,
//	.nr			= UART_NR,
//	.cons			= 0,
	.owner			= THIS_MODULE,
	.driver_name		= "agrserial",
	.devfs_name		= "tts/AGS",
	.dev_name		= "ttyAGS",
	.major			= 62,
	.minor			= 64,
	.nr			= UART_NR,
	.cons			= 0,
//  .state  = 0,
//  .tty_driver = 0,
#endif
};

#ifndef LUCENT_MODEM
/*
 * register_serial and unregister_serial allows for 16x50 serial ports to be
 * configured at run-time, to support PCMCIA modems.
 */

static int __register_serial(struct serial_struct *req, int line)
{
	struct uart_port port;

	port.iobase   = req->port;
	port.membase  = req->iomem_base;
	port.irq      = req->irq;
	port.uartclk  = req->baud_base * 16;
	port.fifosize = req->xmit_fifo_size;
	port.regshift = req->iomem_reg_shift;
	port.iotype   = req->io_type;
	port.flags    = req->flags | UPF_BOOT_AUTOCONF;
	port.mapbase  = req->iomap_base;
	port.line     = line;

	if (share_irqs)
		port.flags |= UPF_SHARE_IRQ;

	if (HIGH_BITS_OFFSET)
		port.iobase |= (long) req->port_high << HIGH_BITS_OFFSET;

	/*
	 * If a clock rate wasn't specified by the low level
	 * driver, then default to the standard clock rate.
	 */
	if (port.uartclk == 0)
		port.uartclk = BASE_BAUD * 16;

	return uart_register_port(&serial8250_reg, &port);
}

/**
 *	register_serial - configure a 16x50 serial port at runtime
 *	@req: request structure
 *
 *	Configure the serial port specified by the request. If the
 *	port exists and is in use an error is returned. If the port
 *	is not currently in the table it is added.
 *
 *	The port is then probed and if necessary the IRQ is autodetected
 *	If this fails an error is returned.
 *
 *	On success the port is ready to use and the line number is returned.
 */
int register_serial(struct serial_struct *req)
{
	return __register_serial(req, -1);
}

int __init early_serial_setup(struct uart_port *port)
{
	serial8250_isa_init_ports();
	serial8250_ports[port->line].port	= *port;
	serial8250_ports[port->line].port.ops	= &serial8250_pops;
	return 0;
}

/**
 *	unregister_serial - remove a 16x50 serial port at runtime
 *	@line: serial line number
 *
 *	Remove one serial port.  This may be called from interrupt
 *	context.
 */
void unregister_serial(int line)
{
	uart_unregister_port(&serial8250_reg, line);
}
#endif  //LUCENT_MODEM

/*
 * This is for ISAPNP only.
 */
void serial8250_get_irq_map(unsigned int *map)
{
	int i;

	for (i = 0; i < UART_NR; i++) {
		if (serial8250_ports[i].port.type != PORT_UNKNOWN &&
		    serial8250_ports[i].port.irq < 16)
			*map |= 1 << serial8250_ports[i].port.irq;
	}
}

/**
 *	serial8250_suspend_port - suspend one serial port
 *	@line: serial line number
 *
 *	Suspend one serial port.
 */
void serial8250_suspend_port(int line)
{
	uart_suspend_port(&serial8250_reg, &serial8250_ports[line].port);
}

/**
 *	serial8250_resume_port - resume one serial port
 *	@line: serial line number
 *
 *	Resume one serial port.
 */
void serial8250_resume_port(int line)
{
	uart_resume_port(&serial8250_reg, &serial8250_ports[line].port);
}

#ifdef LUCENT_MODEM
static int serial8250_init(void)
#else
static int __init serial8250_init(void)
#endif
{
#ifndef LUCENT_MODEM
	int ret, i;

  printk(KERN_INFO "Serial: 8250/16550 driver $Revision: 1.90 $ "
		"IRQ sharing %sabled\n", share_irqs ? "en" : "dis");
#else
	int ret;
#endif

#ifndef LUCENT_MODEM
	for (i = 0; i < NR_IRQS; i++)
		spin_lock_init(&irq_lists[i].lock);
#endif

	ret = uart_register_driver(&serial8250_reg);
	if (ret >= 0)  
#ifdef LUCENT_MODEM
  {
		ret = serial8250_register_ports(&serial8250_reg);
    if (ret < 0)  {
	    printk(KERN_INFO "serial8250_init: serial8250_register_ports returns err code %d\n", ret);
      uart_unregister_driver(&serial8250_reg);
     }
  }
  else
	  printk(KERN_INFO "serial8250_init: uart_register_driver returns err code %d\n", ret);
#else
		serial8250_register_ports(&serial8250_reg);
#endif // LUCENT_MODEM
	return ret;
}

static void __exit serial8250_exit(void)
{
#ifndef LUCENT_MODEM
	int i;

	for (i = 0; i < UART_NR; i++)
		uart_remove_one_port(&serial8250_reg, &serial8250_ports[i].port);
#else
	printk(KERN_INFO "Unloading %s: version %s\n", serialif_name, serialif_version);
  if (uart_flag == 1)
  {
    uart_remove_one_port(&serial8250_reg, &serial8250_ports[PORT_LT_MODEM_IF].port);
    uart_flag = 0;
  }
#endif
	uart_unregister_driver(&serial8250_reg);
#ifdef LUCENT_MODEM
	inter_module_unregister("agr_rs_interrupt");
  inter_module_put("SetAgrModemInterface");
  inter_module_put("GetAgrModemInterface");
  SetAgrModemInterface = NULL;
  GetAgrModemInterface = NULL;
#endif
}

#ifdef LUCENT_MODEM
int __init agr_init_module(void)
{
	int ret_val;

  GetAgrModemInterface = (int (*)(void *mdmdata))inter_module_get_request("GetAgrModemInterface","");
  
  if (GetAgrModemInterface != NULL)
  {
  	GetAgrModemInterface(&lt_modem_ops);
  	if (!(lt_modem_ops.detect_modem(&lt_modem_res)))
    {
      ret_val =  serial8250_init();
      if (ret_val < 0 )
        printk(KERN_ERR"agrserial - serial8250_init failed\n");
    }
	  else
    {
		  printk(KERN_ERR"agrserial - No device detected\n");
		  ret_val = -ENODEV;
    }

	  if (ret_val < 0 )
	  {
      inter_module_put("GetAgrModemInterface");
      GetAgrModemInterface = NULL;
	  }
	  else
	  {
		  lt_modem_ops.init_modem();
		  inter_module_register("agr_rs_interrupt", THIS_MODULE, agr_rs_interrupt);
      SetAgrModemInterface = (int (*)(int state))inter_module_get_request("SetAgrModemInterface","");
	  }
  }
  else
		ret_val = -ENODEV;

  if (ret_val >= 0)
    printk(KERN_INFO "Loading module %s version %s (%s)\n", serialif_name, serialif_version, serialif_revdate);
	return ret_val;
}

module_init(agr_init_module);
module_exit(serial8250_exit);
MODULE_AUTHOR("Agere Systems Inc");
MODULE_DESCRIPTION("Agere Modem Interface driver");
MODULE_LICENSE("GPL");
#else
module_init(serial8250_init);
module_exit(serial8250_exit);

EXPORT_SYMBOL(register_serial);
EXPORT_SYMBOL(unregister_serial);
EXPORT_SYMBOL(serial8250_get_irq_map);
EXPORT_SYMBOL(serial8250_suspend_port);
EXPORT_SYMBOL(serial8250_resume_port);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Generic 8250/16x50 serial driver $Revision: 1.90 $");

MODULE_PARM(share_irqs, "i");
MODULE_PARM_DESC(share_irqs, "Share IRQs with other non-8250/16x50 devices"
	" (unsafe)");
#endif

#if defined(CONFIG_SERIAL_8250_RSA) && defined(MODULE)
MODULE_PARM(probe_rsa, "1-" __MODULE_STRING(PORT_RSA_MAX) "i");
MODULE_PARM_DESC(probe_rsa, "Probe I/O ports for RSA");
MODULE_PARM(force_rsa, "1-" __MODULE_STRING(PORT_RSA_MAX) "i");
MODULE_PARM_DESC(force_rsa, "Force I/O ports for RSA");
#endif

[Index of Archives]     [Linux Media Development]     [Asterisk]     [DCCP]     [Netdev]     [X.org]     [Xfree86]     [Fedora Women]     [Linux USB]

  Powered by Linux