Browse Source

Merge git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty-2.6

* git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty-2.6: (68 commits)
  U6715 16550A serial driver support
  Char: nozomi, set tty->driver_data appropriately
  Char: nozomi, fix tty->count counting
  serial: max3107: Fix gpiolib support
  hsu: call PCI pm hooks in suspend/resume function
  hsu: some code cleanup
  hsu: add a periodic timer to check dma rx channel
  hsu: driver for Medfield High Speed UART device
  mxser: remove unnesesary NULL check
  serial: add support for OX16PCI958 card
  serial: 68328serial.c: remove dead (ALMA_ANS | DRAGONIXVZ | M68EZ328ADS)
  timbuart: use __devinit and __devexit macros for probe and remove
  serial: MMIO32 support for 8250_early.c
  serial: mcf: don't take spinlocks in already protected functions
  serial: general fixes in the serial_rs485 structure
  serial: fix missing bit coverage of ASYNC_FLAGS
  serial: "altera_uart: simplify altera_uart_console_putc()" checkpatch fixes
  serial: crisv10: formatting of pointers in printk()
  vt: Fix warning: statement with no effect due to vt_kern.h
  tty_io: remove casts from void*
  ...
Linus Torvalds 15 years ago
parent
commit
fc385c3132
100 changed files with 5296 additions and 626 deletions
  1. 3 0
      Documentation/kernel-parameters.txt
  2. 2 2
      arch/alpha/include/asm/ioctls.h
  3. 1 0
      arch/alpha/include/asm/termbits.h
  4. 2 0
      arch/arm/include/asm/ioctls.h
  5. 1 0
      arch/arm/include/asm/termbits.h
  6. 2 2
      arch/avr32/include/asm/ioctls.h
  7. 1 0
      arch/avr32/include/asm/termbits.h
  8. 2 2
      arch/cris/include/asm/ioctls.h
  9. 1 0
      arch/cris/include/asm/termbits.h
  10. 2 0
      arch/frv/include/asm/ioctls.h
  11. 1 0
      arch/frv/include/asm/termbits.h
  12. 2 0
      arch/h8300/include/asm/ioctls.h
  13. 1 0
      arch/h8300/include/asm/termbits.h
  14. 2 2
      arch/ia64/include/asm/ioctls.h
  15. 1 0
      arch/ia64/include/asm/termbits.h
  16. 2 2
      arch/m32r/include/asm/ioctls.h
  17. 1 0
      arch/m32r/include/asm/termbits.h
  18. 2 0
      arch/m68k/include/asm/ioctls.h
  19. 1 0
      arch/m68k/include/asm/termbits.h
  20. 2 3
      arch/mips/include/asm/ioctls.h
  21. 1 0
      arch/mips/include/asm/termbits.h
  22. 2 2
      arch/mn10300/include/asm/ioctls.h
  23. 1 0
      arch/mn10300/include/asm/termbits.h
  24. 2 2
      arch/parisc/include/asm/ioctls.h
  25. 1 0
      arch/parisc/include/asm/termbits.h
  26. 2 0
      arch/powerpc/include/asm/ioctls.h
  27. 1 0
      arch/powerpc/include/asm/termbits.h
  28. 2 0
      arch/s390/include/asm/ioctls.h
  29. 2 0
      arch/sh/include/asm/ioctls.h
  30. 2 0
      arch/sparc/include/asm/ioctls.h
  31. 1 0
      arch/sparc/include/asm/termbits.h
  32. 2 0
      arch/xtensa/include/asm/ioctls.h
  33. 1 0
      arch/xtensa/include/asm/termbits.h
  34. 1 0
      drivers/char/Makefile
  35. 17 8
      drivers/char/amiserial.c
  36. 3 3
      drivers/char/briq_panel.c
  37. 10 12
      drivers/char/cyclades.c
  38. 1 3
      drivers/char/epca.c
  39. 4 0
      drivers/char/ip2/ip2main.c
  40. 5 8
      drivers/char/isicom.c
  41. 38 30
      drivers/char/istallion.c
  42. 5 5
      drivers/char/keyboard.c
  43. 1 1
      drivers/char/mxser.c
  44. 0 1
      drivers/char/n_gsm.c
  45. 8 8
      drivers/char/n_hdlc.c
  46. 5 5
      drivers/char/n_r3964.c
  47. 14 3
      drivers/char/n_tty.c
  48. 3 1
      drivers/char/nozomi.c
  49. 34 13
      drivers/char/pty.c
  50. 8 6
      drivers/char/riscom8.c
  51. 19 9
      drivers/char/rocket.c
  52. 11 2
      drivers/char/selection.c
  53. 5 3
      drivers/char/serial167.c
  54. 7 6
      drivers/char/specialix.c
  55. 12 8
      drivers/char/stallion.c
  56. 6 6
      drivers/char/sx.c
  57. 12 9
      drivers/char/synclink.c
  58. 47 45
      drivers/char/synclink_gt.c
  59. 22 21
      drivers/char/synclinkmp.c
  60. 81 69
      drivers/char/tty_io.c
  61. 12 6
      drivers/char/tty_ioctl.c
  62. 30 13
      drivers/char/tty_ldisc.c
  63. 47 0
      drivers/char/tty_mutex.c
  64. 3 1
      drivers/char/tty_port.c
  65. 2 2
      drivers/char/vc_screen.c
  66. 23 14
      drivers/char/vt.c
  67. 10 7
      drivers/char/vt_ioctl.c
  68. 1 3
      drivers/gpu/drm/i915/intel_fb.c
  69. 1 0
      drivers/gpu/drm/nouveau/nouveau_fbcon.c
  70. 1 1
      drivers/gpu/drm/radeon/radeon_fb.c
  71. 3 7
      drivers/serial/21285.c
  72. 7 19
      drivers/serial/68328serial.c
  73. 2 2
      drivers/serial/68360serial.c
  74. 36 10
      drivers/serial/8250.c
  75. 42 15
      drivers/serial/8250_early.c
  76. 13 0
      drivers/serial/8250_pci.c
  77. 44 0
      drivers/serial/Kconfig
  78. 4 0
      drivers/serial/Makefile
  79. 1 1
      drivers/serial/altera_uart.c
  80. 8 3
      drivers/serial/atmel_serial.c
  81. 2 5
      drivers/serial/bfin_5xx.c
  82. 6 6
      drivers/serial/crisv10.c
  83. 4 6
      drivers/serial/imx.c
  84. 5 4
      drivers/serial/ioc3_serial.c
  85. 5 4
      drivers/serial/ioc4_serial.c
  86. 2 5
      drivers/serial/max3100.c
  87. 344 0
      drivers/serial/max3107-aava.c
  88. 1197 0
      drivers/serial/max3107.c
  89. 441 0
      drivers/serial/max3107.h
  90. 13 18
      drivers/serial/mcf.c
  91. 1498 0
      drivers/serial/mfd.c
  92. 844 0
      drivers/serial/mrst_max3110.c
  93. 59 0
      drivers/serial/mrst_max3110.h
  94. 136 152
      drivers/serial/serial_core.c
  95. 3 3
      drivers/serial/timbuart.c
  96. 3 9
      drivers/usb/class/cdc-acm.c
  97. 9 5
      drivers/usb/serial/digi_acceleport.c
  98. 3 1
      drivers/video/console/fbcon.c
  99. 0 2
      drivers/video/console/vgacon.c
  100. 1 0
      fs/compat_ioctl.c

+ 3 - 0
Documentation/kernel-parameters.txt

@@ -681,8 +681,11 @@ and is between 256 and 4096 characters. It is defined in the file
 	earlycon=	[KNL] Output early console device and options.
 	earlycon=	[KNL] Output early console device and options.
 		uart[8250],io,<addr>[,options]
 		uart[8250],io,<addr>[,options]
 		uart[8250],mmio,<addr>[,options]
 		uart[8250],mmio,<addr>[,options]
+		uart[8250],mmio32,<addr>[,options]
 			Start an early, polled-mode console on the 8250/16550
 			Start an early, polled-mode console on the 8250/16550
 			UART at the specified I/O port or MMIO address.
 			UART at the specified I/O port or MMIO address.
+			MMIO inter-register address stride is either 8bit (mmio)
+                        or 32bit (mmio32).
 			The options are the same as for ttyS, above.
 			The options are the same as for ttyS, above.
 
 
 	earlyprintk=	[X86,SH,BLACKFIN]
 	earlyprintk=	[X86,SH,BLACKFIN]

+ 2 - 2
arch/alpha/include/asm/ioctls.h

@@ -80,6 +80,7 @@
 # define TIOCPKT_START		 8
 # define TIOCPKT_START		 8
 # define TIOCPKT_NOSTOP		16
 # define TIOCPKT_NOSTOP		16
 # define TIOCPKT_DOSTOP		32
 # define TIOCPKT_DOSTOP		32
+# define TIOCPKT_IOCTL		64
 
 
 
 
 #define TIOCNOTTY	0x5422
 #define TIOCNOTTY	0x5422
@@ -91,6 +92,7 @@
 #define TIOCGSID	0x5429  /* Return the session ID of FD */
 #define TIOCGSID	0x5429  /* Return the session ID of FD */
 #define TIOCGPTN	_IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
 #define TIOCGPTN	_IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
 #define TIOCSPTLCK	_IOW('T',0x31, int)  /* Lock/unlock Pty */
 #define TIOCSPTLCK	_IOW('T',0x31, int)  /* Lock/unlock Pty */
+#define TIOCSIG		_IOW('T',0x36, int)  /* Generate signal on Pty slave */
 
 
 #define TIOCSERCONFIG	0x5453
 #define TIOCSERCONFIG	0x5453
 #define TIOCSERGWILD	0x5454
 #define TIOCSERGWILD	0x5454
@@ -106,7 +108,5 @@
 
 
 #define TIOCMIWAIT	0x545C	/* wait for a change on serial input line(s) */
 #define TIOCMIWAIT	0x545C	/* wait for a change on serial input line(s) */
 #define TIOCGICOUNT	0x545D	/* read serial port inline interrupt counts */
 #define TIOCGICOUNT	0x545D	/* read serial port inline interrupt counts */
-#define TIOCGHAYESESP	0x545E  /* Get Hayes ESP configuration */
-#define TIOCSHAYESESP	0x545F  /* Set Hayes ESP configuration */
 
 
 #endif /* _ASM_ALPHA_IOCTLS_H */
 #endif /* _ASM_ALPHA_IOCTLS_H */

+ 1 - 0
arch/alpha/include/asm/termbits.h

@@ -180,6 +180,7 @@ struct ktermios {
 #define FLUSHO	0x00800000
 #define FLUSHO	0x00800000
 #define PENDIN	0x20000000
 #define PENDIN	0x20000000
 #define IEXTEN	0x00000400
 #define IEXTEN	0x00000400
+#define EXTPROC	0x10000000
 
 
 /* Values for the ACTION argument to `tcflow'.  */
 /* Values for the ACTION argument to `tcflow'.  */
 #define	TCOOFF		0
 #define	TCOOFF		0

+ 2 - 0
arch/arm/include/asm/ioctls.h

@@ -52,6 +52,7 @@
 #define TCSETSF2	_IOW('T',0x2D, struct termios2)
 #define TCSETSF2	_IOW('T',0x2D, struct termios2)
 #define TIOCGPTN	_IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
 #define TIOCGPTN	_IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
 #define TIOCSPTLCK	_IOW('T',0x31, int)  /* Lock/unlock Pty */
 #define TIOCSPTLCK	_IOW('T',0x31, int)  /* Lock/unlock Pty */
+#define TIOCSIG		_IOW('T',0x36, int)  /* Generate signal on Pty slave */
 
 
 #define TIOCGRS485      0x542E
 #define TIOCGRS485      0x542E
 #define TIOCSRS485      0x542F
 #define TIOCSRS485      0x542F
@@ -81,6 +82,7 @@
 #define TIOCPKT_START		 8
 #define TIOCPKT_START		 8
 #define TIOCPKT_NOSTOP		16
 #define TIOCPKT_NOSTOP		16
 #define TIOCPKT_DOSTOP		32
 #define TIOCPKT_DOSTOP		32
+#define TIOCPKT_IOCTL		64
 
 
 #define TIOCSER_TEMT	0x01	/* Transmitter physically empty */
 #define TIOCSER_TEMT	0x01	/* Transmitter physically empty */
 
 

+ 1 - 0
arch/arm/include/asm/termbits.h

@@ -177,6 +177,7 @@ struct ktermios {
 #define FLUSHO	0010000
 #define FLUSHO	0010000
 #define PENDIN	0040000
 #define PENDIN	0040000
 #define IEXTEN	0100000
 #define IEXTEN	0100000
+#define EXTPROC	0200000
 
 
 /* tcflow() and TCXONC use these */
 /* tcflow() and TCXONC use these */
 #define	TCOOFF		0
 #define	TCOOFF		0

+ 2 - 2
arch/avr32/include/asm/ioctls.h

@@ -53,6 +53,7 @@
 #define TCSETSF2	_IOW('T',0x2D, struct termios2)
 #define TCSETSF2	_IOW('T',0x2D, struct termios2)
 #define TIOCGPTN	_IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
 #define TIOCGPTN	_IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
 #define TIOCSPTLCK	_IOW('T',0x31, int)  /* Lock/unlock Pty */
 #define TIOCSPTLCK	_IOW('T',0x31, int)  /* Lock/unlock Pty */
+#define TIOCSIG		_IOW('T',0x36, int)  /* Generate signal on Pty slave */
 
 
 #define TIOCGRS485      0x542E
 #define TIOCGRS485      0x542E
 #define TIOCSRS485      0x542F
 #define TIOCSRS485      0x542F
@@ -72,8 +73,6 @@
 
 
 #define TIOCMIWAIT	0x545C	/* wait for a change on serial input line(s) */
 #define TIOCMIWAIT	0x545C	/* wait for a change on serial input line(s) */
 #define TIOCGICOUNT	0x545D	/* read serial port inline interrupt counts */
 #define TIOCGICOUNT	0x545D	/* read serial port inline interrupt counts */
-#define TIOCGHAYESESP   0x545E  /* Get Hayes ESP configuration */
-#define TIOCSHAYESESP   0x545F  /* Set Hayes ESP configuration */
 #define FIOQSIZE	0x5460
 #define FIOQSIZE	0x5460
 
 
 /* Used for packet mode */
 /* Used for packet mode */
@@ -84,6 +83,7 @@
 #define TIOCPKT_START		 8
 #define TIOCPKT_START		 8
 #define TIOCPKT_NOSTOP		16
 #define TIOCPKT_NOSTOP		16
 #define TIOCPKT_DOSTOP		32
 #define TIOCPKT_DOSTOP		32
+#define TIOCPKT_IOCTL		64
 
 
 #define TIOCSER_TEMT    0x01	/* Transmitter physically empty */
 #define TIOCSER_TEMT    0x01	/* Transmitter physically empty */
 
 

+ 1 - 0
arch/avr32/include/asm/termbits.h

@@ -175,6 +175,7 @@ struct ktermios {
 #define FLUSHO	0010000
 #define FLUSHO	0010000
 #define PENDIN	0040000
 #define PENDIN	0040000
 #define IEXTEN	0100000
 #define IEXTEN	0100000
+#define EXTPROC	0200000
 
 
 /* tcflow() and TCXONC use these */
 /* tcflow() and TCXONC use these */
 #define	TCOOFF		0
 #define	TCOOFF		0

+ 2 - 2
arch/cris/include/asm/ioctls.h

@@ -54,6 +54,7 @@
 #define TCSETSF2	_IOW('T',0x2D, struct termios2)
 #define TCSETSF2	_IOW('T',0x2D, struct termios2)
 #define TIOCGPTN	_IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
 #define TIOCGPTN	_IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
 #define TIOCSPTLCK	_IOW('T',0x31, int)  /* Lock/unlock Pty */
 #define TIOCSPTLCK	_IOW('T',0x31, int)  /* Lock/unlock Pty */
+#define TIOCSIG		_IOW('T',0x36, int)  /* Generate signal on Pty slave */
 
 
 #define FIONCLEX	0x5450  /* these numbers need to be adjusted. */
 #define FIONCLEX	0x5450  /* these numbers need to be adjusted. */
 #define FIOCLEX		0x5451
 #define FIOCLEX		0x5451
@@ -70,8 +71,6 @@
 
 
 #define TIOCMIWAIT	0x545C	/* wait for a change on serial input line(s) */
 #define TIOCMIWAIT	0x545C	/* wait for a change on serial input line(s) */
 #define TIOCGICOUNT	0x545D	/* read serial port inline interrupt counts */
 #define TIOCGICOUNT	0x545D	/* read serial port inline interrupt counts */
-#define TIOCGHAYESESP   0x545E  /* Get Hayes ESP configuration */
-#define TIOCSHAYESESP   0x545F  /* Set Hayes ESP configuration */
 #define FIOQSIZE	0x5460
 #define FIOQSIZE	0x5460
 
 
 #define TIOCSERSETRS485	0x5461  /* enable rs-485 (deprecated) */
 #define TIOCSERSETRS485	0x5461  /* enable rs-485 (deprecated) */
@@ -87,6 +86,7 @@
 #define TIOCPKT_START		 8
 #define TIOCPKT_START		 8
 #define TIOCPKT_NOSTOP		16
 #define TIOCPKT_NOSTOP		16
 #define TIOCPKT_DOSTOP		32
 #define TIOCPKT_DOSTOP		32
+#define TIOCPKT_IOCTL		64
 
 
 #define TIOCSER_TEMT    0x01	/* Transmitter physically empty */
 #define TIOCSER_TEMT    0x01	/* Transmitter physically empty */
 
 

+ 1 - 0
arch/cris/include/asm/termbits.h

@@ -214,6 +214,7 @@ struct ktermios {
 #define FLUSHO	0010000
 #define FLUSHO	0010000
 #define PENDIN	0040000
 #define PENDIN	0040000
 #define IEXTEN	0100000
 #define IEXTEN	0100000
+#define EXTPROC	0200000
 
 
 /* tcflow() and TCXONC use these */
 /* tcflow() and TCXONC use these */
 #define	TCOOFF		0
 #define	TCOOFF		0

+ 2 - 0
arch/frv/include/asm/ioctls.h

@@ -53,6 +53,7 @@
 #define TCSETSF2	_IOW('T',0x2D, struct termios2)
 #define TCSETSF2	_IOW('T',0x2D, struct termios2)
 #define TIOCGPTN	_IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
 #define TIOCGPTN	_IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
 #define TIOCSPTLCK	_IOW('T',0x31, int)  /* Lock/unlock Pty */
 #define TIOCSPTLCK	_IOW('T',0x31, int)  /* Lock/unlock Pty */
+#define TIOCSIG		_IOW('T',0x36, int)  /* Generate signal on Pty slave */
 
 
 #define FIONCLEX	0x5450  /* these numbers need to be adjusted. */
 #define FIONCLEX	0x5450  /* these numbers need to be adjusted. */
 #define FIOCLEX		0x5451
 #define FIOCLEX		0x5451
@@ -79,6 +80,7 @@
 #define TIOCPKT_START		 8
 #define TIOCPKT_START		 8
 #define TIOCPKT_NOSTOP		16
 #define TIOCPKT_NOSTOP		16
 #define TIOCPKT_DOSTOP		32
 #define TIOCPKT_DOSTOP		32
+#define TIOCPKT_IOCTL		64
 
 
 #define TIOCSER_TEMT    0x01	/* Transmitter physically empty */
 #define TIOCSER_TEMT    0x01	/* Transmitter physically empty */
 
 

+ 1 - 0
arch/frv/include/asm/termbits.h

@@ -180,6 +180,7 @@ struct ktermios {
 #define FLUSHO	0010000
 #define FLUSHO	0010000
 #define PENDIN	0040000
 #define PENDIN	0040000
 #define IEXTEN	0100000
 #define IEXTEN	0100000
+#define EXTPROC	0200000
 
 
 
 
 /* tcflow() and TCXONC use these */
 /* tcflow() and TCXONC use these */

+ 2 - 0
arch/h8300/include/asm/ioctls.h

@@ -53,6 +53,7 @@
 #define TCSETSF2	_IOW('T',0x2D, struct termios2)
 #define TCSETSF2	_IOW('T',0x2D, struct termios2)
 #define TIOCGPTN	_IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
 #define TIOCGPTN	_IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
 #define TIOCSPTLCK	_IOW('T',0x31, int)  /* Lock/unlock Pty */
 #define TIOCSPTLCK	_IOW('T',0x31, int)  /* Lock/unlock Pty */
+#define TIOCSIG		_IOW('T',0x36, int)  /* Generate signal on Pty slave */
 
 
 #define FIONCLEX	0x5450  /* these numbers need to be adjusted. */
 #define FIONCLEX	0x5450  /* these numbers need to be adjusted. */
 #define FIOCLEX		0x5451
 #define FIOCLEX		0x5451
@@ -79,6 +80,7 @@
 #define TIOCPKT_START		 8
 #define TIOCPKT_START		 8
 #define TIOCPKT_NOSTOP		16
 #define TIOCPKT_NOSTOP		16
 #define TIOCPKT_DOSTOP		32
 #define TIOCPKT_DOSTOP		32
+#define TIOCPKT_IOCTL		64
 
 
 #define TIOCSER_TEMT    0x01	/* Transmitter physically empty */
 #define TIOCSER_TEMT    0x01	/* Transmitter physically empty */
 
 

+ 1 - 0
arch/h8300/include/asm/termbits.h

@@ -179,6 +179,7 @@ struct ktermios {
 #define FLUSHO	0010000
 #define FLUSHO	0010000
 #define PENDIN	0040000
 #define PENDIN	0040000
 #define IEXTEN	0100000
 #define IEXTEN	0100000
+#define EXTPROC	0200000
 
 
 
 
 /* tcflow() and TCXONC use these */
 /* tcflow() and TCXONC use these */

+ 2 - 2
arch/ia64/include/asm/ioctls.h

@@ -59,6 +59,7 @@
 #define TCSETSF2	_IOW('T',0x2D, struct termios2)
 #define TCSETSF2	_IOW('T',0x2D, struct termios2)
 #define TIOCGPTN	_IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
 #define TIOCGPTN	_IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
 #define TIOCSPTLCK	_IOW('T',0x31, int)  /* Lock/unlock Pty */
 #define TIOCSPTLCK	_IOW('T',0x31, int)  /* Lock/unlock Pty */
+#define TIOCSIG		_IOW('T',0x36, int)  /* Generate signal on Pty slave */
 
 
 #define FIONCLEX	0x5450  /* these numbers need to be adjusted. */
 #define FIONCLEX	0x5450  /* these numbers need to be adjusted. */
 #define FIOCLEX		0x5451
 #define FIOCLEX		0x5451
@@ -75,8 +76,6 @@
 
 
 #define TIOCMIWAIT	0x545C	/* wait for a change on serial input line(s) */
 #define TIOCMIWAIT	0x545C	/* wait for a change on serial input line(s) */
 #define TIOCGICOUNT	0x545D	/* read serial port inline interrupt counts */
 #define TIOCGICOUNT	0x545D	/* read serial port inline interrupt counts */
-#define TIOCGHAYESESP   0x545E  /* Get Hayes ESP configuration */
-#define TIOCSHAYESESP   0x545F  /* Set Hayes ESP configuration */
 #define FIOQSIZE	0x5460
 #define FIOQSIZE	0x5460
 
 
 /* Used for packet mode */
 /* Used for packet mode */
@@ -87,6 +86,7 @@
 #define TIOCPKT_START		 8
 #define TIOCPKT_START		 8
 #define TIOCPKT_NOSTOP		16
 #define TIOCPKT_NOSTOP		16
 #define TIOCPKT_DOSTOP		32
 #define TIOCPKT_DOSTOP		32
+#define TIOCPKT_IOCTL		64
 
 
 #define TIOCSER_TEMT    0x01	/* Transmitter physically empty */
 #define TIOCSER_TEMT    0x01	/* Transmitter physically empty */
 
 

+ 1 - 0
arch/ia64/include/asm/termbits.h

@@ -187,6 +187,7 @@ struct ktermios {
 #define FLUSHO	0010000
 #define FLUSHO	0010000
 #define PENDIN	0040000
 #define PENDIN	0040000
 #define IEXTEN	0100000
 #define IEXTEN	0100000
+#define EXTPROC	0200000
 
 
 /* tcflow() and TCXONC use these */
 /* tcflow() and TCXONC use these */
 #define	TCOOFF		0
 #define	TCOOFF		0

+ 2 - 2
arch/m32r/include/asm/ioctls.h

@@ -53,6 +53,7 @@
 #define TCSETSF2	_IOW('T',0x2D, struct termios2)
 #define TCSETSF2	_IOW('T',0x2D, struct termios2)
 #define TIOCGPTN	_IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
 #define TIOCGPTN	_IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
 #define TIOCSPTLCK	_IOW('T',0x31, int)  /* Lock/unlock Pty */
 #define TIOCSPTLCK	_IOW('T',0x31, int)  /* Lock/unlock Pty */
+#define TIOCSIG		_IOW('T',0x36, int)  /* Generate signal on Pty slave */
 
 
 #define FIONCLEX	0x5450
 #define FIONCLEX	0x5450
 #define FIOCLEX		0x5451
 #define FIOCLEX		0x5451
@@ -69,8 +70,6 @@
 
 
 #define TIOCMIWAIT	0x545C	/* wait for a change on serial input line(s) */
 #define TIOCMIWAIT	0x545C	/* wait for a change on serial input line(s) */
 #define TIOCGICOUNT	0x545D	/* read serial port inline interrupt counts */
 #define TIOCGICOUNT	0x545D	/* read serial port inline interrupt counts */
-#define TIOCGHAYESESP   0x545E  /* Get Hayes ESP configuration */
-#define TIOCSHAYESESP   0x545F  /* Set Hayes ESP configuration */
 #define FIOQSIZE	0x5460
 #define FIOQSIZE	0x5460
 
 
 /* Used for packet mode */
 /* Used for packet mode */
@@ -81,6 +80,7 @@
 #define TIOCPKT_START		 8
 #define TIOCPKT_START		 8
 #define TIOCPKT_NOSTOP		16
 #define TIOCPKT_NOSTOP		16
 #define TIOCPKT_DOSTOP		32
 #define TIOCPKT_DOSTOP		32
+#define TIOCPKT_IOCTL		64
 
 
 #define TIOCSER_TEMT    0x01	/* Transmitter physically empty */
 #define TIOCSER_TEMT    0x01	/* Transmitter physically empty */
 
 

+ 1 - 0
arch/m32r/include/asm/termbits.h

@@ -179,6 +179,7 @@ struct ktermios {
 #define FLUSHO	0010000
 #define FLUSHO	0010000
 #define PENDIN	0040000
 #define PENDIN	0040000
 #define IEXTEN	0100000
 #define IEXTEN	0100000
+#define EXTPROC	0200000
 
 
 /* tcflow() and TCXONC use these */
 /* tcflow() and TCXONC use these */
 #define	TCOOFF		0
 #define	TCOOFF		0

+ 2 - 0
arch/m68k/include/asm/ioctls.h

@@ -52,6 +52,7 @@
 #define TCSETSF2	_IOW('T',0x2D, struct termios2)
 #define TCSETSF2	_IOW('T',0x2D, struct termios2)
 #define TIOCGPTN	_IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
 #define TIOCGPTN	_IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
 #define TIOCSPTLCK	_IOW('T',0x31, int)  /* Lock/unlock Pty */
 #define TIOCSPTLCK	_IOW('T',0x31, int)  /* Lock/unlock Pty */
+#define TIOCSIG		_IOW('T',0x36, int)  /* Generate signal on Pty slave */
 
 
 #define FIONCLEX	0x5450  /* these numbers need to be adjusted. */
 #define FIONCLEX	0x5450  /* these numbers need to be adjusted. */
 #define FIOCLEX		0x5451
 #define FIOCLEX		0x5451
@@ -78,6 +79,7 @@
 #define TIOCPKT_START		 8
 #define TIOCPKT_START		 8
 #define TIOCPKT_NOSTOP		16
 #define TIOCPKT_NOSTOP		16
 #define TIOCPKT_DOSTOP		32
 #define TIOCPKT_DOSTOP		32
+#define TIOCPKT_IOCTL		64
 
 
 #define TIOCSER_TEMT    0x01	/* Transmitter physically empty */
 #define TIOCSER_TEMT    0x01	/* Transmitter physically empty */
 
 

+ 1 - 0
arch/m68k/include/asm/termbits.h

@@ -179,6 +179,7 @@ struct ktermios {
 #define FLUSHO	0010000
 #define FLUSHO	0010000
 #define PENDIN	0040000
 #define PENDIN	0040000
 #define IEXTEN	0100000
 #define IEXTEN	0100000
+#define EXTPROC	0200000
 
 
 
 
 /* tcflow() and TCXONC use these */
 /* tcflow() and TCXONC use these */

+ 2 - 3
arch/mips/include/asm/ioctls.h

@@ -41,7 +41,7 @@
 #define	 TIOCPKT_START		0x08	/* start output */
 #define	 TIOCPKT_START		0x08	/* start output */
 #define	 TIOCPKT_NOSTOP		0x10	/* no more ^S, ^Q */
 #define	 TIOCPKT_NOSTOP		0x10	/* no more ^S, ^Q */
 #define	 TIOCPKT_DOSTOP		0x20	/* now do ^S ^Q */
 #define	 TIOCPKT_DOSTOP		0x20	/* now do ^S ^Q */
-/* #define  TIOCPKT_IOCTL		0x40	state change of pty driver */
+#define  TIOCPKT_IOCTL		0x40	/* state change of pty driver */
 #define TIOCSWINSZ	_IOW('t', 103, struct winsize)	/* set window size */
 #define TIOCSWINSZ	_IOW('t', 103, struct winsize)	/* set window size */
 #define TIOCGWINSZ	_IOR('t', 104, struct winsize)	/* get window size */
 #define TIOCGWINSZ	_IOR('t', 104, struct winsize)	/* get window size */
 #define TIOCNOTTY	0x5471		/* void tty association */
 #define TIOCNOTTY	0x5471		/* void tty association */
@@ -83,6 +83,7 @@
 #define TCSETSF2	_IOW('T', 0x2D, struct termios2)
 #define TCSETSF2	_IOW('T', 0x2D, struct termios2)
 #define TIOCGPTN	_IOR('T', 0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
 #define TIOCGPTN	_IOR('T', 0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
 #define TIOCSPTLCK	_IOW('T', 0x31, int)  /* Lock/unlock Pty */
 #define TIOCSPTLCK	_IOW('T', 0x31, int)  /* Lock/unlock Pty */
+#define TIOCSIG		_IOW('T', 0x36, int)  /* Generate signal on Pty slave */
 
 
 /* I hope the range from 0x5480 on is free ... */
 /* I hope the range from 0x5480 on is free ... */
 #define TIOCSCTTY	0x5480		/* become controlling tty */
 #define TIOCSCTTY	0x5480		/* become controlling tty */
@@ -103,7 +104,5 @@
 #define TIOCSERSETMULTI 0x5490 /* Set multiport config */
 #define TIOCSERSETMULTI 0x5490 /* Set multiport config */
 #define TIOCMIWAIT      0x5491 /* wait for a change on serial input line(s) */
 #define TIOCMIWAIT      0x5491 /* wait for a change on serial input line(s) */
 #define TIOCGICOUNT     0x5492 /* read serial port inline interrupt counts */
 #define TIOCGICOUNT     0x5492 /* read serial port inline interrupt counts */
-#define TIOCGHAYESESP	0x5493 /* Get Hayes ESP configuration */
-#define TIOCSHAYESESP	0x5494 /* Set Hayes ESP configuration */
 
 
 #endif /* __ASM_IOCTLS_H */
 #endif /* __ASM_IOCTLS_H */

+ 1 - 0
arch/mips/include/asm/termbits.h

@@ -203,6 +203,7 @@ struct ktermios {
 #define PENDIN	0040000		/* Retype pending input (state).  */
 #define PENDIN	0040000		/* Retype pending input (state).  */
 #define TOSTOP	0100000		/* Send SIGTTOU for background output.  */
 #define TOSTOP	0100000		/* Send SIGTTOU for background output.  */
 #define ITOSTOP	TOSTOP
 #define ITOSTOP	TOSTOP
+#define EXTPROC	0200000		/* External processing on pty */
 
 
 /* ioctl (fd, TIOCSERGETLSR, &result) where result may be as below */
 /* ioctl (fd, TIOCSERGETLSR, &result) where result may be as below */
 #define TIOCSER_TEMT    0x01	/* Transmitter physically empty */
 #define TIOCSER_TEMT    0x01	/* Transmitter physically empty */

+ 2 - 2
arch/mn10300/include/asm/ioctls.h

@@ -54,6 +54,7 @@
 #define TIOCGPTN	_IOR('T', 0x30, unsigned int) /* Get Pty Number
 #define TIOCGPTN	_IOR('T', 0x30, unsigned int) /* Get Pty Number
 						       * (of pty-mux device) */
 						       * (of pty-mux device) */
 #define TIOCSPTLCK	_IOW('T', 0x31, int)  /* Lock/unlock Pty */
 #define TIOCSPTLCK	_IOW('T', 0x31, int)  /* Lock/unlock Pty */
+#define TIOCSIG		_IOW('T', 0x36, int)  /* Generate signal on Pty slave */
 
 
 #define FIONCLEX	0x5450
 #define FIONCLEX	0x5450
 #define FIOCLEX		0x5451
 #define FIOCLEX		0x5451
@@ -70,8 +71,6 @@
 
 
 #define TIOCMIWAIT	0x545C	/* wait for a change on serial input line(s) */
 #define TIOCMIWAIT	0x545C	/* wait for a change on serial input line(s) */
 #define TIOCGICOUNT	0x545D	/* read serial port inline interrupt counts */
 #define TIOCGICOUNT	0x545D	/* read serial port inline interrupt counts */
-#define TIOCGHAYESESP   0x545E  /* Get Hayes ESP configuration */
-#define TIOCSHAYESESP   0x545F  /* Set Hayes ESP configuration */
 #define FIOQSIZE	0x5460
 #define FIOQSIZE	0x5460
 
 
 /* Used for packet mode */
 /* Used for packet mode */
@@ -82,6 +81,7 @@
 #define TIOCPKT_START		 8
 #define TIOCPKT_START		 8
 #define TIOCPKT_NOSTOP		16
 #define TIOCPKT_NOSTOP		16
 #define TIOCPKT_DOSTOP		32
 #define TIOCPKT_DOSTOP		32
+#define TIOCPKT_IOCTL		64
 
 
 #define TIOCSER_TEMT    0x01	/* Transmitter physically empty */
 #define TIOCSER_TEMT    0x01	/* Transmitter physically empty */
 
 

+ 1 - 0
arch/mn10300/include/asm/termbits.h

@@ -180,6 +180,7 @@ struct ktermios {
 #define FLUSHO	0010000
 #define FLUSHO	0010000
 #define PENDIN	0040000
 #define PENDIN	0040000
 #define IEXTEN	0100000
 #define IEXTEN	0100000
+#define EXTPROC	0200000
 
 
 /* tcflow() and TCXONC use these */
 /* tcflow() and TCXONC use these */
 #define	TCOOFF		0
 #define	TCOOFF		0

+ 2 - 2
arch/parisc/include/asm/ioctls.h

@@ -52,6 +52,7 @@
 #define TCSETSF2	_IOW('T',0x2D, struct termios2)
 #define TCSETSF2	_IOW('T',0x2D, struct termios2)
 #define TIOCGPTN	_IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
 #define TIOCGPTN	_IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
 #define TIOCSPTLCK	_IOW('T',0x31, int)  /* Lock/unlock Pty */
 #define TIOCSPTLCK	_IOW('T',0x31, int)  /* Lock/unlock Pty */
+#define TIOCSIG		_IOW('T',0x36, int)  /* Generate signal on Pty slave */
 
 
 #define FIONCLEX	0x5450  /* these numbers need to be adjusted. */
 #define FIONCLEX	0x5450  /* these numbers need to be adjusted. */
 #define FIOCLEX		0x5451
 #define FIOCLEX		0x5451
@@ -68,8 +69,6 @@
 
 
 #define TIOCMIWAIT	0x545C	/* wait for a change on serial input line(s) */
 #define TIOCMIWAIT	0x545C	/* wait for a change on serial input line(s) */
 #define TIOCGICOUNT	0x545D	/* read serial port inline interrupt counts */
 #define TIOCGICOUNT	0x545D	/* read serial port inline interrupt counts */
-#define TIOCGHAYESESP   0x545E  /* Get Hayes ESP configuration */
-#define TIOCSHAYESESP   0x545F  /* Set Hayes ESP configuration */
 #define FIOQSIZE	0x5460	/* Get exact space used by quota */
 #define FIOQSIZE	0x5460	/* Get exact space used by quota */
 
 
 #define TIOCSTART	0x5461
 #define TIOCSTART	0x5461
@@ -84,6 +83,7 @@
 #define TIOCPKT_START		 8
 #define TIOCPKT_START		 8
 #define TIOCPKT_NOSTOP		16
 #define TIOCPKT_NOSTOP		16
 #define TIOCPKT_DOSTOP		32
 #define TIOCPKT_DOSTOP		32
+#define TIOCPKT_IOCTL		64
 
 
 #define TIOCSER_TEMT    0x01	/* Transmitter physically empty */
 #define TIOCSER_TEMT    0x01	/* Transmitter physically empty */
 
 

+ 1 - 0
arch/parisc/include/asm/termbits.h

@@ -180,6 +180,7 @@ struct ktermios {
 #define FLUSHO  0010000
 #define FLUSHO  0010000
 #define PENDIN  0040000
 #define PENDIN  0040000
 #define IEXTEN  0100000
 #define IEXTEN  0100000
+#define EXTPROC	0200000
 
 
 /* tcflow() and TCXONC use these */
 /* tcflow() and TCXONC use these */
 #define	TCOOFF		0
 #define	TCOOFF		0

+ 2 - 0
arch/powerpc/include/asm/ioctls.h

@@ -80,6 +80,7 @@
 # define TIOCPKT_START		 8
 # define TIOCPKT_START		 8
 # define TIOCPKT_NOSTOP		16
 # define TIOCPKT_NOSTOP		16
 # define TIOCPKT_DOSTOP		32
 # define TIOCPKT_DOSTOP		32
+# define TIOCPKT_IOCTL		64
 
 
 
 
 #define TIOCNOTTY	0x5422
 #define TIOCNOTTY	0x5422
@@ -93,6 +94,7 @@
 #define TIOCSRS485	0x542f
 #define TIOCSRS485	0x542f
 #define TIOCGPTN	_IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
 #define TIOCGPTN	_IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
 #define TIOCSPTLCK	_IOW('T',0x31, int)  /* Lock/unlock Pty */
 #define TIOCSPTLCK	_IOW('T',0x31, int)  /* Lock/unlock Pty */
+#define TIOCSIG		_IOW('T',0x36, int)  /* Generate signal on Pty slave */
 
 
 #define TIOCSERCONFIG	0x5453
 #define TIOCSERCONFIG	0x5453
 #define TIOCSERGWILD	0x5454
 #define TIOCSERGWILD	0x5454

+ 1 - 0
arch/powerpc/include/asm/termbits.h

@@ -189,6 +189,7 @@ struct ktermios {
 #define FLUSHO	0x00800000
 #define FLUSHO	0x00800000
 #define PENDIN	0x20000000
 #define PENDIN	0x20000000
 #define IEXTEN	0x00000400
 #define IEXTEN	0x00000400
+#define EXTPROC	0x10000000
 
 
 /* Values for the ACTION argument to `tcflow'.  */
 /* Values for the ACTION argument to `tcflow'.  */
 #define	TCOOFF		0
 #define	TCOOFF		0

+ 2 - 0
arch/s390/include/asm/ioctls.h

@@ -60,6 +60,7 @@
 #define TCSETSF2	_IOW('T',0x2D, struct termios2)
 #define TCSETSF2	_IOW('T',0x2D, struct termios2)
 #define TIOCGPTN	_IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
 #define TIOCGPTN	_IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
 #define TIOCSPTLCK	_IOW('T',0x31, int)  /* Lock/unlock Pty */
 #define TIOCSPTLCK	_IOW('T',0x31, int)  /* Lock/unlock Pty */
+#define TIOCSIG		_IOW('T',0x36, int)  /* Generate signal on Pty slave */
 
 
 #define FIONCLEX	0x5450  /* these numbers need to be adjusted. */
 #define FIONCLEX	0x5450  /* these numbers need to be adjusted. */
 #define FIOCLEX		0x5451
 #define FIOCLEX		0x5451
@@ -86,6 +87,7 @@
 #define TIOCPKT_START		 8
 #define TIOCPKT_START		 8
 #define TIOCPKT_NOSTOP		16
 #define TIOCPKT_NOSTOP		16
 #define TIOCPKT_DOSTOP		32
 #define TIOCPKT_DOSTOP		32
+#define TIOCPKT_IOCTL		64
 
 
 #define TIOCSER_TEMT    0x01	/* Transmitter physically empty */
 #define TIOCSER_TEMT    0x01	/* Transmitter physically empty */
 
 

+ 2 - 0
arch/sh/include/asm/ioctls.h

@@ -69,6 +69,7 @@
 # define TIOCPKT_START		 8
 # define TIOCPKT_START		 8
 # define TIOCPKT_NOSTOP		16
 # define TIOCPKT_NOSTOP		16
 # define TIOCPKT_DOSTOP		32
 # define TIOCPKT_DOSTOP		32
+# define TIOCPKT_IOCTL		64
 
 
 
 
 #define TIOCNOTTY	_IO('T', 34) /* 0x5422 */
 #define TIOCNOTTY	_IO('T', 34) /* 0x5422 */
@@ -84,6 +85,7 @@
 #define TCSETSF2	_IOW('T', 45, struct termios2)
 #define TCSETSF2	_IOW('T', 45, struct termios2)
 #define TIOCGPTN	_IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
 #define TIOCGPTN	_IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
 #define TIOCSPTLCK	_IOW('T',0x31, int)  /* Lock/unlock Pty */
 #define TIOCSPTLCK	_IOW('T',0x31, int)  /* Lock/unlock Pty */
+#define TIOCSIG		_IOW('T',0x36, int)  /* Generate signal on Pty slave */
 
 
 #define TIOCSERCONFIG	_IO('T', 83) /* 0x5453 */
 #define TIOCSERCONFIG	_IO('T', 83) /* 0x5453 */
 #define TIOCSERGWILD	_IOR('T', 84,  int) /* 0x5454 */
 #define TIOCSERGWILD	_IOR('T', 84,  int) /* 0x5454 */

+ 2 - 0
arch/sparc/include/asm/ioctls.h

@@ -80,6 +80,7 @@
 /* Get minor device of a pty master's FD -- Solaris equiv is ISPTM */
 /* Get minor device of a pty master's FD -- Solaris equiv is ISPTM */
 #define TIOCGPTN	_IOR('t', 134, unsigned int) /* Get Pty Number */
 #define TIOCGPTN	_IOR('t', 134, unsigned int) /* Get Pty Number */
 #define TIOCSPTLCK	_IOW('t', 135, int) /* Lock/unlock PTY */
 #define TIOCSPTLCK	_IOW('t', 135, int) /* Lock/unlock PTY */
+#define TIOCSIG		_IOW('t', 136, int) /* Generate signal on Pty slave */
 
 
 /* Little f */
 /* Little f */
 #define FIOCLEX		_IO('f', 1)
 #define FIOCLEX		_IO('f', 1)
@@ -132,5 +133,6 @@
 #define TIOCPKT_START		 8
 #define TIOCPKT_START		 8
 #define TIOCPKT_NOSTOP		16
 #define TIOCPKT_NOSTOP		16
 #define TIOCPKT_DOSTOP		32
 #define TIOCPKT_DOSTOP		32
+#define TIOCPKT_IOCTL		64
 
 
 #endif /* !(_ASM_SPARC_IOCTLS_H) */
 #endif /* !(_ASM_SPARC_IOCTLS_H) */

+ 1 - 0
arch/sparc/include/asm/termbits.h

@@ -225,6 +225,7 @@ struct ktermios {
 #define FLUSHO	0x00002000
 #define FLUSHO	0x00002000
 #define PENDIN	0x00004000
 #define PENDIN	0x00004000
 #define IEXTEN	0x00008000
 #define IEXTEN	0x00008000
+#define EXTPROC	0x00010000
 
 
 /* modem lines */
 /* modem lines */
 #define TIOCM_LE	0x001
 #define TIOCM_LE	0x001

+ 2 - 0
arch/xtensa/include/asm/ioctls.h

@@ -81,6 +81,7 @@
 # define TIOCPKT_START		 8
 # define TIOCPKT_START		 8
 # define TIOCPKT_NOSTOP		16
 # define TIOCPKT_NOSTOP		16
 # define TIOCPKT_DOSTOP		32
 # define TIOCPKT_DOSTOP		32
+# define TIOCPKT_IOCTL		64
 
 
 
 
 #define TIOCNOTTY	_IO('T', 34)
 #define TIOCNOTTY	_IO('T', 34)
@@ -97,6 +98,7 @@
 #define TCSETSF2	_IOW('T', 45, struct termios2)
 #define TCSETSF2	_IOW('T', 45, struct termios2)
 #define TIOCGPTN	_IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
 #define TIOCGPTN	_IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
 #define TIOCSPTLCK	_IOW('T',0x31, int)  /* Lock/unlock Pty */
 #define TIOCSPTLCK	_IOW('T',0x31, int)  /* Lock/unlock Pty */
+#define TIOCSIG		_IOW('T',0x36, int)  /* Generate signal on Pty slave */
 
 
 #define TIOCSERCONFIG	_IO('T', 83)
 #define TIOCSERCONFIG	_IO('T', 83)
 #define TIOCSERGWILD	_IOR('T', 84,  int)
 #define TIOCSERGWILD	_IOR('T', 84,  int)

+ 1 - 0
arch/xtensa/include/asm/termbits.h

@@ -196,6 +196,7 @@ struct ktermios {
 #define FLUSHO	0010000
 #define FLUSHO	0010000
 #define PENDIN	0040000
 #define PENDIN	0040000
 #define IEXTEN	0100000
 #define IEXTEN	0100000
+#define EXTPROC	0200000
 
 
 /* tcflow() and TCXONC use these */
 /* tcflow() and TCXONC use these */
 
 

+ 1 - 0
drivers/char/Makefile

@@ -9,6 +9,7 @@ FONTMAPFILE = cp437.uni
 
 
 obj-y	 += mem.o random.o tty_io.o n_tty.o tty_ioctl.o tty_ldisc.o tty_buffer.o tty_port.o
 obj-y	 += mem.o random.o tty_io.o n_tty.o tty_ioctl.o tty_ldisc.o tty_buffer.o tty_port.o
 
 
+obj-y				+= tty_mutex.o
 obj-$(CONFIG_LEGACY_PTYS)	+= pty.o
 obj-$(CONFIG_LEGACY_PTYS)	+= pty.o
 obj-$(CONFIG_UNIX98_PTYS)	+= pty.o
 obj-$(CONFIG_UNIX98_PTYS)	+= pty.o
 obj-y				+= misc.o
 obj-y				+= misc.o

+ 17 - 8
drivers/char/amiserial.c

@@ -1072,7 +1072,7 @@ static int get_serial_info(struct async_struct * info,
 	if (!retinfo)
 	if (!retinfo)
 		return -EFAULT;
 		return -EFAULT;
 	memset(&tmp, 0, sizeof(tmp));
 	memset(&tmp, 0, sizeof(tmp));
-	lock_kernel();
+	tty_lock();
 	tmp.type = state->type;
 	tmp.type = state->type;
 	tmp.line = state->line;
 	tmp.line = state->line;
 	tmp.port = state->port;
 	tmp.port = state->port;
@@ -1083,7 +1083,7 @@ static int get_serial_info(struct async_struct * info,
 	tmp.close_delay = state->close_delay;
 	tmp.close_delay = state->close_delay;
 	tmp.closing_wait = state->closing_wait;
 	tmp.closing_wait = state->closing_wait;
 	tmp.custom_divisor = state->custom_divisor;
 	tmp.custom_divisor = state->custom_divisor;
-	unlock_kernel();
+	tty_unlock();
 	if (copy_to_user(retinfo,&tmp,sizeof(*retinfo)))
 	if (copy_to_user(retinfo,&tmp,sizeof(*retinfo)))
 		return -EFAULT;
 		return -EFAULT;
 	return 0;
 	return 0;
@@ -1100,14 +1100,14 @@ static int set_serial_info(struct async_struct * info,
 	if (copy_from_user(&new_serial,new_info,sizeof(new_serial)))
 	if (copy_from_user(&new_serial,new_info,sizeof(new_serial)))
 		return -EFAULT;
 		return -EFAULT;
 
 
-	lock_kernel();
+	tty_lock();
 	state = info->state;
 	state = info->state;
 	old_state = *state;
 	old_state = *state;
   
   
 	change_irq = new_serial.irq != state->irq;
 	change_irq = new_serial.irq != state->irq;
 	change_port = (new_serial.port != state->port);
 	change_port = (new_serial.port != state->port);
 	if(change_irq || change_port || (new_serial.xmit_fifo_size != state->xmit_fifo_size)) {
 	if(change_irq || change_port || (new_serial.xmit_fifo_size != state->xmit_fifo_size)) {
-	  unlock_kernel();
+	  tty_unlock();
 	  return -EINVAL;
 	  return -EINVAL;
 	}
 	}
   
   
@@ -1127,7 +1127,7 @@ static int set_serial_info(struct async_struct * info,
 	}
 	}
 
 
 	if (new_serial.baud_base < 9600) {
 	if (new_serial.baud_base < 9600) {
-		unlock_kernel();
+		tty_unlock();
 		return -EINVAL;
 		return -EINVAL;
 	}
 	}
 
 
@@ -1163,7 +1163,7 @@ static int set_serial_info(struct async_struct * info,
 		}
 		}
 	} else
 	} else
 		retval = startup(info);
 		retval = startup(info);
-	unlock_kernel();
+	tty_unlock();
 	return retval;
 	return retval;
 }
 }
 
 
@@ -1528,6 +1528,7 @@ static void rs_wait_until_sent(struct tty_struct *tty, int timeout)
 {
 {
 	struct async_struct * info = tty->driver_data;
 	struct async_struct * info = tty->driver_data;
 	unsigned long orig_jiffies, char_time;
 	unsigned long orig_jiffies, char_time;
+	int tty_was_locked = tty_locked();
 	int lsr;
 	int lsr;
 
 
 	if (serial_paranoia_check(info, tty->name, "rs_wait_until_sent"))
 	if (serial_paranoia_check(info, tty->name, "rs_wait_until_sent"))
@@ -1538,7 +1539,12 @@ static void rs_wait_until_sent(struct tty_struct *tty, int timeout)
 
 
 	orig_jiffies = jiffies;
 	orig_jiffies = jiffies;
 
 
-	lock_kernel();
+	/*
+	 * tty_wait_until_sent is called from lots of places,
+	 * with or without the BTM.
+	 */
+	if (!tty_was_locked)
+		tty_lock();
 	/*
 	/*
 	 * Set the check interval to be 1/5 of the estimated time to
 	 * Set the check interval to be 1/5 of the estimated time to
 	 * send a single character, and make it at least 1.  The check
 	 * send a single character, and make it at least 1.  The check
@@ -1579,7 +1585,8 @@ static void rs_wait_until_sent(struct tty_struct *tty, int timeout)
 			break;
 			break;
 	}
 	}
 	__set_current_state(TASK_RUNNING);
 	__set_current_state(TASK_RUNNING);
-	unlock_kernel();
+	if (!tty_was_locked)
+		tty_unlock();
 #ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
 #ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
 	printk("lsr = %d (jiff=%lu)...done\n", lsr, jiffies);
 	printk("lsr = %d (jiff=%lu)...done\n", lsr, jiffies);
 #endif
 #endif
@@ -1703,7 +1710,9 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp,
 		printk("block_til_ready blocking: ttys%d, count = %d\n",
 		printk("block_til_ready blocking: ttys%d, count = %d\n",
 		       info->line, state->count);
 		       info->line, state->count);
 #endif
 #endif
+		tty_unlock();
 		schedule();
 		schedule();
+		tty_lock();
 	}
 	}
 	__set_current_state(TASK_RUNNING);
 	__set_current_state(TASK_RUNNING);
 	remove_wait_queue(&info->open_wait, &wait);
 	remove_wait_queue(&info->open_wait, &wait);

+ 3 - 3
drivers/char/briq_panel.c

@@ -67,15 +67,15 @@ static void set_led(char state)
 
 
 static int briq_panel_open(struct inode *ino, struct file *filep)
 static int briq_panel_open(struct inode *ino, struct file *filep)
 {
 {
-	lock_kernel();
+	tty_lock();
 	/* enforce single access, vfd_is_open is protected by BKL */
 	/* enforce single access, vfd_is_open is protected by BKL */
 	if (vfd_is_open) {
 	if (vfd_is_open) {
-		unlock_kernel();
+		tty_unlock();
 		return -EBUSY;
 		return -EBUSY;
 	}
 	}
 	vfd_is_open = 1;
 	vfd_is_open = 1;
 
 
-	unlock_kernel();
+	tty_unlock();
 	return 0;
 	return 0;
 }
 }
 
 

+ 10 - 12
drivers/char/cyclades.c

@@ -65,7 +65,6 @@
 #include <linux/tty.h>
 #include <linux/tty.h>
 #include <linux/tty_flip.h>
 #include <linux/tty_flip.h>
 #include <linux/serial.h>
 #include <linux/serial.h>
-#include <linux/smp_lock.h>
 #include <linux/major.h>
 #include <linux/major.h>
 #include <linux/string.h>
 #include <linux/string.h>
 #include <linux/fcntl.h>
 #include <linux/fcntl.h>
@@ -1608,7 +1607,7 @@ static int cy_open(struct tty_struct *tty, struct file *filp)
 	 * If the port is the middle of closing, bail out now
 	 * If the port is the middle of closing, bail out now
 	 */
 	 */
 	if (tty_hung_up_p(filp) || (info->port.flags & ASYNC_CLOSING)) {
 	if (tty_hung_up_p(filp) || (info->port.flags & ASYNC_CLOSING)) {
-		wait_event_interruptible(info->port.close_wait,
+		wait_event_interruptible_tty(info->port.close_wait,
 				!(info->port.flags & ASYNC_CLOSING));
 				!(info->port.flags & ASYNC_CLOSING));
 		return (info->port.flags & ASYNC_HUP_NOTIFY) ? -EAGAIN: -ERESTARTSYS;
 		return (info->port.flags & ASYNC_HUP_NOTIFY) ? -EAGAIN: -ERESTARTSYS;
 	}
 	}
@@ -1655,7 +1654,6 @@ static void cy_wait_until_sent(struct tty_struct *tty, int timeout)
 		return;		/* Just in case.... */
 		return;		/* Just in case.... */
 
 
 	orig_jiffies = jiffies;
 	orig_jiffies = jiffies;
-	lock_kernel();
 	/*
 	/*
 	 * Set the check interval to be 1/5 of the estimated time to
 	 * Set the check interval to be 1/5 of the estimated time to
 	 * send a single character, and make it at least 1.  The check
 	 * send a single character, and make it at least 1.  The check
@@ -1702,7 +1700,6 @@ static void cy_wait_until_sent(struct tty_struct *tty, int timeout)
 	}
 	}
 	/* Run one more char cycle */
 	/* Run one more char cycle */
 	msleep_interruptible(jiffies_to_msecs(char_time * 5));
 	msleep_interruptible(jiffies_to_msecs(char_time * 5));
-	unlock_kernel();
 #ifdef CY_DEBUG_WAIT_UNTIL_SENT
 #ifdef CY_DEBUG_WAIT_UNTIL_SENT
 	printk(KERN_DEBUG "Clean (jiff=%lu)...done\n", jiffies);
 	printk(KERN_DEBUG "Clean (jiff=%lu)...done\n", jiffies);
 #endif
 #endif
@@ -1959,7 +1956,6 @@ static int cy_chars_in_buffer(struct tty_struct *tty)
 		int char_count;
 		int char_count;
 		__u32 tx_put, tx_get, tx_bufsize;
 		__u32 tx_put, tx_get, tx_bufsize;
 
 
-		lock_kernel();
 		tx_get = readl(&buf_ctrl->tx_get);
 		tx_get = readl(&buf_ctrl->tx_get);
 		tx_put = readl(&buf_ctrl->tx_put);
 		tx_put = readl(&buf_ctrl->tx_put);
 		tx_bufsize = readl(&buf_ctrl->tx_bufsize);
 		tx_bufsize = readl(&buf_ctrl->tx_bufsize);
@@ -1971,7 +1967,6 @@ static int cy_chars_in_buffer(struct tty_struct *tty)
 		printk(KERN_DEBUG "cyc:cy_chars_in_buffer ttyC%d %d\n",
 		printk(KERN_DEBUG "cyc:cy_chars_in_buffer ttyC%d %d\n",
 			info->line, info->xmit_cnt + char_count);
 			info->line, info->xmit_cnt + char_count);
 #endif
 #endif
-		unlock_kernel();
 		return info->xmit_cnt + char_count;
 		return info->xmit_cnt + char_count;
 	}
 	}
 #endif				/* Z_EXT_CHARS_IN_BUFFER */
 #endif				/* Z_EXT_CHARS_IN_BUFFER */
@@ -2359,17 +2354,22 @@ cy_set_serial_info(struct cyclades_port *info, struct tty_struct *tty,
 		struct serial_struct __user *new_info)
 		struct serial_struct __user *new_info)
 {
 {
 	struct serial_struct new_serial;
 	struct serial_struct new_serial;
+	int ret;
 
 
 	if (copy_from_user(&new_serial, new_info, sizeof(new_serial)))
 	if (copy_from_user(&new_serial, new_info, sizeof(new_serial)))
 		return -EFAULT;
 		return -EFAULT;
 
 
+	mutex_lock(&info->port.mutex);
 	if (!capable(CAP_SYS_ADMIN)) {
 	if (!capable(CAP_SYS_ADMIN)) {
 		if (new_serial.close_delay != info->port.close_delay ||
 		if (new_serial.close_delay != info->port.close_delay ||
 				new_serial.baud_base != info->baud ||
 				new_serial.baud_base != info->baud ||
 				(new_serial.flags & ASYNC_FLAGS &
 				(new_serial.flags & ASYNC_FLAGS &
 					~ASYNC_USR_MASK) !=
 					~ASYNC_USR_MASK) !=
 				(info->port.flags & ASYNC_FLAGS & ~ASYNC_USR_MASK))
 				(info->port.flags & ASYNC_FLAGS & ~ASYNC_USR_MASK))
+		{
+			mutex_unlock(&info->port.mutex);
 			return -EPERM;
 			return -EPERM;
+		}
 		info->port.flags = (info->port.flags & ~ASYNC_USR_MASK) |
 		info->port.flags = (info->port.flags & ~ASYNC_USR_MASK) |
 				(new_serial.flags & ASYNC_USR_MASK);
 				(new_serial.flags & ASYNC_USR_MASK);
 		info->baud = new_serial.baud_base;
 		info->baud = new_serial.baud_base;
@@ -2392,10 +2392,12 @@ cy_set_serial_info(struct cyclades_port *info, struct tty_struct *tty,
 check_and_exit:
 check_and_exit:
 	if (info->port.flags & ASYNC_INITIALIZED) {
 	if (info->port.flags & ASYNC_INITIALIZED) {
 		cy_set_line_char(info, tty);
 		cy_set_line_char(info, tty);
-		return 0;
+		ret = 0;
 	} else {
 	} else {
-		return cy_startup(info, tty);
+		ret = cy_startup(info, tty);
 	}
 	}
+	mutex_unlock(&info->port.mutex);
+	return ret;
 }				/* set_serial_info */
 }				/* set_serial_info */
 
 
 /*
 /*
@@ -2438,7 +2440,6 @@ static int cy_tiocmget(struct tty_struct *tty, struct file *file)
 
 
 	card = info->card;
 	card = info->card;
 
 
-	lock_kernel();
 	if (!cy_is_Z(card)) {
 	if (!cy_is_Z(card)) {
 		unsigned long flags;
 		unsigned long flags;
 		int channel = info->line - card->first_line;
 		int channel = info->line - card->first_line;
@@ -2478,7 +2479,6 @@ static int cy_tiocmget(struct tty_struct *tty, struct file *file)
 			((lstatus & C_RS_CTS) ? TIOCM_CTS : 0);
 			((lstatus & C_RS_CTS) ? TIOCM_CTS : 0);
 	}
 	}
 end:
 end:
-	unlock_kernel();
 	return result;
 	return result;
 }				/* cy_tiomget */
 }				/* cy_tiomget */
 
 
@@ -2696,7 +2696,6 @@ cy_ioctl(struct tty_struct *tty, struct file *file,
 	printk(KERN_DEBUG "cyc:cy_ioctl ttyC%d, cmd = %x arg = %lx\n",
 	printk(KERN_DEBUG "cyc:cy_ioctl ttyC%d, cmd = %x arg = %lx\n",
 		info->line, cmd, arg);
 		info->line, cmd, arg);
 #endif
 #endif
-	lock_kernel();
 
 
 	switch (cmd) {
 	switch (cmd) {
 	case CYGETMON:
 	case CYGETMON:
@@ -2817,7 +2816,6 @@ cy_ioctl(struct tty_struct *tty, struct file *file,
 	default:
 	default:
 		ret_val = -ENOIOCTLCMD;
 		ret_val = -ENOIOCTLCMD;
 	}
 	}
-	unlock_kernel();
 
 
 #ifdef CY_DEBUG_OTHER
 #ifdef CY_DEBUG_OTHER
 	printk(KERN_DEBUG "cyc:cy_ioctl done\n");
 	printk(KERN_DEBUG "cyc:cy_ioctl done\n");

+ 1 - 3
drivers/char/epca.c

@@ -36,7 +36,7 @@
 #include <linux/ctype.h>
 #include <linux/ctype.h>
 #include <linux/tty.h>
 #include <linux/tty.h>
 #include <linux/tty_flip.h>
 #include <linux/tty_flip.h>
-#include <linux/smp_lock.h>
+#include <linux/slab.h>
 #include <linux/ioport.h>
 #include <linux/ioport.h>
 #include <linux/interrupt.h>
 #include <linux/interrupt.h>
 #include <linux/uaccess.h>
 #include <linux/uaccess.h>
@@ -2105,7 +2105,6 @@ static int pc_ioctl(struct tty_struct *tty, struct file *file,
 		break;
 		break;
 	case DIGI_SETAW:
 	case DIGI_SETAW:
 	case DIGI_SETAF:
 	case DIGI_SETAF:
-		lock_kernel();
 		if (cmd == DIGI_SETAW) {
 		if (cmd == DIGI_SETAW) {
 			/* Setup an event to indicate when the transmit
 			/* Setup an event to indicate when the transmit
 			   buffer empties */
 			   buffer empties */
@@ -2118,7 +2117,6 @@ static int pc_ioctl(struct tty_struct *tty, struct file *file,
 			if (tty->ldisc->ops->flush_buffer)
 			if (tty->ldisc->ops->flush_buffer)
 				tty->ldisc->ops->flush_buffer(tty);
 				tty->ldisc->ops->flush_buffer(tty);
 		}
 		}
-		unlock_kernel();
 		/* Fall Thru */
 		/* Fall Thru */
 	case DIGI_SETA:
 	case DIGI_SETA:
 		if (copy_from_user(&ch->digiext, argp, sizeof(digi_t)))
 		if (copy_from_user(&ch->digiext, argp, sizeof(digi_t)))

+ 4 - 0
drivers/char/ip2/ip2main.c

@@ -1486,7 +1486,9 @@ ip2_open( PTTY tty, struct file *pFile )
 
 
 	if ( tty_hung_up_p(pFile) || ( pCh->flags & ASYNC_CLOSING )) {
 	if ( tty_hung_up_p(pFile) || ( pCh->flags & ASYNC_CLOSING )) {
 		if ( pCh->flags & ASYNC_CLOSING ) {
 		if ( pCh->flags & ASYNC_CLOSING ) {
+			tty_unlock();
 			schedule();
 			schedule();
+			tty_lock();
 		}
 		}
 		if ( tty_hung_up_p(pFile) ) {
 		if ( tty_hung_up_p(pFile) ) {
 			set_current_state( TASK_RUNNING );
 			set_current_state( TASK_RUNNING );
@@ -1548,7 +1550,9 @@ ip2_open( PTTY tty, struct file *pFile )
 			rc = (( pCh->flags & ASYNC_HUP_NOTIFY ) ? -EAGAIN : -ERESTARTSYS);
 			rc = (( pCh->flags & ASYNC_HUP_NOTIFY ) ? -EAGAIN : -ERESTARTSYS);
 			break;
 			break;
 		}
 		}
+		tty_unlock();
 		schedule();
 		schedule();
+		tty_lock();
 	}
 	}
 	set_current_state( TASK_RUNNING );
 	set_current_state( TASK_RUNNING );
 	remove_wait_queue(&pCh->open_wait, &wait);
 	remove_wait_queue(&pCh->open_wait, &wait);

+ 5 - 8
drivers/char/isicom.c

@@ -124,7 +124,6 @@
 #include <linux/fs.h>
 #include <linux/fs.h>
 #include <linux/sched.h>
 #include <linux/sched.h>
 #include <linux/serial.h>
 #include <linux/serial.h>
-#include <linux/smp_lock.h>
 #include <linux/mm.h>
 #include <linux/mm.h>
 #include <linux/interrupt.h>
 #include <linux/interrupt.h>
 #include <linux/timer.h>
 #include <linux/timer.h>
@@ -872,7 +871,6 @@ static struct tty_port *isicom_find_port(struct tty_struct *tty)
 static int isicom_open(struct tty_struct *tty, struct file *filp)
 static int isicom_open(struct tty_struct *tty, struct file *filp)
 {
 {
 	struct isi_port *port;
 	struct isi_port *port;
-	struct isi_board *card;
 	struct tty_port *tport;
 	struct tty_port *tport;
 
 
 	tport = isicom_find_port(tty);
 	tport = isicom_find_port(tty);
@@ -1118,8 +1116,7 @@ static int isicom_set_serial_info(struct tty_struct *tty,
 	if (copy_from_user(&newinfo, info, sizeof(newinfo)))
 	if (copy_from_user(&newinfo, info, sizeof(newinfo)))
 		return -EFAULT;
 		return -EFAULT;
 
 
-	lock_kernel();
-
+	mutex_lock(&port->port.mutex);
 	reconfig_port = ((port->port.flags & ASYNC_SPD_MASK) !=
 	reconfig_port = ((port->port.flags & ASYNC_SPD_MASK) !=
 		(newinfo.flags & ASYNC_SPD_MASK));
 		(newinfo.flags & ASYNC_SPD_MASK));
 
 
@@ -1128,7 +1125,7 @@ static int isicom_set_serial_info(struct tty_struct *tty,
 				(newinfo.closing_wait != port->port.closing_wait) ||
 				(newinfo.closing_wait != port->port.closing_wait) ||
 				((newinfo.flags & ~ASYNC_USR_MASK) !=
 				((newinfo.flags & ~ASYNC_USR_MASK) !=
 				(port->port.flags & ~ASYNC_USR_MASK))) {
 				(port->port.flags & ~ASYNC_USR_MASK))) {
-			unlock_kernel();
+			mutex_unlock(&port->port.mutex);
 			return -EPERM;
 			return -EPERM;
 		}
 		}
 		port->port.flags = ((port->port.flags & ~ASYNC_USR_MASK) |
 		port->port.flags = ((port->port.flags & ~ASYNC_USR_MASK) |
@@ -1145,7 +1142,7 @@ static int isicom_set_serial_info(struct tty_struct *tty,
 		isicom_config_port(tty);
 		isicom_config_port(tty);
 		spin_unlock_irqrestore(&port->card->card_lock, flags);
 		spin_unlock_irqrestore(&port->card->card_lock, flags);
 	}
 	}
-	unlock_kernel();
+	mutex_unlock(&port->port.mutex);
 	return 0;
 	return 0;
 }
 }
 
 
@@ -1154,7 +1151,7 @@ static int isicom_get_serial_info(struct isi_port *port,
 {
 {
 	struct serial_struct out_info;
 	struct serial_struct out_info;
 
 
-	lock_kernel();
+	mutex_lock(&port->port.mutex);
 	memset(&out_info, 0, sizeof(out_info));
 	memset(&out_info, 0, sizeof(out_info));
 /*	out_info.type = ? */
 /*	out_info.type = ? */
 	out_info.line = port - isi_ports;
 	out_info.line = port - isi_ports;
@@ -1164,7 +1161,7 @@ static int isicom_get_serial_info(struct isi_port *port,
 /*	out_info.baud_base = ? */
 /*	out_info.baud_base = ? */
 	out_info.close_delay = port->port.close_delay;
 	out_info.close_delay = port->port.close_delay;
 	out_info.closing_wait = port->port.closing_wait;
 	out_info.closing_wait = port->port.closing_wait;
-	unlock_kernel();
+	mutex_unlock(&port->port.mutex);
 	if (copy_to_user(info, &out_info, sizeof(out_info)))
 	if (copy_to_user(info, &out_info, sizeof(out_info)))
 		return -EFAULT;
 		return -EFAULT;
 	return 0;
 	return 0;

+ 38 - 30
drivers/char/istallion.c

@@ -203,9 +203,9 @@ static int		stli_shared;
  *	the board has been detected, and whether it is actually running a slave
  *	the board has been detected, and whether it is actually running a slave
  *	or not.
  *	or not.
  */
  */
-#define	BST_FOUND	0x1
-#define	BST_STARTED	0x2
-#define	BST_PROBED	0x4
+#define	BST_FOUND	0
+#define	BST_STARTED	1
+#define	BST_PROBED	2
 
 
 /*
 /*
  *	Define the set of port state flags. These are marked for internal
  *	Define the set of port state flags. These are marked for internal
@@ -816,7 +816,7 @@ static int stli_open(struct tty_struct *tty, struct file *filp)
 	brdp = stli_brds[brdnr];
 	brdp = stli_brds[brdnr];
 	if (brdp == NULL)
 	if (brdp == NULL)
 		return -ENODEV;
 		return -ENODEV;
-	if ((brdp->state & BST_STARTED) == 0)
+	if (!test_bit(BST_STARTED, &brdp->state))
 		return -ENODEV;
 		return -ENODEV;
 	portnr = MINOR2PORT(minordev);
 	portnr = MINOR2PORT(minordev);
 	if (portnr > brdp->nrports)
 	if (portnr > brdp->nrports)
@@ -954,7 +954,7 @@ static int stli_rawopen(struct stlibrd *brdp, struct stliport *portp, unsigned l
  *	order of opens and closes may not be preserved across shared
  *	order of opens and closes may not be preserved across shared
  *	memory, so we must wait until it is complete.
  *	memory, so we must wait until it is complete.
  */
  */
-	wait_event_interruptible(portp->raw_wait,
+	wait_event_interruptible_tty(portp->raw_wait,
 			!test_bit(ST_CLOSING, &portp->state));
 			!test_bit(ST_CLOSING, &portp->state));
 	if (signal_pending(current)) {
 	if (signal_pending(current)) {
 		return -ERESTARTSYS;
 		return -ERESTARTSYS;
@@ -989,7 +989,7 @@ static int stli_rawopen(struct stlibrd *brdp, struct stliport *portp, unsigned l
 	set_bit(ST_OPENING, &portp->state);
 	set_bit(ST_OPENING, &portp->state);
 	spin_unlock_irqrestore(&brd_lock, flags);
 	spin_unlock_irqrestore(&brd_lock, flags);
 
 
-	wait_event_interruptible(portp->raw_wait,
+	wait_event_interruptible_tty(portp->raw_wait,
 			!test_bit(ST_OPENING, &portp->state));
 			!test_bit(ST_OPENING, &portp->state));
 	if (signal_pending(current))
 	if (signal_pending(current))
 		rc = -ERESTARTSYS;
 		rc = -ERESTARTSYS;
@@ -1020,7 +1020,7 @@ static int stli_rawclose(struct stlibrd *brdp, struct stliport *portp, unsigned
  *	occurs on this port.
  *	occurs on this port.
  */
  */
 	if (wait) {
 	if (wait) {
-		wait_event_interruptible(portp->raw_wait,
+		wait_event_interruptible_tty(portp->raw_wait,
 				!test_bit(ST_CLOSING, &portp->state));
 				!test_bit(ST_CLOSING, &portp->state));
 		if (signal_pending(current)) {
 		if (signal_pending(current)) {
 			return -ERESTARTSYS;
 			return -ERESTARTSYS;
@@ -1052,7 +1052,7 @@ static int stli_rawclose(struct stlibrd *brdp, struct stliport *portp, unsigned
  *	to come back.
  *	to come back.
  */
  */
 	rc = 0;
 	rc = 0;
-	wait_event_interruptible(portp->raw_wait,
+	wait_event_interruptible_tty(portp->raw_wait,
 			!test_bit(ST_CLOSING, &portp->state));
 			!test_bit(ST_CLOSING, &portp->state));
 	if (signal_pending(current))
 	if (signal_pending(current))
 		rc = -ERESTARTSYS;
 		rc = -ERESTARTSYS;
@@ -1073,6 +1073,10 @@ static int stli_rawclose(struct stlibrd *brdp, struct stliport *portp, unsigned
 
 
 static int stli_cmdwait(struct stlibrd *brdp, struct stliport *portp, unsigned long cmd, void *arg, int size, int copyback)
 static int stli_cmdwait(struct stlibrd *brdp, struct stliport *portp, unsigned long cmd, void *arg, int size, int copyback)
 {
 {
+	/*
+	 * no need for wait_event_tty because clearing ST_CMDING cannot block
+	 * on BTM
+	 */
 	wait_event_interruptible(portp->raw_wait,
 	wait_event_interruptible(portp->raw_wait,
 			!test_bit(ST_CMDING, &portp->state));
 			!test_bit(ST_CMDING, &portp->state));
 	if (signal_pending(current))
 	if (signal_pending(current))
@@ -1846,7 +1850,7 @@ static void stli_portinfo(struct seq_file *m, struct stlibrd *brdp, struct stlip
 	rc = stli_portcmdstats(NULL, portp);
 	rc = stli_portcmdstats(NULL, portp);
 
 
 	uart = "UNKNOWN";
 	uart = "UNKNOWN";
-	if (brdp->state & BST_STARTED) {
+	if (test_bit(BST_STARTED, &brdp->state)) {
 		switch (stli_comstats.hwid) {
 		switch (stli_comstats.hwid) {
 		case 0:	uart = "2681"; break;
 		case 0:	uart = "2681"; break;
 		case 1:	uart = "SC26198"; break;
 		case 1:	uart = "SC26198"; break;
@@ -1855,7 +1859,7 @@ static void stli_portinfo(struct seq_file *m, struct stlibrd *brdp, struct stlip
 	}
 	}
 	seq_printf(m, "%d: uart:%s ", portnr, uart);
 	seq_printf(m, "%d: uart:%s ", portnr, uart);
 
 
-	if ((brdp->state & BST_STARTED) && (rc >= 0)) {
+	if (test_bit(BST_STARTED, &brdp->state) && rc >= 0) {
 		char sep;
 		char sep;
 
 
 		seq_printf(m, "tx:%d rx:%d", (int) stli_comstats.txtotal,
 		seq_printf(m, "tx:%d rx:%d", (int) stli_comstats.txtotal,
@@ -2355,7 +2359,7 @@ static void stli_poll(unsigned long arg)
 		brdp = stli_brds[brdnr];
 		brdp = stli_brds[brdnr];
 		if (brdp == NULL)
 		if (brdp == NULL)
 			continue;
 			continue;
-		if ((brdp->state & BST_STARTED) == 0)
+		if (!test_bit(BST_STARTED, &brdp->state))
 			continue;
 			continue;
 
 
 		spin_lock(&brd_lock);
 		spin_lock(&brd_lock);
@@ -3140,7 +3144,7 @@ static int stli_initecp(struct stlibrd *brdp)
 	}
 	}
 
 
 
 
-	brdp->state |= BST_FOUND;
+	set_bit(BST_FOUND, &brdp->state);
 	return 0;
 	return 0;
 err_unmap:
 err_unmap:
 	iounmap(brdp->membase);
 	iounmap(brdp->membase);
@@ -3297,7 +3301,7 @@ static int stli_initonb(struct stlibrd *brdp)
 	brdp->panels[0] = brdp->nrports;
 	brdp->panels[0] = brdp->nrports;
 
 
 
 
-	brdp->state |= BST_FOUND;
+	set_bit(BST_FOUND, &brdp->state);
 	return 0;
 	return 0;
 err_unmap:
 err_unmap:
 	iounmap(brdp->membase);
 	iounmap(brdp->membase);
@@ -3407,7 +3411,7 @@ static int stli_startbrd(struct stlibrd *brdp)
 	spin_unlock_irqrestore(&brd_lock, flags);
 	spin_unlock_irqrestore(&brd_lock, flags);
 
 
 	if (rc == 0)
 	if (rc == 0)
-		brdp->state |= BST_STARTED;
+		set_bit(BST_STARTED, &brdp->state);
 
 
 	if (! stli_timeron) {
 	if (! stli_timeron) {
 		stli_timeron++;
 		stli_timeron++;
@@ -3710,7 +3714,7 @@ static int __devinit stli_pciprobe(struct pci_dev *pdev,
 	if (retval)
 	if (retval)
 		goto err_null;
 		goto err_null;
 
 
-	brdp->state |= BST_PROBED;
+	set_bit(BST_PROBED, &brdp->state);
 	pci_set_drvdata(pdev, brdp);
 	pci_set_drvdata(pdev, brdp);
 
 
 	EBRDENABLE(brdp);
 	EBRDENABLE(brdp);
@@ -3841,7 +3845,7 @@ static int __init stli_initbrds(void)
 			brdp = stli_brds[i];
 			brdp = stli_brds[i];
 			if (brdp == NULL)
 			if (brdp == NULL)
 				continue;
 				continue;
-			if (brdp->state & BST_FOUND) {
+			if (test_bit(BST_FOUND, &brdp->state)) {
 				EBRDENABLE(brdp);
 				EBRDENABLE(brdp);
 				brdp->enable = NULL;
 				brdp->enable = NULL;
 				brdp->disable = NULL;
 				brdp->disable = NULL;
@@ -4011,6 +4015,7 @@ static int stli_getbrdstats(combrd_t __user *bp)
 		return -ENODEV;
 		return -ENODEV;
 
 
 	memset(&stli_brdstats, 0, sizeof(combrd_t));
 	memset(&stli_brdstats, 0, sizeof(combrd_t));
+
 	stli_brdstats.brd = brdp->brdnr;
 	stli_brdstats.brd = brdp->brdnr;
 	stli_brdstats.type = brdp->brdtype;
 	stli_brdstats.type = brdp->brdtype;
 	stli_brdstats.hwid = 0;
 	stli_brdstats.hwid = 0;
@@ -4076,10 +4081,13 @@ static int stli_portcmdstats(struct tty_struct *tty, struct stliport *portp)
 	if (brdp == NULL)
 	if (brdp == NULL)
 		return -ENODEV;
 		return -ENODEV;
 
 
-	if (brdp->state & BST_STARTED) {
+	mutex_lock(&portp->port.mutex);
+	if (test_bit(BST_STARTED, &brdp->state)) {
 		if ((rc = stli_cmdwait(brdp, portp, A_GETSTATS,
 		if ((rc = stli_cmdwait(brdp, portp, A_GETSTATS,
-		    &stli_cdkstats, sizeof(asystats_t), 1)) < 0)
+		    &stli_cdkstats, sizeof(asystats_t), 1)) < 0) {
+			mutex_unlock(&portp->port.mutex);
 			return rc;
 			return rc;
+		}
 	} else {
 	} else {
 		memset(&stli_cdkstats, 0, sizeof(asystats_t));
 		memset(&stli_cdkstats, 0, sizeof(asystats_t));
 	}
 	}
@@ -4124,6 +4132,7 @@ static int stli_portcmdstats(struct tty_struct *tty, struct stliport *portp)
 	stli_comstats.modem = stli_cdkstats.dcdcnt;
 	stli_comstats.modem = stli_cdkstats.dcdcnt;
 	stli_comstats.hwid = stli_cdkstats.hwid;
 	stli_comstats.hwid = stli_cdkstats.hwid;
 	stli_comstats.signals = stli_mktiocm(stli_cdkstats.signals);
 	stli_comstats.signals = stli_mktiocm(stli_cdkstats.signals);
+	mutex_unlock(&portp->port.mutex);
 
 
 	return 0;
 	return 0;
 }
 }
@@ -4186,15 +4195,20 @@ static int stli_clrportstats(struct stliport *portp, comstats_t __user *cp)
 	if (!brdp)
 	if (!brdp)
 		return -ENODEV;
 		return -ENODEV;
 
 
-	if (brdp->state & BST_STARTED) {
-		if ((rc = stli_cmdwait(brdp, portp, A_CLEARSTATS, NULL, 0, 0)) < 0)
+	mutex_lock(&portp->port.mutex);
+
+	if (test_bit(BST_STARTED, &brdp->state)) {
+		if ((rc = stli_cmdwait(brdp, portp, A_CLEARSTATS, NULL, 0, 0)) < 0) {
+			mutex_unlock(&portp->port.mutex);
 			return rc;
 			return rc;
+		}
 	}
 	}
 
 
 	memset(&stli_comstats, 0, sizeof(comstats_t));
 	memset(&stli_comstats, 0, sizeof(comstats_t));
 	stli_comstats.brd = portp->brdnr;
 	stli_comstats.brd = portp->brdnr;
 	stli_comstats.panel = portp->panelnr;
 	stli_comstats.panel = portp->panelnr;
 	stli_comstats.port = portp->portnr;
 	stli_comstats.port = portp->portnr;
+	mutex_unlock(&portp->port.mutex);
 
 
 	if (copy_to_user(cp, &stli_comstats, sizeof(comstats_t)))
 	if (copy_to_user(cp, &stli_comstats, sizeof(comstats_t)))
 		return -EFAULT;
 		return -EFAULT;
@@ -4266,8 +4280,6 @@ static long stli_memioctl(struct file *fp, unsigned int cmd, unsigned long arg)
 	done = 0;
 	done = 0;
 	rc = 0;
 	rc = 0;
 
 
-	lock_kernel();
-
 	switch (cmd) {
 	switch (cmd) {
 	case COM_GETPORTSTATS:
 	case COM_GETPORTSTATS:
 		rc = stli_getportstats(NULL, NULL, argp);
 		rc = stli_getportstats(NULL, NULL, argp);
@@ -4290,8 +4302,6 @@ static long stli_memioctl(struct file *fp, unsigned int cmd, unsigned long arg)
 		done++;
 		done++;
 		break;
 		break;
 	}
 	}
-	unlock_kernel();
-
 	if (done)
 	if (done)
 		return rc;
 		return rc;
 
 
@@ -4308,8 +4318,6 @@ static long stli_memioctl(struct file *fp, unsigned int cmd, unsigned long arg)
 	if (brdp->state == 0)
 	if (brdp->state == 0)
 		return -ENODEV;
 		return -ENODEV;
 
 
-	lock_kernel();
-
 	switch (cmd) {
 	switch (cmd) {
 	case STL_BINTR:
 	case STL_BINTR:
 		EBRDINTR(brdp);
 		EBRDINTR(brdp);
@@ -4318,10 +4326,10 @@ static long stli_memioctl(struct file *fp, unsigned int cmd, unsigned long arg)
 		rc = stli_startbrd(brdp);
 		rc = stli_startbrd(brdp);
 		break;
 		break;
 	case STL_BSTOP:
 	case STL_BSTOP:
-		brdp->state &= ~BST_STARTED;
+		clear_bit(BST_STARTED, &brdp->state);
 		break;
 		break;
 	case STL_BRESET:
 	case STL_BRESET:
-		brdp->state &= ~BST_STARTED;
+		clear_bit(BST_STARTED, &brdp->state);
 		EBRDRESET(brdp);
 		EBRDRESET(brdp);
 		if (stli_shared == 0) {
 		if (stli_shared == 0) {
 			if (brdp->reenable != NULL)
 			if (brdp->reenable != NULL)
@@ -4332,7 +4340,6 @@ static long stli_memioctl(struct file *fp, unsigned int cmd, unsigned long arg)
 		rc = -ENOIOCTLCMD;
 		rc = -ENOIOCTLCMD;
 		break;
 		break;
 	}
 	}
-	unlock_kernel();
 	return rc;
 	return rc;
 }
 }
 
 
@@ -4378,7 +4385,8 @@ static void istallion_cleanup_isa(void)
 	unsigned int j;
 	unsigned int j;
 
 
 	for (j = 0; (j < stli_nrbrds); j++) {
 	for (j = 0; (j < stli_nrbrds); j++) {
-		if ((brdp = stli_brds[j]) == NULL || (brdp->state & BST_PROBED))
+		if ((brdp = stli_brds[j]) == NULL ||
+				test_bit(BST_PROBED, &brdp->state))
 			continue;
 			continue;
 
 
 		stli_cleanup_ports(brdp);
 		stli_cleanup_ports(brdp);

+ 5 - 5
drivers/char/keyboard.c

@@ -299,7 +299,7 @@ int kbd_rate(struct kbd_repeat *rep)
  */
  */
 static void put_queue(struct vc_data *vc, int ch)
 static void put_queue(struct vc_data *vc, int ch)
 {
 {
-	struct tty_struct *tty = vc->vc_tty;
+	struct tty_struct *tty = vc->port.tty;
 
 
 	if (tty) {
 	if (tty) {
 		tty_insert_flip_char(tty, ch, 0);
 		tty_insert_flip_char(tty, ch, 0);
@@ -309,7 +309,7 @@ static void put_queue(struct vc_data *vc, int ch)
 
 
 static void puts_queue(struct vc_data *vc, char *cp)
 static void puts_queue(struct vc_data *vc, char *cp)
 {
 {
-	struct tty_struct *tty = vc->vc_tty;
+	struct tty_struct *tty = vc->port.tty;
 
 
 	if (!tty)
 	if (!tty)
 		return;
 		return;
@@ -485,7 +485,7 @@ static void fn_show_ptregs(struct vc_data *vc)
 
 
 static void fn_hold(struct vc_data *vc)
 static void fn_hold(struct vc_data *vc)
 {
 {
-	struct tty_struct *tty = vc->vc_tty;
+	struct tty_struct *tty = vc->port.tty;
 
 
 	if (rep || !tty)
 	if (rep || !tty)
 		return;
 		return;
@@ -563,7 +563,7 @@ static void fn_inc_console(struct vc_data *vc)
 
 
 static void fn_send_intr(struct vc_data *vc)
 static void fn_send_intr(struct vc_data *vc)
 {
 {
-	struct tty_struct *tty = vc->vc_tty;
+	struct tty_struct *tty = vc->port.tty;
 
 
 	if (!tty)
 	if (!tty)
 		return;
 		return;
@@ -1162,7 +1162,7 @@ static void kbd_keycode(unsigned int keycode, int down, int hw_raw)
 	struct keyboard_notifier_param param = { .vc = vc, .value = keycode, .down = down };
 	struct keyboard_notifier_param param = { .vc = vc, .value = keycode, .down = down };
 	int rc;
 	int rc;
 
 
-	tty = vc->vc_tty;
+	tty = vc->port.tty;
 
 
 	if (tty && (!tty->driver_data)) {
 	if (tty && (!tty->driver_data)) {
 		/* No driver data? Strange. Okay we fix it then. */
 		/* No driver data? Strange. Okay we fix it then. */

+ 1 - 1
drivers/char/mxser.c

@@ -2193,7 +2193,7 @@ static void mxser_transmit_chars(struct tty_struct *tty, struct mxser_port *port
 	port->mon_data.up_txcnt += (cnt - port->xmit_cnt);
 	port->mon_data.up_txcnt += (cnt - port->xmit_cnt);
 	port->icount.tx += (cnt - port->xmit_cnt);
 	port->icount.tx += (cnt - port->xmit_cnt);
 
 
-	if (port->xmit_cnt < WAKEUP_CHARS && tty)
+	if (port->xmit_cnt < WAKEUP_CHARS)
 		tty_wakeup(tty);
 		tty_wakeup(tty);
 
 
 	if (port->xmit_cnt <= 0) {
 	if (port->xmit_cnt <= 0) {

+ 0 - 1
drivers/char/n_gsm.c

@@ -43,7 +43,6 @@
 #include <linux/sched.h>
 #include <linux/sched.h>
 #include <linux/interrupt.h>
 #include <linux/interrupt.h>
 #include <linux/tty.h>
 #include <linux/tty.h>
-#include <linux/timer.h>
 #include <linux/ctype.h>
 #include <linux/ctype.h>
 #include <linux/mm.h>
 #include <linux/mm.h>
 #include <linux/string.h>
 #include <linux/string.h>

+ 8 - 8
drivers/char/n_hdlc.c

@@ -598,18 +598,18 @@ static ssize_t n_hdlc_tty_read(struct tty_struct *tty, struct file *file,
 		return -EFAULT;
 		return -EFAULT;
 	}
 	}
 
 
-	lock_kernel();
+	tty_lock();
 
 
 	for (;;) {
 	for (;;) {
 		if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) {
 		if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) {
-			unlock_kernel();
+			tty_unlock();
 			return -EIO;
 			return -EIO;
 		}
 		}
 
 
 		n_hdlc = tty2n_hdlc (tty);
 		n_hdlc = tty2n_hdlc (tty);
 		if (!n_hdlc || n_hdlc->magic != HDLC_MAGIC ||
 		if (!n_hdlc || n_hdlc->magic != HDLC_MAGIC ||
 			 tty != n_hdlc->tty) {
 			 tty != n_hdlc->tty) {
-			unlock_kernel();
+			tty_unlock();
 			return 0;
 			return 0;
 		}
 		}
 
 
@@ -619,13 +619,13 @@ static ssize_t n_hdlc_tty_read(struct tty_struct *tty, struct file *file,
 			
 			
 		/* no data */
 		/* no data */
 		if (file->f_flags & O_NONBLOCK) {
 		if (file->f_flags & O_NONBLOCK) {
-			unlock_kernel();
+			tty_unlock();
 			return -EAGAIN;
 			return -EAGAIN;
 		}
 		}
 			
 			
 		interruptible_sleep_on (&tty->read_wait);
 		interruptible_sleep_on (&tty->read_wait);
 		if (signal_pending(current)) {
 		if (signal_pending(current)) {
-			unlock_kernel();
+			tty_unlock();
 			return -EINTR;
 			return -EINTR;
 		}
 		}
 	}
 	}
@@ -648,7 +648,7 @@ static ssize_t n_hdlc_tty_read(struct tty_struct *tty, struct file *file,
 		kfree(rbuf);
 		kfree(rbuf);
 	else	
 	else	
 		n_hdlc_buf_put(&n_hdlc->rx_free_buf_list,rbuf);
 		n_hdlc_buf_put(&n_hdlc->rx_free_buf_list,rbuf);
-	unlock_kernel();
+	tty_unlock();
 	return ret;
 	return ret;
 	
 	
 }	/* end of n_hdlc_tty_read() */
 }	/* end of n_hdlc_tty_read() */
@@ -691,7 +691,7 @@ static ssize_t n_hdlc_tty_write(struct tty_struct *tty, struct file *file,
 		count = maxframe;
 		count = maxframe;
 	}
 	}
 	
 	
-	lock_kernel();
+	tty_lock();
 
 
 	add_wait_queue(&tty->write_wait, &wait);
 	add_wait_queue(&tty->write_wait, &wait);
 	set_current_state(TASK_INTERRUPTIBLE);
 	set_current_state(TASK_INTERRUPTIBLE);
@@ -731,7 +731,7 @@ static ssize_t n_hdlc_tty_write(struct tty_struct *tty, struct file *file,
 		n_hdlc_buf_put(&n_hdlc->tx_buf_list,tbuf);
 		n_hdlc_buf_put(&n_hdlc->tx_buf_list,tbuf);
 		n_hdlc_send_frames(n_hdlc,tty);
 		n_hdlc_send_frames(n_hdlc,tty);
 	}
 	}
-	unlock_kernel();
+	tty_unlock();
 	return error;
 	return error;
 	
 	
 }	/* end of n_hdlc_tty_write() */
 }	/* end of n_hdlc_tty_write() */

+ 5 - 5
drivers/char/n_r3964.c

@@ -1067,7 +1067,7 @@ static ssize_t r3964_read(struct tty_struct *tty, struct file *file,
 
 
 	TRACE_L("read()");
 	TRACE_L("read()");
 
 
-	lock_kernel();
+	tty_lock();
 
 
 	pClient = findClient(pInfo, task_pid(current));
 	pClient = findClient(pInfo, task_pid(current));
 	if (pClient) {
 	if (pClient) {
@@ -1079,7 +1079,7 @@ static ssize_t r3964_read(struct tty_struct *tty, struct file *file,
 				goto unlock;
 				goto unlock;
 			}
 			}
 			/* block until there is a message: */
 			/* block until there is a message: */
-			wait_event_interruptible(pInfo->read_wait,
+			wait_event_interruptible_tty(pInfo->read_wait,
 					(pMsg = remove_msg(pInfo, pClient)));
 					(pMsg = remove_msg(pInfo, pClient)));
 		}
 		}
 
 
@@ -1109,7 +1109,7 @@ static ssize_t r3964_read(struct tty_struct *tty, struct file *file,
 	}
 	}
 	ret = -EPERM;
 	ret = -EPERM;
 unlock:
 unlock:
-	unlock_kernel();
+	tty_unlock();
 	return ret;
 	return ret;
 }
 }
 
 
@@ -1158,7 +1158,7 @@ static ssize_t r3964_write(struct tty_struct *tty, struct file *file,
 	pHeader->locks = 0;
 	pHeader->locks = 0;
 	pHeader->owner = NULL;
 	pHeader->owner = NULL;
 
 
-	lock_kernel();
+	tty_lock();
 
 
 	pClient = findClient(pInfo, task_pid(current));
 	pClient = findClient(pInfo, task_pid(current));
 	if (pClient) {
 	if (pClient) {
@@ -1177,7 +1177,7 @@ static ssize_t r3964_write(struct tty_struct *tty, struct file *file,
 	add_tx_queue(pInfo, pHeader);
 	add_tx_queue(pInfo, pHeader);
 	trigger_transmit(pInfo);
 	trigger_transmit(pInfo);
 
 
-	unlock_kernel();
+	tty_unlock();
 
 
 	return 0;
 	return 0;
 }
 }

+ 14 - 3
drivers/char/n_tty.c

@@ -1102,6 +1102,11 @@ static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c)
 	if (I_IUCLC(tty) && L_IEXTEN(tty))
 	if (I_IUCLC(tty) && L_IEXTEN(tty))
 		c = tolower(c);
 		c = tolower(c);
 
 
+	if (L_EXTPROC(tty)) {
+		put_tty_queue(c, tty);
+		return;
+	}
+
 	if (tty->stopped && !tty->flow_stopped && I_IXON(tty) &&
 	if (tty->stopped && !tty->flow_stopped && I_IXON(tty) &&
 	    I_IXANY(tty) && c != START_CHAR(tty) && c != STOP_CHAR(tty) &&
 	    I_IXANY(tty) && c != START_CHAR(tty) && c != STOP_CHAR(tty) &&
 	    c != INTR_CHAR(tty) && c != QUIT_CHAR(tty) && c != SUSP_CHAR(tty)) {
 	    c != INTR_CHAR(tty) && c != QUIT_CHAR(tty) && c != SUSP_CHAR(tty)) {
@@ -1409,7 +1414,8 @@ static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp,
 
 
 	n_tty_set_room(tty);
 	n_tty_set_room(tty);
 
 
-	if (!tty->icanon && (tty->read_cnt >= tty->minimum_to_wake)) {
+	if ((!tty->icanon && (tty->read_cnt >= tty->minimum_to_wake)) ||
+		L_EXTPROC(tty)) {
 		kill_fasync(&tty->fasync, SIGIO, POLL_IN);
 		kill_fasync(&tty->fasync, SIGIO, POLL_IN);
 		if (waitqueue_active(&tty->read_wait))
 		if (waitqueue_active(&tty->read_wait))
 			wake_up_interruptible(&tty->read_wait);
 			wake_up_interruptible(&tty->read_wait);
@@ -1585,7 +1591,7 @@ static int n_tty_open(struct tty_struct *tty)
 static inline int input_available_p(struct tty_struct *tty, int amt)
 static inline int input_available_p(struct tty_struct *tty, int amt)
 {
 {
 	tty_flush_to_ldisc(tty);
 	tty_flush_to_ldisc(tty);
-	if (tty->icanon) {
+	if (tty->icanon && !L_EXTPROC(tty)) {
 		if (tty->canon_data)
 		if (tty->canon_data)
 			return 1;
 			return 1;
 	} else if (tty->read_cnt >= (amt ? amt : 1))
 	} else if (tty->read_cnt >= (amt ? amt : 1))
@@ -1632,6 +1638,11 @@ static int copy_from_read_buf(struct tty_struct *tty,
 		spin_lock_irqsave(&tty->read_lock, flags);
 		spin_lock_irqsave(&tty->read_lock, flags);
 		tty->read_tail = (tty->read_tail + n) & (N_TTY_BUF_SIZE-1);
 		tty->read_tail = (tty->read_tail + n) & (N_TTY_BUF_SIZE-1);
 		tty->read_cnt -= n;
 		tty->read_cnt -= n;
+		/* Turn single EOF into zero-length read */
+		if (L_EXTPROC(tty) && tty->icanon && n == 1) {
+			if (!tty->read_cnt && (*b)[n-1] == EOF_CHAR(tty))
+				n--;
+		}
 		spin_unlock_irqrestore(&tty->read_lock, flags);
 		spin_unlock_irqrestore(&tty->read_lock, flags);
 		*b += n;
 		*b += n;
 		*nr -= n;
 		*nr -= n;
@@ -1812,7 +1823,7 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
 			nr--;
 			nr--;
 		}
 		}
 
 
-		if (tty->icanon) {
+		if (tty->icanon && !L_EXTPROC(tty)) {
 			/* N.B. avoid overrun if nr == 0 */
 			/* N.B. avoid overrun if nr == 0 */
 			while (nr && tty->read_cnt) {
 			while (nr && tty->read_cnt) {
 				int eol;
 				int eol;

+ 3 - 1
drivers/char/nozomi.c

@@ -1611,6 +1611,8 @@ static int ntty_install(struct tty_driver *driver, struct tty_struct *tty)
 	ret = tty_init_termios(tty);
 	ret = tty_init_termios(tty);
 	if (ret == 0) {
 	if (ret == 0) {
 		tty_driver_kref_get(driver);
 		tty_driver_kref_get(driver);
+		tty->count++;
+		tty->driver_data = port;
 		driver->ttys[tty->index] = tty;
 		driver->ttys[tty->index] = tty;
 	}
 	}
 	return ret;
 	return ret;
@@ -1639,7 +1641,7 @@ static int ntty_activate(struct tty_port *tport, struct tty_struct *tty)
 
 
 static int ntty_open(struct tty_struct *tty, struct file *filp)
 static int ntty_open(struct tty_struct *tty, struct file *filp)
 {
 {
-	struct port *port = get_port_by_tty(tty);
+	struct port *port = tty->driver_data;
 	return tty_port_open(&port->port, tty, filp);
 	return tty_port_open(&port->port, tty, filp);
 }
 }
 
 

+ 34 - 13
drivers/char/pty.c

@@ -62,7 +62,9 @@ static void pty_close(struct tty_struct *tty, struct file *filp)
 		if (tty->driver == ptm_driver)
 		if (tty->driver == ptm_driver)
 			devpts_pty_kill(tty->link);
 			devpts_pty_kill(tty->link);
 #endif
 #endif
+		tty_unlock();
 		tty_vhangup(tty->link);
 		tty_vhangup(tty->link);
+		tty_lock();
 	}
 	}
 }
 }
 
 
@@ -171,6 +173,23 @@ static int pty_set_lock(struct tty_struct *tty, int __user *arg)
 	return 0;
 	return 0;
 }
 }
 
 
+/* Send a signal to the slave */
+static int pty_signal(struct tty_struct *tty, int sig)
+{
+	unsigned long flags;
+	struct pid *pgrp;
+
+	if (tty->link) {
+		spin_lock_irqsave(&tty->link->ctrl_lock, flags);
+		pgrp = get_pid(tty->link->pgrp);
+		spin_unlock_irqrestore(&tty->link->ctrl_lock, flags);
+
+		kill_pgrp(pgrp, sig, 1);
+		put_pid(pgrp);
+	}
+	return 0;
+}
+
 static void pty_flush_buffer(struct tty_struct *tty)
 static void pty_flush_buffer(struct tty_struct *tty)
 {
 {
 	struct tty_struct *to = tty->link;
 	struct tty_struct *to = tty->link;
@@ -321,6 +340,8 @@ static int pty_bsd_ioctl(struct tty_struct *tty, struct file *file,
 	switch (cmd) {
 	switch (cmd) {
 	case TIOCSPTLCK: /* Set PT Lock (disallow slave open) */
 	case TIOCSPTLCK: /* Set PT Lock (disallow slave open) */
 		return pty_set_lock(tty, (int __user *) arg);
 		return pty_set_lock(tty, (int __user *) arg);
+	case TIOCSIG:    /* Send signal to other side of pty */
+		return pty_signal(tty, (int) arg);
 	}
 	}
 	return -ENOIOCTLCMD;
 	return -ENOIOCTLCMD;
 }
 }
@@ -476,6 +497,8 @@ static int pty_unix98_ioctl(struct tty_struct *tty, struct file *file,
 		return pty_set_lock(tty, (int __user *)arg);
 		return pty_set_lock(tty, (int __user *)arg);
 	case TIOCGPTN: /* Get PT Number */
 	case TIOCGPTN: /* Get PT Number */
 		return put_user(tty->index, (unsigned int __user *)arg);
 		return put_user(tty->index, (unsigned int __user *)arg);
+	case TIOCSIG:    /* Send signal to other side of pty */
+		return pty_signal(tty, (int) arg);
 	}
 	}
 
 
 	return -ENOIOCTLCMD;
 	return -ENOIOCTLCMD;
@@ -626,7 +649,7 @@ static const struct tty_operations pty_unix98_ops = {
  *		allocated_ptys_lock handles the list of free pty numbers
  *		allocated_ptys_lock handles the list of free pty numbers
  */
  */
 
 
-static int __ptmx_open(struct inode *inode, struct file *filp)
+static int ptmx_open(struct inode *inode, struct file *filp)
 {
 {
 	struct tty_struct *tty;
 	struct tty_struct *tty;
 	int retval;
 	int retval;
@@ -635,11 +658,14 @@ static int __ptmx_open(struct inode *inode, struct file *filp)
 	nonseekable_open(inode, filp);
 	nonseekable_open(inode, filp);
 
 
 	/* find a device that is not in use. */
 	/* find a device that is not in use. */
+	tty_lock();
 	index = devpts_new_index(inode);
 	index = devpts_new_index(inode);
+	tty_unlock();
 	if (index < 0)
 	if (index < 0)
 		return index;
 		return index;
 
 
 	mutex_lock(&tty_mutex);
 	mutex_lock(&tty_mutex);
+	tty_lock();
 	tty = tty_init_dev(ptm_driver, index, 1);
 	tty = tty_init_dev(ptm_driver, index, 1);
 	mutex_unlock(&tty_mutex);
 	mutex_unlock(&tty_mutex);
 
 
@@ -657,26 +683,21 @@ static int __ptmx_open(struct inode *inode, struct file *filp)
 		goto out1;
 		goto out1;
 
 
 	retval = ptm_driver->ops->open(tty, filp);
 	retval = ptm_driver->ops->open(tty, filp);
-	if (!retval)
-		return 0;
+	if (retval)
+		goto out2;
 out1:
 out1:
+	tty_unlock();
+	return retval;
+out2:
+	tty_unlock();
 	tty_release(inode, filp);
 	tty_release(inode, filp);
 	return retval;
 	return retval;
 out:
 out:
 	devpts_kill_index(inode, index);
 	devpts_kill_index(inode, index);
+	tty_unlock();
 	return retval;
 	return retval;
 }
 }
 
 
-static int ptmx_open(struct inode *inode, struct file *filp)
-{
-	int ret;
-
-	lock_kernel();
-	ret = __ptmx_open(inode, filp);
-	unlock_kernel();
-	return ret;
-}
-
 static struct file_operations ptmx_fops;
 static struct file_operations ptmx_fops;
 
 
 static void __init unix98_pty_init(void)
 static void __init unix98_pty_init(void)

+ 8 - 6
drivers/char/riscom8.c

@@ -47,7 +47,6 @@
 #include <linux/init.h>
 #include <linux/init.h>
 #include <linux/delay.h>
 #include <linux/delay.h>
 #include <linux/tty_flip.h>
 #include <linux/tty_flip.h>
-#include <linux/smp_lock.h>
 #include <linux/spinlock.h>
 #include <linux/spinlock.h>
 #include <linux/device.h>
 #include <linux/device.h>
 
 
@@ -1184,6 +1183,7 @@ static int rc_set_serial_info(struct tty_struct *tty, struct riscom_port *port,
 	if (copy_from_user(&tmp, newinfo, sizeof(tmp)))
 	if (copy_from_user(&tmp, newinfo, sizeof(tmp)))
 		return -EFAULT;
 		return -EFAULT;
 
 
+	mutex_lock(&port->port.mutex);
 	change_speed = ((port->port.flags & ASYNC_SPD_MASK) !=
 	change_speed = ((port->port.flags & ASYNC_SPD_MASK) !=
 			(tmp.flags & ASYNC_SPD_MASK));
 			(tmp.flags & ASYNC_SPD_MASK));
 
 
@@ -1191,8 +1191,10 @@ static int rc_set_serial_info(struct tty_struct *tty, struct riscom_port *port,
 		if ((tmp.close_delay != port->port.close_delay) ||
 		if ((tmp.close_delay != port->port.close_delay) ||
 		    (tmp.closing_wait != port->port.closing_wait) ||
 		    (tmp.closing_wait != port->port.closing_wait) ||
 		    ((tmp.flags & ~ASYNC_USR_MASK) !=
 		    ((tmp.flags & ~ASYNC_USR_MASK) !=
-		     (port->port.flags & ~ASYNC_USR_MASK)))
+		     (port->port.flags & ~ASYNC_USR_MASK))) {
+			mutex_unlock(&port->port.mutex);
 			return -EPERM;
 			return -EPERM;
+		}
 		port->port.flags = ((port->port.flags & ~ASYNC_USR_MASK) |
 		port->port.flags = ((port->port.flags & ~ASYNC_USR_MASK) |
 			       (tmp.flags & ASYNC_USR_MASK));
 			       (tmp.flags & ASYNC_USR_MASK));
 	} else  {
 	} else  {
@@ -1208,6 +1210,7 @@ static int rc_set_serial_info(struct tty_struct *tty, struct riscom_port *port,
 		rc_change_speed(tty, bp, port);
 		rc_change_speed(tty, bp, port);
 		spin_unlock_irqrestore(&riscom_lock, flags);
 		spin_unlock_irqrestore(&riscom_lock, flags);
 	}
 	}
+	mutex_unlock(&port->port.mutex);
 	return 0;
 	return 0;
 }
 }
 
 
@@ -1220,12 +1223,15 @@ static int rc_get_serial_info(struct riscom_port *port,
 	memset(&tmp, 0, sizeof(tmp));
 	memset(&tmp, 0, sizeof(tmp));
 	tmp.type = PORT_CIRRUS;
 	tmp.type = PORT_CIRRUS;
 	tmp.line = port - rc_port;
 	tmp.line = port - rc_port;
+
+	mutex_lock(&port->port.mutex);
 	tmp.port = bp->base;
 	tmp.port = bp->base;
 	tmp.irq  = bp->irq;
 	tmp.irq  = bp->irq;
 	tmp.flags = port->port.flags;
 	tmp.flags = port->port.flags;
 	tmp.baud_base = (RC_OSCFREQ + CD180_TPC/2) / CD180_TPC;
 	tmp.baud_base = (RC_OSCFREQ + CD180_TPC/2) / CD180_TPC;
 	tmp.close_delay = port->port.close_delay * HZ/100;
 	tmp.close_delay = port->port.close_delay * HZ/100;
 	tmp.closing_wait = port->port.closing_wait * HZ/100;
 	tmp.closing_wait = port->port.closing_wait * HZ/100;
+	mutex_unlock(&port->port.mutex);
 	tmp.xmit_fifo_size = CD180_NFIFO;
 	tmp.xmit_fifo_size = CD180_NFIFO;
 	return copy_to_user(retinfo, &tmp, sizeof(tmp)) ? -EFAULT : 0;
 	return copy_to_user(retinfo, &tmp, sizeof(tmp)) ? -EFAULT : 0;
 }
 }
@@ -1242,14 +1248,10 @@ static int rc_ioctl(struct tty_struct *tty, struct file *filp,
 
 
 	switch (cmd) {
 	switch (cmd) {
 	case TIOCGSERIAL:
 	case TIOCGSERIAL:
-		lock_kernel();
 		retval = rc_get_serial_info(port, argp);
 		retval = rc_get_serial_info(port, argp);
-		unlock_kernel();
 		break;
 		break;
 	case TIOCSSERIAL:
 	case TIOCSSERIAL:
-		lock_kernel();
 		retval = rc_set_serial_info(tty, port, argp);
 		retval = rc_set_serial_info(tty, port, argp);
-		unlock_kernel();
 		break;
 		break;
 	default:
 	default:
 		retval = -ENOIOCTLCMD;
 		retval = -ENOIOCTLCMD;

+ 19 - 9
drivers/char/rocket.c

@@ -73,7 +73,6 @@
 #include <linux/tty_driver.h>
 #include <linux/tty_driver.h>
 #include <linux/tty_flip.h>
 #include <linux/tty_flip.h>
 #include <linux/serial.h>
 #include <linux/serial.h>
-#include <linux/smp_lock.h>
 #include <linux/string.h>
 #include <linux/string.h>
 #include <linux/fcntl.h>
 #include <linux/fcntl.h>
 #include <linux/ptrace.h>
 #include <linux/ptrace.h>
@@ -1017,6 +1016,7 @@ static void rp_close(struct tty_struct *tty, struct file *filp)
 	if (tty_port_close_start(port, tty, filp) == 0)
 	if (tty_port_close_start(port, tty, filp) == 0)
 		return;
 		return;
 
 
+	mutex_lock(&port->mutex);
 	cp = &info->channel;
 	cp = &info->channel;
 	/*
 	/*
 	 * Before we drop DTR, make sure the UART transmitter
 	 * Before we drop DTR, make sure the UART transmitter
@@ -1060,9 +1060,13 @@ static void rp_close(struct tty_struct *tty, struct file *filp)
 			info->xmit_buf = NULL;
 			info->xmit_buf = NULL;
 		}
 		}
 	}
 	}
+	spin_lock_irq(&port->lock);
 	info->port.flags &= ~(ASYNC_INITIALIZED | ASYNC_CLOSING | ASYNC_NORMAL_ACTIVE);
 	info->port.flags &= ~(ASYNC_INITIALIZED | ASYNC_CLOSING | ASYNC_NORMAL_ACTIVE);
 	tty->closing = 0;
 	tty->closing = 0;
+	spin_unlock_irq(&port->lock);
+	mutex_unlock(&port->mutex);
 	tty_port_tty_set(port, NULL);
 	tty_port_tty_set(port, NULL);
+
 	wake_up_interruptible(&port->close_wait);
 	wake_up_interruptible(&port->close_wait);
 	complete_all(&info->close_wait);
 	complete_all(&info->close_wait);
 	atomic_dec(&rp_num_ports_open);
 	atomic_dec(&rp_num_ports_open);
@@ -1210,11 +1214,13 @@ static int get_config(struct r_port *info, struct rocket_config __user *retinfo)
 	if (!retinfo)
 	if (!retinfo)
 		return -EFAULT;
 		return -EFAULT;
 	memset(&tmp, 0, sizeof (tmp));
 	memset(&tmp, 0, sizeof (tmp));
+	mutex_lock(&info->port.mutex);
 	tmp.line = info->line;
 	tmp.line = info->line;
 	tmp.flags = info->flags;
 	tmp.flags = info->flags;
 	tmp.close_delay = info->port.close_delay;
 	tmp.close_delay = info->port.close_delay;
 	tmp.closing_wait = info->port.closing_wait;
 	tmp.closing_wait = info->port.closing_wait;
 	tmp.port = rcktpt_io_addr[(info->line >> 5) & 3];
 	tmp.port = rcktpt_io_addr[(info->line >> 5) & 3];
+	mutex_unlock(&info->port.mutex);
 
 
 	if (copy_to_user(retinfo, &tmp, sizeof (*retinfo)))
 	if (copy_to_user(retinfo, &tmp, sizeof (*retinfo)))
 		return -EFAULT;
 		return -EFAULT;
@@ -1229,10 +1235,13 @@ static int set_config(struct tty_struct *tty, struct r_port *info,
 	if (copy_from_user(&new_serial, new_info, sizeof (new_serial)))
 	if (copy_from_user(&new_serial, new_info, sizeof (new_serial)))
 		return -EFAULT;
 		return -EFAULT;
 
 
+	mutex_lock(&info->port.mutex);
 	if (!capable(CAP_SYS_ADMIN))
 	if (!capable(CAP_SYS_ADMIN))
 	{
 	{
-		if ((new_serial.flags & ~ROCKET_USR_MASK) != (info->flags & ~ROCKET_USR_MASK))
+		if ((new_serial.flags & ~ROCKET_USR_MASK) != (info->flags & ~ROCKET_USR_MASK)) {
+			mutex_unlock(&info->port.mutex);
 			return -EPERM;
 			return -EPERM;
+		}
 		info->flags = ((info->flags & ~ROCKET_USR_MASK) | (new_serial.flags & ROCKET_USR_MASK));
 		info->flags = ((info->flags & ~ROCKET_USR_MASK) | (new_serial.flags & ROCKET_USR_MASK));
 		configure_r_port(tty, info, NULL);
 		configure_r_port(tty, info, NULL);
 		return 0;
 		return 0;
@@ -1250,6 +1259,7 @@ static int set_config(struct tty_struct *tty, struct r_port *info,
 		tty->alt_speed = 230400;
 		tty->alt_speed = 230400;
 	if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_WARP)
 	if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_WARP)
 		tty->alt_speed = 460800;
 		tty->alt_speed = 460800;
+	mutex_unlock(&info->port.mutex);
 
 
 	configure_r_port(tty, info, NULL);
 	configure_r_port(tty, info, NULL);
 	return 0;
 	return 0;
@@ -1325,8 +1335,6 @@ static int rp_ioctl(struct tty_struct *tty, struct file *file,
 	if (cmd != RCKP_GET_PORTS && rocket_paranoia_check(info, "rp_ioctl"))
 	if (cmd != RCKP_GET_PORTS && rocket_paranoia_check(info, "rp_ioctl"))
 		return -ENXIO;
 		return -ENXIO;
 
 
-	lock_kernel();
-
 	switch (cmd) {
 	switch (cmd) {
 	case RCKP_GET_STRUCT:
 	case RCKP_GET_STRUCT:
 		if (copy_to_user(argp, info, sizeof (struct r_port)))
 		if (copy_to_user(argp, info, sizeof (struct r_port)))
@@ -1350,7 +1358,6 @@ static int rp_ioctl(struct tty_struct *tty, struct file *file,
 	default:
 	default:
 		ret = -ENOIOCTLCMD;
 		ret = -ENOIOCTLCMD;
 	}
 	}
-	unlock_kernel();
 	return ret;
 	return ret;
 }
 }
 
 
@@ -1471,7 +1478,6 @@ static void rp_wait_until_sent(struct tty_struct *tty, int timeout)
 	       jiffies);
 	       jiffies);
 	printk(KERN_INFO "cps=%d...\n", info->cps);
 	printk(KERN_INFO "cps=%d...\n", info->cps);
 #endif
 #endif
-	lock_kernel();
 	while (1) {
 	while (1) {
 		txcnt = sGetTxCnt(cp);
 		txcnt = sGetTxCnt(cp);
 		if (!txcnt) {
 		if (!txcnt) {
@@ -1499,7 +1505,6 @@ static void rp_wait_until_sent(struct tty_struct *tty, int timeout)
 			break;
 			break;
 	}
 	}
 	__set_current_state(TASK_RUNNING);
 	__set_current_state(TASK_RUNNING);
-	unlock_kernel();
 #ifdef ROCKET_DEBUG_WAIT_UNTIL_SENT
 #ifdef ROCKET_DEBUG_WAIT_UNTIL_SENT
 	printk(KERN_INFO "txcnt = %d (jiff=%lu)...done\n", txcnt, jiffies);
 	printk(KERN_INFO "txcnt = %d (jiff=%lu)...done\n", txcnt, jiffies);
 #endif
 #endif
@@ -1512,6 +1517,7 @@ static void rp_hangup(struct tty_struct *tty)
 {
 {
 	CHANNEL_t *cp;
 	CHANNEL_t *cp;
 	struct r_port *info = tty->driver_data;
 	struct r_port *info = tty->driver_data;
+	unsigned long flags;
 
 
 	if (rocket_paranoia_check(info, "rp_hangup"))
 	if (rocket_paranoia_check(info, "rp_hangup"))
 		return;
 		return;
@@ -1520,11 +1526,15 @@ static void rp_hangup(struct tty_struct *tty)
 	printk(KERN_INFO "rp_hangup of ttyR%d...\n", info->line);
 	printk(KERN_INFO "rp_hangup of ttyR%d...\n", info->line);
 #endif
 #endif
 	rp_flush_buffer(tty);
 	rp_flush_buffer(tty);
-	if (info->port.flags & ASYNC_CLOSING)
+	spin_lock_irqsave(&info->port.lock, flags);
+	if (info->port.flags & ASYNC_CLOSING) {
+		spin_unlock_irqrestore(&info->port.lock, flags);
 		return;
 		return;
+	}
 	if (info->port.count)
 	if (info->port.count)
 		atomic_dec(&rp_num_ports_open);
 		atomic_dec(&rp_num_ports_open);
 	clear_bit((info->aiop * 8) + info->chan, (void *) &xmit_flags[info->board]);
 	clear_bit((info->aiop * 8) + info->chan, (void *) &xmit_flags[info->board]);
+	spin_unlock_irqrestore(&info->port.lock, flags);
 
 
 	tty_port_hangup(&info->port);
 	tty_port_hangup(&info->port);
 
 
@@ -1535,7 +1545,7 @@ static void rp_hangup(struct tty_struct *tty)
 	sDisCTSFlowCtl(cp);
 	sDisCTSFlowCtl(cp);
 	sDisTxSoftFlowCtl(cp);
 	sDisTxSoftFlowCtl(cp);
 	sClrTxXOFF(cp);
 	sClrTxXOFF(cp);
-	info->port.flags &= ~ASYNC_INITIALIZED;
+	clear_bit(ASYNCB_INITIALIZED, &info->port.flags);
 
 
 	wake_up_interruptible(&info->port.open_wait);
 	wake_up_interruptible(&info->port.open_wait);
 }
 }

+ 11 - 2
drivers/char/selection.c

@@ -26,6 +26,7 @@
 #include <linux/selection.h>
 #include <linux/selection.h>
 #include <linux/tiocl.h>
 #include <linux/tiocl.h>
 #include <linux/console.h>
 #include <linux/console.h>
+#include <linux/smp_lock.h>
 
 
 /* Don't take this from <ctype.h>: 011-015 on the screen aren't spaces */
 /* Don't take this from <ctype.h>: 011-015 on the screen aren't spaces */
 #define isspace(c)	((c) == ' ')
 #define isspace(c)	((c) == ' ')
@@ -312,12 +313,20 @@ int paste_selection(struct tty_struct *tty)
 	struct  tty_ldisc *ld;
 	struct  tty_ldisc *ld;
 	DECLARE_WAITQUEUE(wait, current);
 	DECLARE_WAITQUEUE(wait, current);
 
 
+	/* always called with BTM from vt_ioctl */
+	WARN_ON(!tty_locked());
+
 	acquire_console_sem();
 	acquire_console_sem();
 	poke_blanked_console();
 	poke_blanked_console();
 	release_console_sem();
 	release_console_sem();
 
 
-	ld = tty_ldisc_ref_wait(tty);
-	
+	ld = tty_ldisc_ref(tty);
+	if (!ld) {
+		tty_unlock();
+		ld = tty_ldisc_ref_wait(tty);
+		tty_lock();
+	}
+
 	add_wait_queue(&vc->paste_wait, &wait);
 	add_wait_queue(&vc->paste_wait, &wait);
 	while (sel_buffer && sel_buffer_lth > pasted) {
 	while (sel_buffer && sel_buffer_lth > pasted) {
 		set_current_state(TASK_INTERRUPTIBLE);
 		set_current_state(TASK_INTERRUPTIBLE);

+ 5 - 3
drivers/char/serial167.c

@@ -1505,7 +1505,7 @@ cy_ioctl(struct tty_struct *tty, struct file *file,
 	printk("cy_ioctl %s, cmd = %x arg = %lx\n", tty->name, cmd, arg);	/* */
 	printk("cy_ioctl %s, cmd = %x arg = %lx\n", tty->name, cmd, arg);	/* */
 #endif
 #endif
 
 
-	lock_kernel();
+	tty_lock();
 
 
 	switch (cmd) {
 	switch (cmd) {
 	case CYGETMON:
 	case CYGETMON:
@@ -1561,7 +1561,7 @@ cy_ioctl(struct tty_struct *tty, struct file *file,
 	default:
 	default:
 		ret_val = -ENOIOCTLCMD;
 		ret_val = -ENOIOCTLCMD;
 	}
 	}
-	unlock_kernel();
+	tty_unlock();
 
 
 #ifdef SERIAL_DEBUG_OTHER
 #ifdef SERIAL_DEBUG_OTHER
 	printk("cy_ioctl done\n");
 	printk("cy_ioctl done\n");
@@ -1786,7 +1786,9 @@ block_til_ready(struct tty_struct *tty, struct file *filp,
 		       tty->name, info->count);
 		       tty->name, info->count);
 		/**/
 		/**/
 #endif
 #endif
-		    schedule();
+		tty_unlock();
+		schedule();
+		tty_lock();
 	}
 	}
 	__set_current_state(TASK_RUNNING);
 	__set_current_state(TASK_RUNNING);
 	remove_wait_queue(&info->open_wait, &wait);
 	remove_wait_queue(&info->open_wait, &wait);

+ 7 - 6
drivers/char/specialix.c

@@ -1365,7 +1365,9 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp,
 			retval = -ERESTARTSYS;
 			retval = -ERESTARTSYS;
 			break;
 			break;
 		}
 		}
+		tty_unlock();
 		schedule();
 		schedule();
+		tty_lock();
 	}
 	}
 
 
 	set_current_state(TASK_RUNNING);
 	set_current_state(TASK_RUNNING);
@@ -1863,8 +1865,7 @@ static int sx_set_serial_info(struct specialix_port *port,
 		return -EFAULT;
 		return -EFAULT;
 	}
 	}
 
 
-	lock_kernel();
-
+	mutex_lock(&port->port.mutex);
 	change_speed = ((port->port.flags & ASYNC_SPD_MASK) !=
 	change_speed = ((port->port.flags & ASYNC_SPD_MASK) !=
 			(tmp.flags & ASYNC_SPD_MASK));
 			(tmp.flags & ASYNC_SPD_MASK));
 	change_speed |= (tmp.custom_divisor != port->custom_divisor);
 	change_speed |= (tmp.custom_divisor != port->custom_divisor);
@@ -1875,7 +1876,7 @@ static int sx_set_serial_info(struct specialix_port *port,
 		    ((tmp.flags & ~ASYNC_USR_MASK) !=
 		    ((tmp.flags & ~ASYNC_USR_MASK) !=
 		     (port->port.flags & ~ASYNC_USR_MASK))) {
 		     (port->port.flags & ~ASYNC_USR_MASK))) {
 			func_exit();
 			func_exit();
-			unlock_kernel();
+			mutex_unlock(&port->port.mutex);
 			return -EPERM;
 			return -EPERM;
 		}
 		}
 		port->port.flags = ((port->port.flags & ~ASYNC_USR_MASK) |
 		port->port.flags = ((port->port.flags & ~ASYNC_USR_MASK) |
@@ -1892,7 +1893,7 @@ static int sx_set_serial_info(struct specialix_port *port,
 		sx_change_speed(bp, port);
 		sx_change_speed(bp, port);
 
 
 	func_exit();
 	func_exit();
-	unlock_kernel();
+	mutex_unlock(&port->port.mutex);
 	return 0;
 	return 0;
 }
 }
 
 
@@ -1906,7 +1907,7 @@ static int sx_get_serial_info(struct specialix_port *port,
 	func_enter();
 	func_enter();
 
 
 	memset(&tmp, 0, sizeof(tmp));
 	memset(&tmp, 0, sizeof(tmp));
-	lock_kernel();
+	mutex_lock(&port->port.mutex);
 	tmp.type = PORT_CIRRUS;
 	tmp.type = PORT_CIRRUS;
 	tmp.line = port - sx_port;
 	tmp.line = port - sx_port;
 	tmp.port = bp->base;
 	tmp.port = bp->base;
@@ -1917,7 +1918,7 @@ static int sx_get_serial_info(struct specialix_port *port,
 	tmp.closing_wait = port->port.closing_wait * HZ/100;
 	tmp.closing_wait = port->port.closing_wait * HZ/100;
 	tmp.custom_divisor =  port->custom_divisor;
 	tmp.custom_divisor =  port->custom_divisor;
 	tmp.xmit_fifo_size = CD186x_NFIFO;
 	tmp.xmit_fifo_size = CD186x_NFIFO;
-	unlock_kernel();
+	mutex_unlock(&port->port.mutex);
 	if (copy_to_user(retinfo, &tmp, sizeof(tmp))) {
 	if (copy_to_user(retinfo, &tmp, sizeof(tmp))) {
 		func_exit();
 		func_exit();
 		return -EFAULT;
 		return -EFAULT;

+ 12 - 8
drivers/char/stallion.c

@@ -807,7 +807,6 @@ static void stl_waituntilsent(struct tty_struct *tty, int timeout)
 		timeout = HZ;
 		timeout = HZ;
 	tend = jiffies + timeout;
 	tend = jiffies + timeout;
 
 
-	lock_kernel();
 	while (stl_datastate(portp)) {
 	while (stl_datastate(portp)) {
 		if (signal_pending(current))
 		if (signal_pending(current))
 			break;
 			break;
@@ -815,7 +814,6 @@ static void stl_waituntilsent(struct tty_struct *tty, int timeout)
 		if (time_after_eq(jiffies, tend))
 		if (time_after_eq(jiffies, tend))
 			break;
 			break;
 	}
 	}
-	unlock_kernel();
 }
 }
 
 
 /*****************************************************************************/
 /*****************************************************************************/
@@ -1029,6 +1027,8 @@ static int stl_getserial(struct stlport *portp, struct serial_struct __user *sp)
 	pr_debug("stl_getserial(portp=%p,sp=%p)\n", portp, sp);
 	pr_debug("stl_getserial(portp=%p,sp=%p)\n", portp, sp);
 
 
 	memset(&sio, 0, sizeof(struct serial_struct));
 	memset(&sio, 0, sizeof(struct serial_struct));
+
+	mutex_lock(&portp->port.mutex);
 	sio.line = portp->portnr;
 	sio.line = portp->portnr;
 	sio.port = portp->ioaddr;
 	sio.port = portp->ioaddr;
 	sio.flags = portp->port.flags;
 	sio.flags = portp->port.flags;
@@ -1048,6 +1048,7 @@ static int stl_getserial(struct stlport *portp, struct serial_struct __user *sp)
 	brdp = stl_brds[portp->brdnr];
 	brdp = stl_brds[portp->brdnr];
 	if (brdp != NULL)
 	if (brdp != NULL)
 		sio.irq = brdp->irq;
 		sio.irq = brdp->irq;
+	mutex_unlock(&portp->port.mutex);
 
 
 	return copy_to_user(sp, &sio, sizeof(struct serial_struct)) ? -EFAULT : 0;
 	return copy_to_user(sp, &sio, sizeof(struct serial_struct)) ? -EFAULT : 0;
 }
 }
@@ -1069,12 +1070,15 @@ static int stl_setserial(struct tty_struct *tty, struct serial_struct __user *sp
 
 
 	if (copy_from_user(&sio, sp, sizeof(struct serial_struct)))
 	if (copy_from_user(&sio, sp, sizeof(struct serial_struct)))
 		return -EFAULT;
 		return -EFAULT;
+	mutex_lock(&portp->port.mutex);
 	if (!capable(CAP_SYS_ADMIN)) {
 	if (!capable(CAP_SYS_ADMIN)) {
 		if ((sio.baud_base != portp->baud_base) ||
 		if ((sio.baud_base != portp->baud_base) ||
 		    (sio.close_delay != portp->close_delay) ||
 		    (sio.close_delay != portp->close_delay) ||
 		    ((sio.flags & ~ASYNC_USR_MASK) !=
 		    ((sio.flags & ~ASYNC_USR_MASK) !=
-		    (portp->port.flags & ~ASYNC_USR_MASK)))
+		    (portp->port.flags & ~ASYNC_USR_MASK))) {
+			mutex_unlock(&portp->port.mutex);
 			return -EPERM;
 			return -EPERM;
+		}
 	} 
 	} 
 
 
 	portp->port.flags = (portp->port.flags & ~ASYNC_USR_MASK) |
 	portp->port.flags = (portp->port.flags & ~ASYNC_USR_MASK) |
@@ -1083,6 +1087,7 @@ static int stl_setserial(struct tty_struct *tty, struct serial_struct __user *sp
 	portp->close_delay = sio.close_delay;
 	portp->close_delay = sio.close_delay;
 	portp->closing_wait = sio.closing_wait;
 	portp->closing_wait = sio.closing_wait;
 	portp->custom_divisor = sio.custom_divisor;
 	portp->custom_divisor = sio.custom_divisor;
+	mutex_unlock(&portp->port.mutex);
 	stl_setport(portp, tty->termios);
 	stl_setport(portp, tty->termios);
 	return 0;
 	return 0;
 }
 }
@@ -1147,8 +1152,6 @@ static int stl_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd
 
 
 	rc = 0;
 	rc = 0;
 
 
-	lock_kernel();
-
 	switch (cmd) {
 	switch (cmd) {
 	case TIOCGSERIAL:
 	case TIOCGSERIAL:
 		rc = stl_getserial(portp, argp);
 		rc = stl_getserial(portp, argp);
@@ -1173,7 +1176,6 @@ static int stl_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd
 		rc = -ENOIOCTLCMD;
 		rc = -ENOIOCTLCMD;
 		break;
 		break;
 	}
 	}
-	unlock_kernel();
 	return rc;
 	return rc;
 }
 }
 
 
@@ -2327,6 +2329,7 @@ static int stl_getportstats(struct tty_struct *tty, struct stlport *portp, comst
 			return -ENODEV;
 			return -ENODEV;
 	}
 	}
 
 
+	mutex_lock(&portp->port.mutex);
 	portp->stats.state = portp->istate;
 	portp->stats.state = portp->istate;
 	portp->stats.flags = portp->port.flags;
 	portp->stats.flags = portp->port.flags;
 	portp->stats.hwid = portp->hwid;
 	portp->stats.hwid = portp->hwid;
@@ -2358,6 +2361,7 @@ static int stl_getportstats(struct tty_struct *tty, struct stlport *portp, comst
 		(STL_TXBUFSIZE - (tail - head));
 		(STL_TXBUFSIZE - (tail - head));
 
 
 	portp->stats.signals = (unsigned long) stl_getsignals(portp);
 	portp->stats.signals = (unsigned long) stl_getsignals(portp);
+	mutex_unlock(&portp->port.mutex);
 
 
 	return copy_to_user(cp, &portp->stats,
 	return copy_to_user(cp, &portp->stats,
 			    sizeof(comstats_t)) ? -EFAULT : 0;
 			    sizeof(comstats_t)) ? -EFAULT : 0;
@@ -2382,10 +2386,12 @@ static int stl_clrportstats(struct stlport *portp, comstats_t __user *cp)
 			return -ENODEV;
 			return -ENODEV;
 	}
 	}
 
 
+	mutex_lock(&portp->port.mutex);
 	memset(&portp->stats, 0, sizeof(comstats_t));
 	memset(&portp->stats, 0, sizeof(comstats_t));
 	portp->stats.brd = portp->brdnr;
 	portp->stats.brd = portp->brdnr;
 	portp->stats.panel = portp->panelnr;
 	portp->stats.panel = portp->panelnr;
 	portp->stats.port = portp->portnr;
 	portp->stats.port = portp->portnr;
+	mutex_unlock(&portp->port.mutex);
 	return copy_to_user(cp, &portp->stats,
 	return copy_to_user(cp, &portp->stats,
 			    sizeof(comstats_t)) ? -EFAULT : 0;
 			    sizeof(comstats_t)) ? -EFAULT : 0;
 }
 }
@@ -2451,7 +2457,6 @@ static long stl_memioctl(struct file *fp, unsigned int cmd, unsigned long arg)
 		return -ENODEV;
 		return -ENODEV;
 	rc = 0;
 	rc = 0;
 
 
-	lock_kernel();
 	switch (cmd) {
 	switch (cmd) {
 	case COM_GETPORTSTATS:
 	case COM_GETPORTSTATS:
 		rc = stl_getportstats(NULL, NULL, argp);
 		rc = stl_getportstats(NULL, NULL, argp);
@@ -2472,7 +2477,6 @@ static long stl_memioctl(struct file *fp, unsigned int cmd, unsigned long arg)
 		rc = -ENOIOCTLCMD;
 		rc = -ENOIOCTLCMD;
 		break;
 		break;
 	}
 	}
-	unlock_kernel();
 	return rc;
 	return rc;
 }
 }
 
 

+ 6 - 6
drivers/char/sx.c

@@ -1699,7 +1699,7 @@ static long sx_fw_ioctl(struct file *filp, unsigned int cmd,
 	if (!capable(CAP_SYS_RAWIO))
 	if (!capable(CAP_SYS_RAWIO))
 		return -EPERM;
 		return -EPERM;
 
 
-	lock_kernel();
+	tty_lock();
 
 
 	sx_dprintk(SX_DEBUG_FIRMWARE, "IOCTL %x: %lx\n", cmd, arg);
 	sx_dprintk(SX_DEBUG_FIRMWARE, "IOCTL %x: %lx\n", cmd, arg);
 
 
@@ -1848,7 +1848,7 @@ static long sx_fw_ioctl(struct file *filp, unsigned int cmd,
 		break;
 		break;
 	}
 	}
 out:
 out:
-	unlock_kernel();
+	tty_unlock();
 	func_exit();
 	func_exit();
 	return rc;
 	return rc;
 }
 }
@@ -1859,7 +1859,7 @@ static int sx_break(struct tty_struct *tty, int flag)
 	int rv;
 	int rv;
 
 
 	func_enter();
 	func_enter();
-	lock_kernel();
+	tty_lock();
 
 
 	if (flag)
 	if (flag)
 		rv = sx_send_command(port, HS_START, -1, HS_IDLE_BREAK);
 		rv = sx_send_command(port, HS_START, -1, HS_IDLE_BREAK);
@@ -1868,7 +1868,7 @@ static int sx_break(struct tty_struct *tty, int flag)
 	if (rv != 1)
 	if (rv != 1)
 		printk(KERN_ERR "sx: couldn't send break (%x).\n",
 		printk(KERN_ERR "sx: couldn't send break (%x).\n",
 			read_sx_byte(port->board, CHAN_OFFSET(port, hi_hstat)));
 			read_sx_byte(port->board, CHAN_OFFSET(port, hi_hstat)));
-	unlock_kernel();
+	tty_unlock();
 	func_exit();
 	func_exit();
 	return 0;
 	return 0;
 }
 }
@@ -1909,7 +1909,7 @@ static int sx_ioctl(struct tty_struct *tty, struct file *filp,
 	/* func_enter2(); */
 	/* func_enter2(); */
 
 
 	rc = 0;
 	rc = 0;
-	lock_kernel();
+	tty_lock();
 	switch (cmd) {
 	switch (cmd) {
 	case TIOCGSERIAL:
 	case TIOCGSERIAL:
 		rc = gs_getserial(&port->gs, argp);
 		rc = gs_getserial(&port->gs, argp);
@@ -1921,7 +1921,7 @@ static int sx_ioctl(struct tty_struct *tty, struct file *filp,
 		rc = -ENOIOCTLCMD;
 		rc = -ENOIOCTLCMD;
 		break;
 		break;
 	}
 	}
-	unlock_kernel();
+	tty_unlock();
 
 
 	/* func_exit(); */
 	/* func_exit(); */
 	return rc;
 	return rc;

+ 12 - 9
drivers/char/synclink.c

@@ -81,7 +81,6 @@
 #include <linux/mm.h>
 #include <linux/mm.h>
 #include <linux/seq_file.h>
 #include <linux/seq_file.h>
 #include <linux/slab.h>
 #include <linux/slab.h>
-#include <linux/smp_lock.h>
 #include <linux/delay.h>
 #include <linux/delay.h>
 #include <linux/netdevice.h>
 #include <linux/netdevice.h>
 #include <linux/vmalloc.h>
 #include <linux/vmalloc.h>
@@ -2436,7 +2435,9 @@ static int mgsl_get_stats(struct mgsl_struct * info, struct mgsl_icount __user *
 	if (!user_icount) {
 	if (!user_icount) {
 		memset(&info->icount, 0, sizeof(info->icount));
 		memset(&info->icount, 0, sizeof(info->icount));
 	} else {
 	} else {
+		mutex_lock(&info->port.mutex);
 		COPY_TO_USER(err, user_icount, &info->icount, sizeof(struct mgsl_icount));
 		COPY_TO_USER(err, user_icount, &info->icount, sizeof(struct mgsl_icount));
+		mutex_unlock(&info->port.mutex);
 		if (err)
 		if (err)
 			return -EFAULT;
 			return -EFAULT;
 	}
 	}
@@ -2461,7 +2462,9 @@ static int mgsl_get_params(struct mgsl_struct * info, MGSL_PARAMS __user *user_p
 		printk("%s(%d):mgsl_get_params(%s)\n",
 		printk("%s(%d):mgsl_get_params(%s)\n",
 			 __FILE__,__LINE__, info->device_name);
 			 __FILE__,__LINE__, info->device_name);
 			
 			
+	mutex_lock(&info->port.mutex);
 	COPY_TO_USER(err,user_params, &info->params, sizeof(MGSL_PARAMS));
 	COPY_TO_USER(err,user_params, &info->params, sizeof(MGSL_PARAMS));
+	mutex_unlock(&info->port.mutex);
 	if (err) {
 	if (err) {
 		if ( debug_level >= DEBUG_LEVEL_INFO )
 		if ( debug_level >= DEBUG_LEVEL_INFO )
 			printk( "%s(%d):mgsl_get_params(%s) user buffer copy failed\n",
 			printk( "%s(%d):mgsl_get_params(%s) user buffer copy failed\n",
@@ -2501,11 +2504,13 @@ static int mgsl_set_params(struct mgsl_struct * info, MGSL_PARAMS __user *new_pa
 		return -EFAULT;
 		return -EFAULT;
 	}
 	}
 	
 	
+	mutex_lock(&info->port.mutex);
 	spin_lock_irqsave(&info->irq_spinlock,flags);
 	spin_lock_irqsave(&info->irq_spinlock,flags);
 	memcpy(&info->params,&tmp_params,sizeof(MGSL_PARAMS));
 	memcpy(&info->params,&tmp_params,sizeof(MGSL_PARAMS));
 	spin_unlock_irqrestore(&info->irq_spinlock,flags);
 	spin_unlock_irqrestore(&info->irq_spinlock,flags);
 	
 	
  	mgsl_change_params(info);
  	mgsl_change_params(info);
+	mutex_unlock(&info->port.mutex);
 	
 	
 	return 0;
 	return 0;
 	
 	
@@ -2935,7 +2940,6 @@ static int mgsl_ioctl(struct tty_struct *tty, struct file * file,
 		    unsigned int cmd, unsigned long arg)
 		    unsigned int cmd, unsigned long arg)
 {
 {
 	struct mgsl_struct * info = tty->driver_data;
 	struct mgsl_struct * info = tty->driver_data;
-	int ret;
 	
 	
 	if (debug_level >= DEBUG_LEVEL_INFO)
 	if (debug_level >= DEBUG_LEVEL_INFO)
 		printk("%s(%d):mgsl_ioctl %s cmd=%08X\n", __FILE__,__LINE__,
 		printk("%s(%d):mgsl_ioctl %s cmd=%08X\n", __FILE__,__LINE__,
@@ -2950,10 +2954,7 @@ static int mgsl_ioctl(struct tty_struct *tty, struct file * file,
 		    return -EIO;
 		    return -EIO;
 	}
 	}
 
 
-	lock_kernel();
-	ret = mgsl_ioctl_common(info, cmd, arg);
-	unlock_kernel();
-	return ret;
+	return mgsl_ioctl_common(info, cmd, arg);
 }
 }
 
 
 static int mgsl_ioctl_common(struct mgsl_struct *info, unsigned int cmd, unsigned long arg)
 static int mgsl_ioctl_common(struct mgsl_struct *info, unsigned int cmd, unsigned long arg)
@@ -3109,12 +3110,14 @@ static void mgsl_close(struct tty_struct *tty, struct file * filp)
 
 
 	if (tty_port_close_start(&info->port, tty, filp) == 0)			 
 	if (tty_port_close_start(&info->port, tty, filp) == 0)			 
 		goto cleanup;
 		goto cleanup;
-			
+
+	mutex_lock(&info->port.mutex);
  	if (info->port.flags & ASYNC_INITIALIZED)
  	if (info->port.flags & ASYNC_INITIALIZED)
  		mgsl_wait_until_sent(tty, info->timeout);
  		mgsl_wait_until_sent(tty, info->timeout);
 	mgsl_flush_buffer(tty);
 	mgsl_flush_buffer(tty);
 	tty_ldisc_flush(tty);
 	tty_ldisc_flush(tty);
 	shutdown(info);
 	shutdown(info);
+	mutex_unlock(&info->port.mutex);
 
 
 	tty_port_close_end(&info->port, tty);	
 	tty_port_close_end(&info->port, tty);	
 	info->port.tty = NULL;
 	info->port.tty = NULL;
@@ -3162,7 +3165,6 @@ static void mgsl_wait_until_sent(struct tty_struct *tty, int timeout)
 	 * Note: use tight timings here to satisfy the NIST-PCTS.
 	 * Note: use tight timings here to satisfy the NIST-PCTS.
 	 */ 
 	 */ 
 
 
-	lock_kernel();
 	if ( info->params.data_rate ) {
 	if ( info->params.data_rate ) {
 	       	char_time = info->timeout/(32 * 5);
 	       	char_time = info->timeout/(32 * 5);
 		if (!char_time)
 		if (!char_time)
@@ -3192,7 +3194,6 @@ static void mgsl_wait_until_sent(struct tty_struct *tty, int timeout)
 				break;
 				break;
 		}
 		}
 	}
 	}
-	unlock_kernel();
       
       
 exit:
 exit:
 	if (debug_level >= DEBUG_LEVEL_INFO)
 	if (debug_level >= DEBUG_LEVEL_INFO)
@@ -3348,7 +3349,9 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp,
 			printk("%s(%d):block_til_ready blocking on %s count=%d\n",
 			printk("%s(%d):block_til_ready blocking on %s count=%d\n",
 				 __FILE__,__LINE__, tty->driver->name, port->count );
 				 __FILE__,__LINE__, tty->driver->name, port->count );
 				 
 				 
+		tty_unlock();
 		schedule();
 		schedule();
+		tty_lock();
 	}
 	}
 	
 	
 	set_current_state(TASK_RUNNING);
 	set_current_state(TASK_RUNNING);

+ 47 - 45
drivers/char/synclink_gt.c

@@ -40,8 +40,8 @@
 #define DBGBH(fmt) if (debug_level >= DEBUG_LEVEL_BH) printk fmt
 #define DBGBH(fmt) if (debug_level >= DEBUG_LEVEL_BH) printk fmt
 #define DBGISR(fmt) if (debug_level >= DEBUG_LEVEL_ISR) printk fmt
 #define DBGISR(fmt) if (debug_level >= DEBUG_LEVEL_ISR) printk fmt
 #define DBGDATA(info, buf, size, label) if (debug_level >= DEBUG_LEVEL_DATA) trace_block((info), (buf), (size), (label))
 #define DBGDATA(info, buf, size, label) if (debug_level >= DEBUG_LEVEL_DATA) trace_block((info), (buf), (size), (label))
-//#define DBGTBUF(info) dump_tbufs(info)
-//#define DBGRBUF(info) dump_rbufs(info)
+/*#define DBGTBUF(info) dump_tbufs(info)*/
+/*#define DBGRBUF(info) dump_rbufs(info)*/
 
 
 
 
 #include <linux/module.h>
 #include <linux/module.h>
@@ -62,7 +62,6 @@
 #include <linux/mm.h>
 #include <linux/mm.h>
 #include <linux/seq_file.h>
 #include <linux/seq_file.h>
 #include <linux/slab.h>
 #include <linux/slab.h>
-#include <linux/smp_lock.h>
 #include <linux/netdevice.h>
 #include <linux/netdevice.h>
 #include <linux/vmalloc.h>
 #include <linux/vmalloc.h>
 #include <linux/init.h>
 #include <linux/init.h>
@@ -676,12 +675,14 @@ static int open(struct tty_struct *tty, struct file *filp)
 		goto cleanup;
 		goto cleanup;
 	}
 	}
 
 
+	mutex_lock(&info->port.mutex);
 	info->port.tty->low_latency = (info->port.flags & ASYNC_LOW_LATENCY) ? 1 : 0;
 	info->port.tty->low_latency = (info->port.flags & ASYNC_LOW_LATENCY) ? 1 : 0;
 
 
 	spin_lock_irqsave(&info->netlock, flags);
 	spin_lock_irqsave(&info->netlock, flags);
 	if (info->netcount) {
 	if (info->netcount) {
 		retval = -EBUSY;
 		retval = -EBUSY;
 		spin_unlock_irqrestore(&info->netlock, flags);
 		spin_unlock_irqrestore(&info->netlock, flags);
+		mutex_unlock(&info->port.mutex);
 		goto cleanup;
 		goto cleanup;
 	}
 	}
 	info->port.count++;
 	info->port.count++;
@@ -693,7 +694,7 @@ static int open(struct tty_struct *tty, struct file *filp)
 		if (retval < 0)
 		if (retval < 0)
 			goto cleanup;
 			goto cleanup;
 	}
 	}
-
+	mutex_unlock(&info->port.mutex);
 	retval = block_til_ready(tty, filp, info);
 	retval = block_til_ready(tty, filp, info);
 	if (retval) {
 	if (retval) {
 		DBGINFO(("%s block_til_ready rc=%d\n", info->device_name, retval));
 		DBGINFO(("%s block_til_ready rc=%d\n", info->device_name, retval));
@@ -725,12 +726,14 @@ static void close(struct tty_struct *tty, struct file *filp)
 	if (tty_port_close_start(&info->port, tty, filp) == 0)
 	if (tty_port_close_start(&info->port, tty, filp) == 0)
 		goto cleanup;
 		goto cleanup;
 
 
+	mutex_lock(&info->port.mutex);
  	if (info->port.flags & ASYNC_INITIALIZED)
  	if (info->port.flags & ASYNC_INITIALIZED)
  		wait_until_sent(tty, info->timeout);
  		wait_until_sent(tty, info->timeout);
 	flush_buffer(tty);
 	flush_buffer(tty);
 	tty_ldisc_flush(tty);
 	tty_ldisc_flush(tty);
 
 
 	shutdown(info);
 	shutdown(info);
+	mutex_unlock(&info->port.mutex);
 
 
 	tty_port_close_end(&info->port, tty);
 	tty_port_close_end(&info->port, tty);
 	info->port.tty = NULL;
 	info->port.tty = NULL;
@@ -741,17 +744,23 @@ static void close(struct tty_struct *tty, struct file *filp)
 static void hangup(struct tty_struct *tty)
 static void hangup(struct tty_struct *tty)
 {
 {
 	struct slgt_info *info = tty->driver_data;
 	struct slgt_info *info = tty->driver_data;
+	unsigned long flags;
 
 
 	if (sanity_check(info, tty->name, "hangup"))
 	if (sanity_check(info, tty->name, "hangup"))
 		return;
 		return;
 	DBGINFO(("%s hangup\n", info->device_name));
 	DBGINFO(("%s hangup\n", info->device_name));
 
 
 	flush_buffer(tty);
 	flush_buffer(tty);
+
+	mutex_lock(&info->port.mutex);
 	shutdown(info);
 	shutdown(info);
 
 
+	spin_lock_irqsave(&info->port.lock, flags);
 	info->port.count = 0;
 	info->port.count = 0;
 	info->port.flags &= ~ASYNC_NORMAL_ACTIVE;
 	info->port.flags &= ~ASYNC_NORMAL_ACTIVE;
 	info->port.tty = NULL;
 	info->port.tty = NULL;
+	spin_unlock_irqrestore(&info->port.lock, flags);
+	mutex_unlock(&info->port.mutex);
 
 
 	wake_up_interruptible(&info->port.open_wait);
 	wake_up_interruptible(&info->port.open_wait);
 }
 }
@@ -901,8 +910,6 @@ static void wait_until_sent(struct tty_struct *tty, int timeout)
 	 * Note: use tight timings here to satisfy the NIST-PCTS.
 	 * Note: use tight timings here to satisfy the NIST-PCTS.
 	 */
 	 */
 
 
-	lock_kernel();
-
 	if (info->params.data_rate) {
 	if (info->params.data_rate) {
 	       	char_time = info->timeout/(32 * 5);
 	       	char_time = info->timeout/(32 * 5);
 		if (!char_time)
 		if (!char_time)
@@ -920,8 +927,6 @@ static void wait_until_sent(struct tty_struct *tty, int timeout)
 		if (timeout && time_after(jiffies, orig_jiffies + timeout))
 		if (timeout && time_after(jiffies, orig_jiffies + timeout))
 			break;
 			break;
 	}
 	}
-	unlock_kernel();
-
 exit:
 exit:
 	DBGINFO(("%s wait_until_sent exit\n", info->device_name));
 	DBGINFO(("%s wait_until_sent exit\n", info->device_name));
 }
 }
@@ -1041,8 +1046,37 @@ static int ioctl(struct tty_struct *tty, struct file *file,
 		    return -EIO;
 		    return -EIO;
 	}
 	}
 
 
-	lock_kernel();
-
+	switch (cmd) {
+	case MGSL_IOCWAITEVENT:
+		return wait_mgsl_event(info, argp);
+	case TIOCMIWAIT:
+		return modem_input_wait(info,(int)arg);
+	case TIOCGICOUNT:
+		spin_lock_irqsave(&info->lock,flags);
+		cnow = info->icount;
+		spin_unlock_irqrestore(&info->lock,flags);
+		p_cuser = argp;
+		if (put_user(cnow.cts, &p_cuser->cts) ||
+		    put_user(cnow.dsr, &p_cuser->dsr) ||
+		    put_user(cnow.rng, &p_cuser->rng) ||
+		    put_user(cnow.dcd, &p_cuser->dcd) ||
+		    put_user(cnow.rx, &p_cuser->rx) ||
+		    put_user(cnow.tx, &p_cuser->tx) ||
+		    put_user(cnow.frame, &p_cuser->frame) ||
+		    put_user(cnow.overrun, &p_cuser->overrun) ||
+		    put_user(cnow.parity, &p_cuser->parity) ||
+		    put_user(cnow.brk, &p_cuser->brk) ||
+		    put_user(cnow.buf_overrun, &p_cuser->buf_overrun))
+			return -EFAULT;
+		return 0;
+	case MGSL_IOCSGPIO:
+		return set_gpio(info, argp);
+	case MGSL_IOCGGPIO:
+		return get_gpio(info, argp);
+	case MGSL_IOCWAITGPIO:
+		return wait_gpio(info, argp);
+	}
+	mutex_lock(&info->port.mutex);
 	switch (cmd) {
 	switch (cmd) {
 	case MGSL_IOCGPARAMS:
 	case MGSL_IOCGPARAMS:
 		ret = get_params(info, argp);
 		ret = get_params(info, argp);
@@ -1068,50 +1102,16 @@ static int ioctl(struct tty_struct *tty, struct file *file,
 	case MGSL_IOCGSTATS:
 	case MGSL_IOCGSTATS:
 		ret = get_stats(info, argp);
 		ret = get_stats(info, argp);
 		break;
 		break;
-	case MGSL_IOCWAITEVENT:
-		ret = wait_mgsl_event(info, argp);
-		break;
-	case TIOCMIWAIT:
-		ret = modem_input_wait(info,(int)arg);
-		break;
 	case MGSL_IOCGIF:
 	case MGSL_IOCGIF:
 		ret = get_interface(info, argp);
 		ret = get_interface(info, argp);
 		break;
 		break;
 	case MGSL_IOCSIF:
 	case MGSL_IOCSIF:
 		ret = set_interface(info,(int)arg);
 		ret = set_interface(info,(int)arg);
 		break;
 		break;
-	case MGSL_IOCSGPIO:
-		ret = set_gpio(info, argp);
-		break;
-	case MGSL_IOCGGPIO:
-		ret = get_gpio(info, argp);
-		break;
-	case MGSL_IOCWAITGPIO:
-		ret = wait_gpio(info, argp);
-		break;
-	case TIOCGICOUNT:
-		spin_lock_irqsave(&info->lock,flags);
-		cnow = info->icount;
-		spin_unlock_irqrestore(&info->lock,flags);
-		p_cuser = argp;
-		if (put_user(cnow.cts, &p_cuser->cts) ||
-		    put_user(cnow.dsr, &p_cuser->dsr) ||
-		    put_user(cnow.rng, &p_cuser->rng) ||
-		    put_user(cnow.dcd, &p_cuser->dcd) ||
-		    put_user(cnow.rx, &p_cuser->rx) ||
-		    put_user(cnow.tx, &p_cuser->tx) ||
-		    put_user(cnow.frame, &p_cuser->frame) ||
-		    put_user(cnow.overrun, &p_cuser->overrun) ||
-		    put_user(cnow.parity, &p_cuser->parity) ||
-		    put_user(cnow.brk, &p_cuser->brk) ||
-		    put_user(cnow.buf_overrun, &p_cuser->buf_overrun))
-			ret = -EFAULT;
-		ret = 0;
-		break;
 	default:
 	default:
 		ret = -ENOIOCTLCMD;
 		ret = -ENOIOCTLCMD;
 	}
 	}
-	unlock_kernel();
+	mutex_unlock(&info->port.mutex);
 	return ret;
 	return ret;
 }
 }
 
 
@@ -3244,7 +3244,9 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp,
 		}
 		}
 
 
 		DBGINFO(("%s block_til_ready wait\n", tty->driver->name));
 		DBGINFO(("%s block_til_ready wait\n", tty->driver->name));
+		tty_unlock();
 		schedule();
 		schedule();
+		tty_lock();
 	}
 	}
 
 
 	set_current_state(TASK_RUNNING);
 	set_current_state(TASK_RUNNING);

+ 22 - 21
drivers/char/synclinkmp.c

@@ -52,7 +52,6 @@
 #include <linux/mm.h>
 #include <linux/mm.h>
 #include <linux/seq_file.h>
 #include <linux/seq_file.h>
 #include <linux/slab.h>
 #include <linux/slab.h>
-#include <linux/smp_lock.h>
 #include <linux/netdevice.h>
 #include <linux/netdevice.h>
 #include <linux/vmalloc.h>
 #include <linux/vmalloc.h>
 #include <linux/init.h>
 #include <linux/init.h>
@@ -813,13 +812,15 @@ static void close(struct tty_struct *tty, struct file *filp)
 
 
 	if (tty_port_close_start(&info->port, tty, filp) == 0)
 	if (tty_port_close_start(&info->port, tty, filp) == 0)
 		goto cleanup;
 		goto cleanup;
-		
+
+	mutex_lock(&info->port.mutex);
  	if (info->port.flags & ASYNC_INITIALIZED)
  	if (info->port.flags & ASYNC_INITIALIZED)
  		wait_until_sent(tty, info->timeout);
  		wait_until_sent(tty, info->timeout);
 
 
 	flush_buffer(tty);
 	flush_buffer(tty);
 	tty_ldisc_flush(tty);
 	tty_ldisc_flush(tty);
 	shutdown(info);
 	shutdown(info);
+	mutex_unlock(&info->port.mutex);
 
 
 	tty_port_close_end(&info->port, tty);
 	tty_port_close_end(&info->port, tty);
 	info->port.tty = NULL;
 	info->port.tty = NULL;
@@ -835,6 +836,7 @@ static void close(struct tty_struct *tty, struct file *filp)
 static void hangup(struct tty_struct *tty)
 static void hangup(struct tty_struct *tty)
 {
 {
 	SLMP_INFO *info = tty->driver_data;
 	SLMP_INFO *info = tty->driver_data;
+	unsigned long flags;
 
 
 	if (debug_level >= DEBUG_LEVEL_INFO)
 	if (debug_level >= DEBUG_LEVEL_INFO)
 		printk("%s(%d):%s hangup()\n",
 		printk("%s(%d):%s hangup()\n",
@@ -843,12 +845,16 @@ static void hangup(struct tty_struct *tty)
 	if (sanity_check(info, tty->name, "hangup"))
 	if (sanity_check(info, tty->name, "hangup"))
 		return;
 		return;
 
 
+	mutex_lock(&info->port.mutex);
 	flush_buffer(tty);
 	flush_buffer(tty);
 	shutdown(info);
 	shutdown(info);
 
 
+	spin_lock_irqsave(&info->port.lock, flags);
 	info->port.count = 0;
 	info->port.count = 0;
 	info->port.flags &= ~ASYNC_NORMAL_ACTIVE;
 	info->port.flags &= ~ASYNC_NORMAL_ACTIVE;
 	info->port.tty = NULL;
 	info->port.tty = NULL;
+	spin_unlock_irqrestore(&info->port.lock, flags);
+	mutex_unlock(&info->port.mutex);
 
 
 	wake_up_interruptible(&info->port.open_wait);
 	wake_up_interruptible(&info->port.open_wait);
 }
 }
@@ -1062,9 +1068,7 @@ static void wait_until_sent(struct tty_struct *tty, int timeout)
 	if (sanity_check(info, tty->name, "wait_until_sent"))
 	if (sanity_check(info, tty->name, "wait_until_sent"))
 		return;
 		return;
 
 
-	lock_kernel();
-
-	if (!(info->port.flags & ASYNC_INITIALIZED))
+	if (!test_bit(ASYNCB_INITIALIZED, &info->port.flags))
 		goto exit;
 		goto exit;
 
 
 	orig_jiffies = jiffies;
 	orig_jiffies = jiffies;
@@ -1094,8 +1098,10 @@ static void wait_until_sent(struct tty_struct *tty, int timeout)
 				break;
 				break;
 		}
 		}
 	} else {
 	} else {
-		//TODO: determine if there is something similar to USC16C32
-		// 	TXSTATUS_ALL_SENT status
+		/*
+		 * TODO: determine if there is something similar to USC16C32
+		 * 	 TXSTATUS_ALL_SENT status
+		 */
 		while ( info->tx_active && info->tx_enabled) {
 		while ( info->tx_active && info->tx_enabled) {
 			msleep_interruptible(jiffies_to_msecs(char_time));
 			msleep_interruptible(jiffies_to_msecs(char_time));
 			if (signal_pending(current))
 			if (signal_pending(current))
@@ -1106,7 +1112,6 @@ static void wait_until_sent(struct tty_struct *tty, int timeout)
 	}
 	}
 
 
 exit:
 exit:
-	unlock_kernel();
 	if (debug_level >= DEBUG_LEVEL_INFO)
 	if (debug_level >= DEBUG_LEVEL_INFO)
 		printk("%s(%d):%s wait_until_sent() exit\n",
 		printk("%s(%d):%s wait_until_sent() exit\n",
 			 __FILE__,__LINE__, info->device_name );
 			 __FILE__,__LINE__, info->device_name );
@@ -1122,7 +1127,6 @@ static int write_room(struct tty_struct *tty)
 	if (sanity_check(info, tty->name, "write_room"))
 	if (sanity_check(info, tty->name, "write_room"))
 		return 0;
 		return 0;
 
 
-	lock_kernel();
 	if (info->params.mode == MGSL_MODE_HDLC) {
 	if (info->params.mode == MGSL_MODE_HDLC) {
 		ret = (info->tx_active) ? 0 : HDLC_MAX_FRAME_SIZE;
 		ret = (info->tx_active) ? 0 : HDLC_MAX_FRAME_SIZE;
 	} else {
 	} else {
@@ -1130,7 +1134,6 @@ static int write_room(struct tty_struct *tty)
 		if (ret < 0)
 		if (ret < 0)
 			ret = 0;
 			ret = 0;
 	}
 	}
-	unlock_kernel();
 
 
 	if (debug_level >= DEBUG_LEVEL_INFO)
 	if (debug_level >= DEBUG_LEVEL_INFO)
 		printk("%s(%d):%s write_room()=%d\n",
 		printk("%s(%d):%s write_room()=%d\n",
@@ -1251,7 +1254,7 @@ static void tx_release(struct tty_struct *tty)
  *
  *
  * Return Value:	0 if success, otherwise error code
  * Return Value:	0 if success, otherwise error code
  */
  */
-static int do_ioctl(struct tty_struct *tty, struct file *file,
+static int ioctl(struct tty_struct *tty, struct file *file,
 		 unsigned int cmd, unsigned long arg)
 		 unsigned int cmd, unsigned long arg)
 {
 {
 	SLMP_INFO *info = tty->driver_data;
 	SLMP_INFO *info = tty->driver_data;
@@ -1341,16 +1344,6 @@ static int do_ioctl(struct tty_struct *tty, struct file *file,
 	return 0;
 	return 0;
 }
 }
 
 
-static int ioctl(struct tty_struct *tty, struct file *file,
-		 unsigned int cmd, unsigned long arg)
-{
-	int ret;
-	lock_kernel();
-	ret = do_ioctl(tty, file, cmd, arg);
-	unlock_kernel();
-	return ret;
-}
-
 /*
 /*
  * /proc fs routines....
  * /proc fs routines....
  */
  */
@@ -2883,7 +2876,9 @@ static int get_stats(SLMP_INFO * info, struct mgsl_icount __user *user_icount)
 	if (!user_icount) {
 	if (!user_icount) {
 		memset(&info->icount, 0, sizeof(info->icount));
 		memset(&info->icount, 0, sizeof(info->icount));
 	} else {
 	} else {
+		mutex_lock(&info->port.mutex);
 		COPY_TO_USER(err, user_icount, &info->icount, sizeof(struct mgsl_icount));
 		COPY_TO_USER(err, user_icount, &info->icount, sizeof(struct mgsl_icount));
+		mutex_unlock(&info->port.mutex);
 		if (err)
 		if (err)
 			return -EFAULT;
 			return -EFAULT;
 	}
 	}
@@ -2898,7 +2893,9 @@ static int get_params(SLMP_INFO * info, MGSL_PARAMS __user *user_params)
 		printk("%s(%d):%s get_params()\n",
 		printk("%s(%d):%s get_params()\n",
 			 __FILE__,__LINE__, info->device_name);
 			 __FILE__,__LINE__, info->device_name);
 
 
+	mutex_lock(&info->port.mutex);
 	COPY_TO_USER(err,user_params, &info->params, sizeof(MGSL_PARAMS));
 	COPY_TO_USER(err,user_params, &info->params, sizeof(MGSL_PARAMS));
+	mutex_unlock(&info->port.mutex);
 	if (err) {
 	if (err) {
 		if ( debug_level >= DEBUG_LEVEL_INFO )
 		if ( debug_level >= DEBUG_LEVEL_INFO )
 			printk( "%s(%d):%s get_params() user buffer copy failed\n",
 			printk( "%s(%d):%s get_params() user buffer copy failed\n",
@@ -2926,11 +2923,13 @@ static int set_params(SLMP_INFO * info, MGSL_PARAMS __user *new_params)
 		return -EFAULT;
 		return -EFAULT;
 	}
 	}
 
 
+	mutex_lock(&info->port.mutex);
 	spin_lock_irqsave(&info->lock,flags);
 	spin_lock_irqsave(&info->lock,flags);
 	memcpy(&info->params,&tmp_params,sizeof(MGSL_PARAMS));
 	memcpy(&info->params,&tmp_params,sizeof(MGSL_PARAMS));
 	spin_unlock_irqrestore(&info->lock,flags);
 	spin_unlock_irqrestore(&info->lock,flags);
 
 
  	change_params(info);
  	change_params(info);
+	mutex_unlock(&info->port.mutex);
 
 
 	return 0;
 	return 0;
 }
 }
@@ -3366,7 +3365,9 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp,
 			printk("%s(%d):%s block_til_ready() count=%d\n",
 			printk("%s(%d):%s block_til_ready() count=%d\n",
 				 __FILE__,__LINE__, tty->driver->name, port->count );
 				 __FILE__,__LINE__, tty->driver->name, port->count );
 
 
+		tty_unlock();
 		schedule();
 		schedule();
+		tty_lock();
 	}
 	}
 
 
 	set_current_state(TASK_RUNNING);
 	set_current_state(TASK_RUNNING);

+ 81 - 69
drivers/char/tty_io.c

@@ -149,6 +149,7 @@ static long tty_compat_ioctl(struct file *file, unsigned int cmd,
 #else
 #else
 #define tty_compat_ioctl NULL
 #define tty_compat_ioctl NULL
 #endif
 #endif
+static int __tty_fasync(int fd, struct file *filp, int on);
 static int tty_fasync(int fd, struct file *filp, int on);
 static int tty_fasync(int fd, struct file *filp, int on);
 static void release_tty(struct tty_struct *tty, int idx);
 static void release_tty(struct tty_struct *tty, int idx);
 static void __proc_set_tty(struct task_struct *tsk, struct tty_struct *tty);
 static void __proc_set_tty(struct task_struct *tsk, struct tty_struct *tty);
@@ -470,7 +471,7 @@ void tty_wakeup(struct tty_struct *tty)
 EXPORT_SYMBOL_GPL(tty_wakeup);
 EXPORT_SYMBOL_GPL(tty_wakeup);
 
 
 /**
 /**
- *	do_tty_hangup		-	actual handler for hangup events
+ *	__tty_hangup		-	actual handler for hangup events
  *	@work: tty device
  *	@work: tty device
  *
  *
  *	This can be called by the "eventd" kernel thread.  That is process
  *	This can be called by the "eventd" kernel thread.  That is process
@@ -483,7 +484,7 @@ EXPORT_SYMBOL_GPL(tty_wakeup);
  *	remains intact.
  *	remains intact.
  *
  *
  *	Locking:
  *	Locking:
- *		BKL
+ *		BTM
  *		  redirect lock for undoing redirection
  *		  redirect lock for undoing redirection
  *		  file list lock for manipulating list of ttys
  *		  file list lock for manipulating list of ttys
  *		  tty_ldisc_lock from called functions
  *		  tty_ldisc_lock from called functions
@@ -491,10 +492,8 @@ EXPORT_SYMBOL_GPL(tty_wakeup);
  *		  tasklist_lock to walk task list for hangup event
  *		  tasklist_lock to walk task list for hangup event
  *		    ->siglock to protect ->signal/->sighand
  *		    ->siglock to protect ->signal/->sighand
  */
  */
-static void do_tty_hangup(struct work_struct *work)
+void __tty_hangup(struct tty_struct *tty)
 {
 {
-	struct tty_struct *tty =
-		container_of(work, struct tty_struct, hangup_work);
 	struct file *cons_filp = NULL;
 	struct file *cons_filp = NULL;
 	struct file *filp, *f = NULL;
 	struct file *filp, *f = NULL;
 	struct task_struct *p;
 	struct task_struct *p;
@@ -513,9 +512,12 @@ static void do_tty_hangup(struct work_struct *work)
 	}
 	}
 	spin_unlock(&redirect_lock);
 	spin_unlock(&redirect_lock);
 
 
-	/* inuse_filps is protected by the single kernel lock */
-	lock_kernel();
-	check_tty_count(tty, "do_tty_hangup");
+	tty_lock();
+
+	/* inuse_filps is protected by the single tty lock,
+	   this really needs to change if we want to flush the
+	   workqueue with the lock held */
+	check_tty_count(tty, "tty_hangup");
 
 
 	file_list_lock();
 	file_list_lock();
 	/* This breaks for file handles being sent over AF_UNIX sockets ? */
 	/* This breaks for file handles being sent over AF_UNIX sockets ? */
@@ -525,7 +527,7 @@ static void do_tty_hangup(struct work_struct *work)
 		if (filp->f_op->write != tty_write)
 		if (filp->f_op->write != tty_write)
 			continue;
 			continue;
 		closecount++;
 		closecount++;
-		tty_fasync(-1, filp, 0);	/* can't block */
+		__tty_fasync(-1, filp, 0);	/* can't block */
 		filp->f_op = &hung_up_tty_fops;
 		filp->f_op = &hung_up_tty_fops;
 	}
 	}
 	file_list_unlock();
 	file_list_unlock();
@@ -594,11 +596,21 @@ static void do_tty_hangup(struct work_struct *work)
 	 */
 	 */
 	set_bit(TTY_HUPPED, &tty->flags);
 	set_bit(TTY_HUPPED, &tty->flags);
 	tty_ldisc_enable(tty);
 	tty_ldisc_enable(tty);
-	unlock_kernel();
+
+	tty_unlock();
+
 	if (f)
 	if (f)
 		fput(f);
 		fput(f);
 }
 }
 
 
+static void do_tty_hangup(struct work_struct *work)
+{
+	struct tty_struct *tty =
+		container_of(work, struct tty_struct, hangup_work);
+
+	__tty_hangup(tty);
+}
+
 /**
 /**
  *	tty_hangup		-	trigger a hangup event
  *	tty_hangup		-	trigger a hangup event
  *	@tty: tty to hangup
  *	@tty: tty to hangup
@@ -634,11 +646,12 @@ void tty_vhangup(struct tty_struct *tty)
 
 
 	printk(KERN_DEBUG "%s vhangup...\n", tty_name(tty, buf));
 	printk(KERN_DEBUG "%s vhangup...\n", tty_name(tty, buf));
 #endif
 #endif
-	do_tty_hangup(&tty->hangup_work);
+	__tty_hangup(tty);
 }
 }
 
 
 EXPORT_SYMBOL(tty_vhangup);
 EXPORT_SYMBOL(tty_vhangup);
 
 
+
 /**
 /**
  *	tty_vhangup_self	-	process vhangup for own ctty
  *	tty_vhangup_self	-	process vhangup for own ctty
  *
  *
@@ -696,7 +709,8 @@ static void session_clear_tty(struct pid *session)
  *	exiting; it is 0 if called by the ioctl TIOCNOTTY.
  *	exiting; it is 0 if called by the ioctl TIOCNOTTY.
  *
  *
  *	Locking:
  *	Locking:
- *		BKL is taken for hysterical raisins
+ *		BTM is taken for hysterical raisins, and held when
+ *		  called from no_tty().
  *		  tty_mutex is taken to protect tty
  *		  tty_mutex is taken to protect tty
  *		  ->siglock is taken to protect ->signal/->sighand
  *		  ->siglock is taken to protect ->signal/->sighand
  *		  tasklist_lock is taken to walk process list for sessions
  *		  tasklist_lock is taken to walk process list for sessions
@@ -714,10 +728,10 @@ void disassociate_ctty(int on_exit)
 	tty = get_current_tty();
 	tty = get_current_tty();
 	if (tty) {
 	if (tty) {
 		tty_pgrp = get_pid(tty->pgrp);
 		tty_pgrp = get_pid(tty->pgrp);
-		lock_kernel();
-		if (on_exit && tty->driver->type != TTY_DRIVER_TYPE_PTY)
-			tty_vhangup(tty);
-		unlock_kernel();
+		if (on_exit) {
+			if (tty->driver->type != TTY_DRIVER_TYPE_PTY)
+				tty_vhangup(tty);
+		}
 		tty_kref_put(tty);
 		tty_kref_put(tty);
 	} else if (on_exit) {
 	} else if (on_exit) {
 		struct pid *old_pgrp;
 		struct pid *old_pgrp;
@@ -774,9 +788,9 @@ void disassociate_ctty(int on_exit)
 void no_tty(void)
 void no_tty(void)
 {
 {
 	struct task_struct *tsk = current;
 	struct task_struct *tsk = current;
-	lock_kernel();
+	tty_lock();
 	disassociate_ctty(0);
 	disassociate_ctty(0);
-	unlock_kernel();
+	tty_unlock();
 	proc_clear_tty(tsk);
 	proc_clear_tty(tsk);
 }
 }
 
 
@@ -879,7 +893,7 @@ static ssize_t tty_read(struct file *file, char __user *buf, size_t count,
 	struct inode *inode;
 	struct inode *inode;
 	struct tty_ldisc *ld;
 	struct tty_ldisc *ld;
 
 
-	tty = (struct tty_struct *)file->private_data;
+	tty = file->private_data;
 	inode = file->f_path.dentry->d_inode;
 	inode = file->f_path.dentry->d_inode;
 	if (tty_paranoia_check(tty, inode, "tty_read"))
 	if (tty_paranoia_check(tty, inode, "tty_read"))
 		return -EIO;
 		return -EIO;
@@ -1013,19 +1027,19 @@ static inline ssize_t do_tty_write(
  * We don't put it into the syslog queue right now maybe in the future if
  * We don't put it into the syslog queue right now maybe in the future if
  * really needed.
  * really needed.
  *
  *
- * We must still hold the BKL and test the CLOSING flag for the moment.
+ * We must still hold the BTM and test the CLOSING flag for the moment.
  */
  */
 
 
 void tty_write_message(struct tty_struct *tty, char *msg)
 void tty_write_message(struct tty_struct *tty, char *msg)
 {
 {
 	if (tty) {
 	if (tty) {
 		mutex_lock(&tty->atomic_write_lock);
 		mutex_lock(&tty->atomic_write_lock);
-		lock_kernel();
+		tty_lock();
 		if (tty->ops->write && !test_bit(TTY_CLOSING, &tty->flags)) {
 		if (tty->ops->write && !test_bit(TTY_CLOSING, &tty->flags)) {
-			unlock_kernel();
+			tty_unlock();
 			tty->ops->write(tty, msg, strlen(msg));
 			tty->ops->write(tty, msg, strlen(msg));
 		} else
 		} else
-			unlock_kernel();
+			tty_unlock();
 		tty_write_unlock(tty);
 		tty_write_unlock(tty);
 	}
 	}
 	return;
 	return;
@@ -1056,7 +1070,7 @@ static ssize_t tty_write(struct file *file, const char __user *buf,
 	ssize_t ret;
 	ssize_t ret;
 	struct tty_ldisc *ld;
 	struct tty_ldisc *ld;
 
 
-	tty = (struct tty_struct *)file->private_data;
+	tty = file->private_data;
 	if (tty_paranoia_check(tty, inode, "tty_write"))
 	if (tty_paranoia_check(tty, inode, "tty_write"))
 		return -EIO;
 		return -EIO;
 	if (!tty || !tty->ops->write ||
 	if (!tty || !tty->ops->write ||
@@ -1208,18 +1222,14 @@ static int tty_driver_install_tty(struct tty_driver *driver,
 	int ret;
 	int ret;
 
 
 	if (driver->ops->install) {
 	if (driver->ops->install) {
-		lock_kernel();
 		ret = driver->ops->install(driver, tty);
 		ret = driver->ops->install(driver, tty);
-		unlock_kernel();
 		return ret;
 		return ret;
 	}
 	}
 
 
 	if (tty_init_termios(tty) == 0) {
 	if (tty_init_termios(tty) == 0) {
-		lock_kernel();
 		tty_driver_kref_get(driver);
 		tty_driver_kref_get(driver);
 		tty->count++;
 		tty->count++;
 		driver->ttys[idx] = tty;
 		driver->ttys[idx] = tty;
-		unlock_kernel();
 		return 0;
 		return 0;
 	}
 	}
 	return -ENOMEM;
 	return -ENOMEM;
@@ -1312,14 +1322,11 @@ struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx,
 	struct tty_struct *tty;
 	struct tty_struct *tty;
 	int retval;
 	int retval;
 
 
-	lock_kernel();
 	/* Check if pty master is being opened multiple times */
 	/* Check if pty master is being opened multiple times */
 	if (driver->subtype == PTY_TYPE_MASTER &&
 	if (driver->subtype == PTY_TYPE_MASTER &&
 		(driver->flags & TTY_DRIVER_DEVPTS_MEM) && !first_ok) {
 		(driver->flags & TTY_DRIVER_DEVPTS_MEM) && !first_ok) {
-		unlock_kernel();
 		return ERR_PTR(-EIO);
 		return ERR_PTR(-EIO);
 	}
 	}
-	unlock_kernel();
 
 
 	/*
 	/*
 	 * First time open is complex, especially for PTY devices.
 	 * First time open is complex, especially for PTY devices.
@@ -1363,9 +1370,7 @@ struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx,
 	if (printk_ratelimit())
 	if (printk_ratelimit())
 		printk(KERN_INFO "tty_init_dev: ldisc open failed, "
 		printk(KERN_INFO "tty_init_dev: ldisc open failed, "
 				 "clearing slot %d\n", idx);
 				 "clearing slot %d\n", idx);
-	lock_kernel();
 	release_tty(tty, idx);
 	release_tty(tty, idx);
-	unlock_kernel();
 	return ERR_PTR(retval);
 	return ERR_PTR(retval);
 }
 }
 
 
@@ -1508,14 +1513,14 @@ int tty_release(struct inode *inode, struct file *filp)
 	int	idx;
 	int	idx;
 	char	buf[64];
 	char	buf[64];
 
 
-	tty = (struct tty_struct *)filp->private_data;
+	tty = filp->private_data;
 	if (tty_paranoia_check(tty, inode, "tty_release_dev"))
 	if (tty_paranoia_check(tty, inode, "tty_release_dev"))
 		return 0;
 		return 0;
 
 
-	lock_kernel();
+	tty_lock();
 	check_tty_count(tty, "tty_release_dev");
 	check_tty_count(tty, "tty_release_dev");
 
 
-	tty_fasync(-1, filp, 0);
+	__tty_fasync(-1, filp, 0);
 
 
 	idx = tty->index;
 	idx = tty->index;
 	pty_master = (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
 	pty_master = (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
@@ -1527,18 +1532,18 @@ int tty_release(struct inode *inode, struct file *filp)
 	if (idx < 0 || idx >= tty->driver->num) {
 	if (idx < 0 || idx >= tty->driver->num) {
 		printk(KERN_DEBUG "tty_release_dev: bad idx when trying to "
 		printk(KERN_DEBUG "tty_release_dev: bad idx when trying to "
 				  "free (%s)\n", tty->name);
 				  "free (%s)\n", tty->name);
-		unlock_kernel();
+		tty_unlock();
 		return 0;
 		return 0;
 	}
 	}
 	if (!devpts) {
 	if (!devpts) {
 		if (tty != tty->driver->ttys[idx]) {
 		if (tty != tty->driver->ttys[idx]) {
-			unlock_kernel();
+			tty_unlock();
 			printk(KERN_DEBUG "tty_release_dev: driver.table[%d] not tty "
 			printk(KERN_DEBUG "tty_release_dev: driver.table[%d] not tty "
 			       "for (%s)\n", idx, tty->name);
 			       "for (%s)\n", idx, tty->name);
 			return 0;
 			return 0;
 		}
 		}
 		if (tty->termios != tty->driver->termios[idx]) {
 		if (tty->termios != tty->driver->termios[idx]) {
-			unlock_kernel();
+			tty_unlock();
 			printk(KERN_DEBUG "tty_release_dev: driver.termios[%d] not termios "
 			printk(KERN_DEBUG "tty_release_dev: driver.termios[%d] not termios "
 			       "for (%s)\n",
 			       "for (%s)\n",
 			       idx, tty->name);
 			       idx, tty->name);
@@ -1556,21 +1561,21 @@ int tty_release(struct inode *inode, struct file *filp)
 	if (tty->driver->other &&
 	if (tty->driver->other &&
 	     !(tty->driver->flags & TTY_DRIVER_DEVPTS_MEM)) {
 	     !(tty->driver->flags & TTY_DRIVER_DEVPTS_MEM)) {
 		if (o_tty != tty->driver->other->ttys[idx]) {
 		if (o_tty != tty->driver->other->ttys[idx]) {
-			unlock_kernel();
+			tty_unlock();
 			printk(KERN_DEBUG "tty_release_dev: other->table[%d] "
 			printk(KERN_DEBUG "tty_release_dev: other->table[%d] "
 					  "not o_tty for (%s)\n",
 					  "not o_tty for (%s)\n",
 			       idx, tty->name);
 			       idx, tty->name);
 			return 0 ;
 			return 0 ;
 		}
 		}
 		if (o_tty->termios != tty->driver->other->termios[idx]) {
 		if (o_tty->termios != tty->driver->other->termios[idx]) {
-			unlock_kernel();
+			tty_unlock();
 			printk(KERN_DEBUG "tty_release_dev: other->termios[%d] "
 			printk(KERN_DEBUG "tty_release_dev: other->termios[%d] "
 					  "not o_termios for (%s)\n",
 					  "not o_termios for (%s)\n",
 			       idx, tty->name);
 			       idx, tty->name);
 			return 0;
 			return 0;
 		}
 		}
 		if (o_tty->link != tty) {
 		if (o_tty->link != tty) {
-			unlock_kernel();
+			tty_unlock();
 			printk(KERN_DEBUG "tty_release_dev: bad pty pointers\n");
 			printk(KERN_DEBUG "tty_release_dev: bad pty pointers\n");
 			return 0;
 			return 0;
 		}
 		}
@@ -1579,7 +1584,7 @@ int tty_release(struct inode *inode, struct file *filp)
 	if (tty->ops->close)
 	if (tty->ops->close)
 		tty->ops->close(tty, filp);
 		tty->ops->close(tty, filp);
 
 
-	unlock_kernel();
+	tty_unlock();
 	/*
 	/*
 	 * Sanity check: if tty->count is going to zero, there shouldn't be
 	 * Sanity check: if tty->count is going to zero, there shouldn't be
 	 * any waiters on tty->read_wait or tty->write_wait.  We test the
 	 * any waiters on tty->read_wait or tty->write_wait.  We test the
@@ -1602,7 +1607,7 @@ int tty_release(struct inode *inode, struct file *filp)
 		   opens on /dev/tty */
 		   opens on /dev/tty */
 
 
 		mutex_lock(&tty_mutex);
 		mutex_lock(&tty_mutex);
-		lock_kernel();
+		tty_lock();
 		tty_closing = tty->count <= 1;
 		tty_closing = tty->count <= 1;
 		o_tty_closing = o_tty &&
 		o_tty_closing = o_tty &&
 			(o_tty->count <= (pty_master ? 1 : 0));
 			(o_tty->count <= (pty_master ? 1 : 0));
@@ -1633,7 +1638,7 @@ int tty_release(struct inode *inode, struct file *filp)
 
 
 		printk(KERN_WARNING "tty_release_dev: %s: read/write wait queue "
 		printk(KERN_WARNING "tty_release_dev: %s: read/write wait queue "
 				    "active!\n", tty_name(tty, buf));
 				    "active!\n", tty_name(tty, buf));
-		unlock_kernel();
+		tty_unlock();
 		mutex_unlock(&tty_mutex);
 		mutex_unlock(&tty_mutex);
 		schedule();
 		schedule();
 	}
 	}
@@ -1698,7 +1703,7 @@ int tty_release(struct inode *inode, struct file *filp)
 
 
 	/* check whether both sides are closing ... */
 	/* check whether both sides are closing ... */
 	if (!tty_closing || (o_tty && !o_tty_closing)) {
 	if (!tty_closing || (o_tty && !o_tty_closing)) {
-		unlock_kernel();
+		tty_unlock();
 		return 0;
 		return 0;
 	}
 	}
 
 
@@ -1718,7 +1723,7 @@ int tty_release(struct inode *inode, struct file *filp)
 	/* Make this pty number available for reallocation */
 	/* Make this pty number available for reallocation */
 	if (devpts)
 	if (devpts)
 		devpts_kill_index(inode, idx);
 		devpts_kill_index(inode, idx);
-	unlock_kernel();
+	tty_unlock();
 	return 0;
 	return 0;
 }
 }
 
 
@@ -1760,12 +1765,12 @@ static int tty_open(struct inode *inode, struct file *filp)
 	retval = 0;
 	retval = 0;
 
 
 	mutex_lock(&tty_mutex);
 	mutex_lock(&tty_mutex);
-	lock_kernel();
+	tty_lock();
 
 
 	if (device == MKDEV(TTYAUX_MAJOR, 0)) {
 	if (device == MKDEV(TTYAUX_MAJOR, 0)) {
 		tty = get_current_tty();
 		tty = get_current_tty();
 		if (!tty) {
 		if (!tty) {
-			unlock_kernel();
+			tty_unlock();
 			mutex_unlock(&tty_mutex);
 			mutex_unlock(&tty_mutex);
 			return -ENXIO;
 			return -ENXIO;
 		}
 		}
@@ -1797,14 +1802,14 @@ static int tty_open(struct inode *inode, struct file *filp)
 				goto got_driver;
 				goto got_driver;
 			}
 			}
 		}
 		}
-		unlock_kernel();
+		tty_unlock();
 		mutex_unlock(&tty_mutex);
 		mutex_unlock(&tty_mutex);
 		return -ENODEV;
 		return -ENODEV;
 	}
 	}
 
 
 	driver = get_tty_driver(device, &index);
 	driver = get_tty_driver(device, &index);
 	if (!driver) {
 	if (!driver) {
-		unlock_kernel();
+		tty_unlock();
 		mutex_unlock(&tty_mutex);
 		mutex_unlock(&tty_mutex);
 		return -ENODEV;
 		return -ENODEV;
 	}
 	}
@@ -1814,7 +1819,7 @@ static int tty_open(struct inode *inode, struct file *filp)
 		tty = tty_driver_lookup_tty(driver, inode, index);
 		tty = tty_driver_lookup_tty(driver, inode, index);
 
 
 		if (IS_ERR(tty)) {
 		if (IS_ERR(tty)) {
-			unlock_kernel();
+			tty_unlock();
 			mutex_unlock(&tty_mutex);
 			mutex_unlock(&tty_mutex);
 			return PTR_ERR(tty);
 			return PTR_ERR(tty);
 		}
 		}
@@ -1830,7 +1835,7 @@ static int tty_open(struct inode *inode, struct file *filp)
 	mutex_unlock(&tty_mutex);
 	mutex_unlock(&tty_mutex);
 	tty_driver_kref_put(driver);
 	tty_driver_kref_put(driver);
 	if (IS_ERR(tty)) {
 	if (IS_ERR(tty)) {
-		unlock_kernel();
+		tty_unlock();
 		return PTR_ERR(tty);
 		return PTR_ERR(tty);
 	}
 	}
 
 
@@ -1860,29 +1865,29 @@ static int tty_open(struct inode *inode, struct file *filp)
 		printk(KERN_DEBUG "error %d in opening %s...", retval,
 		printk(KERN_DEBUG "error %d in opening %s...", retval,
 		       tty->name);
 		       tty->name);
 #endif
 #endif
+		tty_unlock(); /* need to call tty_release without BTM */
 		tty_release(inode, filp);
 		tty_release(inode, filp);
-		if (retval != -ERESTARTSYS) {
-			unlock_kernel();
+		if (retval != -ERESTARTSYS)
 			return retval;
 			return retval;
-		}
-		if (signal_pending(current)) {
-			unlock_kernel();
+
+		if (signal_pending(current))
 			return retval;
 			return retval;
-		}
+
 		schedule();
 		schedule();
 		/*
 		/*
 		 * Need to reset f_op in case a hangup happened.
 		 * Need to reset f_op in case a hangup happened.
 		 */
 		 */
+		tty_lock();
 		if (filp->f_op == &hung_up_tty_fops)
 		if (filp->f_op == &hung_up_tty_fops)
 			filp->f_op = &tty_fops;
 			filp->f_op = &tty_fops;
-		unlock_kernel();
+		tty_unlock();
 		goto retry_open;
 		goto retry_open;
 	}
 	}
-	unlock_kernel();
+	tty_unlock();
 
 
 
 
 	mutex_lock(&tty_mutex);
 	mutex_lock(&tty_mutex);
-	lock_kernel();
+	tty_lock();
 	spin_lock_irq(&current->sighand->siglock);
 	spin_lock_irq(&current->sighand->siglock);
 	if (!noctty &&
 	if (!noctty &&
 	    current->signal->leader &&
 	    current->signal->leader &&
@@ -1890,7 +1895,7 @@ static int tty_open(struct inode *inode, struct file *filp)
 	    tty->session == NULL)
 	    tty->session == NULL)
 		__proc_set_tty(current, tty);
 		__proc_set_tty(current, tty);
 	spin_unlock_irq(&current->sighand->siglock);
 	spin_unlock_irq(&current->sighand->siglock);
-	unlock_kernel();
+	tty_unlock();
 	mutex_unlock(&tty_mutex);
 	mutex_unlock(&tty_mutex);
 	return 0;
 	return 0;
 }
 }
@@ -1915,7 +1920,7 @@ static unsigned int tty_poll(struct file *filp, poll_table *wait)
 	struct tty_ldisc *ld;
 	struct tty_ldisc *ld;
 	int ret = 0;
 	int ret = 0;
 
 
-	tty = (struct tty_struct *)filp->private_data;
+	tty = filp->private_data;
 	if (tty_paranoia_check(tty, filp->f_path.dentry->d_inode, "tty_poll"))
 	if (tty_paranoia_check(tty, filp->f_path.dentry->d_inode, "tty_poll"))
 		return 0;
 		return 0;
 
 
@@ -1926,14 +1931,13 @@ static unsigned int tty_poll(struct file *filp, poll_table *wait)
 	return ret;
 	return ret;
 }
 }
 
 
-static int tty_fasync(int fd, struct file *filp, int on)
+static int __tty_fasync(int fd, struct file *filp, int on)
 {
 {
 	struct tty_struct *tty;
 	struct tty_struct *tty;
 	unsigned long flags;
 	unsigned long flags;
 	int retval = 0;
 	int retval = 0;
 
 
-	lock_kernel();
-	tty = (struct tty_struct *)filp->private_data;
+	tty = filp->private_data;
 	if (tty_paranoia_check(tty, filp->f_path.dentry->d_inode, "tty_fasync"))
 	if (tty_paranoia_check(tty, filp->f_path.dentry->d_inode, "tty_fasync"))
 		goto out;
 		goto out;
 
 
@@ -1966,7 +1970,15 @@ static int tty_fasync(int fd, struct file *filp, int on)
 	}
 	}
 	retval = 0;
 	retval = 0;
 out:
 out:
-	unlock_kernel();
+	return retval;
+}
+
+static int tty_fasync(int fd, struct file *filp, int on)
+{
+	int retval;
+	tty_lock();
+	retval = __tty_fasync(fd, filp, on);
+	tty_unlock();
 	return retval;
 	return retval;
 }
 }
 
 
@@ -2485,7 +2497,7 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 	struct tty_ldisc *ld;
 	struct tty_ldisc *ld;
 	struct inode *inode = file->f_dentry->d_inode;
 	struct inode *inode = file->f_dentry->d_inode;
 
 
-	tty = (struct tty_struct *)file->private_data;
+	tty = file->private_data;
 	if (tty_paranoia_check(tty, inode, "tty_ioctl"))
 	if (tty_paranoia_check(tty, inode, "tty_ioctl"))
 		return -EINVAL;
 		return -EINVAL;
 
 

+ 12 - 6
drivers/char/tty_ioctl.c

@@ -517,19 +517,25 @@ static void change_termios(struct tty_struct *tty, struct ktermios *new_termios)
 
 
 	/* See if packet mode change of state. */
 	/* See if packet mode change of state. */
 	if (tty->link && tty->link->packet) {
 	if (tty->link && tty->link->packet) {
+		int extproc = (old_termios.c_lflag & EXTPROC) |
+				(tty->termios->c_lflag & EXTPROC);
 		int old_flow = ((old_termios.c_iflag & IXON) &&
 		int old_flow = ((old_termios.c_iflag & IXON) &&
 				(old_termios.c_cc[VSTOP] == '\023') &&
 				(old_termios.c_cc[VSTOP] == '\023') &&
 				(old_termios.c_cc[VSTART] == '\021'));
 				(old_termios.c_cc[VSTART] == '\021'));
 		int new_flow = (I_IXON(tty) &&
 		int new_flow = (I_IXON(tty) &&
 				STOP_CHAR(tty) == '\023' &&
 				STOP_CHAR(tty) == '\023' &&
 				START_CHAR(tty) == '\021');
 				START_CHAR(tty) == '\021');
-		if (old_flow != new_flow) {
+		if ((old_flow != new_flow) || extproc) {
 			spin_lock_irqsave(&tty->ctrl_lock, flags);
 			spin_lock_irqsave(&tty->ctrl_lock, flags);
-			tty->ctrl_status &= ~(TIOCPKT_DOSTOP | TIOCPKT_NOSTOP);
-			if (new_flow)
-				tty->ctrl_status |= TIOCPKT_DOSTOP;
-			else
-				tty->ctrl_status |= TIOCPKT_NOSTOP;
+			if (old_flow != new_flow) {
+				tty->ctrl_status &= ~(TIOCPKT_DOSTOP | TIOCPKT_NOSTOP);
+				if (new_flow)
+					tty->ctrl_status |= TIOCPKT_DOSTOP;
+				else
+					tty->ctrl_status |= TIOCPKT_NOSTOP;
+			}
+			if (extproc)
+				tty->ctrl_status |= TIOCPKT_IOCTL;
 			spin_unlock_irqrestore(&tty->ctrl_lock, flags);
 			spin_unlock_irqrestore(&tty->ctrl_lock, flags);
 			wake_up_interruptible(&tty->link->read_wait);
 			wake_up_interruptible(&tty->link->read_wait);
 		}
 		}

+ 30 - 13
drivers/char/tty_ldisc.c

@@ -440,6 +440,8 @@ static void tty_set_termios_ldisc(struct tty_struct *tty, int num)
  *
  *
  *	A helper opening method. Also a convenient debugging and check
  *	A helper opening method. Also a convenient debugging and check
  *	point.
  *	point.
+ *
+ *	Locking: always called with BTM already held.
  */
  */
 
 
 static int tty_ldisc_open(struct tty_struct *tty, struct tty_ldisc *ld)
 static int tty_ldisc_open(struct tty_struct *tty, struct tty_ldisc *ld)
@@ -447,10 +449,9 @@ static int tty_ldisc_open(struct tty_struct *tty, struct tty_ldisc *ld)
 	WARN_ON(test_and_set_bit(TTY_LDISC_OPEN, &tty->flags));
 	WARN_ON(test_and_set_bit(TTY_LDISC_OPEN, &tty->flags));
 	if (ld->ops->open) {
 	if (ld->ops->open) {
 		int ret;
 		int ret;
-                /* BKL here locks verus a hangup event */
-		lock_kernel();
+                /* BTM here locks versus a hangup event */
+		WARN_ON(!tty_locked());
 		ret = ld->ops->open(tty);
 		ret = ld->ops->open(tty);
-		unlock_kernel();
 		return ret;
 		return ret;
 	}
 	}
 	return 0;
 	return 0;
@@ -553,7 +554,7 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc)
 	if (IS_ERR(new_ldisc))
 	if (IS_ERR(new_ldisc))
 		return PTR_ERR(new_ldisc);
 		return PTR_ERR(new_ldisc);
 
 
-	lock_kernel();
+	tty_lock();
 	/*
 	/*
 	 *	We need to look at the tty locking here for pty/tty pairs
 	 *	We need to look at the tty locking here for pty/tty pairs
 	 *	when both sides try to change in parallel.
 	 *	when both sides try to change in parallel.
@@ -567,12 +568,12 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc)
 	 */
 	 */
 
 
 	if (tty->ldisc->ops->num == ldisc) {
 	if (tty->ldisc->ops->num == ldisc) {
-		unlock_kernel();
+		tty_unlock();
 		tty_ldisc_put(new_ldisc);
 		tty_ldisc_put(new_ldisc);
 		return 0;
 		return 0;
 	}
 	}
 
 
-	unlock_kernel();
+	tty_unlock();
 	/*
 	/*
 	 *	Problem: What do we do if this blocks ?
 	 *	Problem: What do we do if this blocks ?
 	 *	We could deadlock here
 	 *	We could deadlock here
@@ -580,6 +581,7 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc)
 
 
 	tty_wait_until_sent(tty, 0);
 	tty_wait_until_sent(tty, 0);
 
 
+	tty_lock();
 	mutex_lock(&tty->ldisc_mutex);
 	mutex_lock(&tty->ldisc_mutex);
 
 
 	/*
 	/*
@@ -589,13 +591,13 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc)
 
 
 	while (test_bit(TTY_LDISC_CHANGING, &tty->flags)) {
 	while (test_bit(TTY_LDISC_CHANGING, &tty->flags)) {
 		mutex_unlock(&tty->ldisc_mutex);
 		mutex_unlock(&tty->ldisc_mutex);
+		tty_unlock();
 		wait_event(tty_ldisc_wait,
 		wait_event(tty_ldisc_wait,
 			test_bit(TTY_LDISC_CHANGING, &tty->flags) == 0);
 			test_bit(TTY_LDISC_CHANGING, &tty->flags) == 0);
+		tty_lock();
 		mutex_lock(&tty->ldisc_mutex);
 		mutex_lock(&tty->ldisc_mutex);
 	}
 	}
 
 
-	lock_kernel();
-
 	set_bit(TTY_LDISC_CHANGING, &tty->flags);
 	set_bit(TTY_LDISC_CHANGING, &tty->flags);
 
 
 	/*
 	/*
@@ -607,7 +609,7 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc)
 
 
 	o_ldisc = tty->ldisc;
 	o_ldisc = tty->ldisc;
 
 
-	unlock_kernel();
+	tty_unlock();
 	/*
 	/*
 	 *	Make sure we don't change while someone holds a
 	 *	Make sure we don't change while someone holds a
 	 *	reference to the line discipline. The TTY_LDISC bit
 	 *	reference to the line discipline. The TTY_LDISC bit
@@ -632,15 +634,15 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc)
 
 
 	flush_scheduled_work();
 	flush_scheduled_work();
 
 
+	tty_lock();
 	mutex_lock(&tty->ldisc_mutex);
 	mutex_lock(&tty->ldisc_mutex);
-	lock_kernel();
 	if (test_bit(TTY_HUPPED, &tty->flags)) {
 	if (test_bit(TTY_HUPPED, &tty->flags)) {
 		/* We were raced by the hangup method. It will have stomped
 		/* We were raced by the hangup method. It will have stomped
 		   the ldisc data and closed the ldisc down */
 		   the ldisc data and closed the ldisc down */
 		clear_bit(TTY_LDISC_CHANGING, &tty->flags);
 		clear_bit(TTY_LDISC_CHANGING, &tty->flags);
 		mutex_unlock(&tty->ldisc_mutex);
 		mutex_unlock(&tty->ldisc_mutex);
 		tty_ldisc_put(new_ldisc);
 		tty_ldisc_put(new_ldisc);
-		unlock_kernel();
+		tty_unlock();
 		return -EIO;
 		return -EIO;
 	}
 	}
 
 
@@ -682,7 +684,7 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc)
 	if (o_work)
 	if (o_work)
 		schedule_delayed_work(&o_tty->buf.work, 1);
 		schedule_delayed_work(&o_tty->buf.work, 1);
 	mutex_unlock(&tty->ldisc_mutex);
 	mutex_unlock(&tty->ldisc_mutex);
-	unlock_kernel();
+	tty_unlock();
 	return retval;
 	return retval;
 }
 }
 
 
@@ -780,7 +782,20 @@ void tty_ldisc_hangup(struct tty_struct *tty)
 	 * Avoid racing set_ldisc or tty_ldisc_release
 	 * Avoid racing set_ldisc or tty_ldisc_release
 	 */
 	 */
 	mutex_lock(&tty->ldisc_mutex);
 	mutex_lock(&tty->ldisc_mutex);
-	tty_ldisc_halt(tty);
+
+	/*
+	 * this is like tty_ldisc_halt, but we need to give up
+	 * the BTM before calling cancel_delayed_work_sync,
+	 * which may need to wait for another function taking the BTM
+	 */
+	clear_bit(TTY_LDISC, &tty->flags);
+	tty_unlock();
+	cancel_delayed_work_sync(&tty->buf.work);
+	mutex_unlock(&tty->ldisc_mutex);
+
+	tty_lock();
+	mutex_lock(&tty->ldisc_mutex);
+
 	/* At this point we have a closed ldisc and we want to
 	/* At this point we have a closed ldisc and we want to
 	   reopen it. We could defer this to the next open but
 	   reopen it. We could defer this to the next open but
 	   it means auditing a lot of other paths so this is
 	   it means auditing a lot of other paths so this is
@@ -851,8 +866,10 @@ void tty_ldisc_release(struct tty_struct *tty, struct tty_struct *o_tty)
 	 * race with the set_ldisc code path.
 	 * race with the set_ldisc code path.
 	 */
 	 */
 
 
+	tty_unlock();
 	tty_ldisc_halt(tty);
 	tty_ldisc_halt(tty);
 	flush_scheduled_work();
 	flush_scheduled_work();
+	tty_lock();
 
 
 	mutex_lock(&tty->ldisc_mutex);
 	mutex_lock(&tty->ldisc_mutex);
 	/*
 	/*

+ 47 - 0
drivers/char/tty_mutex.c

@@ -0,0 +1,47 @@
+/*
+ * drivers/char/tty_lock.c
+ */
+#include <linux/tty.h>
+#include <linux/module.h>
+#include <linux/kallsyms.h>
+#include <linux/semaphore.h>
+#include <linux/sched.h>
+
+/*
+ * The 'big tty mutex'
+ *
+ * This mutex is taken and released by tty_lock() and tty_unlock(),
+ * replacing the older big kernel lock.
+ * It can no longer be taken recursively, and does not get
+ * released implicitly while sleeping.
+ *
+ * Don't use in new code.
+ */
+static DEFINE_MUTEX(big_tty_mutex);
+struct task_struct *__big_tty_mutex_owner;
+EXPORT_SYMBOL_GPL(__big_tty_mutex_owner);
+
+/*
+ * Getting the big tty mutex.
+ */
+void __lockfunc tty_lock(void)
+{
+	struct task_struct *task = current;
+
+	WARN_ON(__big_tty_mutex_owner == task);
+
+	mutex_lock(&big_tty_mutex);
+	__big_tty_mutex_owner = task;
+}
+EXPORT_SYMBOL(tty_lock);
+
+void __lockfunc tty_unlock(void)
+{
+	struct task_struct *task = current;
+
+	WARN_ON(__big_tty_mutex_owner != task);
+	__big_tty_mutex_owner = NULL;
+
+	mutex_unlock(&big_tty_mutex);
+}
+EXPORT_SYMBOL(tty_unlock);

+ 3 - 1
drivers/char/tty_port.c

@@ -231,7 +231,7 @@ int tty_port_block_til_ready(struct tty_port *port,
 
 
 	/* block if port is in the process of being closed */
 	/* block if port is in the process of being closed */
 	if (tty_hung_up_p(filp) || port->flags & ASYNC_CLOSING) {
 	if (tty_hung_up_p(filp) || port->flags & ASYNC_CLOSING) {
-		wait_event_interruptible(port->close_wait,
+		wait_event_interruptible_tty(port->close_wait,
 				!(port->flags & ASYNC_CLOSING));
 				!(port->flags & ASYNC_CLOSING));
 		if (port->flags & ASYNC_HUP_NOTIFY)
 		if (port->flags & ASYNC_HUP_NOTIFY)
 			return -EAGAIN;
 			return -EAGAIN;
@@ -294,7 +294,9 @@ int tty_port_block_til_ready(struct tty_port *port,
 			retval = -ERESTARTSYS;
 			retval = -ERESTARTSYS;
 			break;
 			break;
 		}
 		}
+		tty_unlock();
 		schedule();
 		schedule();
+		tty_lock();
 	}
 	}
 	finish_wait(&port->open_wait, &wait);
 	finish_wait(&port->open_wait, &wait);
 
 

+ 2 - 2
drivers/char/vc_screen.c

@@ -463,10 +463,10 @@ vcs_open(struct inode *inode, struct file *filp)
 	unsigned int currcons = iminor(inode) & 127;
 	unsigned int currcons = iminor(inode) & 127;
 	int ret = 0;
 	int ret = 0;
 	
 	
-	lock_kernel();
+	tty_lock();
 	if(currcons && !vc_cons_allocated(currcons-1))
 	if(currcons && !vc_cons_allocated(currcons-1))
 		ret = -ENXIO;
 		ret = -ENXIO;
-	unlock_kernel();
+	tty_unlock();
 	return ret;
 	return ret;
 }
 }
 
 

+ 23 - 14
drivers/char/vt.c

@@ -105,6 +105,7 @@
 #include <asm/system.h>
 #include <asm/system.h>
 #include <linux/uaccess.h>
 #include <linux/uaccess.h>
 #include <linux/kdb.h>
 #include <linux/kdb.h>
+#include <linux/ctype.h>
 
 
 #define MAX_NR_CON_DRIVER 16
 #define MAX_NR_CON_DRIVER 16
 
 
@@ -286,8 +287,12 @@ static inline unsigned short *screenpos(struct vc_data *vc, int offset, int view
 	return p;
 	return p;
 }
 }
 
 
+/* Called  from the keyboard irq path.. */
 static inline void scrolldelta(int lines)
 static inline void scrolldelta(int lines)
 {
 {
+	/* FIXME */
+	/* scrolldelta needs some kind of consistency lock, but the BKL was
+	   and still is not protecting versus the scheduled back end */
 	scrollback_delta += lines;
 	scrollback_delta += lines;
 	schedule_console_callback();
 	schedule_console_callback();
 }
 }
@@ -704,7 +709,10 @@ void redraw_screen(struct vc_data *vc, int is_switch)
 			update_attr(vc);
 			update_attr(vc);
 			clear_buffer_attributes(vc);
 			clear_buffer_attributes(vc);
 		}
 		}
-		if (update && vc->vc_mode != KD_GRAPHICS)
+
+		/* Forcibly update if we're panicing */
+		if ((update && vc->vc_mode != KD_GRAPHICS) ||
+		    vt_force_oops_output(vc))
 			do_update_region(vc, vc->vc_origin, vc->vc_screenbuf_size / 2);
 			do_update_region(vc, vc->vc_origin, vc->vc_screenbuf_size / 2);
 	}
 	}
 	set_cursor(vc);
 	set_cursor(vc);
@@ -742,6 +750,7 @@ static void visual_init(struct vc_data *vc, int num, int init)
 	vc->vc_hi_font_mask = 0;
 	vc->vc_hi_font_mask = 0;
 	vc->vc_complement_mask = 0;
 	vc->vc_complement_mask = 0;
 	vc->vc_can_do_color = 0;
 	vc->vc_can_do_color = 0;
+	vc->vc_panic_force_write = false;
 	vc->vc_sw->con_init(vc, init);
 	vc->vc_sw->con_init(vc, init);
 	if (!vc->vc_complement_mask)
 	if (!vc->vc_complement_mask)
 		vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800;
 		vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800;
@@ -774,6 +783,7 @@ int vc_allocate(unsigned int currcons)	/* return 0 on success */
 	    if (!vc)
 	    if (!vc)
 		return -ENOMEM;
 		return -ENOMEM;
 	    vc_cons[currcons].d = vc;
 	    vc_cons[currcons].d = vc;
+	    tty_port_init(&vc->port);
 	    INIT_WORK(&vc_cons[currcons].SAK_work, vc_SAK);
 	    INIT_WORK(&vc_cons[currcons].SAK_work, vc_SAK);
 	    visual_init(vc, currcons, 1);
 	    visual_init(vc, currcons, 1);
 	    if (!*vc->vc_uni_pagedir_loc)
 	    if (!*vc->vc_uni_pagedir_loc)
@@ -963,12 +973,12 @@ static int vc_do_resize(struct tty_struct *tty, struct vc_data *vc,
  *	Resize a virtual console as seen from the console end of things. We
  *	Resize a virtual console as seen from the console end of things. We
  *	use the common vc_do_resize methods to update the structures. The
  *	use the common vc_do_resize methods to update the structures. The
  *	caller must hold the console sem to protect console internals and
  *	caller must hold the console sem to protect console internals and
- *	vc->vc_tty
+ *	vc->port.tty
  */
  */
 
 
 int vc_resize(struct vc_data *vc, unsigned int cols, unsigned int rows)
 int vc_resize(struct vc_data *vc, unsigned int cols, unsigned int rows)
 {
 {
-	return vc_do_resize(vc->vc_tty, vc, cols, rows);
+	return vc_do_resize(vc->port.tty, vc, cols, rows);
 }
 }
 
 
 /**
 /**
@@ -1796,8 +1806,8 @@ static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, int c)
 			vc->vc_state = ESnormal;
 			vc->vc_state = ESnormal;
 		return;
 		return;
 	case ESpalette:
 	case ESpalette:
-		if ( (c>='0'&&c<='9') || (c>='A'&&c<='F') || (c>='a'&&c<='f') ) {
-			vc->vc_par[vc->vc_npar++] = (c > '9' ? (c & 0xDF) - 'A' + 10 : c - '0');
+		if (isxdigit(c)) {
+			vc->vc_par[vc->vc_npar++] = hex_to_bin(c);
 			if (vc->vc_npar == 7) {
 			if (vc->vc_npar == 7) {
 				int i = vc->vc_par[0] * 3, j = 1;
 				int i = vc->vc_par[0] * 3, j = 1;
 				vc->vc_palette[i] = 16 * vc->vc_par[j++];
 				vc->vc_palette[i] = 16 * vc->vc_par[j++];
@@ -2505,7 +2515,7 @@ static void vt_console_print(struct console *co, const char *b, unsigned count)
 		goto quit;
 		goto quit;
 	}
 	}
 
 
-	if (vc->vc_mode != KD_TEXT)
+	if (vc->vc_mode != KD_TEXT && !vt_force_oops_output(vc))
 		goto quit;
 		goto quit;
 
 
 	/* undraw cursor first */
 	/* undraw cursor first */
@@ -2611,8 +2621,6 @@ int tioclinux(struct tty_struct *tty, unsigned long arg)
 		return -EFAULT;
 		return -EFAULT;
 	ret = 0;
 	ret = 0;
 
 
-	lock_kernel();
-
 	switch (type)
 	switch (type)
 	{
 	{
 		case TIOCL_SETSEL:
 		case TIOCL_SETSEL:
@@ -2687,7 +2695,6 @@ int tioclinux(struct tty_struct *tty, unsigned long arg)
 			ret = -EINVAL;
 			ret = -EINVAL;
 			break;
 			break;
 	}
 	}
-	unlock_kernel();
 	return ret;
 	return ret;
 }
 }
 
 
@@ -2800,12 +2807,12 @@ static int con_open(struct tty_struct *tty, struct file *filp)
 			struct vc_data *vc = vc_cons[currcons].d;
 			struct vc_data *vc = vc_cons[currcons].d;
 
 
 			/* Still being freed */
 			/* Still being freed */
-			if (vc->vc_tty) {
+			if (vc->port.tty) {
 				release_console_sem();
 				release_console_sem();
 				return -ERESTARTSYS;
 				return -ERESTARTSYS;
 			}
 			}
 			tty->driver_data = vc;
 			tty->driver_data = vc;
-			vc->vc_tty = tty;
+			vc->port.tty = tty;
 
 
 			if (!tty->winsize.ws_row && !tty->winsize.ws_col) {
 			if (!tty->winsize.ws_row && !tty->winsize.ws_col) {
 				tty->winsize.ws_row = vc_cons[currcons].d->vc_rows;
 				tty->winsize.ws_row = vc_cons[currcons].d->vc_rows;
@@ -2833,7 +2840,7 @@ static void con_shutdown(struct tty_struct *tty)
 	struct vc_data *vc = tty->driver_data;
 	struct vc_data *vc = tty->driver_data;
 	BUG_ON(vc == NULL);
 	BUG_ON(vc == NULL);
 	acquire_console_sem();
 	acquire_console_sem();
-	vc->vc_tty = NULL;
+	vc->port.tty = NULL;
 	release_console_sem();
 	release_console_sem();
 	tty_shutdown(tty);
 	tty_shutdown(tty);
 }
 }
@@ -2915,6 +2922,7 @@ static int __init con_init(void)
 	for (currcons = 0; currcons < MIN_NR_CONSOLES; currcons++) {
 	for (currcons = 0; currcons < MIN_NR_CONSOLES; currcons++) {
 		vc_cons[currcons].d = vc = kzalloc(sizeof(struct vc_data), GFP_NOWAIT);
 		vc_cons[currcons].d = vc = kzalloc(sizeof(struct vc_data), GFP_NOWAIT);
 		INIT_WORK(&vc_cons[currcons].SAK_work, vc_SAK);
 		INIT_WORK(&vc_cons[currcons].SAK_work, vc_SAK);
+		tty_port_init(&vc->port);
 		visual_init(vc, currcons, 1);
 		visual_init(vc, currcons, 1);
 		vc->vc_screenbuf = kzalloc(vc->vc_screenbuf_size, GFP_NOWAIT);
 		vc->vc_screenbuf = kzalloc(vc->vc_screenbuf_size, GFP_NOWAIT);
 		vc_init(vc, vc->vc_rows, vc->vc_cols,
 		vc_init(vc, vc->vc_rows, vc->vc_cols,
@@ -3783,7 +3791,8 @@ void do_unblank_screen(int leaving_gfx)
 		return;
 		return;
 	}
 	}
 	vc = vc_cons[fg_console].d;
 	vc = vc_cons[fg_console].d;
-	if (vc->vc_mode != KD_TEXT)
+	/* Try to unblank in oops case too */
+	if (vc->vc_mode != KD_TEXT && !vt_force_oops_output(vc))
 		return; /* but leave console_blanked != 0 */
 		return; /* but leave console_blanked != 0 */
 
 
 	if (blankinterval) {
 	if (blankinterval) {
@@ -3792,7 +3801,7 @@ void do_unblank_screen(int leaving_gfx)
 	}
 	}
 
 
 	console_blanked = 0;
 	console_blanked = 0;
-	if (vc->vc_sw->con_blank(vc, 0, leaving_gfx))
+	if (vc->vc_sw->con_blank(vc, 0, leaving_gfx) || vt_force_oops_output(vc))
 		/* Low-level driver cannot restore -> do it ourselves */
 		/* Low-level driver cannot restore -> do it ourselves */
 		update_screen(vc);
 		update_screen(vc);
 	if (console_blank_hook)
 	if (console_blank_hook)

+ 10 - 7
drivers/char/vt_ioctl.c

@@ -133,7 +133,7 @@ static void vt_event_wait(struct vt_event_wait *vw)
 	list_add(&vw->list, &vt_events);
 	list_add(&vw->list, &vt_events);
 	spin_unlock_irqrestore(&vt_event_lock, flags);
 	spin_unlock_irqrestore(&vt_event_lock, flags);
 	/* Wait for it to pass */
 	/* Wait for it to pass */
-	wait_event_interruptible(vt_event_waitqueue, vw->done);
+	wait_event_interruptible_tty(vt_event_waitqueue, vw->done);
 	/* Dequeue it */
 	/* Dequeue it */
 	spin_lock_irqsave(&vt_event_lock, flags);
 	spin_lock_irqsave(&vt_event_lock, flags);
 	list_del(&vw->list);
 	list_del(&vw->list);
@@ -509,7 +509,7 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
 
 
 	console = vc->vc_num;
 	console = vc->vc_num;
 
 
-	lock_kernel();
+	tty_lock();
 
 
 	if (!vc_cons_allocated(console)) { 	/* impossible? */
 	if (!vc_cons_allocated(console)) { 	/* impossible? */
 		ret = -ENOIOCTLCMD;
 		ret = -ENOIOCTLCMD;
@@ -1336,7 +1336,7 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
 		ret = -ENOIOCTLCMD;
 		ret = -ENOIOCTLCMD;
 	}
 	}
 out:
 out:
-	unlock_kernel();
+	tty_unlock();
 	return ret;
 	return ret;
 eperm:
 eperm:
 	ret = -EPERM;
 	ret = -EPERM;
@@ -1369,7 +1369,7 @@ void vc_SAK(struct work_struct *work)
 	acquire_console_sem();
 	acquire_console_sem();
 	vc = vc_con->d;
 	vc = vc_con->d;
 	if (vc) {
 	if (vc) {
-		tty = vc->vc_tty;
+		tty = vc->port.tty;
 		/*
 		/*
 		 * SAK should also work in all raw modes and reset
 		 * SAK should also work in all raw modes and reset
 		 * them properly.
 		 * them properly.
@@ -1503,7 +1503,7 @@ long vt_compat_ioctl(struct tty_struct *tty, struct file * file,
 
 
 	console = vc->vc_num;
 	console = vc->vc_num;
 
 
-	lock_kernel();
+	tty_lock();
 
 
 	if (!vc_cons_allocated(console)) { 	/* impossible? */
 	if (!vc_cons_allocated(console)) { 	/* impossible? */
 		ret = -ENOIOCTLCMD;
 		ret = -ENOIOCTLCMD;
@@ -1571,11 +1571,11 @@ long vt_compat_ioctl(struct tty_struct *tty, struct file * file,
 		goto fallback;
 		goto fallback;
 	}
 	}
 out:
 out:
-	unlock_kernel();
+	tty_unlock();
 	return ret;
 	return ret;
 
 
 fallback:
 fallback:
-	unlock_kernel();
+	tty_unlock();
 	return vt_ioctl(tty, file, cmd, arg);
 	return vt_ioctl(tty, file, cmd, arg);
 }
 }
 
 
@@ -1761,10 +1761,13 @@ int vt_move_to_console(unsigned int vt, int alloc)
 		return -EIO;
 		return -EIO;
 	}
 	}
 	release_console_sem();
 	release_console_sem();
+	tty_lock();
 	if (vt_waitactive(vt + 1)) {
 	if (vt_waitactive(vt + 1)) {
 		pr_debug("Suspend: Can't switch VCs.");
 		pr_debug("Suspend: Can't switch VCs.");
+		tty_unlock();
 		return -EINTR;
 		return -EINTR;
 	}
 	}
+	tty_unlock();
 	return prev;
 	return prev;
 }
 }
 
 

+ 1 - 3
drivers/gpu/drm/i915/intel_fb.c

@@ -130,7 +130,7 @@ static int intelfb_create(struct intel_fbdev *ifbdev,
 
 
 	strcpy(info->fix.id, "inteldrmfb");
 	strcpy(info->fix.id, "inteldrmfb");
 
 
-	info->flags = FBINFO_DEFAULT;
+	info->flags = FBINFO_DEFAULT | FBINFO_CAN_FORCE_OUTPUT;
 	info->fbops = &intelfb_ops;
 	info->fbops = &intelfb_ops;
 
 
 	/* setup aperture base/size for vesafb takeover */
 	/* setup aperture base/size for vesafb takeover */
@@ -148,8 +148,6 @@ static int intelfb_create(struct intel_fbdev *ifbdev,
 	info->fix.smem_start = dev->mode_config.fb_base + obj_priv->gtt_offset;
 	info->fix.smem_start = dev->mode_config.fb_base + obj_priv->gtt_offset;
 	info->fix.smem_len = size;
 	info->fix.smem_len = size;
 
 
-	info->flags = FBINFO_DEFAULT;
-
 	info->screen_base = ioremap_wc(dev->agp->base + obj_priv->gtt_offset,
 	info->screen_base = ioremap_wc(dev->agp->base + obj_priv->gtt_offset,
 				       size);
 				       size);
 	if (!info->screen_base) {
 	if (!info->screen_base) {

+ 1 - 0
drivers/gpu/drm/nouveau/nouveau_fbcon.c

@@ -250,6 +250,7 @@ nouveau_fbcon_create(struct nouveau_fbdev *nfbdev,
 		info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_COPYAREA |
 		info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_COPYAREA |
 			      FBINFO_HWACCEL_FILLRECT |
 			      FBINFO_HWACCEL_FILLRECT |
 			      FBINFO_HWACCEL_IMAGEBLIT;
 			      FBINFO_HWACCEL_IMAGEBLIT;
+	info->flags |= FBINFO_CAN_FORCE_OUTPUT;
 	info->fbops = &nouveau_fbcon_ops;
 	info->fbops = &nouveau_fbcon_ops;
 	info->fix.smem_start = dev->mode_config.fb_base + nvbo->bo.offset -
 	info->fix.smem_start = dev->mode_config.fb_base + nvbo->bo.offset -
 			       dev_priv->vm_vram_base;
 			       dev_priv->vm_vram_base;

+ 1 - 1
drivers/gpu/drm/radeon/radeon_fb.c

@@ -224,7 +224,7 @@ static int radeonfb_create(struct radeon_fbdev *rfbdev,
 
 
 	drm_fb_helper_fill_fix(info, fb->pitch, fb->depth);
 	drm_fb_helper_fill_fix(info, fb->pitch, fb->depth);
 
 
-	info->flags = FBINFO_DEFAULT;
+	info->flags = FBINFO_DEFAULT | FBINFO_CAN_FORCE_OUTPUT;
 	info->fbops = &radeonfb_ops;
 	info->fbops = &radeonfb_ops;
 
 
 	tmp = radeon_bo_gpu_offset(rbo) - rdev->mc.vram_start;
 	tmp = radeon_bo_gpu_offset(rbo) - rdev->mc.vram_start;

+ 3 - 7
drivers/serial/21285.c

@@ -216,7 +216,7 @@ serial21285_set_termios(struct uart_port *port, struct ktermios *termios,
 			struct ktermios *old)
 			struct ktermios *old)
 {
 {
 	unsigned long flags;
 	unsigned long flags;
-	unsigned int baud, quot, h_lcr;
+	unsigned int baud, quot, h_lcr, b;
 
 
 	/*
 	/*
 	 * We don't support modem control lines.
 	 * We don't support modem control lines.
@@ -234,12 +234,8 @@ serial21285_set_termios(struct uart_port *port, struct ktermios *termios,
 	 */
 	 */
 	baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); 
 	baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); 
 	quot = uart_get_divisor(port, baud);
 	quot = uart_get_divisor(port, baud);
-
-	if (port->state && port->state->port.tty) {
-		struct tty_struct *tty = port->state->port.tty;
-		unsigned int b = port->uartclk / (16 * quot);
-		tty_encode_baud_rate(tty, b, b);
-	}
+	b = port->uartclk / (16 * quot);
+	tty_termios_encode_baud_rate(termios, b, b);
 
 
 	switch (termios->c_cflag & CSIZE) {
 	switch (termios->c_cflag & CSIZE) {
 	case CS5:
 	case CS5:

+ 7 - 19
drivers/serial/68328serial.c

@@ -78,10 +78,6 @@ struct m68k_serial *m68k_consinfo = 0;
 
 
 #define M68K_CLOCK (16667000) /* FIXME: 16MHz is likely wrong */
 #define M68K_CLOCK (16667000) /* FIXME: 16MHz is likely wrong */
 
 
-#ifdef CONFIG_CONSOLE
-extern wait_queue_head_t keypress_wait; 
-#endif
-
 struct tty_driver *serial_driver;
 struct tty_driver *serial_driver;
 
 
 /* number of characters left in xmit buffer before we ask for more */
 /* number of characters left in xmit buffer before we ask for more */
@@ -102,19 +98,13 @@ static void change_speed(struct m68k_serial *info);
  *	Setup for console. Argument comes from the boot command line.
  *	Setup for console. Argument comes from the boot command line.
  */
  */
 
 
-#if defined(CONFIG_M68EZ328ADS) || defined(CONFIG_ALMA_ANS) || defined(CONFIG_DRAGONIXVZ)
-#define	CONSOLE_BAUD_RATE	115200
-#define	DEFAULT_CBAUD		B115200
-#else
-	/* (es) */
-	/* note: this is messy, but it works, again, perhaps defined somewhere else?*/
-	#ifdef CONFIG_M68VZ328
-	#define CONSOLE_BAUD_RATE	19200
-	#define DEFAULT_CBAUD		B19200
-	#endif
-	/* (/es) */
+/* note: this is messy, but it works, again, perhaps defined somewhere else?*/
+#ifdef CONFIG_M68VZ328
+#define CONSOLE_BAUD_RATE	19200
+#define DEFAULT_CBAUD		B19200
 #endif
 #endif
 
 
+
 #ifndef CONSOLE_BAUD_RATE
 #ifndef CONSOLE_BAUD_RATE
 #define	CONSOLE_BAUD_RATE	9600
 #define	CONSOLE_BAUD_RATE	9600
 #define	DEFAULT_CBAUD		B9600
 #define	DEFAULT_CBAUD		B9600
@@ -300,10 +290,6 @@ static void receive_chars(struct m68k_serial *info, unsigned short rx)
 				return;
 				return;
 #endif /* CONFIG_MAGIC_SYSRQ */
 #endif /* CONFIG_MAGIC_SYSRQ */
 			}
 			}
-			/* It is a 'keyboard interrupt' ;-) */
-#ifdef CONFIG_CONSOLE
-			wake_up(&keypress_wait);
-#endif			
 		}
 		}
 
 
 		if(!tty)
 		if(!tty)
@@ -1243,7 +1229,9 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp,
 			retval = -ERESTARTSYS;
 			retval = -ERESTARTSYS;
 			break;
 			break;
 		}
 		}
+		tty_unlock();
 		schedule();
 		schedule();
+		tty_lock();
 	}
 	}
 	current->state = TASK_RUNNING;
 	current->state = TASK_RUNNING;
 	remove_wait_queue(&info->open_wait, &wait);
 	remove_wait_queue(&info->open_wait, &wait);

+ 2 - 2
drivers/serial/68360serial.c

@@ -1705,7 +1705,6 @@ static void rs_360_wait_until_sent(struct tty_struct *tty, int timeout)
 	printk("jiff=%lu...", jiffies);
 	printk("jiff=%lu...", jiffies);
 #endif
 #endif
 
 
-	lock_kernel();
 	/* We go through the loop at least once because we can't tell
 	/* We go through the loop at least once because we can't tell
 	 * exactly when the last character exits the shifter.  There can
 	 * exactly when the last character exits the shifter.  There can
 	 * be at least two characters waiting to be sent after the buffers
 	 * be at least two characters waiting to be sent after the buffers
@@ -1734,7 +1733,6 @@ static void rs_360_wait_until_sent(struct tty_struct *tty, int timeout)
 			bdp--;
 			bdp--;
 	} while (bdp->status & BD_SC_READY);
 	} while (bdp->status & BD_SC_READY);
 	current->state = TASK_RUNNING;
 	current->state = TASK_RUNNING;
-	unlock_kernel();
 #ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
 #ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
 	printk("lsr = %d (jiff=%lu)...done\n", lsr, jiffies);
 	printk("lsr = %d (jiff=%lu)...done\n", lsr, jiffies);
 #endif
 #endif
@@ -1862,7 +1860,9 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp,
 		printk("block_til_ready blocking: ttys%d, count = %d\n",
 		printk("block_til_ready blocking: ttys%d, count = %d\n",
 		       info->line, state->count);
 		       info->line, state->count);
 #endif
 #endif
+		tty_unlock();
 		schedule();
 		schedule();
+		tty_lock();
 	}
 	}
 	current->state = TASK_RUNNING;
 	current->state = TASK_RUNNING;
 	remove_wait_queue(&info->open_wait, &wait);
 	remove_wait_queue(&info->open_wait, &wait);

+ 36 - 10
drivers/serial/8250.c

@@ -241,7 +241,7 @@ static const struct serial8250_config uart_config[] = {
 		.fifo_size	= 128,
 		.fifo_size	= 128,
 		.tx_loadsz	= 128,
 		.tx_loadsz	= 128,
 		.fcr		= UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
 		.fcr		= UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
-		.flags		= UART_CAP_FIFO,
+		.flags		= UART_CAP_FIFO | UART_CAP_EFR | UART_CAP_SLEEP,
 	},
 	},
 	[PORT_16654] = {
 	[PORT_16654] = {
 		.name		= "ST16654",
 		.name		= "ST16654",
@@ -300,6 +300,13 @@ static const struct serial8250_config uart_config[] = {
 		.fcr		= UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_00,
 		.fcr		= UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_00,
 		.flags		= UART_CAP_FIFO | UART_CAP_AFE,
 		.flags		= UART_CAP_FIFO | UART_CAP_AFE,
 	},
 	},
+	[PORT_U6_16550A] = {
+		.name		= "U6_16550A",
+		.fifo_size	= 64,
+		.tx_loadsz	= 64,
+		.fcr		= UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
+		.flags		= UART_CAP_FIFO | UART_CAP_AFE,
+	},
 };
 };
 
 
 #if defined(CONFIG_MIPS_ALCHEMY)
 #if defined(CONFIG_MIPS_ALCHEMY)
@@ -1070,6 +1077,15 @@ static void autoconfig_16550a(struct uart_8250_port *up)
 		DEBUG_AUTOCONF("Couldn't force IER_UUE to 0 ");
 		DEBUG_AUTOCONF("Couldn't force IER_UUE to 0 ");
 	}
 	}
 	serial_outp(up, UART_IER, iersave);
 	serial_outp(up, UART_IER, iersave);
+
+	/*
+	 * We distinguish between 16550A and U6 16550A by counting
+	 * how many bytes are in the FIFO.
+	 */
+	if (up->port.type == PORT_16550A && size_fifo(up) == 64) {
+		up->port.type = PORT_U6_16550A;
+		up->capabilities |= UART_CAP_AFE;
+	}
 }
 }
 
 
 /*
 /*
@@ -2224,9 +2240,9 @@ static unsigned int serial8250_get_divisor(struct uart_port *port, unsigned int
 	return quot;
 	return quot;
 }
 }
 
 
-static void
-serial8250_set_termios(struct uart_port *port, struct ktermios *termios,
-		       struct ktermios *old)
+void
+serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios,
+		          struct ktermios *old)
 {
 {
 	struct uart_8250_port *up = (struct uart_8250_port *)port;
 	struct uart_8250_port *up = (struct uart_8250_port *)port;
 	unsigned char cval, fcr = 0;
 	unsigned char cval, fcr = 0;
@@ -2402,16 +2418,22 @@ serial8250_set_termios(struct uart_port *port, struct ktermios *termios,
 	if (tty_termios_baud_rate(termios))
 	if (tty_termios_baud_rate(termios))
 		tty_termios_encode_baud_rate(termios, baud, baud);
 		tty_termios_encode_baud_rate(termios, baud, baud);
 }
 }
+EXPORT_SYMBOL(serial8250_do_set_termios);
 
 
 static void
 static void
-serial8250_set_ldisc(struct uart_port *port)
+serial8250_set_termios(struct uart_port *port, struct ktermios *termios,
+		       struct ktermios *old)
 {
 {
-	int line = port->line;
-
-	if (line >= port->state->port.tty->driver->num)
-		return;
+	if (port->set_termios)
+		port->set_termios(port, termios, old);
+	else
+		serial8250_do_set_termios(port, termios, old);
+}
 
 
-	if (port->state->port.tty->ldisc->ops->num == N_PPS) {
+static void
+serial8250_set_ldisc(struct uart_port *port, int new)
+{
+	if (new == N_PPS) {
 		port->flags |= UPF_HARDPPS_CD;
 		port->flags |= UPF_HARDPPS_CD;
 		serial8250_enable_ms(port);
 		serial8250_enable_ms(port);
 	} else
 	} else
@@ -2987,6 +3009,7 @@ static int __devinit serial8250_probe(struct platform_device *dev)
 		port.type		= p->type;
 		port.type		= p->type;
 		port.serial_in		= p->serial_in;
 		port.serial_in		= p->serial_in;
 		port.serial_out		= p->serial_out;
 		port.serial_out		= p->serial_out;
+		port.set_termios	= p->set_termios;
 		port.dev		= &dev->dev;
 		port.dev		= &dev->dev;
 		port.irqflags		|= irqflag;
 		port.irqflags		|= irqflag;
 		ret = serial8250_register_port(&port);
 		ret = serial8250_register_port(&port);
@@ -3150,6 +3173,9 @@ int serial8250_register_port(struct uart_port *port)
 			uart->port.serial_in = port->serial_in;
 			uart->port.serial_in = port->serial_in;
 		if (port->serial_out)
 		if (port->serial_out)
 			uart->port.serial_out = port->serial_out;
 			uart->port.serial_out = port->serial_out;
+		/*  Possibly override set_termios call */
+		if (port->set_termios)
+			uart->port.set_termios = port->set_termios;
 
 
 		ret = uart_add_one_port(&serial8250_reg, &uart->port);
 		ret = uart_add_one_port(&serial8250_reg, &uart->port);
 		if (ret == 0)
 		if (ret == 0)

+ 42 - 15
drivers/serial/8250_early.c

@@ -19,9 +19,11 @@
  * The user can specify the device directly, e.g.,
  * The user can specify the device directly, e.g.,
  *	earlycon=uart8250,io,0x3f8,9600n8
  *	earlycon=uart8250,io,0x3f8,9600n8
  *	earlycon=uart8250,mmio,0xff5e0000,115200n8
  *	earlycon=uart8250,mmio,0xff5e0000,115200n8
+ *	earlycon=uart8250,mmio32,0xff5e0000,115200n8
  * or
  * or
  *	console=uart8250,io,0x3f8,9600n8
  *	console=uart8250,io,0x3f8,9600n8
  *	console=uart8250,mmio,0xff5e0000,115200n8
  *	console=uart8250,mmio,0xff5e0000,115200n8
+ *	console=uart8250,mmio32,0xff5e0000,115200n8
  */
  */
 
 
 #include <linux/tty.h>
 #include <linux/tty.h>
@@ -48,18 +50,31 @@ static struct early_serial8250_device early_device;
 
 
 static unsigned int __init serial_in(struct uart_port *port, int offset)
 static unsigned int __init serial_in(struct uart_port *port, int offset)
 {
 {
-	if (port->iotype == UPIO_MEM)
+	switch (port->iotype) {
+	case UPIO_MEM:
 		return readb(port->membase + offset);
 		return readb(port->membase + offset);
-	else
+	case UPIO_MEM32:
+		return readl(port->membase + (offset << 2));
+	case UPIO_PORT:
 		return inb(port->iobase + offset);
 		return inb(port->iobase + offset);
+	default:
+		return 0;
+	}
 }
 }
 
 
 static void __init serial_out(struct uart_port *port, int offset, int value)
 static void __init serial_out(struct uart_port *port, int offset, int value)
 {
 {
-	if (port->iotype == UPIO_MEM)
+	switch (port->iotype) {
+	case UPIO_MEM:
 		writeb(value, port->membase + offset);
 		writeb(value, port->membase + offset);
-	else
+		break;
+	case UPIO_MEM32:
+		writel(value, port->membase + (offset << 2));
+		break;
+	case UPIO_PORT:
 		outb(value, port->iobase + offset);
 		outb(value, port->iobase + offset);
+		break;
+	}
 }
 }
 
 
 #define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE)
 #define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE)
@@ -137,15 +152,21 @@ static int __init parse_options(struct early_serial8250_device *device,
 								char *options)
 								char *options)
 {
 {
 	struct uart_port *port = &device->port;
 	struct uart_port *port = &device->port;
-	int mmio, length;
+	int mmio, mmio32, length;
 
 
 	if (!options)
 	if (!options)
 		return -ENODEV;
 		return -ENODEV;
 
 
 	port->uartclk = BASE_BAUD * 16;
 	port->uartclk = BASE_BAUD * 16;
-	if (!strncmp(options, "mmio,", 5)) {
-		port->iotype = UPIO_MEM;
-		port->mapbase = simple_strtoul(options + 5, &options, 0);
+
+	mmio = !strncmp(options, "mmio,", 5);
+	mmio32 = !strncmp(options, "mmio32,", 7);
+	if (mmio || mmio32) {
+		port->iotype = (mmio ? UPIO_MEM : UPIO_MEM32);
+		port->mapbase = simple_strtoul(options + (mmio ? 5 : 7),
+					       &options, 0);
+		if (mmio32)
+			port->regshift = 2;
 #ifdef CONFIG_FIX_EARLYCON_MEM
 #ifdef CONFIG_FIX_EARLYCON_MEM
 		set_fixmap_nocache(FIX_EARLYCON_MEM_BASE,
 		set_fixmap_nocache(FIX_EARLYCON_MEM_BASE,
 					port->mapbase & PAGE_MASK);
 					port->mapbase & PAGE_MASK);
@@ -157,11 +178,10 @@ static int __init parse_options(struct early_serial8250_device *device,
 		if (!port->membase) {
 		if (!port->membase) {
 			printk(KERN_ERR "%s: Couldn't ioremap 0x%llx\n",
 			printk(KERN_ERR "%s: Couldn't ioremap 0x%llx\n",
 				__func__,
 				__func__,
-			       (unsigned long long)port->mapbase);
+			       (unsigned long long) port->mapbase);
 			return -ENOMEM;
 			return -ENOMEM;
 		}
 		}
 #endif
 #endif
-		mmio = 1;
 	} else if (!strncmp(options, "io,", 3)) {
 	} else if (!strncmp(options, "io,", 3)) {
 		port->iotype = UPIO_PORT;
 		port->iotype = UPIO_PORT;
 		port->iobase = simple_strtoul(options + 3, &options, 0);
 		port->iobase = simple_strtoul(options + 3, &options, 0);
@@ -181,11 +201,18 @@ static int __init parse_options(struct early_serial8250_device *device,
 			device->baud);
 			device->baud);
 	}
 	}
 
 
-	printk(KERN_INFO "Early serial console at %s 0x%llx (options '%s')\n",
-		mmio ? "MMIO" : "I/O port",
-		mmio ? (unsigned long long) port->mapbase
-		     : (unsigned long long) port->iobase,
-		device->options);
+	if (mmio || mmio32)
+		printk(KERN_INFO
+		       "Early serial console at MMIO%s 0x%llu (options '%s')\n",
+			mmio32 ? "32" : "",
+			(unsigned long long)port->mapbase,
+			device->options);
+	else
+		printk(KERN_INFO
+		      "Early serial console at I/O port 0x%lu (options '%s')\n",
+			port->iobase,
+			device->options);
+
 	return 0;
 	return 0;
 }
 }
 
 

+ 13 - 0
drivers/serial/8250_pci.c

@@ -994,6 +994,7 @@ static int skip_tx_en_setup(struct serial_private *priv,
 #define PCI_DEVICE_ID_TITAN_800E	0xA014
 #define PCI_DEVICE_ID_TITAN_800E	0xA014
 #define PCI_DEVICE_ID_TITAN_200EI	0xA016
 #define PCI_DEVICE_ID_TITAN_200EI	0xA016
 #define PCI_DEVICE_ID_TITAN_200EISI	0xA017
 #define PCI_DEVICE_ID_TITAN_200EISI	0xA017
+#define PCI_DEVICE_ID_OXSEMI_16PCI958	0x9538
 
 
 /* Unknown vendors/cards - this should not be in linux/pci_ids.h */
 /* Unknown vendors/cards - this should not be in linux/pci_ids.h */
 #define PCI_SUBDEVICE_ID_UNKNOWN_0x1584	0x1584
 #define PCI_SUBDEVICE_ID_UNKNOWN_0x1584	0x1584
@@ -1542,6 +1543,8 @@ enum pci_board_num_t {
 	pbn_b2_4_921600,
 	pbn_b2_4_921600,
 	pbn_b2_8_921600,
 	pbn_b2_8_921600,
 
 
+	pbn_b2_8_1152000,
+
 	pbn_b2_bt_1_115200,
 	pbn_b2_bt_1_115200,
 	pbn_b2_bt_2_115200,
 	pbn_b2_bt_2_115200,
 	pbn_b2_bt_4_115200,
 	pbn_b2_bt_4_115200,
@@ -1960,6 +1963,13 @@ static struct pciserial_board pci_boards[] __devinitdata = {
 		.uart_offset	= 8,
 		.uart_offset	= 8,
 	},
 	},
 
 
+	[pbn_b2_8_1152000] = {
+		.flags		= FL_BASE2,
+		.num_ports	= 8,
+		.base_baud	= 1152000,
+		.uart_offset	= 8,
+	},
+
 	[pbn_b2_bt_1_115200] = {
 	[pbn_b2_bt_1_115200] = {
 		.flags		= FL_BASE2|FL_BASE_BARS,
 		.flags		= FL_BASE2|FL_BASE_BARS,
 		.num_ports	= 1,
 		.num_ports	= 1,
@@ -2875,6 +2885,9 @@ static struct pci_device_id serial_pci_tbl[] = {
 	{	PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI952,
 	{	PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI952,
 		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
 		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
 		pbn_b0_bt_2_921600 },
 		pbn_b0_bt_2_921600 },
+	{	PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI958,
+		PCI_ANY_ID , PCI_ANY_ID, 0, 0,
+		pbn_b2_8_1152000 },
 
 
 	/*
 	/*
 	 * Oxford Semiconductor Inc. Tornado PCI express device range.
 	 * Oxford Semiconductor Inc. Tornado PCI express device range.

+ 44 - 0
drivers/serial/Kconfig

@@ -542,6 +542,7 @@ config SERIAL_S5PV210
 	help
 	help
 	  Serial port support for Samsung's S5P Family of SoC's
 	  Serial port support for Samsung's S5P Family of SoC's
 
 
+
 config SERIAL_MAX3100
 config SERIAL_MAX3100
 	tristate "MAX3100 support"
 	tristate "MAX3100 support"
 	depends on SPI
 	depends on SPI
@@ -549,6 +550,22 @@ config SERIAL_MAX3100
 	help
 	help
 	  MAX3100 chip support
 	  MAX3100 chip support
 
 
+config SERIAL_MAX3107
+	tristate "MAX3107 support"
+	depends on SPI
+	select SERIAL_CORE
+	help
+	  MAX3107 chip support
+
+config SERIAL_MAX3107_AAVA
+	tristate "MAX3107 AAVA platform support"
+	depends on X86_MRST && SERIAL_MAX3107 && GPIOLIB
+	select SERIAL_CORE
+	help
+	  Support for the MAX3107 chip configuration found on the AAVA
+	  platform. Includes the extra initialisation and GPIO support
+	  neded for this device.
+
 config SERIAL_DZ
 config SERIAL_DZ
 	bool "DECstation DZ serial driver"
 	bool "DECstation DZ serial driver"
 	depends on MACH_DECSTATION && 32BIT
 	depends on MACH_DECSTATION && 32BIT
@@ -690,6 +707,33 @@ config SERIAL_SA1100_CONSOLE
 	  your boot loader (lilo or loadlin) about how to pass options to the
 	  your boot loader (lilo or loadlin) about how to pass options to the
 	  kernel at boot time.)
 	  kernel at boot time.)
 
 
+config SERIAL_MRST_MAX3110
+	tristate "SPI UART driver for Max3110"
+	depends on SPI_DW_PCI
+	select SERIAL_CORE
+	select SERIAL_CORE_CONSOLE
+	help
+	  This is the UART protocol driver for the MAX3110 device on
+	  the Intel Moorestown platform. On other systems use the max3100
+	  driver.
+
+config MRST_MAX3110_IRQ
+	boolean "Enable GPIO IRQ for Max3110 over Moorestown"
+	default n
+	depends on SERIAL_MRST_MAX3110 && GPIO_LANGWELL
+	help
+	  This has to be enabled after Moorestown GPIO driver is loaded
+
+config SERIAL_MFD_HSU
+	tristate "Medfield High Speed UART support"
+	depends on PCI
+	select SERIAL_CORE
+
+config SERIAL_MFD_HSU_CONSOLE
+	boolean "Medfile HSU serial console support"
+	depends on SERIAL_MFD_HSU=y
+	select SERIAL_CORE_CONSOLE
+
 config SERIAL_BFIN
 config SERIAL_BFIN
 	tristate "Blackfin serial port support"
 	tristate "Blackfin serial port support"
 	depends on BLACKFIN
 	depends on BLACKFIN

+ 4 - 0
drivers/serial/Makefile

@@ -46,6 +46,8 @@ obj-$(CONFIG_SERIAL_S3C24A0) += s3c24a0.o
 obj-$(CONFIG_SERIAL_S3C6400) += s3c6400.o
 obj-$(CONFIG_SERIAL_S3C6400) += s3c6400.o
 obj-$(CONFIG_SERIAL_S5PV210) += s5pv210.o
 obj-$(CONFIG_SERIAL_S5PV210) += s5pv210.o
 obj-$(CONFIG_SERIAL_MAX3100) += max3100.o
 obj-$(CONFIG_SERIAL_MAX3100) += max3100.o
+obj-$(CONFIG_SERIAL_MAX3107) += max3107.o
+obj-$(CONFIG_SERIAL_MAX3107_AAVA) += max3107-aava.o
 obj-$(CONFIG_SERIAL_IP22_ZILOG) += ip22zilog.o
 obj-$(CONFIG_SERIAL_IP22_ZILOG) += ip22zilog.o
 obj-$(CONFIG_SERIAL_MUX) += mux.o
 obj-$(CONFIG_SERIAL_MUX) += mux.o
 obj-$(CONFIG_SERIAL_68328) += 68328serial.o
 obj-$(CONFIG_SERIAL_68328) += 68328serial.o
@@ -84,3 +86,5 @@ obj-$(CONFIG_SERIAL_TIMBERDALE)	+= timbuart.o
 obj-$(CONFIG_SERIAL_GRLIB_GAISLER_APBUART) += apbuart.o
 obj-$(CONFIG_SERIAL_GRLIB_GAISLER_APBUART) += apbuart.o
 obj-$(CONFIG_SERIAL_ALTERA_JTAGUART) += altera_jtaguart.o
 obj-$(CONFIG_SERIAL_ALTERA_JTAGUART) += altera_jtaguart.o
 obj-$(CONFIG_SERIAL_ALTERA_UART) += altera_uart.o
 obj-$(CONFIG_SERIAL_ALTERA_UART) += altera_uart.o
+obj-$(CONFIG_SERIAL_MRST_MAX3110)	+= mrst_max3110.o
+obj-$(CONFIG_SERIAL_MFD_HSU)	+= mfd.o

+ 1 - 1
drivers/serial/altera_uart.c

@@ -394,7 +394,7 @@ int __init early_altera_uart_setup(struct altera_uart_platform_uart *platp)
 static void altera_uart_console_putc(struct uart_port *port, const char c)
 static void altera_uart_console_putc(struct uart_port *port, const char c)
 {
 {
 	while (!(readl(port->membase + ALTERA_UART_STATUS_REG) &
 	while (!(readl(port->membase + ALTERA_UART_STATUS_REG) &
-	         ALTERA_UART_STATUS_TRDY_MSK))
+		 ALTERA_UART_STATUS_TRDY_MSK))
 		cpu_relax();
 		cpu_relax();
 
 
 	writel(c, port->membase + ALTERA_UART_TXDATA_REG);
 	writel(c, port->membase + ALTERA_UART_TXDATA_REG);

+ 8 - 3
drivers/serial/atmel_serial.c

@@ -217,7 +217,8 @@ void atmel_config_rs485(struct uart_port *port, struct serial_rs485 *rs485conf)
 	if (rs485conf->flags & SER_RS485_ENABLED) {
 	if (rs485conf->flags & SER_RS485_ENABLED) {
 		dev_dbg(port->dev, "Setting UART to RS485\n");
 		dev_dbg(port->dev, "Setting UART to RS485\n");
 		atmel_port->tx_done_mask = ATMEL_US_TXEMPTY;
 		atmel_port->tx_done_mask = ATMEL_US_TXEMPTY;
-		UART_PUT_TTGR(port, rs485conf->delay_rts_before_send);
+		if (rs485conf->flags & SER_RS485_RTS_AFTER_SEND)
+			UART_PUT_TTGR(port, rs485conf->delay_rts_after_send);
 		mode |= ATMEL_US_USMODE_RS485;
 		mode |= ATMEL_US_USMODE_RS485;
 	} else {
 	} else {
 		dev_dbg(port->dev, "Setting UART to RS232\n");
 		dev_dbg(port->dev, "Setting UART to RS232\n");
@@ -292,7 +293,9 @@ static void atmel_set_mctrl(struct uart_port *port, u_int mctrl)
 
 
 	if (atmel_port->rs485.flags & SER_RS485_ENABLED) {
 	if (atmel_port->rs485.flags & SER_RS485_ENABLED) {
 		dev_dbg(port->dev, "Setting UART to RS485\n");
 		dev_dbg(port->dev, "Setting UART to RS485\n");
-		UART_PUT_TTGR(port, atmel_port->rs485.delay_rts_before_send);
+		if (atmel_port->rs485.flags & SER_RS485_RTS_AFTER_SEND)
+			UART_PUT_TTGR(port,
+					atmel_port->rs485.delay_rts_after_send);
 		mode |= ATMEL_US_USMODE_RS485;
 		mode |= ATMEL_US_USMODE_RS485;
 	} else {
 	} else {
 		dev_dbg(port->dev, "Setting UART to RS232\n");
 		dev_dbg(port->dev, "Setting UART to RS232\n");
@@ -1211,7 +1214,9 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios,
 
 
 	if (atmel_port->rs485.flags & SER_RS485_ENABLED) {
 	if (atmel_port->rs485.flags & SER_RS485_ENABLED) {
 		dev_dbg(port->dev, "Setting UART to RS485\n");
 		dev_dbg(port->dev, "Setting UART to RS485\n");
-		UART_PUT_TTGR(port, atmel_port->rs485.delay_rts_before_send);
+		if (atmel_port->rs485.flags & SER_RS485_RTS_AFTER_SEND)
+			UART_PUT_TTGR(port,
+					atmel_port->rs485.delay_rts_after_send);
 		mode |= ATMEL_US_USMODE_RS485;
 		mode |= ATMEL_US_USMODE_RS485;
 	} else {
 	} else {
 		dev_dbg(port->dev, "Setting UART to RS232\n");
 		dev_dbg(port->dev, "Setting UART to RS232\n");

+ 2 - 5
drivers/serial/bfin_5xx.c

@@ -957,15 +957,12 @@ bfin_serial_verify_port(struct uart_port *port, struct serial_struct *ser)
  * Enable the IrDA function if tty->ldisc.num is N_IRDA.
  * Enable the IrDA function if tty->ldisc.num is N_IRDA.
  * In other cases, disable IrDA function.
  * In other cases, disable IrDA function.
  */
  */
-static void bfin_serial_set_ldisc(struct uart_port *port)
+static void bfin_serial_set_ldisc(struct uart_port *port, int ld)
 {
 {
 	int line = port->line;
 	int line = port->line;
 	unsigned short val;
 	unsigned short val;
 
 
-	if (line >= port->state->port.tty->driver->num)
-		return;
-
-	switch (port->state->port.tty->termios->c_line) {
+	switch (ld) {
 	case N_IRDA:
 	case N_IRDA:
 		val = UART_GET_GCTL(&bfin_serial_ports[line]);
 		val = UART_GET_GCTL(&bfin_serial_ports[line]);
 		val |= (IREN | RPOLC);
 		val |= (IREN | RPOLC);

+ 6 - 6
drivers/serial/crisv10.c

@@ -3935,7 +3935,6 @@ static void rs_wait_until_sent(struct tty_struct *tty, int timeout)
 	 * Check R_DMA_CHx_STATUS bit 0-6=number of available bytes in FIFO
 	 * Check R_DMA_CHx_STATUS bit 0-6=number of available bytes in FIFO
 	 * R_DMA_CHx_HWSW bit 31-16=nbr of bytes left in DMA buffer (0=64k)
 	 * R_DMA_CHx_HWSW bit 31-16=nbr of bytes left in DMA buffer (0=64k)
 	 */
 	 */
-	lock_kernel();
 	orig_jiffies = jiffies;
 	orig_jiffies = jiffies;
 	while (info->xmit.head != info->xmit.tail || /* More in send queue */
 	while (info->xmit.head != info->xmit.tail || /* More in send queue */
 	       (*info->ostatusadr & 0x007f) ||  /* more in FIFO */
 	       (*info->ostatusadr & 0x007f) ||  /* more in FIFO */
@@ -3952,7 +3951,6 @@ static void rs_wait_until_sent(struct tty_struct *tty, int timeout)
 			curr_time_usec - info->last_tx_active_usec;
 			curr_time_usec - info->last_tx_active_usec;
 	}
 	}
 	set_current_state(TASK_RUNNING);
 	set_current_state(TASK_RUNNING);
-	unlock_kernel();
 }
 }
 
 
 /*
 /*
@@ -3992,7 +3990,7 @@ block_til_ready(struct tty_struct *tty, struct file * filp,
 	 */
 	 */
 	if (tty_hung_up_p(filp) ||
 	if (tty_hung_up_p(filp) ||
 	    (info->flags & ASYNC_CLOSING)) {
 	    (info->flags & ASYNC_CLOSING)) {
-		wait_event_interruptible(info->close_wait,
+		wait_event_interruptible_tty(info->close_wait,
 			!(info->flags & ASYNC_CLOSING));
 			!(info->flags & ASYNC_CLOSING));
 #ifdef SERIAL_DO_RESTART
 #ifdef SERIAL_DO_RESTART
 		if (info->flags & ASYNC_HUP_NOTIFY)
 		if (info->flags & ASYNC_HUP_NOTIFY)
@@ -4068,7 +4066,9 @@ block_til_ready(struct tty_struct *tty, struct file * filp,
 		printk("block_til_ready blocking: ttyS%d, count = %d\n",
 		printk("block_til_ready blocking: ttyS%d, count = %d\n",
 		       info->line, info->count);
 		       info->line, info->count);
 #endif
 #endif
+		tty_unlock();
 		schedule();
 		schedule();
+		tty_lock();
 	}
 	}
 	set_current_state(TASK_RUNNING);
 	set_current_state(TASK_RUNNING);
 	remove_wait_queue(&info->open_wait, &wait);
 	remove_wait_queue(&info->open_wait, &wait);
@@ -4150,7 +4150,7 @@ rs_open(struct tty_struct *tty, struct file * filp)
 	 */
 	 */
 	if (tty_hung_up_p(filp) ||
 	if (tty_hung_up_p(filp) ||
 	    (info->flags & ASYNC_CLOSING)) {
 	    (info->flags & ASYNC_CLOSING)) {
-		wait_event_interruptible(info->close_wait,
+		wait_event_interruptible_tty(info->close_wait,
 			!(info->flags & ASYNC_CLOSING));
 			!(info->flags & ASYNC_CLOSING));
 #ifdef SERIAL_DO_RESTART
 #ifdef SERIAL_DO_RESTART
 		return ((info->flags & ASYNC_HUP_NOTIFY) ?
 		return ((info->flags & ASYNC_HUP_NOTIFY) ?
@@ -4533,8 +4533,8 @@ static int __init rs_init(void)
 		INIT_WORK(&info->work, do_softint);
 		INIT_WORK(&info->work, do_softint);
 
 
 		if (info->enabled) {
 		if (info->enabled) {
-			printk(KERN_INFO "%s%d at 0x%x is a builtin UART with DMA\n",
-			       serial_driver->name, info->line, (unsigned int)info->ioport);
+			printk(KERN_INFO "%s%d at %p is a builtin UART with DMA\n",
+			       serial_driver->name, info->line, info->ioport);
 		}
 		}
 	}
 	}
 #ifdef CONFIG_ETRAX_FAST_TIMER
 #ifdef CONFIG_ETRAX_FAST_TIMER

+ 4 - 6
drivers/serial/imx.c

@@ -909,13 +909,11 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
 	rational_best_approximation(16 * div * baud, sport->port.uartclk,
 	rational_best_approximation(16 * div * baud, sport->port.uartclk,
 		1 << 16, 1 << 16, &num, &denom);
 		1 << 16, 1 << 16, &num, &denom);
 
 
-	if (port->state && port->state->port.tty) {
-		tdiv64 = sport->port.uartclk;
-		tdiv64 *= num;
-		do_div(tdiv64, denom * 16 * div);
-		tty_encode_baud_rate(sport->port.state->port.tty,
+	tdiv64 = sport->port.uartclk;
+	tdiv64 *= num;
+	do_div(tdiv64, denom * 16 * div);
+	tty_termios_encode_baud_rate(termios,
 				(speed_t)tdiv64, (speed_t)tdiv64);
 				(speed_t)tdiv64, (speed_t)tdiv64);
-	}
 
 
 	num -= 1;
 	num -= 1;
 	denom -= 1;
 	denom -= 1;

+ 5 - 4
drivers/serial/ioc3_serial.c

@@ -954,12 +954,13 @@ ioc3_change_speed(struct uart_port *the_port,
 		  struct ktermios *new_termios, struct ktermios *old_termios)
 		  struct ktermios *new_termios, struct ktermios *old_termios)
 {
 {
 	struct ioc3_port *port = get_ioc3_port(the_port);
 	struct ioc3_port *port = get_ioc3_port(the_port);
-	unsigned int cflag;
+	unsigned int cflag, iflag;
 	int baud;
 	int baud;
 	int new_parity = 0, new_parity_enable = 0, new_stop = 0, new_data = 8;
 	int new_parity = 0, new_parity_enable = 0, new_stop = 0, new_data = 8;
 	struct uart_state *state = the_port->state;
 	struct uart_state *state = the_port->state;
 
 
 	cflag = new_termios->c_cflag;
 	cflag = new_termios->c_cflag;
+	iflag = new_termios->c_iflag;
 
 
 	switch (cflag & CSIZE) {
 	switch (cflag & CSIZE) {
 	case CS5:
 	case CS5:
@@ -1000,12 +1001,12 @@ ioc3_change_speed(struct uart_port *the_port,
 
 
 	state->port.tty->low_latency = 1;
 	state->port.tty->low_latency = 1;
 
 
-	if (I_IGNPAR(state->port.tty))
+	if (iflag & IGNPAR)
 		the_port->ignore_status_mask &= ~(N_PARITY_ERROR
 		the_port->ignore_status_mask &= ~(N_PARITY_ERROR
 						  | N_FRAMING_ERROR);
 						  | N_FRAMING_ERROR);
-	if (I_IGNBRK(state->port.tty)) {
+	if (iflag & IGNBRK) {
 		the_port->ignore_status_mask &= ~N_BREAK;
 		the_port->ignore_status_mask &= ~N_BREAK;
-		if (I_IGNPAR(state->port.tty))
+		if (iflag & IGNPAR)
 			the_port->ignore_status_mask &= ~N_OVERRUN_ERROR;
 			the_port->ignore_status_mask &= ~N_OVERRUN_ERROR;
 	}
 	}
 	if (!(cflag & CREAD)) {
 	if (!(cflag & CREAD)) {

+ 5 - 4
drivers/serial/ioc4_serial.c

@@ -1685,11 +1685,12 @@ ioc4_change_speed(struct uart_port *the_port,
 {
 {
 	struct ioc4_port *port = get_ioc4_port(the_port, 0);
 	struct ioc4_port *port = get_ioc4_port(the_port, 0);
 	int baud, bits;
 	int baud, bits;
-	unsigned cflag;
+	unsigned cflag, iflag;
 	int new_parity = 0, new_parity_enable = 0, new_stop = 0, new_data = 8;
 	int new_parity = 0, new_parity_enable = 0, new_stop = 0, new_data = 8;
 	struct uart_state *state = the_port->state;
 	struct uart_state *state = the_port->state;
 
 
 	cflag = new_termios->c_cflag;
 	cflag = new_termios->c_cflag;
+	iflag = new_termios->c_iflag;
 
 
 	switch (cflag & CSIZE) {
 	switch (cflag & CSIZE) {
 	case CS5:
 	case CS5:
@@ -1741,12 +1742,12 @@ ioc4_change_speed(struct uart_port *the_port,
 
 
 	state->port.tty->low_latency = 1;
 	state->port.tty->low_latency = 1;
 
 
-	if (I_IGNPAR(state->port.tty))
+	if (iflag & IGNPAR)
 		the_port->ignore_status_mask &= ~(N_PARITY_ERROR
 		the_port->ignore_status_mask &= ~(N_PARITY_ERROR
 						| N_FRAMING_ERROR);
 						| N_FRAMING_ERROR);
-	if (I_IGNBRK(state->port.tty)) {
+	if (iflag & IGNBRK) {
 		the_port->ignore_status_mask &= ~N_BREAK;
 		the_port->ignore_status_mask &= ~N_BREAK;
-		if (I_IGNPAR(state->port.tty))
+		if (iflag & IGNPAR)
 			the_port->ignore_status_mask &= ~N_OVERRUN_ERROR;
 			the_port->ignore_status_mask &= ~N_OVERRUN_ERROR;
 	}
 	}
 	if (!(cflag & CREAD)) {
 	if (!(cflag & CREAD)) {

+ 2 - 5
drivers/serial/max3100.c

@@ -430,17 +430,14 @@ max3100_set_termios(struct uart_port *port, struct ktermios *termios,
 	int baud = 0;
 	int baud = 0;
 	unsigned cflag;
 	unsigned cflag;
 	u32 param_new, param_mask, parity = 0;
 	u32 param_new, param_mask, parity = 0;
-	struct tty_struct *tty = s->port.state->port.tty;
 
 
 	dev_dbg(&s->spi->dev, "%s\n", __func__);
 	dev_dbg(&s->spi->dev, "%s\n", __func__);
-	if (!tty)
-		return;
 
 
 	cflag = termios->c_cflag;
 	cflag = termios->c_cflag;
 	param_new = 0;
 	param_new = 0;
 	param_mask = 0;
 	param_mask = 0;
 
 
-	baud = tty_get_baud_rate(tty);
+	baud = tty_termios_baud_rate(termios);
 	param_new = s->conf & MAX3100_BAUD;
 	param_new = s->conf & MAX3100_BAUD;
 	switch (baud) {
 	switch (baud) {
 	case 300:
 	case 300:
@@ -485,7 +482,7 @@ max3100_set_termios(struct uart_port *port, struct ktermios *termios,
 	default:
 	default:
 		baud = s->baud;
 		baud = s->baud;
 	}
 	}
-	tty_encode_baud_rate(tty, baud, baud);
+	tty_termios_encode_baud_rate(termios, baud, baud);
 	s->baud = baud;
 	s->baud = baud;
 	param_mask |= MAX3100_BAUD;
 	param_mask |= MAX3100_BAUD;
 
 

+ 344 - 0
drivers/serial/max3107-aava.c

@@ -0,0 +1,344 @@
+/*
+ *  max3107.c - spi uart protocol driver for Maxim 3107
+ *  Based on max3100.c
+ *	by Christian Pellegrin <chripell@evolware.org>
+ *  and	max3110.c
+ *	by Feng Tang <feng.tang@intel.com>
+ *
+ *  Copyright (C) Aavamobile 2009
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/serial_core.h>
+#include <linux/serial.h>
+#include <linux/spi/spi.h>
+#include <linux/freezer.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/sfi.h>
+#include <asm/mrst.h>
+#include "max3107.h"
+
+/* GPIO direction to input function */
+static int max3107_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
+{
+	struct max3107_port *s = container_of(chip, struct max3107_port, chip);
+	u16 buf[1];		/* Buffer for SPI transfer */
+
+	if (offset >= MAX3107_GPIO_COUNT) {
+		dev_err(&s->spi->dev, "Invalid GPIO\n");
+		return -EINVAL;
+	}
+
+	/* Read current GPIO configuration register */
+	buf[0] = MAX3107_GPIOCFG_REG;
+	/* Perform SPI transfer */
+	if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 2)) {
+		dev_err(&s->spi->dev, "SPI transfer GPIO read failed\n");
+		return -EIO;
+	}
+	buf[0] &= MAX3107_SPI_RX_DATA_MASK;
+
+	/* Set GPIO to input */
+	buf[0] &= ~(0x0001 << offset);
+
+	/* Write new GPIO configuration register value */
+	buf[0] |= (MAX3107_WRITE_BIT | MAX3107_GPIOCFG_REG);
+	/* Perform SPI transfer */
+	if (max3107_rw(s, (u8 *)buf, NULL, 2)) {
+		dev_err(&s->spi->dev, "SPI transfer GPIO write failed\n");
+		return -EIO;
+	}
+	return 0;
+}
+
+/* GPIO direction to output function */
+static int max3107_gpio_direction_out(struct gpio_chip *chip, unsigned offset,
+					int value)
+{
+	struct max3107_port *s = container_of(chip, struct max3107_port, chip);
+	u16 buf[2];	/* Buffer for SPI transfers */
+
+	if (offset >= MAX3107_GPIO_COUNT) {
+		dev_err(&s->spi->dev, "Invalid GPIO\n");
+		return -EINVAL;
+	}
+
+	/* Read current GPIO configuration and data registers */
+	buf[0] = MAX3107_GPIOCFG_REG;
+	buf[1] = MAX3107_GPIODATA_REG;
+	/* Perform SPI transfer */
+	if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 4)) {
+		dev_err(&s->spi->dev, "SPI transfer gpio failed\n");
+		return -EIO;
+	}
+	buf[0] &= MAX3107_SPI_RX_DATA_MASK;
+	buf[1] &= MAX3107_SPI_RX_DATA_MASK;
+
+	/* Set GPIO to output */
+	buf[0] |= (0x0001 << offset);
+	/* Set value */
+	if (value)
+		buf[1] |= (0x0001 << offset);
+	else
+		buf[1] &= ~(0x0001 << offset);
+
+	/* Write new GPIO configuration and data register values */
+	buf[0] |= (MAX3107_WRITE_BIT | MAX3107_GPIOCFG_REG);
+	buf[1] |= (MAX3107_WRITE_BIT | MAX3107_GPIODATA_REG);
+	/* Perform SPI transfer */
+	if (max3107_rw(s, (u8 *)buf, NULL, 4)) {
+		dev_err(&s->spi->dev,
+			"SPI transfer for GPIO conf data w failed\n");
+		return -EIO;
+	}
+	return 0;
+}
+
+/* GPIO value query function */
+static int max3107_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+	struct max3107_port *s = container_of(chip, struct max3107_port, chip);
+	u16 buf[1];	/* Buffer for SPI transfer */
+
+	if (offset >= MAX3107_GPIO_COUNT) {
+		dev_err(&s->spi->dev, "Invalid GPIO\n");
+		return -EINVAL;
+	}
+
+	/* Read current GPIO data register */
+	buf[0] = MAX3107_GPIODATA_REG;
+	/* Perform SPI transfer */
+	if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 2)) {
+		dev_err(&s->spi->dev, "SPI transfer GPIO data r failed\n");
+		return -EIO;
+	}
+	buf[0] &= MAX3107_SPI_RX_DATA_MASK;
+
+	/* Return value */
+	return buf[0] & (0x0001 << offset);
+}
+
+/* GPIO value set function */
+static void max3107_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+	struct max3107_port *s = container_of(chip, struct max3107_port, chip);
+	u16 buf[2];	/* Buffer for SPI transfers */
+
+	if (offset >= MAX3107_GPIO_COUNT) {
+		dev_err(&s->spi->dev, "Invalid GPIO\n");
+		return;
+	}
+
+	/* Read current GPIO configuration registers*/
+	buf[0] = MAX3107_GPIODATA_REG;
+	buf[1] = MAX3107_GPIOCFG_REG;
+	/* Perform SPI transfer */
+	if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 4)) {
+		dev_err(&s->spi->dev,
+			"SPI transfer for GPIO data and config read failed\n");
+		return;
+	}
+	buf[0] &= MAX3107_SPI_RX_DATA_MASK;
+	buf[1] &= MAX3107_SPI_RX_DATA_MASK;
+
+	if (!(buf[1] & (0x0001 << offset))) {
+		/* Configured as input, can't set value */
+		dev_warn(&s->spi->dev,
+				"Trying to set value for input GPIO\n");
+		return;
+	}
+
+	/* Set value */
+	if (value)
+		buf[0] |= (0x0001 << offset);
+	else
+		buf[0] &= ~(0x0001 << offset);
+
+	/* Write new GPIO data register value */
+	buf[0] |= (MAX3107_WRITE_BIT | MAX3107_GPIODATA_REG);
+	/* Perform SPI transfer */
+	if (max3107_rw(s, (u8 *)buf, NULL, 2))
+		dev_err(&s->spi->dev, "SPI transfer GPIO data w failed\n");
+}
+
+/* GPIO chip data */
+static struct gpio_chip max3107_gpio_chip = {
+	.owner			= THIS_MODULE,
+	.direction_input	= max3107_gpio_direction_in,
+	.direction_output	= max3107_gpio_direction_out,
+	.get			= max3107_gpio_get,
+	.set			= max3107_gpio_set,
+	.can_sleep		= 1,
+	.base			= MAX3107_GPIO_BASE,
+	.ngpio			= MAX3107_GPIO_COUNT,
+};
+
+/**
+ *	max3107_aava_reset	-	reset on AAVA systems
+ *	@spi: The SPI device we are probing
+ *
+ *	Reset the device ready for probing.
+ */
+
+static int max3107_aava_reset(struct spi_device *spi)
+{
+	/* Reset the chip */
+	if (gpio_request(MAX3107_RESET_GPIO, "max3107")) {
+		pr_err("Requesting RESET GPIO failed\n");
+		return -EIO;
+	}
+	if (gpio_direction_output(MAX3107_RESET_GPIO, 0)) {
+		pr_err("Setting RESET GPIO to 0 failed\n");
+		gpio_free(MAX3107_RESET_GPIO);
+		return -EIO;
+	}
+	msleep(MAX3107_RESET_DELAY);
+	if (gpio_direction_output(MAX3107_RESET_GPIO, 1)) {
+		pr_err("Setting RESET GPIO to 1 failed\n");
+		gpio_free(MAX3107_RESET_GPIO);
+		return -EIO;
+	}
+	gpio_free(MAX3107_RESET_GPIO);
+	msleep(MAX3107_WAKEUP_DELAY);
+	return 0;
+}
+
+static int max3107_aava_configure(struct max3107_port *s)
+{
+	int retval;
+
+	/* Initialize GPIO chip data */
+	s->chip = max3107_gpio_chip;
+	s->chip.label = s->spi->modalias;
+	s->chip.dev = &s->spi->dev;
+
+	/* Add GPIO chip */
+	retval = gpiochip_add(&s->chip);
+	if (retval) {
+		dev_err(&s->spi->dev, "Adding GPIO chip failed\n");
+		return retval;
+	}
+
+	/* Temporary fix for EV2 boot problems, set modem reset to 0 */
+	max3107_gpio_direction_out(&s->chip, 3, 0);
+	return 0;
+}
+
+#if 0
+/* This will get enabled once we have the board stuff merged for this
+   specific case */
+
+static const struct baud_table brg13_ext[] = {
+	{ 300,    MAX3107_BRG13_B300 },
+	{ 600,    MAX3107_BRG13_B600 },
+	{ 1200,   MAX3107_BRG13_B1200 },
+	{ 2400,   MAX3107_BRG13_B2400 },
+	{ 4800,   MAX3107_BRG13_B4800 },
+	{ 9600,   MAX3107_BRG13_B9600 },
+	{ 19200,  MAX3107_BRG13_B19200 },
+	{ 57600,  MAX3107_BRG13_B57600 },
+	{ 115200, MAX3107_BRG13_B115200 },
+	{ 230400, MAX3107_BRG13_B230400 },
+	{ 460800, MAX3107_BRG13_B460800 },
+	{ 921600, MAX3107_BRG13_B921600 },
+	{ 0, 0 }
+};
+
+static void max3107_aava_init(struct max3107_port *s)
+{
+	/*override for AAVA SC specific*/
+	if (mrst_platform_id() == MRST_PLATFORM_AAVA_SC) {
+		if (get_koski_build_id() <= KOSKI_EV2)
+			if (s->ext_clk) {
+				s->brg_cfg = MAX3107_BRG13_B9600;
+				s->baud_tbl = (struct baud_table *)brg13_ext;
+			}
+	}
+}
+#endif
+
+static int __devexit max3107_aava_remove(struct spi_device *spi)
+{
+	struct max3107_port *s = dev_get_drvdata(&spi->dev);
+
+	/* Remove GPIO chip */
+	if (gpiochip_remove(&s->chip))
+		dev_warn(&spi->dev, "Removing GPIO chip failed\n");
+
+	/* Then do the default remove */
+	return max3107_remove(spi);
+}
+
+/* Platform data */
+static struct max3107_plat aava_plat_data = {
+	.loopback               = 0,
+	.ext_clk                = 1,
+/*	.init			= max3107_aava_init, */
+	.configure		= max3107_aava_configure,
+	.hw_suspend		= max3107_hw_susp,
+	.polled_mode            = 0,
+	.poll_time              = 0,
+};
+
+
+static int __devinit max3107_probe_aava(struct spi_device *spi)
+{
+	int err = max3107_aava_reset(spi);
+	if (err < 0)
+		return err;
+	return max3107_probe(spi, &aava_plat_data);
+}
+
+/* Spi driver data */
+static struct spi_driver max3107_driver = {
+	.driver = {
+		.name		= "aava-max3107",
+		.bus		= &spi_bus_type,
+		.owner		= THIS_MODULE,
+	},
+	.probe		= max3107_probe_aava,
+	.remove		= __devexit_p(max3107_aava_remove),
+	.suspend	= max3107_suspend,
+	.resume		= max3107_resume,
+};
+
+/* Driver init function */
+static int __init max3107_init(void)
+{
+	return spi_register_driver(&max3107_driver);
+}
+
+/* Driver exit function */
+static void __exit max3107_exit(void)
+{
+	spi_unregister_driver(&max3107_driver);
+}
+
+module_init(max3107_init);
+module_exit(max3107_exit);
+
+MODULE_DESCRIPTION("MAX3107 driver");
+MODULE_AUTHOR("Aavamobile");
+MODULE_ALIAS("aava-max3107-spi");
+MODULE_LICENSE("GPL v2");

+ 1197 - 0
drivers/serial/max3107.c

@@ -0,0 +1,1197 @@
+/*
+ *  max3107.c - spi uart protocol driver for Maxim 3107
+ *  Based on max3100.c
+ *	by Christian Pellegrin <chripell@evolware.org>
+ *  and	max3110.c
+ *	by Feng Tang <feng.tang@intel.com>
+ *
+ *  Copyright (C) Aavamobile 2009
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/serial_core.h>
+#include <linux/serial.h>
+#include <linux/gpio.h>
+#include <linux/spi/spi.h>
+#include <linux/freezer.h>
+#include "max3107.h"
+
+static const struct baud_table brg26_ext[] = {
+	{ 300,    MAX3107_BRG26_B300 },
+	{ 600,    MAX3107_BRG26_B600 },
+	{ 1200,   MAX3107_BRG26_B1200 },
+	{ 2400,   MAX3107_BRG26_B2400 },
+	{ 4800,   MAX3107_BRG26_B4800 },
+	{ 9600,   MAX3107_BRG26_B9600 },
+	{ 19200,  MAX3107_BRG26_B19200 },
+	{ 57600,  MAX3107_BRG26_B57600 },
+	{ 115200, MAX3107_BRG26_B115200 },
+	{ 230400, MAX3107_BRG26_B230400 },
+	{ 460800, MAX3107_BRG26_B460800 },
+	{ 921600, MAX3107_BRG26_B921600 },
+	{ 0, 0 }
+};
+
+static const struct baud_table brg13_int[] = {
+	{ 300,    MAX3107_BRG13_IB300 },
+	{ 600,    MAX3107_BRG13_IB600 },
+	{ 1200,   MAX3107_BRG13_IB1200 },
+	{ 2400,   MAX3107_BRG13_IB2400 },
+	{ 4800,   MAX3107_BRG13_IB4800 },
+	{ 9600,   MAX3107_BRG13_IB9600 },
+	{ 19200,  MAX3107_BRG13_IB19200 },
+	{ 57600,  MAX3107_BRG13_IB57600 },
+	{ 115200, MAX3107_BRG13_IB115200 },
+	{ 230400, MAX3107_BRG13_IB230400 },
+	{ 460800, MAX3107_BRG13_IB460800 },
+	{ 921600, MAX3107_BRG13_IB921600 },
+	{ 0, 0 }
+};
+
+static u32 get_new_brg(int baud, struct max3107_port *s)
+{
+	int i;
+	const struct baud_table *baud_tbl = s->baud_tbl;
+
+	for (i = 0; i < 13; i++) {
+		if (baud == baud_tbl[i].baud)
+			return baud_tbl[i].new_brg;
+	}
+
+	return 0;
+}
+
+/* Perform SPI transfer for write/read of device register(s) */
+int max3107_rw(struct max3107_port *s, u8 *tx, u8 *rx, int len)
+{
+	struct spi_message spi_msg;
+	struct spi_transfer spi_xfer;
+
+	/* Initialize SPI ,message */
+	spi_message_init(&spi_msg);
+
+	/* Initialize SPI transfer */
+	memset(&spi_xfer, 0, sizeof spi_xfer);
+	spi_xfer.len = len;
+	spi_xfer.tx_buf = tx;
+	spi_xfer.rx_buf = rx;
+	spi_xfer.speed_hz = MAX3107_SPI_SPEED;
+
+	/* Add SPI transfer to SPI message */
+	spi_message_add_tail(&spi_xfer, &spi_msg);
+
+#ifdef DBG_TRACE_SPI_DATA
+	{
+		int i;
+		pr_info("tx len %d:\n", spi_xfer.len);
+		for (i = 0 ; i < spi_xfer.len && i < 32 ; i++)
+			pr_info(" %x", ((u8 *)spi_xfer.tx_buf)[i]);
+		pr_info("\n");
+	}
+#endif
+
+	/* Perform synchronous SPI transfer */
+	if (spi_sync(s->spi, &spi_msg)) {
+		dev_err(&s->spi->dev, "spi_sync failure\n");
+		return -EIO;
+	}
+
+#ifdef DBG_TRACE_SPI_DATA
+	if (spi_xfer.rx_buf) {
+		int i;
+		pr_info("rx len %d:\n", spi_xfer.len);
+		for (i = 0 ; i < spi_xfer.len && i < 32 ; i++)
+			pr_info(" %x", ((u8 *)spi_xfer.rx_buf)[i]);
+		pr_info("\n");
+	}
+#endif
+	return 0;
+}
+EXPORT_SYMBOL_GPL(max3107_rw);
+
+/* Puts received data to circular buffer */
+static void put_data_to_circ_buf(struct max3107_port *s, unsigned char *data,
+					int len)
+{
+	struct uart_port *port = &s->port;
+	struct tty_struct *tty;
+
+	if (!port->state)
+		return;
+
+	tty = port->state->port.tty;
+	if (!tty)
+		return;
+
+	/* Insert received data */
+	tty_insert_flip_string(tty, data, len);
+	/* Update RX counter */
+	port->icount.rx += len;
+}
+
+/* Handle data receiving */
+static void max3107_handlerx(struct max3107_port *s, u16 rxlvl)
+{
+	int i;
+	int j;
+	int len;				/* SPI transfer buffer length */
+	u16 *buf;
+	u8 *valid_str;
+
+	if (!s->rx_enabled)
+		/* RX is disabled */
+		return;
+
+	if (rxlvl == 0) {
+		/* RX fifo is empty */
+		return;
+	} else if (rxlvl >= MAX3107_RX_FIFO_SIZE) {
+		dev_warn(&s->spi->dev, "Possible RX FIFO overrun %d\n", rxlvl);
+		/* Ensure sanity of RX level */
+		rxlvl = MAX3107_RX_FIFO_SIZE;
+	}
+	if ((s->rxbuf == 0) || (s->rxstr == 0)) {
+		dev_warn(&s->spi->dev, "Rx buffer/str isn't ready\n");
+		return;
+	}
+	buf = s->rxbuf;
+	valid_str = s->rxstr;
+	while (rxlvl) {
+		pr_debug("rxlvl %d\n", rxlvl);
+		/* Clear buffer */
+		memset(buf, 0, sizeof(u16) * (MAX3107_RX_FIFO_SIZE + 2));
+		len = 0;
+		if (s->irqen_reg & MAX3107_IRQ_RXFIFO_BIT) {
+			/* First disable RX FIFO interrupt */
+			pr_debug("Disabling RX INT\n");
+			buf[0] = (MAX3107_WRITE_BIT | MAX3107_IRQEN_REG);
+			s->irqen_reg &= ~MAX3107_IRQ_RXFIFO_BIT;
+			buf[0] |= s->irqen_reg;
+			len++;
+		}
+		/* Just increase the length by amount of words in FIFO since
+		 * buffer was zeroed and SPI transfer of 0x0000 means reading
+		 * from RX FIFO
+		 */
+		len += rxlvl;
+		/* Append RX level query */
+		buf[len] = MAX3107_RXFIFOLVL_REG;
+		len++;
+
+		/* Perform the SPI transfer */
+		if (max3107_rw(s, (u8 *)buf, (u8 *)buf, len * 2)) {
+			dev_err(&s->spi->dev, "SPI transfer for RX h failed\n");
+			return;
+		}
+
+		/* Skip RX FIFO interrupt disabling word if it was added */
+		j = ((len - 1) - rxlvl);
+		/* Read received words */
+		for (i = 0; i < rxlvl; i++, j++)
+			valid_str[i] = (u8)buf[j];
+		put_data_to_circ_buf(s, valid_str, rxlvl);
+		/* Get new RX level */
+		rxlvl = (buf[len - 1] & MAX3107_SPI_RX_DATA_MASK);
+	}
+
+	if (s->rx_enabled) {
+		/* RX still enabled, re-enable RX FIFO interrupt */
+		pr_debug("Enabling RX INT\n");
+		buf[0] = (MAX3107_WRITE_BIT | MAX3107_IRQEN_REG);
+		s->irqen_reg |= MAX3107_IRQ_RXFIFO_BIT;
+		buf[0] |= s->irqen_reg;
+		if (max3107_rw(s, (u8 *)buf, NULL, 2))
+			dev_err(&s->spi->dev, "RX FIFO INT enabling failed\n");
+	}
+
+	/* Push the received data to receivers */
+	if (s->port.state->port.tty)
+		tty_flip_buffer_push(s->port.state->port.tty);
+}
+
+
+/* Handle data sending */
+static void max3107_handletx(struct max3107_port *s)
+{
+	struct circ_buf *xmit = &s->port.state->xmit;
+	int i;
+	unsigned long flags;
+	int len;				/* SPI transfer buffer length */
+	u16 *buf;
+
+	if (!s->tx_fifo_empty)
+		/* Don't send more data before previous data is sent */
+		return;
+
+	if (uart_circ_empty(xmit) || uart_tx_stopped(&s->port))
+		/* No data to send or TX is stopped */
+		return;
+
+	if (!s->txbuf) {
+		dev_warn(&s->spi->dev, "Txbuf isn't ready\n");
+		return;
+	}
+	buf = s->txbuf;
+	/* Get length of data pending in circular buffer */
+	len = uart_circ_chars_pending(xmit);
+	if (len) {
+		/* Limit to size of TX FIFO */
+		if (len > MAX3107_TX_FIFO_SIZE)
+			len = MAX3107_TX_FIFO_SIZE;
+
+		pr_debug("txlen %d\n", len);
+
+		/* Update TX counter */
+		s->port.icount.tx += len;
+
+		/* TX FIFO will no longer be empty */
+		s->tx_fifo_empty = 0;
+
+		i = 0;
+		if (s->irqen_reg & MAX3107_IRQ_TXEMPTY_BIT) {
+			/* First disable TX empty interrupt */
+			pr_debug("Disabling TE INT\n");
+			buf[i] = (MAX3107_WRITE_BIT | MAX3107_IRQEN_REG);
+			s->irqen_reg &= ~MAX3107_IRQ_TXEMPTY_BIT;
+			buf[i] |= s->irqen_reg;
+			i++;
+			len++;
+		}
+		/* Add data to send */
+		spin_lock_irqsave(&s->port.lock, flags);
+		for ( ; i < len ; i++) {
+			buf[i] = (MAX3107_WRITE_BIT | MAX3107_THR_REG);
+			buf[i] |= ((u16)xmit->buf[xmit->tail] &
+						MAX3107_SPI_TX_DATA_MASK);
+			xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+		}
+		spin_unlock_irqrestore(&s->port.lock, flags);
+		if (!(s->irqen_reg & MAX3107_IRQ_TXEMPTY_BIT)) {
+			/* Enable TX empty interrupt */
+			pr_debug("Enabling TE INT\n");
+			buf[i] = (MAX3107_WRITE_BIT | MAX3107_IRQEN_REG);
+			s->irqen_reg |= MAX3107_IRQ_TXEMPTY_BIT;
+			buf[i] |= s->irqen_reg;
+			i++;
+			len++;
+		}
+		if (!s->tx_enabled) {
+			/* Enable TX */
+			pr_debug("Enable TX\n");
+			buf[i] = (MAX3107_WRITE_BIT | MAX3107_MODE1_REG);
+			spin_lock_irqsave(&s->data_lock, flags);
+			s->mode1_reg &= ~MAX3107_MODE1_TXDIS_BIT;
+			buf[i] |= s->mode1_reg;
+			spin_unlock_irqrestore(&s->data_lock, flags);
+			s->tx_enabled = 1;
+			i++;
+			len++;
+		}
+
+		/* Perform the SPI transfer */
+		if (max3107_rw(s, (u8 *)buf, NULL, len*2)) {
+			dev_err(&s->spi->dev,
+				"SPI transfer TX handling failed\n");
+			return;
+		}
+	}
+
+	/* Indicate wake up if circular buffer is getting low on data */
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(&s->port);
+
+}
+
+/* Handle interrupts
+ * Also reads and returns current RX FIFO level
+ */
+static u16 handle_interrupt(struct max3107_port *s)
+{
+	u16 buf[4];	/* Buffer for SPI transfers */
+	u8 irq_status;
+	u16 rx_level;
+	unsigned long flags;
+
+	/* Read IRQ status register */
+	buf[0] = MAX3107_IRQSTS_REG;
+	/* Read status IRQ status register */
+	buf[1] = MAX3107_STS_IRQSTS_REG;
+	/* Read LSR IRQ status register */
+	buf[2] = MAX3107_LSR_IRQSTS_REG;
+	/* Query RX level */
+	buf[3] = MAX3107_RXFIFOLVL_REG;
+
+	if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 8)) {
+		dev_err(&s->spi->dev,
+			"SPI transfer for INTR handling failed\n");
+		return 0;
+	}
+
+	irq_status = (u8)buf[0];
+	pr_debug("IRQSTS %x\n", irq_status);
+	rx_level = (buf[3] & MAX3107_SPI_RX_DATA_MASK);
+
+	if (irq_status & MAX3107_IRQ_LSR_BIT) {
+		/* LSR interrupt */
+		if (buf[2] & MAX3107_LSR_RXTO_BIT)
+			/* RX timeout interrupt,
+			 * handled by normal RX handling
+			 */
+			pr_debug("RX TO INT\n");
+	}
+
+	if (irq_status & MAX3107_IRQ_TXEMPTY_BIT) {
+		/* Tx empty interrupt,
+		 * disable TX and set tx_fifo_empty flag
+		 */
+		pr_debug("TE INT, disabling TX\n");
+		buf[0] = (MAX3107_WRITE_BIT | MAX3107_MODE1_REG);
+		spin_lock_irqsave(&s->data_lock, flags);
+		s->mode1_reg |= MAX3107_MODE1_TXDIS_BIT;
+		buf[0] |= s->mode1_reg;
+		spin_unlock_irqrestore(&s->data_lock, flags);
+		if (max3107_rw(s, (u8 *)buf, NULL, 2))
+			dev_err(&s->spi->dev, "SPI transfer TX dis failed\n");
+		s->tx_enabled = 0;
+		s->tx_fifo_empty = 1;
+	}
+
+	if (irq_status & MAX3107_IRQ_RXFIFO_BIT)
+		/* RX FIFO interrupt,
+		 * handled by normal RX handling
+		 */
+		pr_debug("RFIFO INT\n");
+
+	/* Return RX level */
+	return rx_level;
+}
+
+/* Trigger work thread*/
+static void max3107_dowork(struct max3107_port *s)
+{
+	if (!work_pending(&s->work) && !freezing(current) && !s->suspended)
+		queue_work(s->workqueue, &s->work);
+	else
+		dev_warn(&s->spi->dev, "interrup isn't serviced normally!\n");
+}
+
+/* Work thread */
+static void max3107_work(struct work_struct *w)
+{
+	struct max3107_port *s = container_of(w, struct max3107_port, work);
+	u16 rxlvl = 0;
+	int len;	/* SPI transfer buffer length */
+	u16 buf[5];	/* Buffer for SPI transfers */
+	unsigned long flags;
+
+	/* Start by reading current RX FIFO level */
+	buf[0] = MAX3107_RXFIFOLVL_REG;
+	if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 2)) {
+		dev_err(&s->spi->dev, "SPI transfer RX lev failed\n");
+		rxlvl = 0;
+	} else {
+		rxlvl = (buf[0] & MAX3107_SPI_RX_DATA_MASK);
+	}
+
+	do {
+		pr_debug("rxlvl %d\n", rxlvl);
+
+		/* Handle RX */
+		max3107_handlerx(s, rxlvl);
+		rxlvl = 0;
+
+		if (s->handle_irq) {
+			/* Handle pending interrupts
+			 * We also get new RX FIFO level since new data may
+			 * have been received while pushing received data to
+			 * receivers
+			 */
+			s->handle_irq = 0;
+			rxlvl = handle_interrupt(s);
+		}
+
+		/* Handle TX */
+		max3107_handletx(s);
+
+		/* Handle configuration changes */
+		len = 0;
+		spin_lock_irqsave(&s->data_lock, flags);
+		if (s->mode1_commit) {
+			pr_debug("mode1_commit\n");
+			buf[len] = (MAX3107_WRITE_BIT | MAX3107_MODE1_REG);
+			buf[len++] |= s->mode1_reg;
+			s->mode1_commit = 0;
+		}
+		if (s->lcr_commit) {
+			pr_debug("lcr_commit\n");
+			buf[len] = (MAX3107_WRITE_BIT | MAX3107_LCR_REG);
+			buf[len++] |= s->lcr_reg;
+			s->lcr_commit = 0;
+		}
+		if (s->brg_commit) {
+			pr_debug("brg_commit\n");
+			buf[len] = (MAX3107_WRITE_BIT | MAX3107_BRGDIVMSB_REG);
+			buf[len++] |= ((s->brg_cfg >> 16) &
+						MAX3107_SPI_TX_DATA_MASK);
+			buf[len] = (MAX3107_WRITE_BIT | MAX3107_BRGDIVLSB_REG);
+			buf[len++] |= ((s->brg_cfg >> 8) &
+						MAX3107_SPI_TX_DATA_MASK);
+			buf[len] = (MAX3107_WRITE_BIT | MAX3107_BRGCFG_REG);
+			buf[len++] |= ((s->brg_cfg) & 0xff);
+			s->brg_commit = 0;
+		}
+		spin_unlock_irqrestore(&s->data_lock, flags);
+
+		if (len > 0) {
+			if (max3107_rw(s, (u8 *)buf, NULL, len * 2))
+				dev_err(&s->spi->dev,
+					"SPI transfer config failed\n");
+		}
+
+		/* Reloop if interrupt handling indicated data in RX FIFO */
+	} while (rxlvl);
+
+}
+
+/* Set sleep mode */
+static void max3107_set_sleep(struct max3107_port *s, int mode)
+{
+	u16 buf[1];	/* Buffer for SPI transfer */
+	unsigned long flags;
+	pr_debug("enter, mode %d\n", mode);
+
+	buf[0] = (MAX3107_WRITE_BIT | MAX3107_MODE1_REG);
+	spin_lock_irqsave(&s->data_lock, flags);
+	switch (mode) {
+	case MAX3107_DISABLE_FORCED_SLEEP:
+			s->mode1_reg &= ~MAX3107_MODE1_FORCESLEEP_BIT;
+			break;
+	case MAX3107_ENABLE_FORCED_SLEEP:
+			s->mode1_reg |= MAX3107_MODE1_FORCESLEEP_BIT;
+			break;
+	case MAX3107_DISABLE_AUTOSLEEP:
+			s->mode1_reg &= ~MAX3107_MODE1_AUTOSLEEP_BIT;
+			break;
+	case MAX3107_ENABLE_AUTOSLEEP:
+			s->mode1_reg |= MAX3107_MODE1_AUTOSLEEP_BIT;
+			break;
+	default:
+		spin_unlock_irqrestore(&s->data_lock, flags);
+		dev_warn(&s->spi->dev, "invalid sleep mode\n");
+		return;
+	}
+	buf[0] |= s->mode1_reg;
+	spin_unlock_irqrestore(&s->data_lock, flags);
+
+	if (max3107_rw(s, (u8 *)buf, NULL, 2))
+		dev_err(&s->spi->dev, "SPI transfer sleep mode failed\n");
+
+	if (mode == MAX3107_DISABLE_AUTOSLEEP ||
+			mode == MAX3107_DISABLE_FORCED_SLEEP)
+		msleep(MAX3107_WAKEUP_DELAY);
+}
+
+/* Perform full register initialization */
+static void max3107_register_init(struct max3107_port *s)
+{
+	u16 buf[11];	/* Buffer for SPI transfers */
+
+	/* 1. Configure baud rate, 9600 as default */
+	s->baud = 9600;
+	/* the below is default*/
+	if (s->ext_clk) {
+		s->brg_cfg = MAX3107_BRG26_B9600;
+		s->baud_tbl = (struct baud_table *)brg26_ext;
+	} else {
+		s->brg_cfg = MAX3107_BRG13_IB9600;
+		s->baud_tbl = (struct baud_table *)brg13_int;
+	}
+
+	if (s->pdata->init)
+		s->pdata->init(s);
+
+	buf[0] = (MAX3107_WRITE_BIT | MAX3107_BRGDIVMSB_REG)
+		| ((s->brg_cfg >> 16) & MAX3107_SPI_TX_DATA_MASK);
+	buf[1] = (MAX3107_WRITE_BIT | MAX3107_BRGDIVLSB_REG)
+		| ((s->brg_cfg >> 8) & MAX3107_SPI_TX_DATA_MASK);
+	buf[2] = (MAX3107_WRITE_BIT | MAX3107_BRGCFG_REG)
+		| ((s->brg_cfg) & 0xff);
+
+	/* 2. Configure LCR register, 8N1 mode by default */
+	s->lcr_reg = MAX3107_LCR_WORD_LEN_8;
+	buf[3] = (MAX3107_WRITE_BIT | MAX3107_LCR_REG)
+		| s->lcr_reg;
+
+	/* 3. Configure MODE 1 register */
+	s->mode1_reg = 0;
+	/* Enable IRQ pin */
+	s->mode1_reg |= MAX3107_MODE1_IRQSEL_BIT;
+	/* Disable TX */
+	s->mode1_reg |= MAX3107_MODE1_TXDIS_BIT;
+	s->tx_enabled = 0;
+	/* RX is enabled */
+	s->rx_enabled = 1;
+	buf[4] = (MAX3107_WRITE_BIT | MAX3107_MODE1_REG)
+		| s->mode1_reg;
+
+	/* 4. Configure MODE 2 register */
+	buf[5] = (MAX3107_WRITE_BIT | MAX3107_MODE2_REG);
+	if (s->loopback) {
+		/* Enable loopback */
+		buf[5] |= MAX3107_MODE2_LOOPBACK_BIT;
+	}
+	/* Reset FIFOs */
+	buf[5] |= MAX3107_MODE2_FIFORST_BIT;
+	s->tx_fifo_empty = 1;
+
+	/* 5. Configure FIFO trigger level register */
+	buf[6] = (MAX3107_WRITE_BIT | MAX3107_FIFOTRIGLVL_REG);
+	/* RX FIFO trigger for 16 words, TX FIFO trigger not used */
+	buf[6] |= (MAX3107_FIFOTRIGLVL_RX(16) | MAX3107_FIFOTRIGLVL_TX(0));
+
+	/* 6. Configure flow control levels */
+	buf[7] = (MAX3107_WRITE_BIT | MAX3107_FLOWLVL_REG);
+	/* Flow control halt level 96, resume level 48 */
+	buf[7] |= (MAX3107_FLOWLVL_RES(48) | MAX3107_FLOWLVL_HALT(96));
+
+	/* 7. Configure flow control */
+	buf[8] = (MAX3107_WRITE_BIT | MAX3107_FLOWCTRL_REG);
+	/* Enable auto CTS and auto RTS flow control */
+	buf[8] |= (MAX3107_FLOWCTRL_AUTOCTS_BIT | MAX3107_FLOWCTRL_AUTORTS_BIT);
+
+	/* 8. Configure RX timeout register */
+	buf[9] = (MAX3107_WRITE_BIT | MAX3107_RXTO_REG);
+	/* Timeout after 48 character intervals */
+	buf[9] |= 0x0030;
+
+	/* 9. Configure LSR interrupt enable register */
+	buf[10] = (MAX3107_WRITE_BIT | MAX3107_LSR_IRQEN_REG);
+	/* Enable RX timeout interrupt */
+	buf[10] |= MAX3107_LSR_RXTO_BIT;
+
+	/* Perform SPI transfer */
+	if (max3107_rw(s, (u8 *)buf, NULL, 22))
+		dev_err(&s->spi->dev, "SPI transfer for init failed\n");
+
+	/* 10. Clear IRQ status register by reading it */
+	buf[0] = MAX3107_IRQSTS_REG;
+
+	/* 11. Configure interrupt enable register */
+	/* Enable LSR interrupt */
+	s->irqen_reg = MAX3107_IRQ_LSR_BIT;
+	/* Enable RX FIFO interrupt */
+	s->irqen_reg |= MAX3107_IRQ_RXFIFO_BIT;
+	buf[1] = (MAX3107_WRITE_BIT | MAX3107_IRQEN_REG)
+		| s->irqen_reg;
+
+	/* 12. Clear FIFO reset that was set in step 6 */
+	buf[2] = (MAX3107_WRITE_BIT | MAX3107_MODE2_REG);
+	if (s->loopback) {
+		/* Keep loopback enabled */
+		buf[2] |= MAX3107_MODE2_LOOPBACK_BIT;
+	}
+
+	/* Perform SPI transfer */
+	if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 6))
+		dev_err(&s->spi->dev, "SPI transfer for init failed\n");
+
+}
+
+/* IRQ handler */
+static irqreturn_t max3107_irq(int irqno, void *dev_id)
+{
+	struct max3107_port *s = dev_id;
+
+	if (irqno != s->spi->irq) {
+		/* Unexpected IRQ */
+		return IRQ_NONE;
+	}
+
+	/* Indicate irq */
+	s->handle_irq = 1;
+
+	/* Trigger work thread */
+	max3107_dowork(s);
+
+	return IRQ_HANDLED;
+}
+
+/* HW suspension function
+ *
+ * Currently autosleep is used to decrease current consumption, alternative
+ * approach would be to set the chip to reset mode if UART is not being
+ * used but that would mess the GPIOs
+ *
+ */
+void max3107_hw_susp(struct max3107_port *s, int suspend)
+{
+	pr_debug("enter, suspend %d\n", suspend);
+
+	if (suspend) {
+		/* Suspend requested,
+		 * enable autosleep to decrease current consumption
+		 */
+		s->suspended = 1;
+		max3107_set_sleep(s, MAX3107_ENABLE_AUTOSLEEP);
+	} else {
+		/* Resume requested,
+		 * disable autosleep
+		 */
+		s->suspended = 0;
+		max3107_set_sleep(s, MAX3107_DISABLE_AUTOSLEEP);
+	}
+}
+EXPORT_SYMBOL_GPL(max3107_hw_susp);
+
+/* Modem status IRQ enabling */
+static void max3107_enable_ms(struct uart_port *port)
+{
+	/* Modem status not supported */
+}
+
+/* Data send function */
+static void max3107_start_tx(struct uart_port *port)
+{
+	struct max3107_port *s = container_of(port, struct max3107_port, port);
+
+	/* Trigger work thread for sending data */
+	max3107_dowork(s);
+}
+
+/* Function for checking that there is no pending transfers */
+static unsigned int max3107_tx_empty(struct uart_port *port)
+{
+	struct max3107_port *s = container_of(port, struct max3107_port, port);
+
+	pr_debug("returning %d\n",
+		  (s->tx_fifo_empty && uart_circ_empty(&s->port.state->xmit)));
+	return s->tx_fifo_empty && uart_circ_empty(&s->port.state->xmit);
+}
+
+/* Function for stopping RX */
+static void max3107_stop_rx(struct uart_port *port)
+{
+	struct max3107_port *s = container_of(port, struct max3107_port, port);
+	unsigned long flags;
+
+	/* Set RX disabled in MODE 1 register */
+	spin_lock_irqsave(&s->data_lock, flags);
+	s->mode1_reg |= MAX3107_MODE1_RXDIS_BIT;
+	s->mode1_commit = 1;
+	spin_unlock_irqrestore(&s->data_lock, flags);
+	/* Set RX disabled */
+	s->rx_enabled = 0;
+	/* Trigger work thread for doing the actual configuration change */
+	max3107_dowork(s);
+}
+
+/* Function for returning control pin states */
+static unsigned int max3107_get_mctrl(struct uart_port *port)
+{
+	/* DCD and DSR are not wired and CTS/RTS is handled automatically
+	 * so just indicate DSR and CAR asserted
+	 */
+	return TIOCM_DSR | TIOCM_CAR;
+}
+
+/* Function for setting control pin states */
+static void max3107_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+	/* DCD and DSR are not wired and CTS/RTS is hadnled automatically
+	 * so do nothing
+	 */
+}
+
+/* Function for configuring UART parameters */
+static void max3107_set_termios(struct uart_port *port,
+				struct ktermios *termios,
+				struct ktermios *old)
+{
+	struct max3107_port *s = container_of(port, struct max3107_port, port);
+	struct tty_struct *tty;
+	int baud;
+	u16 new_lcr = 0;
+	u32 new_brg = 0;
+	unsigned long flags;
+
+	if (!port->state)
+		return;
+
+	tty = port->state->port.tty;
+	if (!tty)
+		return;
+
+	/* Get new LCR register values */
+	/* Word size */
+	if ((termios->c_cflag & CSIZE) == CS7)
+		new_lcr |= MAX3107_LCR_WORD_LEN_7;
+	else
+		new_lcr |= MAX3107_LCR_WORD_LEN_8;
+
+	/* Parity */
+	if (termios->c_cflag & PARENB) {
+		new_lcr |= MAX3107_LCR_PARITY_BIT;
+		if (!(termios->c_cflag & PARODD))
+			new_lcr |= MAX3107_LCR_EVENPARITY_BIT;
+	}
+
+	/* Stop bits */
+	if (termios->c_cflag & CSTOPB) {
+		/* 2 stop bits */
+		new_lcr |= MAX3107_LCR_STOPLEN_BIT;
+	}
+
+	/* Mask termios capabilities we don't support */
+	termios->c_cflag &= ~CMSPAR;
+
+	/* Set status ignore mask */
+	s->port.ignore_status_mask = 0;
+	if (termios->c_iflag & IGNPAR)
+		s->port.ignore_status_mask |= MAX3107_ALL_ERRORS;
+
+	/* Set low latency to immediately handle pushed data */
+	s->port.state->port.tty->low_latency = 1;
+
+	/* Get new baud rate generator configuration */
+	baud = tty_get_baud_rate(tty);
+
+	spin_lock_irqsave(&s->data_lock, flags);
+	new_brg = get_new_brg(baud, s);
+	/* if can't find the corrent config, use previous */
+	if (!new_brg) {
+		baud = s->baud;
+		new_brg = s->brg_cfg;
+	}
+	spin_unlock_irqrestore(&s->data_lock, flags);
+	tty_termios_encode_baud_rate(termios, baud, baud);
+	s->baud = baud;
+
+	/* Update timeout according to new baud rate */
+	uart_update_timeout(port, termios->c_cflag, baud);
+
+	spin_lock_irqsave(&s->data_lock, flags);
+	if (s->lcr_reg != new_lcr) {
+		s->lcr_reg = new_lcr;
+		s->lcr_commit = 1;
+	}
+	if (s->brg_cfg != new_brg) {
+		s->brg_cfg = new_brg;
+		s->brg_commit = 1;
+	}
+	spin_unlock_irqrestore(&s->data_lock, flags);
+
+	/* Trigger work thread for doing the actual configuration change */
+	max3107_dowork(s);
+}
+
+/* Port shutdown function */
+static void max3107_shutdown(struct uart_port *port)
+{
+	struct max3107_port *s = container_of(port, struct max3107_port, port);
+
+	if (s->suspended && s->pdata->hw_suspend)
+		s->pdata->hw_suspend(s, 0);
+
+	/* Free the interrupt */
+	free_irq(s->spi->irq, s);
+
+	if (s->workqueue) {
+		/* Flush and destroy work queue */
+		flush_workqueue(s->workqueue);
+		destroy_workqueue(s->workqueue);
+		s->workqueue = NULL;
+	}
+
+	/* Suspend HW */
+	if (s->pdata->hw_suspend)
+		s->pdata->hw_suspend(s, 1);
+}
+
+/* Port startup function */
+static int max3107_startup(struct uart_port *port)
+{
+	struct max3107_port *s = container_of(port, struct max3107_port, port);
+
+	/* Initialize work queue */
+	s->workqueue = create_freezeable_workqueue("max3107");
+	if (!s->workqueue) {
+		dev_err(&s->spi->dev, "Workqueue creation failed\n");
+		return -EBUSY;
+	}
+	INIT_WORK(&s->work, max3107_work);
+
+	/* Setup IRQ */
+	if (request_irq(s->spi->irq, max3107_irq, IRQF_TRIGGER_FALLING,
+			"max3107", s)) {
+		dev_err(&s->spi->dev, "IRQ reguest failed\n");
+		destroy_workqueue(s->workqueue);
+		s->workqueue = NULL;
+		return -EBUSY;
+	}
+
+	/* Resume HW */
+	if (s->pdata->hw_suspend)
+		s->pdata->hw_suspend(s, 0);
+
+	/* Init registers */
+	max3107_register_init(s);
+
+	return 0;
+}
+
+/* Port type function */
+static const char *max3107_type(struct uart_port *port)
+{
+	struct max3107_port *s = container_of(port, struct max3107_port, port);
+	return s->spi->modalias;
+}
+
+/* Port release function */
+static void max3107_release_port(struct uart_port *port)
+{
+	/* Do nothing */
+}
+
+/* Port request function */
+static int max3107_request_port(struct uart_port *port)
+{
+	/* Do nothing */
+	return 0;
+}
+
+/* Port config function */
+static void max3107_config_port(struct uart_port *port, int flags)
+{
+	struct max3107_port *s = container_of(port, struct max3107_port, port);
+	s->port.type = PORT_MAX3107;
+}
+
+/* Port verify function */
+static int max3107_verify_port(struct uart_port *port,
+				struct serial_struct *ser)
+{
+	if (ser->type == PORT_UNKNOWN || ser->type == PORT_MAX3107)
+		return 0;
+
+	return -EINVAL;
+}
+
+/* Port stop TX function */
+static void max3107_stop_tx(struct uart_port *port)
+{
+	/* Do nothing */
+}
+
+/* Port break control function */
+static void max3107_break_ctl(struct uart_port *port, int break_state)
+{
+	/* We don't support break control, do nothing */
+}
+
+
+/* Port functions */
+static struct uart_ops max3107_ops = {
+	.tx_empty       = max3107_tx_empty,
+	.set_mctrl      = max3107_set_mctrl,
+	.get_mctrl      = max3107_get_mctrl,
+	.stop_tx        = max3107_stop_tx,
+	.start_tx       = max3107_start_tx,
+	.stop_rx        = max3107_stop_rx,
+	.enable_ms      = max3107_enable_ms,
+	.break_ctl      = max3107_break_ctl,
+	.startup        = max3107_startup,
+	.shutdown       = max3107_shutdown,
+	.set_termios    = max3107_set_termios,
+	.type           = max3107_type,
+	.release_port   = max3107_release_port,
+	.request_port   = max3107_request_port,
+	.config_port    = max3107_config_port,
+	.verify_port    = max3107_verify_port,
+};
+
+/* UART driver data */
+static struct uart_driver max3107_uart_driver = {
+	.owner          = THIS_MODULE,
+	.driver_name    = "ttyMAX",
+	.dev_name       = "ttyMAX",
+	.nr             = 1,
+};
+
+static int driver_registered = 0;
+
+
+
+/* 'Generic' platform data */
+static struct max3107_plat generic_plat_data = {
+	.loopback               = 0,
+	.ext_clk                = 1,
+	.hw_suspend		= max3107_hw_susp,
+	.polled_mode            = 0,
+	.poll_time              = 0,
+};
+
+
+/*******************************************************************/
+
+/**
+ *	max3107_probe		-	SPI bus probe entry point
+ *	@spi: the spi device
+ *
+ *	SPI wants us to probe this device and if appropriate claim it.
+ *	Perform any platform specific requirements and then initialise
+ *	the device.
+ */
+
+int max3107_probe(struct spi_device *spi, struct max3107_plat *pdata)
+{
+	struct max3107_port *s;
+	u16 buf[2];	/* Buffer for SPI transfers */
+	int retval;
+
+	pr_info("enter max3107 probe\n");
+
+	/* Allocate port structure */
+	s = kzalloc(sizeof(*s), GFP_KERNEL);
+	if (!s) {
+		pr_err("Allocating port structure failed\n");
+		return -ENOMEM;
+	}
+
+	s->pdata = pdata;
+
+	/* SPI Rx buffer
+	 * +2 for RX FIFO interrupt
+	 * disabling and RX level query
+	 */
+	s->rxbuf = kzalloc(sizeof(u16) * (MAX3107_RX_FIFO_SIZE+2), GFP_KERNEL);
+	if (!s->rxbuf) {
+		pr_err("Allocating RX buffer failed\n");
+		return -ENOMEM;
+	}
+	s->rxstr = kzalloc(sizeof(u8) * MAX3107_RX_FIFO_SIZE, GFP_KERNEL);
+	if (!s->rxstr) {
+		pr_err("Allocating RX buffer failed\n");
+		return -ENOMEM;
+	}
+	/* SPI Tx buffer
+	 * SPI transfer buffer
+	 * +3 for TX FIFO empty
+	 * interrupt disabling and
+	 * enabling and TX enabling
+	 */
+	s->txbuf = kzalloc(sizeof(u16) * MAX3107_TX_FIFO_SIZE + 3, GFP_KERNEL);
+	if (!s->txbuf) {
+		pr_err("Allocating TX buffer failed\n");
+		return -ENOMEM;
+	}
+	/* Initialize shared data lock */
+	spin_lock_init(&s->data_lock);
+
+	/* SPI intializations */
+	dev_set_drvdata(&spi->dev, s);
+	spi->mode = SPI_MODE_0;
+	spi->dev.platform_data = pdata;
+	spi->bits_per_word = 16;
+	s->ext_clk = pdata->ext_clk;
+	s->loopback = pdata->loopback;
+	spi_setup(spi);
+	s->spi = spi;
+
+	/* Check REV ID to ensure we are talking to what we expect */
+	buf[0] = MAX3107_REVID_REG;
+	if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 2)) {
+		dev_err(&s->spi->dev, "SPI transfer for REVID read failed\n");
+		return -EIO;
+	}
+	if ((buf[0] & MAX3107_SPI_RX_DATA_MASK) != MAX3107_REVID1 &&
+		(buf[0] & MAX3107_SPI_RX_DATA_MASK) != MAX3107_REVID2) {
+		dev_err(&s->spi->dev, "REVID %x does not match\n",
+				(buf[0] & MAX3107_SPI_RX_DATA_MASK));
+		return -ENODEV;
+	}
+
+	/* Disable all interrupts */
+	buf[0] = (MAX3107_WRITE_BIT | MAX3107_IRQEN_REG | 0x0000);
+	buf[0] |= 0x0000;
+
+	/* Configure clock source */
+	buf[1] = (MAX3107_WRITE_BIT | MAX3107_CLKSRC_REG);
+	if (s->ext_clk) {
+		/* External clock */
+		buf[1] |= MAX3107_CLKSRC_EXTCLK_BIT;
+	}
+
+	/* PLL bypass ON */
+	buf[1] |= MAX3107_CLKSRC_PLLBYP_BIT;
+
+	/* Perform SPI transfer */
+	if (max3107_rw(s, (u8 *)buf, NULL, 4)) {
+		dev_err(&s->spi->dev, "SPI transfer for init failed\n");
+		return -EIO;
+	}
+
+	/* Register UART driver */
+	if (!driver_registered) {
+		retval = uart_register_driver(&max3107_uart_driver);
+		if (retval) {
+			dev_err(&s->spi->dev, "Registering UART driver failed\n");
+			return retval;
+		}
+		driver_registered = 1;
+	}
+
+	/* Initialize UART port data */
+	s->port.fifosize = 128;
+	s->port.ops = &max3107_ops;
+	s->port.line = 0;
+	s->port.dev = &spi->dev;
+	s->port.uartclk = 9600;
+	s->port.flags = UPF_SKIP_TEST | UPF_BOOT_AUTOCONF;
+	s->port.irq = s->spi->irq;
+	s->port.type = PORT_MAX3107;
+
+	/* Add UART port */
+	retval = uart_add_one_port(&max3107_uart_driver, &s->port);
+	if (retval < 0) {
+		dev_err(&s->spi->dev, "Adding UART port failed\n");
+		return retval;
+	}
+
+	if (pdata->configure) {
+		retval = pdata->configure(s);
+		if (retval < 0)
+			return retval;
+	}
+
+	/* Go to suspend mode */
+	if (pdata->hw_suspend)
+		pdata->hw_suspend(s, 1);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(max3107_probe);
+
+/* Driver remove function */
+int max3107_remove(struct spi_device *spi)
+{
+	struct max3107_port *s = dev_get_drvdata(&spi->dev);
+
+	pr_info("enter max3107 remove\n");
+
+	/* Remove port */
+	if (uart_remove_one_port(&max3107_uart_driver, &s->port))
+		dev_warn(&s->spi->dev, "Removing UART port failed\n");
+
+
+	/* Free TxRx buffer */
+	kfree(s->rxbuf);
+	kfree(s->rxstr);
+	kfree(s->txbuf);
+
+	/* Free port structure */
+	kfree(s);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(max3107_remove);
+
+/* Driver suspend function */
+int max3107_suspend(struct spi_device *spi, pm_message_t state)
+{
+#ifdef CONFIG_PM
+	struct max3107_port *s = dev_get_drvdata(&spi->dev);
+
+	pr_debug("enter suspend\n");
+
+	/* Suspend UART port */
+	uart_suspend_port(&max3107_uart_driver, &s->port);
+
+	/* Go to suspend mode */
+	if (s->pdata->hw_suspend)
+		s->pdata->hw_suspend(s, 1);
+#endif	/* CONFIG_PM */
+	return 0;
+}
+EXPORT_SYMBOL_GPL(max3107_suspend);
+
+/* Driver resume function */
+int max3107_resume(struct spi_device *spi)
+{
+#ifdef CONFIG_PM
+	struct max3107_port *s = dev_get_drvdata(&spi->dev);
+
+	pr_debug("enter resume\n");
+
+	/* Resume from suspend */
+	if (s->pdata->hw_suspend)
+		s->pdata->hw_suspend(s, 0);
+
+	/* Resume UART port */
+	uart_resume_port(&max3107_uart_driver, &s->port);
+#endif	/* CONFIG_PM */
+	return 0;
+}
+EXPORT_SYMBOL_GPL(max3107_resume);
+
+static int max3107_probe_generic(struct spi_device *spi)
+{
+	return max3107_probe(spi, &generic_plat_data);
+}
+
+/* Spi driver data */
+static struct spi_driver max3107_driver = {
+	.driver = {
+		.name		= "max3107",
+		.bus		= &spi_bus_type,
+		.owner		= THIS_MODULE,
+	},
+	.probe		= max3107_probe_generic,
+	.remove		= __devexit_p(max3107_remove),
+	.suspend	= max3107_suspend,
+	.resume		= max3107_resume,
+};
+
+/* Driver init function */
+static int __init max3107_init(void)
+{
+	pr_info("enter max3107 init\n");
+	return spi_register_driver(&max3107_driver);
+}
+
+/* Driver exit function */
+static void __exit max3107_exit(void)
+{
+	pr_info("enter max3107 exit\n");
+	/* Unregister UART driver */
+	if (driver_registered)
+		uart_unregister_driver(&max3107_uart_driver);
+	spi_unregister_driver(&max3107_driver);
+}
+
+module_init(max3107_init);
+module_exit(max3107_exit);
+
+MODULE_DESCRIPTION("MAX3107 driver");
+MODULE_AUTHOR("Aavamobile");
+MODULE_ALIAS("max3107-spi");
+MODULE_LICENSE("GPL v2");

+ 441 - 0
drivers/serial/max3107.h

@@ -0,0 +1,441 @@
+/*
+ * max3107.h - spi uart protocol driver header for Maxim 3107
+ *
+ * Copyright (C) Aavamobile 2009
+ * Based on serial_max3100.h by Christian Pellegrin
+ *
+ * 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.
+ */
+
+#ifndef _MAX3107_H
+#define _MAX3107_H
+
+/* Serial error status definitions */
+#define MAX3107_PARITY_ERROR	1
+#define MAX3107_FRAME_ERROR	2
+#define MAX3107_OVERRUN_ERROR	4
+#define MAX3107_ALL_ERRORS	(MAX3107_PARITY_ERROR | \
+				 MAX3107_FRAME_ERROR | \
+				 MAX3107_OVERRUN_ERROR)
+
+/* GPIO definitions */
+#define MAX3107_GPIO_BASE	88
+#define MAX3107_GPIO_COUNT	4
+
+
+/* GPIO connected to chip's reset pin */
+#define MAX3107_RESET_GPIO	87
+
+
+/* Chip reset delay */
+#define MAX3107_RESET_DELAY	10
+
+/* Chip wakeup delay */
+#define MAX3107_WAKEUP_DELAY	50
+
+
+/* Sleep mode definitions */
+#define MAX3107_DISABLE_FORCED_SLEEP	0
+#define MAX3107_ENABLE_FORCED_SLEEP	1
+#define MAX3107_DISABLE_AUTOSLEEP	2
+#define MAX3107_ENABLE_AUTOSLEEP	3
+
+
+/* Definitions for register access with SPI transfers
+ *
+ * SPI transfer format:
+ *
+ * Master to slave bits xzzzzzzzyyyyyyyy
+ * Slave to master bits aaaaaaaabbbbbbbb
+ *
+ * where:
+ * x = 0 for reads, 1 for writes
+ * z = register address
+ * y = new register value if write, 0 if read
+ * a = unspecified
+ * b = register value if read, unspecified if write
+ */
+
+/* SPI speed */
+#define MAX3107_SPI_SPEED	(3125000 * 2)
+
+/* Write bit */
+#define MAX3107_WRITE_BIT	(1 << 15)
+
+/* SPI TX data mask */
+#define MAX3107_SPI_RX_DATA_MASK	(0x00ff)
+
+/* SPI RX data mask */
+#define MAX3107_SPI_TX_DATA_MASK	(0x00ff)
+
+/* Register access masks */
+#define MAX3107_RHR_REG			(0x0000) /* RX FIFO */
+#define MAX3107_THR_REG			(0x0000) /* TX FIFO */
+#define MAX3107_IRQEN_REG		(0x0100) /* IRQ enable */
+#define MAX3107_IRQSTS_REG		(0x0200) /* IRQ status */
+#define MAX3107_LSR_IRQEN_REG		(0x0300) /* LSR IRQ enable */
+#define MAX3107_LSR_IRQSTS_REG		(0x0400) /* LSR IRQ status */
+#define MAX3107_SPCHR_IRQEN_REG		(0x0500) /* Special char IRQ enable */
+#define MAX3107_SPCHR_IRQSTS_REG	(0x0600) /* Special char IRQ status */
+#define MAX3107_STS_IRQEN_REG		(0x0700) /* Status IRQ enable */
+#define MAX3107_STS_IRQSTS_REG		(0x0800) /* Status IRQ status */
+#define MAX3107_MODE1_REG		(0x0900) /* MODE1 */
+#define MAX3107_MODE2_REG		(0x0a00) /* MODE2 */
+#define MAX3107_LCR_REG			(0x0b00) /* LCR */
+#define MAX3107_RXTO_REG		(0x0c00) /* RX timeout */
+#define MAX3107_HDPIXDELAY_REG		(0x0d00) /* Auto transceiver delays */
+#define MAX3107_IRDA_REG		(0x0e00) /* IRDA settings */
+#define MAX3107_FLOWLVL_REG		(0x0f00) /* Flow control levels */
+#define MAX3107_FIFOTRIGLVL_REG		(0x1000) /* FIFO IRQ trigger levels */
+#define MAX3107_TXFIFOLVL_REG		(0x1100) /* TX FIFO level */
+#define MAX3107_RXFIFOLVL_REG		(0x1200) /* RX FIFO level */
+#define MAX3107_FLOWCTRL_REG		(0x1300) /* Flow control */
+#define MAX3107_XON1_REG		(0x1400) /* XON1 character */
+#define MAX3107_XON2_REG		(0x1500) /* XON2 character */
+#define MAX3107_XOFF1_REG		(0x1600) /* XOFF1 character */
+#define MAX3107_XOFF2_REG		(0x1700) /* XOFF2 character */
+#define MAX3107_GPIOCFG_REG		(0x1800) /* GPIO config */
+#define MAX3107_GPIODATA_REG		(0x1900) /* GPIO data */
+#define MAX3107_PLLCFG_REG		(0x1a00) /* PLL config */
+#define MAX3107_BRGCFG_REG		(0x1b00) /* Baud rate generator conf */
+#define MAX3107_BRGDIVLSB_REG		(0x1c00) /* Baud rate divisor LSB */
+#define MAX3107_BRGDIVMSB_REG		(0x1d00) /* Baud rate divisor MSB */
+#define MAX3107_CLKSRC_REG		(0x1e00) /* Clock source */
+#define MAX3107_REVID_REG		(0x1f00) /* Revision identification */
+
+/* IRQ register bits */
+#define MAX3107_IRQ_LSR_BIT	(1 << 0) /* LSR interrupt */
+#define MAX3107_IRQ_SPCHR_BIT	(1 << 1) /* Special char interrupt */
+#define MAX3107_IRQ_STS_BIT	(1 << 2) /* Status interrupt */
+#define MAX3107_IRQ_RXFIFO_BIT	(1 << 3) /* RX FIFO interrupt */
+#define MAX3107_IRQ_TXFIFO_BIT	(1 << 4) /* TX FIFO interrupt */
+#define MAX3107_IRQ_TXEMPTY_BIT	(1 << 5) /* TX FIFO empty interrupt */
+#define MAX3107_IRQ_RXEMPTY_BIT	(1 << 6) /* RX FIFO empty interrupt */
+#define MAX3107_IRQ_CTS_BIT	(1 << 7) /* CTS interrupt */
+
+/* LSR register bits */
+#define MAX3107_LSR_RXTO_BIT	(1 << 0) /* RX timeout */
+#define MAX3107_LSR_RXOVR_BIT	(1 << 1) /* RX overrun */
+#define MAX3107_LSR_RXPAR_BIT	(1 << 2) /* RX parity error */
+#define MAX3107_LSR_FRERR_BIT	(1 << 3) /* Frame error */
+#define MAX3107_LSR_RXBRK_BIT	(1 << 4) /* RX break */
+#define MAX3107_LSR_RXNOISE_BIT	(1 << 5) /* RX noise */
+#define MAX3107_LSR_UNDEF6_BIT	(1 << 6) /* Undefined/not used */
+#define MAX3107_LSR_CTS_BIT	(1 << 7) /* CTS pin state */
+
+/* Special character register bits */
+#define MAX3107_SPCHR_XON1_BIT		(1 << 0) /* XON1 character */
+#define MAX3107_SPCHR_XON2_BIT		(1 << 1) /* XON2 character */
+#define MAX3107_SPCHR_XOFF1_BIT		(1 << 2) /* XOFF1 character */
+#define MAX3107_SPCHR_XOFF2_BIT		(1 << 3) /* XOFF2 character */
+#define MAX3107_SPCHR_BREAK_BIT		(1 << 4) /* RX break */
+#define MAX3107_SPCHR_MULTIDROP_BIT	(1 << 5) /* 9-bit multidrop addr char */
+#define MAX3107_SPCHR_UNDEF6_BIT	(1 << 6) /* Undefined/not used */
+#define MAX3107_SPCHR_UNDEF7_BIT	(1 << 7) /* Undefined/not used */
+
+/* Status register bits */
+#define MAX3107_STS_GPIO0_BIT		(1 << 0) /* GPIO 0 interrupt */
+#define MAX3107_STS_GPIO1_BIT		(1 << 1) /* GPIO 1 interrupt */
+#define MAX3107_STS_GPIO2_BIT		(1 << 2) /* GPIO 2 interrupt */
+#define MAX3107_STS_GPIO3_BIT		(1 << 3) /* GPIO 3 interrupt */
+#define MAX3107_STS_UNDEF4_BIT		(1 << 4) /* Undefined/not used */
+#define MAX3107_STS_CLKREADY_BIT	(1 << 5) /* Clock ready */
+#define MAX3107_STS_SLEEP_BIT		(1 << 6) /* Sleep interrupt */
+#define MAX3107_STS_UNDEF7_BIT		(1 << 7) /* Undefined/not used */
+
+/* MODE1 register bits */
+#define MAX3107_MODE1_RXDIS_BIT		(1 << 0) /* RX disable */
+#define MAX3107_MODE1_TXDIS_BIT		(1 << 1) /* TX disable */
+#define MAX3107_MODE1_TXHIZ_BIT		(1 << 2) /* TX pin three-state */
+#define MAX3107_MODE1_RTSHIZ_BIT	(1 << 3) /* RTS pin three-state */
+#define MAX3107_MODE1_TRNSCVCTRL_BIT	(1 << 4) /* Transceiver ctrl enable */
+#define MAX3107_MODE1_FORCESLEEP_BIT	(1 << 5) /* Force sleep mode */
+#define MAX3107_MODE1_AUTOSLEEP_BIT	(1 << 6) /* Auto sleep enable */
+#define MAX3107_MODE1_IRQSEL_BIT	(1 << 7) /* IRQ pin enable */
+
+/* MODE2 register bits */
+#define MAX3107_MODE2_RST_BIT		(1 << 0) /* Chip reset */
+#define MAX3107_MODE2_FIFORST_BIT	(1 << 1) /* FIFO reset */
+#define MAX3107_MODE2_RXTRIGINV_BIT	(1 << 2) /* RX FIFO INT invert */
+#define MAX3107_MODE2_RXEMPTINV_BIT	(1 << 3) /* RX FIFO empty INT invert */
+#define MAX3107_MODE2_SPCHR_BIT		(1 << 4) /* Special chr detect enable */
+#define MAX3107_MODE2_LOOPBACK_BIT	(1 << 5) /* Internal loopback enable */
+#define MAX3107_MODE2_MULTIDROP_BIT	(1 << 6) /* 9-bit multidrop enable */
+#define MAX3107_MODE2_ECHOSUPR_BIT	(1 << 7) /* ECHO suppression enable */
+
+/* LCR register bits */
+#define MAX3107_LCR_LENGTH0_BIT		(1 << 0) /* Word length bit 0 */
+#define MAX3107_LCR_LENGTH1_BIT		(1 << 1) /* Word length bit 1
+						  *
+						  * Word length bits table:
+						  * 00 -> 5 bit words
+						  * 01 -> 6 bit words
+						  * 10 -> 7 bit words
+						  * 11 -> 8 bit words
+						  */
+#define MAX3107_LCR_STOPLEN_BIT		(1 << 2) /* STOP length bit
+						  *
+						  * STOP length bit table:
+						  * 0 -> 1 stop bit
+						  * 1 -> 1-1.5 stop bits if
+						  *      word length is 5,
+						  *      2 stop bits otherwise
+						  */
+#define MAX3107_LCR_PARITY_BIT		(1 << 3) /* Parity bit enable */
+#define MAX3107_LCR_EVENPARITY_BIT	(1 << 4) /* Even parity bit enable */
+#define MAX3107_LCR_FORCEPARITY_BIT	(1 << 5) /* 9-bit multidrop parity */
+#define MAX3107_LCR_TXBREAK_BIT		(1 << 6) /* TX break enable */
+#define MAX3107_LCR_RTS_BIT		(1 << 7) /* RTS pin control */
+#define MAX3107_LCR_WORD_LEN_5		(0x0000)
+#define MAX3107_LCR_WORD_LEN_6		(0x0001)
+#define MAX3107_LCR_WORD_LEN_7		(0x0002)
+#define MAX3107_LCR_WORD_LEN_8		(0x0003)
+
+
+/* IRDA register bits */
+#define MAX3107_IRDA_IRDAEN_BIT		(1 << 0) /* IRDA mode enable */
+#define MAX3107_IRDA_SIR_BIT		(1 << 1) /* SIR mode enable */
+#define MAX3107_IRDA_SHORTIR_BIT	(1 << 2) /* Short SIR mode enable */
+#define MAX3107_IRDA_MIR_BIT		(1 << 3) /* MIR mode enable */
+#define MAX3107_IRDA_RXINV_BIT		(1 << 4) /* RX logic inversion enable */
+#define MAX3107_IRDA_TXINV_BIT		(1 << 5) /* TX logic inversion enable */
+#define MAX3107_IRDA_UNDEF6_BIT		(1 << 6) /* Undefined/not used */
+#define MAX3107_IRDA_UNDEF7_BIT		(1 << 7) /* Undefined/not used */
+
+/* Flow control trigger level register masks */
+#define MAX3107_FLOWLVL_HALT_MASK	(0x000f) /* Flow control halt level */
+#define MAX3107_FLOWLVL_RES_MASK	(0x00f0) /* Flow control resume level */
+#define MAX3107_FLOWLVL_HALT(words)	((words/8) & 0x000f)
+#define MAX3107_FLOWLVL_RES(words)	(((words/8) & 0x000f) << 4)
+
+/* FIFO interrupt trigger level register masks */
+#define MAX3107_FIFOTRIGLVL_TX_MASK	(0x000f) /* TX FIFO trigger level */
+#define MAX3107_FIFOTRIGLVL_RX_MASK	(0x00f0) /* RX FIFO trigger level */
+#define MAX3107_FIFOTRIGLVL_TX(words)	((words/8) & 0x000f)
+#define MAX3107_FIFOTRIGLVL_RX(words)	(((words/8) & 0x000f) << 4)
+
+/* Flow control register bits */
+#define MAX3107_FLOWCTRL_AUTORTS_BIT	(1 << 0) /* Auto RTS flow ctrl enable */
+#define MAX3107_FLOWCTRL_AUTOCTS_BIT	(1 << 1) /* Auto CTS flow ctrl enable */
+#define MAX3107_FLOWCTRL_GPIADDR_BIT	(1 << 2) /* Enables that GPIO inputs
+						  * are used in conjunction with
+						  * XOFF2 for definition of
+						  * special character */
+#define MAX3107_FLOWCTRL_SWFLOWEN_BIT	(1 << 3) /* Auto SW flow ctrl enable */
+#define MAX3107_FLOWCTRL_SWFLOW0_BIT	(1 << 4) /* SWFLOW bit 0 */
+#define MAX3107_FLOWCTRL_SWFLOW1_BIT	(1 << 5) /* SWFLOW bit 1
+						  *
+						  * SWFLOW bits 1 & 0 table:
+						  * 00 -> no transmitter flow
+						  *       control
+						  * 01 -> receiver compares
+						  *       XON2 and XOFF2
+						  *       and controls
+						  *       transmitter
+						  * 10 -> receiver compares
+						  *       XON1 and XOFF1
+						  *       and controls
+						  *       transmitter
+						  * 11 -> receiver compares
+						  *       XON1, XON2, XOFF1 and
+						  *       XOFF2 and controls
+						  *       transmitter
+						  */
+#define MAX3107_FLOWCTRL_SWFLOW2_BIT	(1 << 6) /* SWFLOW bit 2 */
+#define MAX3107_FLOWCTRL_SWFLOW3_BIT	(1 << 7) /* SWFLOW bit 3
+						  *
+						  * SWFLOW bits 3 & 2 table:
+						  * 00 -> no received flow
+						  *       control
+						  * 01 -> transmitter generates
+						  *       XON2 and XOFF2
+						  * 10 -> transmitter generates
+						  *       XON1 and XOFF1
+						  * 11 -> transmitter generates
+						  *       XON1, XON2, XOFF1 and
+						  *       XOFF2
+						  */
+
+/* GPIO configuration register bits */
+#define MAX3107_GPIOCFG_GP0OUT_BIT	(1 << 0) /* GPIO 0 output enable */
+#define MAX3107_GPIOCFG_GP1OUT_BIT	(1 << 1) /* GPIO 1 output enable */
+#define MAX3107_GPIOCFG_GP2OUT_BIT	(1 << 2) /* GPIO 2 output enable */
+#define MAX3107_GPIOCFG_GP3OUT_BIT	(1 << 3) /* GPIO 3 output enable */
+#define MAX3107_GPIOCFG_GP0OD_BIT	(1 << 4) /* GPIO 0 open-drain enable */
+#define MAX3107_GPIOCFG_GP1OD_BIT	(1 << 5) /* GPIO 1 open-drain enable */
+#define MAX3107_GPIOCFG_GP2OD_BIT	(1 << 6) /* GPIO 2 open-drain enable */
+#define MAX3107_GPIOCFG_GP3OD_BIT	(1 << 7) /* GPIO 3 open-drain enable */
+
+/* GPIO DATA register bits */
+#define MAX3107_GPIODATA_GP0OUT_BIT	(1 << 0) /* GPIO 0 output value */
+#define MAX3107_GPIODATA_GP1OUT_BIT	(1 << 1) /* GPIO 1 output value */
+#define MAX3107_GPIODATA_GP2OUT_BIT	(1 << 2) /* GPIO 2 output value */
+#define MAX3107_GPIODATA_GP3OUT_BIT	(1 << 3) /* GPIO 3 output value */
+#define MAX3107_GPIODATA_GP0IN_BIT	(1 << 4) /* GPIO 0 input value */
+#define MAX3107_GPIODATA_GP1IN_BIT	(1 << 5) /* GPIO 1 input value */
+#define MAX3107_GPIODATA_GP2IN_BIT	(1 << 6) /* GPIO 2 input value */
+#define MAX3107_GPIODATA_GP3IN_BIT	(1 << 7) /* GPIO 3 input value */
+
+/* PLL configuration register masks */
+#define MAX3107_PLLCFG_PREDIV_MASK	(0x003f) /* PLL predivision value */
+#define MAX3107_PLLCFG_PLLFACTOR_MASK	(0x00c0) /* PLL multiplication factor */
+
+/* Baud rate generator configuration register masks and bits */
+#define MAX3107_BRGCFG_FRACT_MASK	(0x000f) /* Fractional portion of
+						  * Baud rate generator divisor
+						  */
+#define MAX3107_BRGCFG_2XMODE_BIT	(1 << 4) /* Double baud rate */
+#define MAX3107_BRGCFG_4XMODE_BIT	(1 << 5) /* Quadruple baud rate */
+#define MAX3107_BRGCFG_UNDEF6_BIT	(1 << 6) /* Undefined/not used */
+#define MAX3107_BRGCFG_UNDEF7_BIT	(1 << 7) /* Undefined/not used */
+
+/* Clock source register bits */
+#define MAX3107_CLKSRC_INTOSC_BIT	(1 << 0) /* Internal osc enable */
+#define MAX3107_CLKSRC_CRYST_BIT	(1 << 1) /* Crystal osc enable */
+#define MAX3107_CLKSRC_PLL_BIT		(1 << 2) /* PLL enable */
+#define MAX3107_CLKSRC_PLLBYP_BIT	(1 << 3) /* PLL bypass */
+#define MAX3107_CLKSRC_EXTCLK_BIT	(1 << 4) /* External clock enable */
+#define MAX3107_CLKSRC_UNDEF5_BIT	(1 << 5) /* Undefined/not used */
+#define MAX3107_CLKSRC_UNDEF6_BIT	(1 << 6) /* Undefined/not used */
+#define MAX3107_CLKSRC_CLK2RTS_BIT	(1 << 7) /* Baud clk to RTS pin */
+
+
+/* HW definitions */
+#define MAX3107_RX_FIFO_SIZE	128
+#define MAX3107_TX_FIFO_SIZE	128
+#define MAX3107_REVID1		0x00a0
+#define MAX3107_REVID2		0x00a1
+
+
+/* Baud rate generator configuration values for external clock 13MHz */
+#define MAX3107_BRG13_B300	(0x0A9400 | 0x05)
+#define MAX3107_BRG13_B600	(0x054A00 | 0x03)
+#define MAX3107_BRG13_B1200	(0x02A500 | 0x01)
+#define MAX3107_BRG13_B2400	(0x015200 | 0x09)
+#define MAX3107_BRG13_B4800	(0x00A900 | 0x04)
+#define MAX3107_BRG13_B9600	(0x005400 | 0x0A)
+#define MAX3107_BRG13_B19200	(0x002A00 | 0x05)
+#define MAX3107_BRG13_B38400	(0x001500 | 0x03)
+#define MAX3107_BRG13_B57600	(0x000E00 | 0x02)
+#define MAX3107_BRG13_B115200	(0x000700 | 0x01)
+#define MAX3107_BRG13_B230400	(0x000300 | 0x08)
+#define MAX3107_BRG13_B460800	(0x000100 | 0x0c)
+#define MAX3107_BRG13_B921600	(0x000100 | 0x1c)
+
+/* Baud rate generator configuration values for external clock 26MHz */
+#define MAX3107_BRG26_B300	(0x152800 | 0x0A)
+#define MAX3107_BRG26_B600	(0x0A9400 | 0x05)
+#define MAX3107_BRG26_B1200	(0x054A00 | 0x03)
+#define MAX3107_BRG26_B2400	(0x02A500 | 0x01)
+#define MAX3107_BRG26_B4800	(0x015200 | 0x09)
+#define MAX3107_BRG26_B9600	(0x00A900 | 0x04)
+#define MAX3107_BRG26_B19200	(0x005400 | 0x0A)
+#define MAX3107_BRG26_B38400	(0x002A00 | 0x05)
+#define MAX3107_BRG26_B57600	(0x001C00 | 0x03)
+#define MAX3107_BRG26_B115200	(0x000E00 | 0x02)
+#define MAX3107_BRG26_B230400	(0x000700 | 0x01)
+#define MAX3107_BRG26_B460800	(0x000300 | 0x08)
+#define MAX3107_BRG26_B921600	(0x000100 | 0x0C)
+
+/* Baud rate generator configuration values for internal clock */
+#define MAX3107_BRG13_IB300	(0x008000 | 0x00)
+#define MAX3107_BRG13_IB600	(0x004000 | 0x00)
+#define MAX3107_BRG13_IB1200	(0x002000 | 0x00)
+#define MAX3107_BRG13_IB2400	(0x001000 | 0x00)
+#define MAX3107_BRG13_IB4800	(0x000800 | 0x00)
+#define MAX3107_BRG13_IB9600	(0x000400 | 0x00)
+#define MAX3107_BRG13_IB19200	(0x000200 | 0x00)
+#define MAX3107_BRG13_IB38400	(0x000100 | 0x00)
+#define MAX3107_BRG13_IB57600	(0x000000 | 0x0B)
+#define MAX3107_BRG13_IB115200	(0x000000 | 0x05)
+#define MAX3107_BRG13_IB230400	(0x000000 | 0x03)
+#define MAX3107_BRG13_IB460800	(0x000000 | 0x00)
+#define MAX3107_BRG13_IB921600	(0x000000 | 0x00)
+
+
+struct baud_table {
+	int baud;
+	u32 new_brg;
+};
+
+struct max3107_port {
+	/* UART port structure */
+	struct uart_port port;
+
+	/* SPI device structure */
+	struct spi_device *spi;
+
+#if defined(CONFIG_GPIOLIB)
+	/* GPIO chip stucture */
+	struct gpio_chip chip;
+#endif
+
+	/* Workqueue that does all the magic */
+	struct workqueue_struct *workqueue;
+	struct work_struct work;
+
+	/* Lock for shared data */
+	spinlock_t data_lock;
+
+	/* Device configuration */
+	int ext_clk;		/* 1 if external clock used */
+	int loopback;		/* Current loopback mode state */
+	int baud;			/* Current baud rate */
+
+	/* State flags */
+	int suspended;		/* Indicates suspend mode */
+	int tx_fifo_empty;	/* Flag for TX FIFO state */
+	int rx_enabled;		/* Flag for receiver state */
+	int tx_enabled;		/* Flag for transmitter state */
+
+	u16 irqen_reg;		/* Current IRQ enable register value */
+	/* Shared data */
+	u16 mode1_reg;		/* Current mode1 register value*/
+	int mode1_commit;	/* Flag for setting new mode1 register value */
+	u16 lcr_reg;		/* Current LCR register value */
+	int lcr_commit;		/* Flag for setting new LCR register value */
+	u32 brg_cfg;		/* Current Baud rate generator config  */
+	int brg_commit;		/* Flag for setting new baud rate generator
+				 * config
+				 */
+	struct baud_table *baud_tbl;
+	int handle_irq;		/* Indicates that IRQ should be handled */
+
+	/* Rx buffer and str*/
+	u16 *rxbuf;
+	u8  *rxstr;
+	/* Tx buffer*/
+	u16 *txbuf;
+
+	struct max3107_plat *pdata;	/* Platform data */
+};
+
+/* Platform data structure */
+struct max3107_plat {
+	/* Loopback mode enable */
+	int loopback;
+	/* External clock enable */
+	int ext_clk;
+	/* Called during the register initialisation */
+	void (*init)(struct max3107_port *s);
+	/* Called when the port is found and configured */
+	int (*configure)(struct max3107_port *s);
+	/* HW suspend function */
+	void (*hw_suspend) (struct max3107_port *s, int suspend);
+	/* Polling mode enable */
+	int polled_mode;
+	/* Polling period if polling mode enabled */
+	int poll_time;
+};
+
+extern int max3107_rw(struct max3107_port *s, u8 *tx, u8 *rx, int len);
+extern void max3107_hw_susp(struct max3107_port *s, int suspend);
+extern int max3107_probe(struct spi_device *spi, struct max3107_plat *pdata);
+extern int max3107_remove(struct spi_device *spi);
+extern int max3107_suspend(struct spi_device *spi, pm_message_t state);
+extern int max3107_resume(struct spi_device *spi);
+
+#endif /* _LINUX_SERIAL_MAX3107_H */

+ 13 - 18
drivers/serial/mcf.c

@@ -70,16 +70,14 @@ static unsigned int mcf_tx_empty(struct uart_port *port)
 static unsigned int mcf_get_mctrl(struct uart_port *port)
 static unsigned int mcf_get_mctrl(struct uart_port *port)
 {
 {
 	struct mcf_uart *pp = container_of(port, struct mcf_uart, port);
 	struct mcf_uart *pp = container_of(port, struct mcf_uart, port);
-	unsigned long flags;
 	unsigned int sigs;
 	unsigned int sigs;
 
 
-	spin_lock_irqsave(&port->lock, flags);
 	sigs = (readb(port->membase + MCFUART_UIPR) & MCFUART_UIPR_CTS) ?
 	sigs = (readb(port->membase + MCFUART_UIPR) & MCFUART_UIPR_CTS) ?
 		0 : TIOCM_CTS;
 		0 : TIOCM_CTS;
 	sigs |= (pp->sigs & TIOCM_RTS);
 	sigs |= (pp->sigs & TIOCM_RTS);
 	sigs |= (mcf_getppdcd(port->line) ? TIOCM_CD : 0);
 	sigs |= (mcf_getppdcd(port->line) ? TIOCM_CD : 0);
 	sigs |= (mcf_getppdtr(port->line) ? TIOCM_DTR : 0);
 	sigs |= (mcf_getppdtr(port->line) ? TIOCM_DTR : 0);
-	spin_unlock_irqrestore(&port->lock, flags);
+
 	return sigs;
 	return sigs;
 }
 }
 
 
@@ -88,16 +86,13 @@ static unsigned int mcf_get_mctrl(struct uart_port *port)
 static void mcf_set_mctrl(struct uart_port *port, unsigned int sigs)
 static void mcf_set_mctrl(struct uart_port *port, unsigned int sigs)
 {
 {
 	struct mcf_uart *pp = container_of(port, struct mcf_uart, port);
 	struct mcf_uart *pp = container_of(port, struct mcf_uart, port);
-	unsigned long flags;
 
 
-	spin_lock_irqsave(&port->lock, flags);
 	pp->sigs = sigs;
 	pp->sigs = sigs;
 	mcf_setppdtr(port->line, (sigs & TIOCM_DTR));
 	mcf_setppdtr(port->line, (sigs & TIOCM_DTR));
 	if (sigs & TIOCM_RTS)
 	if (sigs & TIOCM_RTS)
 		writeb(MCFUART_UOP_RTS, port->membase + MCFUART_UOP1);
 		writeb(MCFUART_UOP_RTS, port->membase + MCFUART_UOP1);
 	else
 	else
 		writeb(MCFUART_UOP_RTS, port->membase + MCFUART_UOP0);
 		writeb(MCFUART_UOP_RTS, port->membase + MCFUART_UOP0);
-	spin_unlock_irqrestore(&port->lock, flags);
 }
 }
 
 
 /****************************************************************************/
 /****************************************************************************/
@@ -105,12 +100,9 @@ static void mcf_set_mctrl(struct uart_port *port, unsigned int sigs)
 static void mcf_start_tx(struct uart_port *port)
 static void mcf_start_tx(struct uart_port *port)
 {
 {
 	struct mcf_uart *pp = container_of(port, struct mcf_uart, port);
 	struct mcf_uart *pp = container_of(port, struct mcf_uart, port);
-	unsigned long flags;
 
 
-	spin_lock_irqsave(&port->lock, flags);
 	pp->imr |= MCFUART_UIR_TXREADY;
 	pp->imr |= MCFUART_UIR_TXREADY;
 	writeb(pp->imr, port->membase + MCFUART_UIMR);
 	writeb(pp->imr, port->membase + MCFUART_UIMR);
-	spin_unlock_irqrestore(&port->lock, flags);
 }
 }
 
 
 /****************************************************************************/
 /****************************************************************************/
@@ -118,12 +110,9 @@ static void mcf_start_tx(struct uart_port *port)
 static void mcf_stop_tx(struct uart_port *port)
 static void mcf_stop_tx(struct uart_port *port)
 {
 {
 	struct mcf_uart *pp = container_of(port, struct mcf_uart, port);
 	struct mcf_uart *pp = container_of(port, struct mcf_uart, port);
-	unsigned long flags;
 
 
-	spin_lock_irqsave(&port->lock, flags);
 	pp->imr &= ~MCFUART_UIR_TXREADY;
 	pp->imr &= ~MCFUART_UIR_TXREADY;
 	writeb(pp->imr, port->membase + MCFUART_UIMR);
 	writeb(pp->imr, port->membase + MCFUART_UIMR);
-	spin_unlock_irqrestore(&port->lock, flags);
 }
 }
 
 
 /****************************************************************************/
 /****************************************************************************/
@@ -131,12 +120,9 @@ static void mcf_stop_tx(struct uart_port *port)
 static void mcf_stop_rx(struct uart_port *port)
 static void mcf_stop_rx(struct uart_port *port)
 {
 {
 	struct mcf_uart *pp = container_of(port, struct mcf_uart, port);
 	struct mcf_uart *pp = container_of(port, struct mcf_uart, port);
-	unsigned long flags;
 
 
-	spin_lock_irqsave(&port->lock, flags);
 	pp->imr &= ~MCFUART_UIR_RXREADY;
 	pp->imr &= ~MCFUART_UIR_RXREADY;
 	writeb(pp->imr, port->membase + MCFUART_UIMR);
 	writeb(pp->imr, port->membase + MCFUART_UIMR);
-	spin_unlock_irqrestore(&port->lock, flags);
 }
 }
 
 
 /****************************************************************************/
 /****************************************************************************/
@@ -366,13 +352,22 @@ static irqreturn_t mcf_interrupt(int irq, void *data)
 	struct uart_port *port = data;
 	struct uart_port *port = data;
 	struct mcf_uart *pp = container_of(port, struct mcf_uart, port);
 	struct mcf_uart *pp = container_of(port, struct mcf_uart, port);
 	unsigned int isr;
 	unsigned int isr;
+	irqreturn_t ret = IRQ_NONE;
 
 
 	isr = readb(port->membase + MCFUART_UISR) & pp->imr;
 	isr = readb(port->membase + MCFUART_UISR) & pp->imr;
-	if (isr & MCFUART_UIR_RXREADY)
+
+	spin_lock(&port->lock);
+	if (isr & MCFUART_UIR_RXREADY) {
 		mcf_rx_chars(pp);
 		mcf_rx_chars(pp);
-	if (isr & MCFUART_UIR_TXREADY)
+		ret = IRQ_HANDLED;
+	}
+	if (isr & MCFUART_UIR_TXREADY) {
 		mcf_tx_chars(pp);
 		mcf_tx_chars(pp);
-	return IRQ_HANDLED;
+		ret = IRQ_HANDLED;
+	}
+	spin_unlock(&port->lock);
+
+	return ret;
 }
 }
 
 
 /****************************************************************************/
 /****************************************************************************/

+ 1498 - 0
drivers/serial/mfd.c

@@ -0,0 +1,1498 @@
+/*
+ * mfd.c: driver for High Speed UART device of Intel Medfield platform
+ *
+ * Refer pxa.c, 8250.c and some other drivers in drivers/serial/
+ *
+ * (C) Copyright 2010 Intel Corporation
+ *
+ * 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; version 2
+ * of the License.
+ */
+
+/* Notes:
+ * 1. DMA channel allocation: 0/1 channel are assigned to port 0,
+ *    2/3 chan to port 1, 4/5 chan to port 3. Even number chans
+ *    are used for RX, odd chans for TX
+ *
+ * 2. In A0 stepping, UART will not support TX half empty flag
+ *
+ * 3. The RI/DSR/DCD/DTR are not pinned out, DCD & DSR are always
+ *    asserted, only when the HW is reset the DDCD and DDSR will
+ *    be triggered
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/console.h>
+#include <linux/sysrq.h>
+#include <linux/serial_reg.h>
+#include <linux/circ_buf.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial_core.h>
+#include <linux/serial_mfd.h>
+#include <linux/dma-mapping.h>
+#include <linux/pci.h>
+#include <linux/io.h>
+#include <linux/debugfs.h>
+
+#define  MFD_HSU_A0_STEPPING	1
+
+#define HSU_DMA_BUF_SIZE	2048
+
+#define chan_readl(chan, offset)	readl(chan->reg + offset)
+#define chan_writel(chan, offset, val)	writel(val, chan->reg + offset)
+
+#define mfd_readl(obj, offset)		readl(obj->reg + offset)
+#define mfd_writel(obj, offset, val)	writel(val, obj->reg + offset)
+
+#define HSU_DMA_TIMEOUT_CHECK_FREQ	(HZ/10)
+
+struct hsu_dma_buffer {
+	u8		*buf;
+	dma_addr_t	dma_addr;
+	u32		dma_size;
+	u32		ofs;
+};
+
+struct hsu_dma_chan {
+	u32	id;
+	enum dma_data_direction	dirt;
+	struct uart_hsu_port	*uport;
+	void __iomem		*reg;
+	struct timer_list	rx_timer; /* only needed by RX channel */
+};
+
+struct uart_hsu_port {
+	struct uart_port        port;
+	unsigned char           ier;
+	unsigned char           lcr;
+	unsigned char           mcr;
+	unsigned int            lsr_break_flag;
+	char			name[12];
+	int			index;
+	struct device		*dev;
+
+	struct hsu_dma_chan	*txc;
+	struct hsu_dma_chan	*rxc;
+	struct hsu_dma_buffer	txbuf;
+	struct hsu_dma_buffer	rxbuf;
+	int			use_dma;	/* flag for DMA/PIO */
+	int			running;
+	int			dma_tx_on;
+};
+
+/* Top level data structure of HSU */
+struct hsu_port {
+	void __iomem	*reg;
+	unsigned long	paddr;
+	unsigned long	iolen;
+	u32		irq;
+
+	struct uart_hsu_port	port[3];
+	struct hsu_dma_chan	chans[10];
+
+	struct dentry *debugfs;
+};
+
+static inline unsigned int serial_in(struct uart_hsu_port *up, int offset)
+{
+	unsigned int val;
+
+	if (offset > UART_MSR) {
+		offset <<= 2;
+		val = readl(up->port.membase + offset);
+	} else
+		val = (unsigned int)readb(up->port.membase + offset);
+
+	return val;
+}
+
+static inline void serial_out(struct uart_hsu_port *up, int offset, int value)
+{
+	if (offset > UART_MSR) {
+		offset <<= 2;
+		writel(value, up->port.membase + offset);
+	} else {
+		unsigned char val = value & 0xff;
+		writeb(val, up->port.membase + offset);
+	}
+}
+
+#ifdef CONFIG_DEBUG_FS
+
+#define HSU_REGS_BUFSIZE	1024
+
+static int hsu_show_regs_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+	return 0;
+}
+
+static ssize_t port_show_regs(struct file *file, char __user *user_buf,
+				size_t count, loff_t *ppos)
+{
+	struct uart_hsu_port *up = file->private_data;
+	char *buf;
+	u32 len = 0;
+	ssize_t ret;
+
+	buf = kzalloc(HSU_REGS_BUFSIZE, GFP_KERNEL);
+	if (!buf)
+		return 0;
+
+	len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+			"MFD HSU port[%d] regs:\n", up->index);
+
+	len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+			"=================================\n");
+	len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+			"IER: \t\t0x%08x\n", serial_in(up, UART_IER));
+	len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+			"IIR: \t\t0x%08x\n", serial_in(up, UART_IIR));
+	len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+			"LCR: \t\t0x%08x\n", serial_in(up, UART_LCR));
+	len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+			"MCR: \t\t0x%08x\n", serial_in(up, UART_MCR));
+	len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+			"LSR: \t\t0x%08x\n", serial_in(up, UART_LSR));
+	len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+			"MSR: \t\t0x%08x\n", serial_in(up, UART_MSR));
+	len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+			"FOR: \t\t0x%08x\n", serial_in(up, UART_FOR));
+	len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+			"PS: \t\t0x%08x\n", serial_in(up, UART_PS));
+	len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+			"MUL: \t\t0x%08x\n", serial_in(up, UART_MUL));
+	len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+			"DIV: \t\t0x%08x\n", serial_in(up, UART_DIV));
+
+	ret =  simple_read_from_buffer(user_buf, count, ppos, buf, len);
+	kfree(buf);
+	return ret;
+}
+
+static ssize_t dma_show_regs(struct file *file, char __user *user_buf,
+				size_t count, loff_t *ppos)
+{
+	struct hsu_dma_chan *chan = file->private_data;
+	char *buf;
+	u32 len = 0;
+	ssize_t ret;
+
+	buf = kzalloc(HSU_REGS_BUFSIZE, GFP_KERNEL);
+	if (!buf)
+		return 0;
+
+	len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+			"MFD HSU DMA channel [%d] regs:\n", chan->id);
+
+	len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+			"=================================\n");
+	len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+			"CR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_CR));
+	len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+			"DCR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_DCR));
+	len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+			"BSR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_BSR));
+	len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+			"MOTSR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_MOTSR));
+	len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+			"D0SAR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_D0SAR));
+	len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+			"D0TSR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_D0TSR));
+	len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+			"D0SAR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_D1SAR));
+	len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+			"D0TSR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_D1TSR));
+	len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+			"D0SAR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_D2SAR));
+	len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+			"D0TSR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_D2TSR));
+	len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+			"D0SAR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_D3SAR));
+	len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+			"D0TSR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_D3TSR));
+
+	ret =  simple_read_from_buffer(user_buf, count, ppos, buf, len);
+	kfree(buf);
+	return ret;
+}
+
+static const struct file_operations port_regs_ops = {
+	.owner		= THIS_MODULE,
+	.open		= hsu_show_regs_open,
+	.read		= port_show_regs,
+};
+
+static const struct file_operations dma_regs_ops = {
+	.owner		= THIS_MODULE,
+	.open		= hsu_show_regs_open,
+	.read		= dma_show_regs,
+};
+
+static int hsu_debugfs_init(struct hsu_port *hsu)
+{
+	int i;
+	char name[32];
+
+	hsu->debugfs = debugfs_create_dir("hsu", NULL);
+	if (!hsu->debugfs)
+		return -ENOMEM;
+
+	for (i = 0; i < 3; i++) {
+		snprintf(name, sizeof(name), "port_%d_regs", i);
+		debugfs_create_file(name, S_IFREG | S_IRUGO,
+			hsu->debugfs, (void *)(&hsu->port[i]), &port_regs_ops);
+	}
+
+	for (i = 0; i < 6; i++) {
+		snprintf(name, sizeof(name), "dma_chan_%d_regs", i);
+		debugfs_create_file(name, S_IFREG | S_IRUGO,
+			hsu->debugfs, (void *)&hsu->chans[i], &dma_regs_ops);
+	}
+
+	return 0;
+}
+
+static void hsu_debugfs_remove(struct hsu_port *hsu)
+{
+	if (hsu->debugfs)
+		debugfs_remove_recursive(hsu->debugfs);
+}
+
+#else
+static inline int hsu_debugfs_init(struct hsu_port *hsu)
+{
+	return 0;
+}
+
+static inline void hsu_debugfs_remove(struct hsu_port *hsu)
+{
+}
+#endif /* CONFIG_DEBUG_FS */
+
+static void serial_hsu_enable_ms(struct uart_port *port)
+{
+	struct uart_hsu_port *up =
+		container_of(port, struct uart_hsu_port, port);
+
+	up->ier |= UART_IER_MSI;
+	serial_out(up, UART_IER, up->ier);
+}
+
+void hsu_dma_tx(struct uart_hsu_port *up)
+{
+	struct circ_buf *xmit = &up->port.state->xmit;
+	struct hsu_dma_buffer *dbuf = &up->txbuf;
+	int count;
+
+	/* test_and_set_bit may be better, but anyway it's in lock protected mode */
+	if (up->dma_tx_on)
+		return;
+
+	/* Update the circ buf info */
+	xmit->tail += dbuf->ofs;
+	xmit->tail &= UART_XMIT_SIZE - 1;
+
+	up->port.icount.tx += dbuf->ofs;
+	dbuf->ofs = 0;
+
+	/* Disable the channel */
+	chan_writel(up->txc, HSU_CH_CR, 0x0);
+
+	if (!uart_circ_empty(xmit) && !uart_tx_stopped(&up->port)) {
+		dma_sync_single_for_device(up->port.dev,
+					   dbuf->dma_addr,
+					   dbuf->dma_size,
+					   DMA_TO_DEVICE);
+
+		count = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
+		dbuf->ofs = count;
+
+		/* Reprogram the channel */
+		chan_writel(up->txc, HSU_CH_D0SAR, dbuf->dma_addr + xmit->tail);
+		chan_writel(up->txc, HSU_CH_D0TSR, count);
+
+		/* Reenable the channel */
+		chan_writel(up->txc, HSU_CH_DCR, 0x1
+						 | (0x1 << 8)
+						 | (0x1 << 16)
+						 | (0x1 << 24));
+		up->dma_tx_on = 1;
+		chan_writel(up->txc, HSU_CH_CR, 0x1);
+	}
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(&up->port);
+}
+
+/* The buffer is already cache coherent */
+void hsu_dma_start_rx_chan(struct hsu_dma_chan *rxc, struct hsu_dma_buffer *dbuf)
+{
+	dbuf->ofs = 0;
+
+	chan_writel(rxc, HSU_CH_BSR, 32);
+	chan_writel(rxc, HSU_CH_MOTSR, 4);
+
+	chan_writel(rxc, HSU_CH_D0SAR, dbuf->dma_addr);
+	chan_writel(rxc, HSU_CH_D0TSR, dbuf->dma_size);
+	chan_writel(rxc, HSU_CH_DCR, 0x1 | (0x1 << 8)
+					 | (0x1 << 16)
+					 | (0x1 << 24)	/* timeout bit, see HSU Errata 1 */
+					 );
+	chan_writel(rxc, HSU_CH_CR, 0x3);
+
+	mod_timer(&rxc->rx_timer, jiffies + HSU_DMA_TIMEOUT_CHECK_FREQ);
+}
+
+/* Protected by spin_lock_irqsave(port->lock) */
+static void serial_hsu_start_tx(struct uart_port *port)
+{
+	struct uart_hsu_port *up =
+		container_of(port, struct uart_hsu_port, port);
+
+	if (up->use_dma) {
+		hsu_dma_tx(up);
+	} else if (!(up->ier & UART_IER_THRI)) {
+		up->ier |= UART_IER_THRI;
+		serial_out(up, UART_IER, up->ier);
+	}
+}
+
+static void serial_hsu_stop_tx(struct uart_port *port)
+{
+	struct uart_hsu_port *up =
+		container_of(port, struct uart_hsu_port, port);
+	struct hsu_dma_chan *txc = up->txc;
+
+	if (up->use_dma)
+		chan_writel(txc, HSU_CH_CR, 0x0);
+	else if (up->ier & UART_IER_THRI) {
+		up->ier &= ~UART_IER_THRI;
+		serial_out(up, UART_IER, up->ier);
+	}
+}
+
+/* This is always called in spinlock protected mode, so
+ * modify timeout timer is safe here */
+void hsu_dma_rx(struct uart_hsu_port *up, u32 int_sts)
+{
+	struct hsu_dma_buffer *dbuf = &up->rxbuf;
+	struct hsu_dma_chan *chan = up->rxc;
+	struct uart_port *port = &up->port;
+	struct tty_struct *tty = port->state->port.tty;
+	int count;
+
+	if (!tty)
+		return;
+
+	/*
+	 * First need to know how many is already transferred,
+	 * then check if its a timeout DMA irq, and return
+	 * the trail bytes out, push them up and reenable the
+	 * channel
+	 */
+
+	/* Timeout IRQ, need wait some time, see Errata 2 */
+	if (int_sts & 0xf00)
+		udelay(2);
+
+	/* Stop the channel */
+	chan_writel(chan, HSU_CH_CR, 0x0);
+
+	count = chan_readl(chan, HSU_CH_D0SAR) - dbuf->dma_addr;
+	if (!count) {
+		/* Restart the channel before we leave */
+		chan_writel(chan, HSU_CH_CR, 0x3);
+		return;
+	}
+	del_timer(&chan->rx_timer);
+
+	dma_sync_single_for_cpu(port->dev, dbuf->dma_addr,
+			dbuf->dma_size, DMA_FROM_DEVICE);
+
+	/*
+	 * Head will only wrap around when we recycle
+	 * the DMA buffer, and when that happens, we
+	 * explicitly set tail to 0. So head will
+	 * always be greater than tail.
+	 */
+	tty_insert_flip_string(tty, dbuf->buf, count);
+	port->icount.rx += count;
+
+	dma_sync_single_for_device(up->port.dev, dbuf->dma_addr,
+			dbuf->dma_size, DMA_FROM_DEVICE);
+
+	/* Reprogram the channel */
+	chan_writel(chan, HSU_CH_D0SAR, dbuf->dma_addr);
+	chan_writel(chan, HSU_CH_D0TSR, dbuf->dma_size);
+	chan_writel(chan, HSU_CH_DCR, 0x1
+					 | (0x1 << 8)
+					 | (0x1 << 16)
+					 | (0x1 << 24)	/* timeout bit, see HSU Errata 1 */
+					 );
+	tty_flip_buffer_push(tty);
+
+	chan_writel(chan, HSU_CH_CR, 0x3);
+	chan->rx_timer.expires = jiffies + HSU_DMA_TIMEOUT_CHECK_FREQ;
+	add_timer(&chan->rx_timer);
+
+}
+
+static void serial_hsu_stop_rx(struct uart_port *port)
+{
+	struct uart_hsu_port *up =
+		container_of(port, struct uart_hsu_port, port);
+	struct hsu_dma_chan *chan = up->rxc;
+
+	if (up->use_dma)
+		chan_writel(chan, HSU_CH_CR, 0x2);
+	else {
+		up->ier &= ~UART_IER_RLSI;
+		up->port.read_status_mask &= ~UART_LSR_DR;
+		serial_out(up, UART_IER, up->ier);
+	}
+}
+
+static inline void receive_chars(struct uart_hsu_port *up, int *status)
+{
+	struct tty_struct *tty = up->port.state->port.tty;
+	unsigned int ch, flag;
+	unsigned int max_count = 256;
+
+	if (!tty)
+		return;
+
+	do {
+		ch = serial_in(up, UART_RX);
+		flag = TTY_NORMAL;
+		up->port.icount.rx++;
+
+		if (unlikely(*status & (UART_LSR_BI | UART_LSR_PE |
+				       UART_LSR_FE | UART_LSR_OE))) {
+
+			dev_warn(up->dev, "We really rush into ERR/BI case"
+				"status = 0x%02x", *status);
+			/* 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 ignored. */
+			*status &= up->port.read_status_mask;
+
+#ifdef CONFIG_SERIAL_MFD_HSU_CONSOLE
+			if (up->port.cons &&
+				up->port.cons->index == up->port.line) {
+				/* Recover the break flag from console xmit */
+				*status |= up->lsr_break_flag;
+				up->lsr_break_flag = 0;
+			}
+#endif
+			if (*status & UART_LSR_BI) {
+				flag = TTY_BREAK;
+			} else if (*status & UART_LSR_PE)
+				flag = TTY_PARITY;
+			else if (*status & UART_LSR_FE)
+				flag = TTY_FRAME;
+		}
+
+		if (uart_handle_sysrq_char(&up->port, ch))
+			goto ignore_char;
+
+		uart_insert_char(&up->port, *status, UART_LSR_OE, ch, flag);
+	ignore_char:
+		*status = serial_in(up, UART_LSR);
+	} while ((*status & UART_LSR_DR) && max_count--);
+	tty_flip_buffer_push(tty);
+}
+
+static void transmit_chars(struct uart_hsu_port *up)
+{
+	struct circ_buf *xmit = &up->port.state->xmit;
+	int count;
+
+	if (up->port.x_char) {
+		serial_out(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)) {
+		serial_hsu_stop_tx(&up->port);
+		return;
+	}
+
+#ifndef MFD_HSU_A0_STEPPING
+	count = up->port.fifosize / 2;
+#else
+	/*
+	 * A0 only supports fully empty IRQ, and the first char written
+	 * into it won't clear the EMPT bit, so we may need be cautious
+	 * by useing a shorter buffer
+	 */
+	count = up->port.fifosize - 4;
+#endif
+	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);
+
+	if (uart_circ_empty(xmit))
+		serial_hsu_stop_tx(&up->port);
+}
+
+static inline void check_modem_status(struct uart_hsu_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++;
+	/* We may only get DDCD when HW init and reset */
+	if (status & UART_MSR_DDCD)
+		uart_handle_dcd_change(&up->port, status & UART_MSR_DCD);
+	/* Will start/stop_tx accordingly */
+	if (status & UART_MSR_DCTS)
+		uart_handle_cts_change(&up->port, status & UART_MSR_CTS);
+
+	wake_up_interruptible(&up->port.state->port.delta_msr_wait);
+}
+
+/*
+ * This handles the interrupt from one port.
+ */
+static irqreturn_t port_irq(int irq, void *dev_id)
+{
+	struct uart_hsu_port *up = dev_id;
+	unsigned int iir, lsr;
+	unsigned long flags;
+
+	if (unlikely(!up->running))
+		return IRQ_NONE;
+
+	spin_lock_irqsave(&up->port.lock, flags);
+	if (up->use_dma) {
+		lsr = serial_in(up, UART_LSR);
+		if (unlikely(lsr & (UART_LSR_BI | UART_LSR_PE |
+				       UART_LSR_FE | UART_LSR_OE)))
+			dev_warn(up->dev,
+				"Got lsr irq while using DMA, lsr = 0x%2x\n",
+				lsr);
+		check_modem_status(up);
+		spin_unlock_irqrestore(&up->port.lock, flags);
+		return IRQ_HANDLED;
+	}
+
+	iir = serial_in(up, UART_IIR);
+	if (iir & UART_IIR_NO_INT) {
+		spin_unlock_irqrestore(&up->port.lock, flags);
+		return IRQ_NONE;
+	}
+
+	lsr = serial_in(up, UART_LSR);
+	if (lsr & UART_LSR_DR)
+		receive_chars(up, &lsr);
+	check_modem_status(up);
+
+	/* lsr will be renewed during the receive_chars */
+	if (lsr & UART_LSR_THRE)
+		transmit_chars(up);
+
+	spin_unlock_irqrestore(&up->port.lock, flags);
+	return IRQ_HANDLED;
+}
+
+static inline void dma_chan_irq(struct hsu_dma_chan *chan)
+{
+	struct uart_hsu_port *up = chan->uport;
+	unsigned long flags;
+	u32 int_sts;
+
+	spin_lock_irqsave(&up->port.lock, flags);
+
+	if (!up->use_dma || !up->running)
+		goto exit;
+
+	/*
+	 * No matter what situation, need read clear the IRQ status
+	 * There is a bug, see Errata 5, HSD 2900918
+	 */
+	int_sts = chan_readl(chan, HSU_CH_SR);
+
+	/* Rx channel */
+	if (chan->dirt == DMA_FROM_DEVICE)
+		hsu_dma_rx(up, int_sts);
+
+	/* Tx channel */
+	if (chan->dirt == DMA_TO_DEVICE) {
+		chan_writel(chan, HSU_CH_CR, 0x0);
+		up->dma_tx_on = 0;
+		hsu_dma_tx(up);
+	}
+
+exit:
+	spin_unlock_irqrestore(&up->port.lock, flags);
+	return;
+}
+
+static irqreturn_t dma_irq(int irq, void *dev_id)
+{
+	struct hsu_port *hsu = dev_id;
+	u32 int_sts, i;
+
+	int_sts = mfd_readl(hsu, HSU_GBL_DMAISR);
+
+	/* Currently we only have 6 channels may be used */
+	for (i = 0; i < 6; i++) {
+		if (int_sts & 0x1)
+			dma_chan_irq(&hsu->chans[i]);
+		int_sts >>= 1;
+	}
+
+	return IRQ_HANDLED;
+}
+
+static unsigned int serial_hsu_tx_empty(struct uart_port *port)
+{
+	struct uart_hsu_port *up =
+		container_of(port, struct uart_hsu_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;
+}
+
+static unsigned int serial_hsu_get_mctrl(struct uart_port *port)
+{
+	struct uart_hsu_port *up =
+		container_of(port, struct uart_hsu_port, port);
+	unsigned char status;
+	unsigned int ret;
+
+	status = serial_in(up, UART_MSR);
+
+	ret = 0;
+	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;
+	return ret;
+}
+
+static void serial_hsu_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+	struct uart_hsu_port *up =
+		container_of(port, struct uart_hsu_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 |= up->mcr;
+
+	serial_out(up, UART_MCR, mcr);
+}
+
+static void serial_hsu_break_ctl(struct uart_port *port, int break_state)
+{
+	struct uart_hsu_port *up =
+		container_of(port, struct uart_hsu_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);
+}
+
+/*
+ * What special to do:
+ * 1. chose the 64B fifo mode
+ * 2. make sure not to select half empty mode for A0 stepping
+ * 3. start dma or pio depends on configuration
+ * 4. we only allocate dma memory when needed
+ */
+static int serial_hsu_startup(struct uart_port *port)
+{
+	struct uart_hsu_port *up =
+		container_of(port, struct uart_hsu_port, port);
+	unsigned long flags;
+
+	/*
+	 * Clear the FIFO buffers and disable them.
+	 * (they will be reenabled in set_termios())
+	 */
+	serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO);
+	serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO |
+			UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
+	serial_out(up, UART_FCR, 0);
+
+	/* Clear the interrupt registers. */
+	(void) serial_in(up, UART_LSR);
+	(void) serial_in(up, UART_RX);
+	(void) serial_in(up, UART_IIR);
+	(void) serial_in(up, UART_MSR);
+
+	/* Now, initialize the UART, default is 8n1 */
+	serial_out(up, UART_LCR, UART_LCR_WLEN8);
+
+	spin_lock_irqsave(&up->port.lock, flags);
+
+	up->port.mctrl |= TIOCM_OUT2;
+	serial_hsu_set_mctrl(&up->port, up->port.mctrl);
+
+	/*
+	 * 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.
+	 */
+	if (!up->use_dma)
+		up->ier = UART_IER_RLSI | UART_IER_RDI | UART_IER_RTOIE;
+	else
+		up->ier = 0;
+	serial_out(up, UART_IER, up->ier);
+
+	spin_unlock_irqrestore(&up->port.lock, flags);
+
+	/* DMA init */
+	if (up->use_dma) {
+		struct hsu_dma_buffer *dbuf;
+		struct circ_buf *xmit = &port->state->xmit;
+
+		up->dma_tx_on = 0;
+
+		/* First allocate the RX buffer */
+		dbuf = &up->rxbuf;
+		dbuf->buf = kzalloc(HSU_DMA_BUF_SIZE, GFP_KERNEL);
+		if (!dbuf->buf) {
+			up->use_dma = 0;
+			goto exit;
+		}
+		dbuf->dma_addr = dma_map_single(port->dev,
+						dbuf->buf,
+						HSU_DMA_BUF_SIZE,
+						DMA_FROM_DEVICE);
+		dbuf->dma_size = HSU_DMA_BUF_SIZE;
+
+		/* Start the RX channel right now */
+		hsu_dma_start_rx_chan(up->rxc, dbuf);
+
+		/* Next init the TX DMA */
+		dbuf = &up->txbuf;
+		dbuf->buf = xmit->buf;
+		dbuf->dma_addr = dma_map_single(port->dev,
+					       dbuf->buf,
+					       UART_XMIT_SIZE,
+					       DMA_TO_DEVICE);
+		dbuf->dma_size = UART_XMIT_SIZE;
+
+		/* This should not be changed all around */
+		chan_writel(up->txc, HSU_CH_BSR, 32);
+		chan_writel(up->txc, HSU_CH_MOTSR, 4);
+		dbuf->ofs = 0;
+	}
+
+exit:
+	 /* And clear the interrupt registers again for luck. */
+	(void) serial_in(up, UART_LSR);
+	(void) serial_in(up, UART_RX);
+	(void) serial_in(up, UART_IIR);
+	(void) serial_in(up, UART_MSR);
+
+	up->running = 1;
+	return 0;
+}
+
+static void serial_hsu_shutdown(struct uart_port *port)
+{
+	struct uart_hsu_port *up =
+		container_of(port, struct uart_hsu_port, port);
+	unsigned long flags;
+
+	del_timer_sync(&up->rxc->rx_timer);
+
+	/* Disable interrupts from this port */
+	up->ier = 0;
+	serial_out(up, UART_IER, 0);
+	up->running = 0;
+
+	spin_lock_irqsave(&up->port.lock, flags);
+	up->port.mctrl &= ~TIOCM_OUT2;
+	serial_hsu_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_in(up, UART_LCR) & ~UART_LCR_SBC);
+	serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO |
+				  UART_FCR_CLEAR_RCVR |
+				  UART_FCR_CLEAR_XMIT);
+	serial_out(up, UART_FCR, 0);
+}
+
+static void
+serial_hsu_set_termios(struct uart_port *port, struct ktermios *termios,
+		       struct ktermios *old)
+{
+	struct uart_hsu_port *up =
+			container_of(port, struct uart_hsu_port, port);
+	struct tty_struct *tty = port->state->port.tty;
+	unsigned char cval, fcr = 0;
+	unsigned long flags;
+	unsigned int baud, quot;
+	u32 mul = 0x3600;
+	u32 ps = 0x10;
+
+	switch (termios->c_cflag & CSIZE) {
+	case CS5:
+		cval = UART_LCR_WLEN5;
+		break;
+	case CS6:
+		cval = UART_LCR_WLEN6;
+		break;
+	case CS7:
+		cval = UART_LCR_WLEN7;
+		break;
+	default:
+	case CS8:
+		cval = UART_LCR_WLEN8;
+		break;
+	}
+
+	/* CMSPAR isn't supported by this driver */
+	if (tty)
+		tty->termios->c_cflag &= ~CMSPAR;
+
+	if (termios->c_cflag & CSTOPB)
+		cval |= UART_LCR_STOP;
+	if (termios->c_cflag & PARENB)
+		cval |= UART_LCR_PARITY;
+	if (!(termios->c_cflag & PARODD))
+		cval |= UART_LCR_EPAR;
+
+	/*
+	 * For those basic low baud rate we can get the direct
+	 * scalar from 2746800, like 115200 = 2746800/24, for those
+	 * higher baud rate, we have to handle them case by case,
+	 * but DIV reg is never touched as its default value 0x3d09
+	 */
+	baud = uart_get_baud_rate(port, termios, old, 0, 4000000);
+	quot = uart_get_divisor(port, baud);
+
+	switch (baud) {
+	case 3500000:
+		mul = 0x3345;
+		ps = 0xC;
+		quot = 1;
+		break;
+	case 2500000:
+		mul = 0x2710;
+		ps = 0x10;
+		quot = 1;
+		break;
+	case 18432000:
+		mul = 0x2400;
+		ps = 0x10;
+		quot = 1;
+		break;
+	case 1500000:
+		mul = 0x1D4C;
+		ps = 0xc;
+		quot = 1;
+		break;
+	default:
+		;
+	}
+
+	if ((up->port.uartclk / quot) < (2400 * 16))
+		fcr = UART_FCR_ENABLE_FIFO | UART_FCR_HSU_64_1B;
+	else if ((up->port.uartclk / quot) < (230400 * 16))
+		fcr = UART_FCR_ENABLE_FIFO | UART_FCR_HSU_64_16B;
+	else
+		fcr = UART_FCR_ENABLE_FIFO | UART_FCR_HSU_64_32B;
+
+	fcr |= UART_FCR_HSU_64B_FIFO;
+#ifdef MFD_HSU_A0_STEPPING
+	/* A0 doesn't support half empty IRQ */
+	fcr |= UART_FCR_FULL_EMPT_TXI;
+#endif
+
+	/*
+	 * 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;
+
+	/* Characters 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, disable
+	 * MSI by default
+	 */
+	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 (termios->c_cflag & CRTSCTS)
+		up->mcr |= UART_MCR_AFE | UART_MCR_RTS;
+	else
+		up->mcr &= ~UART_MCR_AFE;
+
+	serial_out(up, UART_LCR, cval | UART_LCR_DLAB);	/* set DLAB */
+	serial_out(up, UART_DLL, quot & 0xff);		/* LS of divisor */
+	serial_out(up, UART_DLM, quot >> 8);		/* MS of divisor */
+	serial_out(up, UART_LCR, cval);			/* reset DLAB */
+	serial_out(up, UART_MUL, mul);			/* set MUL */
+	serial_out(up, UART_PS, ps);			/* set PS */
+	up->lcr = cval;					/* Save LCR */
+	serial_hsu_set_mctrl(&up->port, up->port.mctrl);
+	serial_out(up, UART_FCR, fcr);
+	spin_unlock_irqrestore(&up->port.lock, flags);
+}
+
+static void
+serial_hsu_pm(struct uart_port *port, unsigned int state,
+	      unsigned int oldstate)
+{
+}
+
+static void serial_hsu_release_port(struct uart_port *port)
+{
+}
+
+static int serial_hsu_request_port(struct uart_port *port)
+{
+	return 0;
+}
+
+static void serial_hsu_config_port(struct uart_port *port, int flags)
+{
+	struct uart_hsu_port *up =
+		container_of(port, struct uart_hsu_port, port);
+	up->port.type = PORT_MFD;
+}
+
+static int
+serial_hsu_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+	/* We don't want the core code to modify any port params */
+	return -EINVAL;
+}
+
+static const char *
+serial_hsu_type(struct uart_port *port)
+{
+	struct uart_hsu_port *up =
+		container_of(port, struct uart_hsu_port, port);
+	return up->name;
+}
+
+/* Mainly for uart console use */
+static struct uart_hsu_port *serial_hsu_ports[3];
+static struct uart_driver serial_hsu_reg;
+
+#ifdef CONFIG_SERIAL_MFD_HSU_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_hsu_port *up)
+{
+	unsigned int status, tmout = 1000;
+
+	/* Wait up to 1ms for the character 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));
+
+	/* 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);
+	}
+}
+
+static void serial_hsu_console_putchar(struct uart_port *port, int ch)
+{
+	struct uart_hsu_port *up =
+		container_of(port, struct uart_hsu_port, port);
+
+	wait_for_xmitr(up);
+	serial_out(up, UART_TX, ch);
+}
+
+/*
+ * 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
+serial_hsu_console_write(struct console *co, const char *s, unsigned int count)
+{
+	struct uart_hsu_port *up = serial_hsu_ports[co->index];
+	unsigned long flags;
+	unsigned int ier;
+	int locked = 1;
+
+	local_irq_save(flags);
+	if (up->port.sysrq)
+		locked = 0;
+	else if (oops_in_progress) {
+		locked = spin_trylock(&up->port.lock);
+	} else
+		spin_lock(&up->port.lock);
+
+	/* First save the IER then disable the interrupts */
+	ier = serial_in(up, UART_IER);
+	serial_out(up, UART_IER, 0);
+
+	uart_console_write(&up->port, s, count, serial_hsu_console_putchar);
+
+	/*
+	 * Finally, wait for transmitter to become empty
+	 * and restore the IER
+	 */
+	wait_for_xmitr(up);
+	serial_out(up, UART_IER, ier);
+
+	if (locked)
+		spin_unlock(&up->port.lock);
+	local_irq_restore(flags);
+}
+
+static struct console serial_hsu_console;
+
+static int __init
+serial_hsu_console_setup(struct console *co, char *options)
+{
+	struct uart_hsu_port *up;
+	int baud = 115200;
+	int bits = 8;
+	int parity = 'n';
+	int flow = 'n';
+	int ret;
+
+	if (co->index == -1 || co->index >= serial_hsu_reg.nr)
+		co->index = 0;
+	up = serial_hsu_ports[co->index];
+	if (!up)
+		return -ENODEV;
+
+	if (options)
+		uart_parse_options(options, &baud, &parity, &bits, &flow);
+
+	ret = uart_set_options(&up->port, co, baud, parity, bits, flow);
+
+	return ret;
+}
+
+static struct console serial_hsu_console = {
+	.name		= "ttyMFD",
+	.write		= serial_hsu_console_write,
+	.device		= uart_console_device,
+	.setup		= serial_hsu_console_setup,
+	.flags		= CON_PRINTBUFFER,
+	.index		= 2,
+	.data		= &serial_hsu_reg,
+};
+#endif
+
+struct uart_ops serial_hsu_pops = {
+	.tx_empty	= serial_hsu_tx_empty,
+	.set_mctrl	= serial_hsu_set_mctrl,
+	.get_mctrl	= serial_hsu_get_mctrl,
+	.stop_tx	= serial_hsu_stop_tx,
+	.start_tx	= serial_hsu_start_tx,
+	.stop_rx	= serial_hsu_stop_rx,
+	.enable_ms	= serial_hsu_enable_ms,
+	.break_ctl	= serial_hsu_break_ctl,
+	.startup	= serial_hsu_startup,
+	.shutdown	= serial_hsu_shutdown,
+	.set_termios	= serial_hsu_set_termios,
+	.pm		= serial_hsu_pm,
+	.type		= serial_hsu_type,
+	.release_port	= serial_hsu_release_port,
+	.request_port	= serial_hsu_request_port,
+	.config_port	= serial_hsu_config_port,
+	.verify_port	= serial_hsu_verify_port,
+};
+
+static struct uart_driver serial_hsu_reg = {
+	.owner		= THIS_MODULE,
+	.driver_name	= "MFD serial",
+	.dev_name	= "ttyMFD",
+	.major		= TTY_MAJOR,
+	.minor		= 128,
+	.nr		= 3,
+};
+
+#ifdef CONFIG_PM
+static int serial_hsu_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+	void *priv = pci_get_drvdata(pdev);
+	struct uart_hsu_port *up;
+
+	/* Make sure this is not the internal dma controller */
+	if (priv && (pdev->device != 0x081E)) {
+		up = priv;
+		uart_suspend_port(&serial_hsu_reg, &up->port);
+	}
+
+	pci_save_state(pdev);
+	pci_set_power_state(pdev, pci_choose_state(pdev, state));
+        return 0;
+}
+
+static int serial_hsu_resume(struct pci_dev *pdev)
+{
+	void *priv = pci_get_drvdata(pdev);
+	struct uart_hsu_port *up;
+	int ret;
+
+	pci_set_power_state(pdev, PCI_D0);
+	pci_restore_state(pdev);
+
+	ret = pci_enable_device(pdev);
+	if (ret)
+		dev_warn(&pdev->dev,
+			"HSU: can't re-enable device, try to continue\n");
+
+	if (priv && (pdev->device != 0x081E)) {
+		up = priv;
+		uart_resume_port(&serial_hsu_reg, &up->port);
+	}
+	return 0;
+}
+#else
+#define serial_hsu_suspend	NULL
+#define serial_hsu_resume	NULL
+#endif
+
+/* temp global pointer before we settle down on using one or four PCI dev */
+static struct hsu_port *phsu;
+
+static int serial_hsu_probe(struct pci_dev *pdev,
+				const struct pci_device_id *ent)
+{
+	struct uart_hsu_port *uport;
+	int index, ret;
+
+	printk(KERN_INFO "HSU: found PCI Serial controller(ID: %04x:%04x)\n",
+		pdev->vendor, pdev->device);
+
+	switch (pdev->device) {
+	case 0x081B:
+		index = 0;
+		break;
+	case 0x081C:
+		index = 1;
+		break;
+	case 0x081D:
+		index = 2;
+		break;
+	case 0x081E:
+		/* internal DMA controller */
+		index = 3;
+		break;
+	default:
+		dev_err(&pdev->dev, "HSU: out of index!");
+		return -ENODEV;
+	}
+
+	ret = pci_enable_device(pdev);
+	if (ret)
+		return ret;
+
+	if (index == 3) {
+		/* DMA controller */
+		ret = request_irq(pdev->irq, dma_irq, 0, "hsu_dma", phsu);
+		if (ret) {
+			dev_err(&pdev->dev, "can not get IRQ\n");
+			goto err_disable;
+		}
+		pci_set_drvdata(pdev, phsu);
+	} else {
+		/* UART port 0~2 */
+		uport = &phsu->port[index];
+		uport->port.irq = pdev->irq;
+		uport->port.dev = &pdev->dev;
+		uport->dev = &pdev->dev;
+
+		ret = request_irq(pdev->irq, port_irq, 0, uport->name, uport);
+		if (ret) {
+			dev_err(&pdev->dev, "can not get IRQ\n");
+			goto err_disable;
+		}
+		uart_add_one_port(&serial_hsu_reg, &uport->port);
+
+#ifdef CONFIG_SERIAL_MFD_HSU_CONSOLE
+		if (index == 2) {
+			register_console(&serial_hsu_console);
+			uport->port.cons = &serial_hsu_console;
+		}
+#endif
+		pci_set_drvdata(pdev, uport);
+	}
+
+	return 0;
+
+err_disable:
+	pci_disable_device(pdev);
+	return ret;
+}
+
+static void hsu_dma_rx_timeout(unsigned long data)
+{
+	struct hsu_dma_chan *chan = (void *)data;
+	struct uart_hsu_port *up = chan->uport;
+	struct hsu_dma_buffer *dbuf = &up->rxbuf;
+	int count = 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(&up->port.lock, flags);
+
+	count = chan_readl(chan, HSU_CH_D0SAR) - dbuf->dma_addr;
+
+	if (!count) {
+		mod_timer(&chan->rx_timer, jiffies + HSU_DMA_TIMEOUT_CHECK_FREQ);
+		goto exit;
+	}
+
+	hsu_dma_rx(up, 0);
+exit:
+	spin_unlock_irqrestore(&up->port.lock, flags);
+}
+
+static void hsu_global_init(void)
+{
+	struct hsu_port *hsu;
+	struct uart_hsu_port *uport;
+	struct hsu_dma_chan *dchan;
+	int i, ret;
+
+	hsu = kzalloc(sizeof(struct hsu_port), GFP_KERNEL);
+	if (!hsu)
+		return;
+
+	/* Get basic io resource and map it */
+	hsu->paddr = 0xffa28000;
+	hsu->iolen = 0x1000;
+
+	if (!(request_mem_region(hsu->paddr, hsu->iolen, "HSU global")))
+		pr_warning("HSU: error in request mem region\n");
+
+	hsu->reg = ioremap_nocache((unsigned long)hsu->paddr, hsu->iolen);
+	if (!hsu->reg) {
+		pr_err("HSU: error in ioremap\n");
+		ret = -ENOMEM;
+		goto err_free_region;
+	}
+
+	/* Initialise the 3 UART ports */
+	uport = hsu->port;
+	for (i = 0; i < 3; i++) {
+		uport->port.type = PORT_MFD;
+		uport->port.iotype = UPIO_MEM;
+		uport->port.mapbase = (resource_size_t)hsu->paddr
+					+ HSU_PORT_REG_OFFSET
+					+ i * HSU_PORT_REG_LENGTH;
+		uport->port.membase = hsu->reg + HSU_PORT_REG_OFFSET
+					+ i * HSU_PORT_REG_LENGTH;
+
+		sprintf(uport->name, "hsu_port%d", i);
+		uport->port.fifosize = 64;
+		uport->port.ops = &serial_hsu_pops;
+		uport->port.line = i;
+		uport->port.flags = UPF_IOREMAP;
+		/* set the scalable maxim support rate to 2746800 bps */
+		uport->port.uartclk = 115200 * 24 * 16;
+
+		uport->running = 0;
+		uport->txc = &hsu->chans[i * 2];
+		uport->rxc = &hsu->chans[i * 2 + 1];
+
+		serial_hsu_ports[i] = uport;
+		uport->index = i;
+		uport++;
+	}
+
+	/* Initialise 6 dma channels */
+	dchan = hsu->chans;
+	for (i = 0; i < 6; i++) {
+		dchan->id = i;
+		dchan->dirt = (i & 0x1) ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
+		dchan->uport = &hsu->port[i/2];
+		dchan->reg = hsu->reg + HSU_DMA_CHANS_REG_OFFSET +
+				i * HSU_DMA_CHANS_REG_LENGTH;
+
+		/* Work around for RX */
+		if (dchan->dirt == DMA_FROM_DEVICE) {
+			init_timer(&dchan->rx_timer);
+			dchan->rx_timer.function = hsu_dma_rx_timeout;
+			dchan->rx_timer.data = (unsigned long)dchan;
+		}
+		dchan++;
+	}
+
+	phsu = hsu;
+
+	hsu_debugfs_init(hsu);
+	return;
+
+err_free_region:
+	release_mem_region(hsu->paddr, hsu->iolen);
+	kfree(hsu);
+	return;
+}
+
+static void serial_hsu_remove(struct pci_dev *pdev)
+{
+	struct hsu_port *hsu;
+	int i;
+
+	hsu = pci_get_drvdata(pdev);
+	if (!hsu)
+		return;
+
+	for (i = 0; i < 3; i++)
+		uart_remove_one_port(&serial_hsu_reg, &hsu->port[i].port);
+
+	pci_set_drvdata(pdev, NULL);
+	free_irq(hsu->irq, hsu);
+	pci_disable_device(pdev);
+}
+
+/* First 3 are UART ports, and the 4th is the DMA */
+static const struct pci_device_id pci_ids[] __devinitdata = {
+	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x081B) },
+	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x081C) },
+	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x081D) },
+	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x081E) },
+	{},
+};
+
+static struct pci_driver hsu_pci_driver = {
+	.name =		"HSU serial",
+	.id_table =	pci_ids,
+	.probe =	serial_hsu_probe,
+	.remove =	__devexit_p(serial_hsu_remove),
+	.suspend =	serial_hsu_suspend,
+	.resume	=	serial_hsu_resume,
+};
+
+static int __init hsu_pci_init(void)
+{
+	int ret;
+
+	hsu_global_init();
+
+	ret = uart_register_driver(&serial_hsu_reg);
+	if (ret)
+		return ret;
+
+	return pci_register_driver(&hsu_pci_driver);
+}
+
+static void __exit hsu_pci_exit(void)
+{
+	pci_unregister_driver(&hsu_pci_driver);
+	uart_unregister_driver(&serial_hsu_reg);
+
+	hsu_debugfs_remove(phsu);
+
+	kfree(phsu);
+}
+
+module_init(hsu_pci_init);
+module_exit(hsu_pci_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:medfield-hsu");

+ 844 - 0
drivers/serial/mrst_max3110.c

@@ -0,0 +1,844 @@
+/*
+ *  max3110.c - spi uart protocol driver for Maxim 3110 on Moorestown
+ *
+ *  Copyright (C) Intel 2008 Feng Tang <feng.tang@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/*
+ * Note:
+ * 1. From Max3110 spec, the Rx FIFO has 8 words, while the Tx FIFO only has
+ *    1 word. If SPI master controller doesn't support sclk frequency change,
+ *    then the char need be sent out one by one with some delay
+ *
+ * 2. Currently only RX availabe interrrupt is used, no need for waiting TXE
+ *    interrupt for a low speed UART device
+ */
+
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/console.h>
+#include <linux/sysrq.h>
+#include <linux/platform_device.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial_core.h>
+#include <linux/serial_reg.h>
+
+#include <linux/kthread.h>
+#include <linux/delay.h>
+#include <asm/atomic.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/dw_spi.h>
+
+#include "mrst_max3110.h"
+
+#define PR_FMT	"mrst_max3110: "
+
+#define UART_TX_NEEDED 1
+#define CON_TX_NEEDED  2
+#define BIT_IRQ_PENDING    3
+
+struct uart_max3110 {
+	struct uart_port port;
+	struct spi_device *spi;
+	char *name;
+
+	wait_queue_head_t wq;
+	struct task_struct *main_thread;
+	struct task_struct *read_thread;
+	struct mutex thread_mutex;;
+
+	u32 baud;
+	u16 cur_conf;
+	u8 clock;
+	u8 parity, word_7bits;
+
+	unsigned long uart_flags;
+
+	/* console related */
+	struct circ_buf con_xmit;
+
+	/* irq related */
+	u16 irq;
+};
+
+/* global data structure, may need be removed */
+struct uart_max3110 *pmax;
+static inline void receive_char(struct uart_max3110 *max, u8 ch);
+static void receive_chars(struct uart_max3110 *max,
+				unsigned char *str, int len);
+static int max3110_read_multi(struct uart_max3110 *max, int len, u8 *buf);
+static void max3110_console_receive(struct uart_max3110 *max);
+
+int max3110_write_then_read(struct uart_max3110 *max,
+		const u8 *txbuf, u8 *rxbuf, unsigned len, int always_fast)
+{
+	struct spi_device *spi = max->spi;
+	struct spi_message	message;
+	struct spi_transfer	x;
+	int ret;
+
+	if (!txbuf || !rxbuf)
+		return -EINVAL;
+
+	spi_message_init(&message);
+	memset(&x, 0, sizeof x);
+	x.len = len;
+	x.tx_buf = txbuf;
+	x.rx_buf = rxbuf;
+	spi_message_add_tail(&x, &message);
+
+	if (always_fast)
+		x.speed_hz = 3125000;
+	else if (max->baud)
+		x.speed_hz = max->baud;
+
+	/* Do the i/o */
+	ret = spi_sync(spi, &message);
+	return ret;
+}
+
+/* Write a u16 to the device, and return one u16 read back */
+int max3110_out(struct uart_max3110 *max, const u16 out)
+{
+	u16 tmp;
+	int ret;
+
+	ret = max3110_write_then_read(max, (u8 *)&out, (u8 *)&tmp, 2, 1);
+	if (ret)
+		return ret;
+
+	/* If some valid data is read back */
+	if (tmp & MAX3110_READ_DATA_AVAILABLE)
+		receive_char(max, (tmp & 0xff));
+
+	return ret;
+}
+
+#define MAX_READ_LEN	20
+/*
+ * This is usually used to read data from SPIC RX FIFO, which doesn't
+ * need any delay like flushing character out. It returns how many
+ * valide bytes are read back
+ */
+static int max3110_read_multi(struct uart_max3110 *max, int len, u8 *buf)
+{
+	u16 out[MAX_READ_LEN], in[MAX_READ_LEN];
+	u8 *pbuf, valid_str[MAX_READ_LEN];
+	int i, j, bytelen;
+
+	if (len > MAX_READ_LEN) {
+		pr_err(PR_FMT "read len %d is too large\n", len);
+		return 0;
+	}
+
+	bytelen = len * 2;
+	memset(out, 0, bytelen);
+	memset(in, 0, bytelen);
+
+	if (max3110_write_then_read(max, (u8 *)out, (u8 *)in, bytelen, 1))
+		return 0;
+
+	/* If caller don't provide a buffer, then handle received char */
+	pbuf = buf ? buf : valid_str;
+
+	for (i = 0, j = 0; i < len; i++) {
+		if (in[i] & MAX3110_READ_DATA_AVAILABLE)
+			pbuf[j++] = (u8)(in[i] & 0xff);
+	}
+
+	if (j && (pbuf == valid_str))
+		receive_chars(max, valid_str, j);
+
+	return j;
+}
+
+static void serial_m3110_con_putchar(struct uart_port *port, int ch)
+{
+	struct uart_max3110 *max =
+		container_of(port, struct uart_max3110, port);
+	struct circ_buf *xmit = &max->con_xmit;
+
+	if (uart_circ_chars_free(xmit)) {
+		xmit->buf[xmit->head] = (char)ch;
+		xmit->head = (xmit->head + 1) & (PAGE_SIZE - 1);
+	}
+
+
+	if (!test_and_set_bit(CON_TX_NEEDED, &max->uart_flags))
+		wake_up_process(max->main_thread);
+}
+
+/*
+ * 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 serial_m3110_con_write(struct console *co,
+				const char *s, unsigned int count)
+{
+	if (!pmax)
+		return;
+
+	uart_console_write(&pmax->port, s, count, serial_m3110_con_putchar);
+}
+
+static int __init
+serial_m3110_con_setup(struct console *co, char *options)
+{
+	struct uart_max3110 *max = pmax;
+	int baud = 115200;
+	int bits = 8;
+	int parity = 'n';
+	int flow = 'n';
+
+	pr_info(PR_FMT "setting up console\n");
+
+	if (!max) {
+		pr_err(PR_FMT "pmax is NULL, return");
+		return -ENODEV;
+	}
+
+	if (options)
+		uart_parse_options(options, &baud, &parity, &bits, &flow);
+
+	return uart_set_options(&max->port, co, baud, parity, bits, flow);
+}
+
+static struct tty_driver *serial_m3110_con_device(struct console *co,
+							int *index)
+{
+	struct uart_driver *p = co->data;
+	*index = co->index;
+	return p->tty_driver;
+}
+
+static struct uart_driver serial_m3110_reg;
+static struct console serial_m3110_console = {
+	.name		= "ttyS",
+	.write		= serial_m3110_con_write,
+	.device		= serial_m3110_con_device,
+	.setup		= serial_m3110_con_setup,
+	.flags		= CON_PRINTBUFFER,
+	.index		= -1,
+	.data		= &serial_m3110_reg,
+};
+
+#define MRST_CONSOLE	(&serial_m3110_console)
+
+static unsigned int serial_m3110_tx_empty(struct uart_port *port)
+{
+	return 1;
+}
+
+static void serial_m3110_stop_tx(struct uart_port *port)
+{
+	return;
+}
+
+/* stop_rx will be called in spin_lock env */
+static void serial_m3110_stop_rx(struct uart_port *port)
+{
+	return;
+}
+
+#define WORDS_PER_XFER	128
+static inline void send_circ_buf(struct uart_max3110 *max,
+				struct circ_buf *xmit)
+{
+	int len, left = 0;
+	u16 obuf[WORDS_PER_XFER], ibuf[WORDS_PER_XFER];
+	u8 valid_str[WORDS_PER_XFER];
+	int i, j;
+
+	while (!uart_circ_empty(xmit)) {
+		left = uart_circ_chars_pending(xmit);
+		while (left) {
+			len = (left >= WORDS_PER_XFER) ? WORDS_PER_XFER : left;
+
+			memset(obuf, 0, len * 2);
+			memset(ibuf, 0, len * 2);
+			for (i = 0; i < len; i++) {
+				obuf[i] = (u8)xmit->buf[xmit->tail] | WD_TAG;
+				xmit->tail = (xmit->tail + 1) &
+						(UART_XMIT_SIZE - 1);
+			}
+			max3110_write_then_read(max, (u8 *)obuf,
+						(u8 *)ibuf, len * 2, 0);
+
+			for (i = 0, j = 0; i < len; i++) {
+				if (ibuf[i] & MAX3110_READ_DATA_AVAILABLE)
+					valid_str[j++] = (u8)(ibuf[i] & 0xff);
+			}
+
+			if (j)
+				receive_chars(max, valid_str, j);
+
+			max->port.icount.tx += len;
+			left -= len;
+		}
+	}
+}
+
+static void transmit_char(struct uart_max3110 *max)
+{
+	struct uart_port *port = &max->port;
+	struct circ_buf *xmit = &port->state->xmit;
+
+	if (uart_circ_empty(xmit) || uart_tx_stopped(port))
+		return;
+
+	send_circ_buf(max, xmit);
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(port);
+
+	if (uart_circ_empty(xmit))
+		serial_m3110_stop_tx(port);
+}
+
+/* This will be called by uart_write() and tty_write, can't
+ * go to sleep */
+static void serial_m3110_start_tx(struct uart_port *port)
+{
+	struct uart_max3110 *max =
+		container_of(port, struct uart_max3110, port);
+
+	if (!test_and_set_bit(UART_TX_NEEDED, &max->uart_flags))
+		wake_up_process(max->main_thread);
+}
+
+static void receive_chars(struct uart_max3110 *max, unsigned char *str, int len)
+{
+	struct uart_port *port = &max->port;
+	struct tty_struct *tty;
+	int usable;
+
+	/* If uart is not opened, just return */
+	if (!port->state)
+		return;
+
+	tty = port->state->port.tty;
+	if (!tty)
+		return;	/* receive some char before the tty is opened */
+
+	while (len) {
+		usable = tty_buffer_request_room(tty, len);
+		if (usable) {
+			tty_insert_flip_string(tty, str, usable);
+			str += usable;
+			port->icount.rx += usable;
+			tty_flip_buffer_push(tty);
+		}
+		len -= usable;
+	}
+}
+
+static inline void receive_char(struct uart_max3110 *max, u8 ch)
+{
+	receive_chars(max, &ch, 1);
+}
+
+static void max3110_console_receive(struct uart_max3110 *max)
+{
+	int loop = 1, num, total = 0;
+	u8 recv_buf[512], *pbuf;
+
+	pbuf = recv_buf;
+	do {
+		num = max3110_read_multi(max, 8, pbuf);
+
+		if (num) {
+			loop = 10;
+			pbuf += num;
+			total += num;
+
+			if (total >= 500) {
+				receive_chars(max, recv_buf, total);
+				pbuf = recv_buf;
+				total = 0;
+			}
+		}
+	} while (--loop);
+
+	if (total)
+		receive_chars(max, recv_buf, total);
+}
+
+static int max3110_main_thread(void *_max)
+{
+	struct uart_max3110 *max = _max;
+	wait_queue_head_t *wq = &max->wq;
+	int ret = 0;
+	struct circ_buf *xmit = &max->con_xmit;
+
+	init_waitqueue_head(wq);
+	pr_info(PR_FMT "start main thread\n");
+
+	do {
+		wait_event_interruptible(*wq, max->uart_flags || kthread_should_stop());
+
+		mutex_lock(&max->thread_mutex);
+
+		if (test_and_clear_bit(BIT_IRQ_PENDING, &max->uart_flags))
+			max3110_console_receive(max);
+
+		/* first handle console output */
+		if (test_and_clear_bit(CON_TX_NEEDED, &max->uart_flags))
+			send_circ_buf(max, xmit);
+
+		/* handle uart output */
+		if (test_and_clear_bit(UART_TX_NEEDED, &max->uart_flags))
+			transmit_char(max);
+
+		mutex_unlock(&max->thread_mutex);
+
+	} while (!kthread_should_stop());
+
+	return ret;
+}
+
+#ifdef CONFIG_MRST_MAX3110_IRQ
+static irqreturn_t serial_m3110_irq(int irq, void *dev_id)
+{
+	struct uart_max3110 *max = dev_id;
+
+	/* max3110's irq is a falling edge, not level triggered,
+	 * so no need to disable the irq */
+	if (!test_and_set_bit(BIT_IRQ_PENDING, &max->uart_flags))
+		wake_up_process(max->main_thread);
+
+	return IRQ_HANDLED;
+}
+#else
+/* if don't use RX IRQ, then need a thread to polling read */
+static int max3110_read_thread(void *_max)
+{
+	struct uart_max3110 *max = _max;
+
+	pr_info(PR_FMT "start read thread\n");
+	do {
+		mutex_lock(&max->thread_mutex);
+		max3110_console_receive(max);
+		mutex_unlock(&max->thread_mutex);
+
+		set_current_state(TASK_INTERRUPTIBLE);
+		schedule_timeout(HZ / 20);
+	} while (!kthread_should_stop());
+
+	return 0;
+}
+#endif
+
+static int serial_m3110_startup(struct uart_port *port)
+{
+	struct uart_max3110 *max =
+		container_of(port, struct uart_max3110, port);
+	u16 config = 0;
+	int ret = 0;
+
+	if (port->line != 0)
+		pr_err(PR_FMT "uart port startup failed\n");
+
+	/* firstly disable all IRQ and config it to 115200, 8n1 */
+	config = WC_TAG | WC_FIFO_ENABLE
+			| WC_1_STOPBITS
+			| WC_8BIT_WORD
+			| WC_BAUD_DR2;
+	ret = max3110_out(max, config);
+
+	/* as we use thread to handle tx/rx, need set low latency */
+	port->state->port.tty->low_latency = 1;
+
+#ifdef CONFIG_MRST_MAX3110_IRQ
+	ret = request_irq(max->irq, serial_m3110_irq,
+				IRQ_TYPE_EDGE_FALLING, "max3110", max);
+	if (ret)
+		return ret;
+
+	/* enable RX IRQ only */
+	config |= WC_RXA_IRQ_ENABLE;
+	max3110_out(max, config);
+#else
+	/* if IRQ is disabled, start a read thread for input data */
+	max->read_thread =
+		kthread_run(max3110_read_thread, max, "max3110_read");
+#endif
+
+	max->cur_conf = config;
+	return 0;
+}
+
+static void serial_m3110_shutdown(struct uart_port *port)
+{
+	struct uart_max3110 *max =
+		container_of(port, struct uart_max3110, port);
+	u16 config;
+
+	if (max->read_thread) {
+		kthread_stop(max->read_thread);
+		max->read_thread = NULL;
+	}
+
+#ifdef CONFIG_MRST_MAX3110_IRQ
+	free_irq(max->irq, max);
+#endif
+
+	/* Disable interrupts from this port */
+	config = WC_TAG | WC_SW_SHDI;
+	max3110_out(max, config);
+}
+
+static void serial_m3110_release_port(struct uart_port *port)
+{
+}
+
+static int serial_m3110_request_port(struct uart_port *port)
+{
+	return 0;
+}
+
+static void serial_m3110_config_port(struct uart_port *port, int flags)
+{
+	/* give it fake type */
+	port->type = PORT_PXA;
+}
+
+static int
+serial_m3110_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+	/* we don't want the core code to modify any port params */
+	return -EINVAL;
+}
+
+
+static const char *serial_m3110_type(struct uart_port *port)
+{
+	struct uart_max3110 *max =
+		container_of(port, struct uart_max3110, port);
+	return max->name;
+}
+
+static void
+serial_m3110_set_termios(struct uart_port *port, struct ktermios *termios,
+		       struct ktermios *old)
+{
+	struct uart_max3110 *max =
+		container_of(port, struct uart_max3110, port);
+	unsigned char cval;
+	unsigned int baud, parity = 0;
+	int clk_div = -1;
+	u16 new_conf = max->cur_conf;
+
+	switch (termios->c_cflag & CSIZE) {
+	case CS7:
+		cval = UART_LCR_WLEN7;
+		new_conf |= WC_7BIT_WORD;
+		break;
+	default:
+	case CS8:
+		cval = UART_LCR_WLEN8;
+		new_conf |= WC_8BIT_WORD;
+		break;
+	}
+
+	baud = uart_get_baud_rate(port, termios, old, 0, 230400);
+
+	/* first calc the div for 1.8MHZ clock case */
+	switch (baud) {
+	case 300:
+		clk_div = WC_BAUD_DR384;
+		break;
+	case 600:
+		clk_div = WC_BAUD_DR192;
+		break;
+	case 1200:
+		clk_div = WC_BAUD_DR96;
+		break;
+	case 2400:
+		clk_div = WC_BAUD_DR48;
+		break;
+	case 4800:
+		clk_div = WC_BAUD_DR24;
+		break;
+	case 9600:
+		clk_div = WC_BAUD_DR12;
+		break;
+	case 19200:
+		clk_div = WC_BAUD_DR6;
+		break;
+	case 38400:
+		clk_div = WC_BAUD_DR3;
+		break;
+	case 57600:
+		clk_div = WC_BAUD_DR2;
+		break;
+	case 115200:
+		clk_div = WC_BAUD_DR1;
+		break;
+	case 230400:
+		if (max->clock & MAX3110_HIGH_CLK)
+			break;
+	default:
+		/* pick the previous baud rate */
+		baud = max->baud;
+		clk_div = max->cur_conf & WC_BAUD_DIV_MASK;
+		tty_termios_encode_baud_rate(termios, baud, baud);
+	}
+
+	if (max->clock & MAX3110_HIGH_CLK) {
+		clk_div += 1;
+		/* high clk version max3110 doesn't support B300 */
+		if (baud == 300)
+			baud = 600;
+		if (baud == 230400)
+			clk_div = WC_BAUD_DR1;
+		tty_termios_encode_baud_rate(termios, baud, baud);
+	}
+
+	new_conf = (new_conf & ~WC_BAUD_DIV_MASK) | clk_div;
+	if (termios->c_cflag & CSTOPB)
+		new_conf |= WC_2_STOPBITS;
+	else
+		new_conf &= ~WC_2_STOPBITS;
+
+	if (termios->c_cflag & PARENB) {
+		new_conf |= WC_PARITY_ENABLE;
+		parity |= UART_LCR_PARITY;
+	} else
+		new_conf &= ~WC_PARITY_ENABLE;
+
+	if (!(termios->c_cflag & PARODD))
+		parity |= UART_LCR_EPAR;
+	max->parity = parity;
+
+	uart_update_timeout(port, termios->c_cflag, baud);
+
+	new_conf |= WC_TAG;
+	if (new_conf != max->cur_conf) {
+		max3110_out(max, new_conf);
+		max->cur_conf = new_conf;
+		max->baud = baud;
+	}
+}
+
+/* don't handle hw handshaking */
+static unsigned int serial_m3110_get_mctrl(struct uart_port *port)
+{
+	return TIOCM_DSR | TIOCM_CAR | TIOCM_DSR;
+}
+
+static void serial_m3110_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+}
+
+static void serial_m3110_break_ctl(struct uart_port *port, int break_state)
+{
+}
+
+static void serial_m3110_pm(struct uart_port *port, unsigned int state,
+			unsigned int oldstate)
+{
+}
+
+static void serial_m3110_enable_ms(struct uart_port *port)
+{
+}
+
+struct uart_ops serial_m3110_ops = {
+	.tx_empty	= serial_m3110_tx_empty,
+	.set_mctrl	= serial_m3110_set_mctrl,
+	.get_mctrl	= serial_m3110_get_mctrl,
+	.stop_tx	= serial_m3110_stop_tx,
+	.start_tx	= serial_m3110_start_tx,
+	.stop_rx	= serial_m3110_stop_rx,
+	.enable_ms	= serial_m3110_enable_ms,
+	.break_ctl	= serial_m3110_break_ctl,
+	.startup	= serial_m3110_startup,
+	.shutdown	= serial_m3110_shutdown,
+	.set_termios	= serial_m3110_set_termios,	/* must have */
+	.pm		= serial_m3110_pm,
+	.type		= serial_m3110_type,
+	.release_port	= serial_m3110_release_port,
+	.request_port	= serial_m3110_request_port,
+	.config_port	= serial_m3110_config_port,
+	.verify_port	= serial_m3110_verify_port,
+};
+
+static struct uart_driver serial_m3110_reg = {
+	.owner		= THIS_MODULE,
+	.driver_name	= "MRST serial",
+	.dev_name	= "ttyS",
+	.major		= TTY_MAJOR,
+	.minor		= 64,
+	.nr		= 1,
+	.cons		= MRST_CONSOLE,
+};
+
+static int serial_m3110_suspend(struct spi_device *spi, pm_message_t state)
+{
+	return 0;
+}
+
+static int serial_m3110_resume(struct spi_device *spi)
+{
+	return 0;
+}
+
+static struct dw_spi_chip spi0_uart = {
+	.poll_mode = 1,
+	.enable_dma = 0,
+	.type = SPI_FRF_SPI,
+};
+
+static int serial_m3110_probe(struct spi_device *spi)
+{
+	struct uart_max3110 *max;
+	int ret;
+	unsigned char *buffer;
+	u16 res;
+	max = kzalloc(sizeof(*max), GFP_KERNEL);
+	if (!max)
+		return -ENOMEM;
+
+	/* set spi info */
+	spi->mode = SPI_MODE_0;
+	spi->bits_per_word = 16;
+	max->clock = MAX3110_HIGH_CLK;
+	spi->controller_data = &spi0_uart;
+
+	spi_setup(spi);
+
+	max->port.type = PORT_PXA;	/* need apply for a max3110 type */
+	max->port.fifosize = 2;		/* only have 16b buffer */
+	max->port.ops = &serial_m3110_ops;
+	max->port.line = 0;
+	max->port.dev = &spi->dev;
+	max->port.uartclk = 115200;
+
+	max->spi = spi;
+	max->name = spi->modalias;	/* use spi name as the name */
+	max->irq = (u16)spi->irq;
+
+	mutex_init(&max->thread_mutex);
+
+	max->word_7bits = 0;
+	max->parity = 0;
+	max->baud = 0;
+
+	max->cur_conf = 0;
+	max->uart_flags = 0;
+
+	/* Check if reading configuration register returns something sane */
+
+	res = RC_TAG;
+	ret = max3110_write_then_read(max, (u8 *)&res, (u8 *)&res, 2, 0);
+	if (ret < 0 || res == 0 || res == 0xffff) {
+		printk(KERN_ERR "MAX3111 deemed not present (conf reg %04x)",
+									res);
+		ret = -ENODEV;
+		goto err_get_page;
+	}
+	buffer = (unsigned char *)__get_free_page(GFP_KERNEL);
+	if (!buffer) {
+		ret = -ENOMEM;
+		goto err_get_page;
+	}
+	max->con_xmit.buf = (unsigned char *)buffer;
+	max->con_xmit.head = max->con_xmit.tail = 0;
+
+	max->main_thread = kthread_run(max3110_main_thread,
+					max, "max3110_main");
+	if (IS_ERR(max->main_thread)) {
+		ret = PTR_ERR(max->main_thread);
+		goto err_kthread;
+	}
+
+	pmax = max;
+	/* give membase a psudo value to pass serial_core's check */
+	max->port.membase = (void *)0xff110000;
+	uart_add_one_port(&serial_m3110_reg, &max->port);
+
+	return 0;
+
+err_kthread:
+	free_page((unsigned long)buffer);
+err_get_page:
+	pmax = NULL;
+	kfree(max);
+	return ret;
+}
+
+static int max3110_remove(struct spi_device *dev)
+{
+	struct uart_max3110 *max = pmax;
+
+	if (!pmax)
+		return 0;
+
+	pmax = NULL;
+	uart_remove_one_port(&serial_m3110_reg, &max->port);
+
+	free_page((unsigned long)max->con_xmit.buf);
+
+	if (max->main_thread)
+		kthread_stop(max->main_thread);
+
+	kfree(max);
+	return 0;
+}
+
+static struct spi_driver uart_max3110_driver = {
+	.driver = {
+			.name	= "spi_max3111",
+			.bus	= &spi_bus_type,
+			.owner	= THIS_MODULE,
+	},
+	.probe		= serial_m3110_probe,
+	.remove		= __devexit_p(max3110_remove),
+	.suspend	= serial_m3110_suspend,
+	.resume		= serial_m3110_resume,
+};
+
+
+int __init serial_m3110_init(void)
+{
+	int ret = 0;
+
+	ret = uart_register_driver(&serial_m3110_reg);
+	if (ret)
+		return ret;
+
+	ret = spi_register_driver(&uart_max3110_driver);
+	if (ret)
+		uart_unregister_driver(&serial_m3110_reg);
+
+	return ret;
+}
+
+void __exit serial_m3110_exit(void)
+{
+	spi_unregister_driver(&uart_max3110_driver);
+	uart_unregister_driver(&serial_m3110_reg);
+}
+
+module_init(serial_m3110_init);
+module_exit(serial_m3110_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("max3110-uart");

+ 59 - 0
drivers/serial/mrst_max3110.h

@@ -0,0 +1,59 @@
+#ifndef _MRST_MAX3110_H
+#define _MRST_MAX3110_H
+
+#define MAX3110_HIGH_CLK	0x1	/* 3.6864 MHZ */
+#define MAX3110_LOW_CLK		0x0	/* 1.8432 MHZ */
+
+/* status bits for all 4 MAX3110 operate modes */
+#define MAX3110_READ_DATA_AVAILABLE	(1 << 15)
+#define MAX3110_WRITE_BUF_EMPTY		(1 << 14)
+
+#define WC_TAG			(3 << 14)
+#define RC_TAG			(1 << 14)
+#define WD_TAG			(2 << 14)
+#define RD_TAG			(0 << 14)
+
+/* bits def for write configuration */
+#define WC_FIFO_ENABLE_MASK	(1 << 13)
+#define WC_FIFO_ENABLE		(0 << 13)
+
+#define WC_SW_SHDI		(1 << 12)
+
+#define WC_IRQ_MASK		(0xF << 8)
+#define WC_TXE_IRQ_ENABLE	(1 << 11)	/* TX empty irq */
+#define WC_RXA_IRQ_ENABLE	(1 << 10)	/* RX availabe irq */
+#define WC_PAR_HIGH_IRQ_ENABLE	(1 << 9)
+#define WC_REC_ACT_IRQ_ENABLE	(1 << 8)
+
+#define WC_IRDA_ENABLE		(1 << 7)
+
+#define WC_STOPBITS_MASK	(1 << 6)
+#define WC_2_STOPBITS		(1 << 6)
+#define WC_1_STOPBITS		(0 << 6)
+
+#define WC_PARITY_ENABLE_MASK	(1 << 5)
+#define WC_PARITY_ENABLE	(1 << 5)
+
+#define WC_WORDLEN_MASK		(1 << 4)
+#define WC_7BIT_WORD		(1 << 4)
+#define WC_8BIT_WORD		(0 << 4)
+
+#define WC_BAUD_DIV_MASK	(0xF)
+#define WC_BAUD_DR1		(0x0)
+#define WC_BAUD_DR2		(0x1)
+#define WC_BAUD_DR4		(0x2)
+#define WC_BAUD_DR8		(0x3)
+#define WC_BAUD_DR16		(0x4)
+#define WC_BAUD_DR32		(0x5)
+#define WC_BAUD_DR64		(0x6)
+#define WC_BAUD_DR128		(0x7)
+#define WC_BAUD_DR3		(0x8)
+#define WC_BAUD_DR6		(0x9)
+#define WC_BAUD_DR12		(0xA)
+#define WC_BAUD_DR24		(0xB)
+#define WC_BAUD_DR48		(0xC)
+#define WC_BAUD_DR96		(0xD)
+#define WC_BAUD_DR192		(0xE)
+#define WC_BAUD_DR384		(0xF)
+
+#endif

+ 136 - 152
drivers/serial/serial_core.c

@@ -58,9 +58,9 @@ static struct lock_class_key port_lock_key;
 #define uart_console(port)	(0)
 #define uart_console(port)	(0)
 #endif
 #endif
 
 
-static void uart_change_speed(struct uart_state *state,
+static void uart_change_speed(struct tty_struct *tty, struct uart_state *state,
 					struct ktermios *old_termios);
 					struct ktermios *old_termios);
-static void uart_wait_until_sent(struct tty_struct *tty, int timeout);
+static void __uart_wait_until_sent(struct uart_port *port, int timeout);
 static void uart_change_pm(struct uart_state *state, int pm_state);
 static void uart_change_pm(struct uart_state *state, int pm_state);
 
 
 /*
 /*
@@ -137,7 +137,7 @@ uart_update_mctrl(struct uart_port *port, unsigned int set, unsigned int clear)
  * Startup the port.  This will be called once per open.  All calls
  * Startup the port.  This will be called once per open.  All calls
  * will be serialised by the per-port mutex.
  * will be serialised by the per-port mutex.
  */
  */
-static int uart_startup(struct uart_state *state, int init_hw)
+static int uart_startup(struct tty_struct *tty, struct uart_state *state, int init_hw)
 {
 {
 	struct uart_port *uport = state->uart_port;
 	struct uart_port *uport = state->uart_port;
 	struct tty_port *port = &state->port;
 	struct tty_port *port = &state->port;
@@ -152,7 +152,7 @@ static int uart_startup(struct uart_state *state, int init_hw)
 	 * once we have successfully opened the port.  Also set
 	 * once we have successfully opened the port.  Also set
 	 * up the tty->alt_speed kludge
 	 * up the tty->alt_speed kludge
 	 */
 	 */
-	set_bit(TTY_IO_ERROR, &port->tty->flags);
+	set_bit(TTY_IO_ERROR, &tty->flags);
 
 
 	if (uport->type == PORT_UNKNOWN)
 	if (uport->type == PORT_UNKNOWN)
 		return 0;
 		return 0;
@@ -177,26 +177,26 @@ static int uart_startup(struct uart_state *state, int init_hw)
 			/*
 			/*
 			 * Initialise the hardware port settings.
 			 * Initialise the hardware port settings.
 			 */
 			 */
-			uart_change_speed(state, NULL);
+			uart_change_speed(tty, state, NULL);
 
 
 			/*
 			/*
 			 * Setup the RTS and DTR signals once the
 			 * Setup the RTS and DTR signals once the
 			 * port is open and ready to respond.
 			 * port is open and ready to respond.
 			 */
 			 */
-			if (port->tty->termios->c_cflag & CBAUD)
+			if (tty->termios->c_cflag & CBAUD)
 				uart_set_mctrl(uport, TIOCM_RTS | TIOCM_DTR);
 				uart_set_mctrl(uport, TIOCM_RTS | TIOCM_DTR);
 		}
 		}
 
 
 		if (port->flags & ASYNC_CTS_FLOW) {
 		if (port->flags & ASYNC_CTS_FLOW) {
 			spin_lock_irq(&uport->lock);
 			spin_lock_irq(&uport->lock);
 			if (!(uport->ops->get_mctrl(uport) & TIOCM_CTS))
 			if (!(uport->ops->get_mctrl(uport) & TIOCM_CTS))
-				port->tty->hw_stopped = 1;
+				tty->hw_stopped = 1;
 			spin_unlock_irq(&uport->lock);
 			spin_unlock_irq(&uport->lock);
 		}
 		}
 
 
 		set_bit(ASYNCB_INITIALIZED, &port->flags);
 		set_bit(ASYNCB_INITIALIZED, &port->flags);
 
 
-		clear_bit(TTY_IO_ERROR, &port->tty->flags);
+		clear_bit(TTY_IO_ERROR, &tty->flags);
 	}
 	}
 
 
 	if (retval && capable(CAP_SYS_ADMIN))
 	if (retval && capable(CAP_SYS_ADMIN))
@@ -210,11 +210,10 @@ static int uart_startup(struct uart_state *state, int init_hw)
  * DTR is dropped if the hangup on close termio flag is on.  Calls to
  * DTR is dropped if the hangup on close termio flag is on.  Calls to
  * uart_shutdown are serialised by the per-port semaphore.
  * uart_shutdown are serialised by the per-port semaphore.
  */
  */
-static void uart_shutdown(struct uart_state *state)
+static void uart_shutdown(struct tty_struct *tty, struct uart_state *state)
 {
 {
 	struct uart_port *uport = state->uart_port;
 	struct uart_port *uport = state->uart_port;
 	struct tty_port *port = &state->port;
 	struct tty_port *port = &state->port;
-	struct tty_struct *tty = port->tty;
 
 
 	/*
 	/*
 	 * Set the TTY IO error marker
 	 * Set the TTY IO error marker
@@ -430,11 +429,10 @@ uart_get_divisor(struct uart_port *port, unsigned int baud)
 EXPORT_SYMBOL(uart_get_divisor);
 EXPORT_SYMBOL(uart_get_divisor);
 
 
 /* FIXME: Consistent locking policy */
 /* FIXME: Consistent locking policy */
-static void
-uart_change_speed(struct uart_state *state, struct ktermios *old_termios)
+static void uart_change_speed(struct tty_struct *tty, struct uart_state *state,
+					struct ktermios *old_termios)
 {
 {
 	struct tty_port *port = &state->port;
 	struct tty_port *port = &state->port;
-	struct tty_struct *tty = port->tty;
 	struct uart_port *uport = state->uart_port;
 	struct uart_port *uport = state->uart_port;
 	struct ktermios *termios;
 	struct ktermios *termios;
 
 
@@ -463,8 +461,8 @@ uart_change_speed(struct uart_state *state, struct ktermios *old_termios)
 	uport->ops->set_termios(uport, termios, old_termios);
 	uport->ops->set_termios(uport, termios, old_termios);
 }
 }
 
 
-static inline int
-__uart_put_char(struct uart_port *port, struct circ_buf *circ, unsigned char c)
+static inline int __uart_put_char(struct uart_port *port,
+				struct circ_buf *circ, unsigned char c)
 {
 {
 	unsigned long flags;
 	unsigned long flags;
 	int ret = 0;
 	int ret = 0;
@@ -494,8 +492,8 @@ static void uart_flush_chars(struct tty_struct *tty)
 	uart_start(tty);
 	uart_start(tty);
 }
 }
 
 
-static int
-uart_write(struct tty_struct *tty, const unsigned char *buf, int count)
+static int uart_write(struct tty_struct *tty,
+					const unsigned char *buf, int count)
 {
 {
 	struct uart_state *state = tty->driver_data;
 	struct uart_state *state = tty->driver_data;
 	struct uart_port *port;
 	struct uart_port *port;
@@ -675,7 +673,7 @@ static int uart_get_info(struct uart_state *state,
 	return 0;
 	return 0;
 }
 }
 
 
-static int uart_set_info(struct uart_state *state,
+static int uart_set_info(struct tty_struct *tty, struct uart_state *state,
 			 struct serial_struct __user *newinfo)
 			 struct serial_struct __user *newinfo)
 {
 {
 	struct serial_struct new_serial;
 	struct serial_struct new_serial;
@@ -770,7 +768,7 @@ static int uart_set_info(struct uart_state *state,
 		 * We need to shutdown the serial port at the old
 		 * We need to shutdown the serial port at the old
 		 * port/type/irq combination.
 		 * port/type/irq combination.
 		 */
 		 */
-		uart_shutdown(state);
+		uart_shutdown(tty, state);
 	}
 	}
 
 
 	if (change_port) {
 	if (change_port) {
@@ -869,25 +867,27 @@ static int uart_set_info(struct uart_state *state,
 				       "is deprecated.\n", current->comm,
 				       "is deprecated.\n", current->comm,
 				       tty_name(port->tty, buf));
 				       tty_name(port->tty, buf));
 			}
 			}
-			uart_change_speed(state, NULL);
+			uart_change_speed(tty, state, NULL);
 		}
 		}
 	} else
 	} else
-		retval = uart_startup(state, 1);
+		retval = uart_startup(tty, state, 1);
  exit:
  exit:
 	mutex_unlock(&port->mutex);
 	mutex_unlock(&port->mutex);
 	return retval;
 	return retval;
 }
 }
 
 
-
-/*
- * uart_get_lsr_info - get line status register info.
- * Note: uart_ioctl protects us against hangups.
+/**
+ *	uart_get_lsr_info	-	get line status register info
+ *	@tty: tty associated with the UART
+ *	@state: UART being queried
+ *	@value: returned modem value
+ *
+ *	Note: uart_ioctl protects us against hangups.
  */
  */
-static int uart_get_lsr_info(struct uart_state *state,
-			     unsigned int __user *value)
+static int uart_get_lsr_info(struct tty_struct *tty,
+			struct uart_state *state, unsigned int __user *value)
 {
 {
 	struct uart_port *uport = state->uart_port;
 	struct uart_port *uport = state->uart_port;
-	struct tty_port *port = &state->port;
 	unsigned int result;
 	unsigned int result;
 
 
 	result = uport->ops->tx_empty(uport);
 	result = uport->ops->tx_empty(uport);
@@ -900,7 +900,7 @@ static int uart_get_lsr_info(struct uart_state *state,
 	 */
 	 */
 	if (uport->x_char ||
 	if (uport->x_char ||
 	    ((uart_circ_chars_pending(&state->xmit) > 0) &&
 	    ((uart_circ_chars_pending(&state->xmit) > 0) &&
-	     !port->tty->stopped && !port->tty->hw_stopped))
+	     !tty->stopped && !tty->hw_stopped))
 		result &= ~TIOCSER_TEMT;
 		result &= ~TIOCSER_TEMT;
 
 
 	return put_user(result, value);
 	return put_user(result, value);
@@ -961,7 +961,7 @@ static int uart_break_ctl(struct tty_struct *tty, int break_state)
 	return 0;
 	return 0;
 }
 }
 
 
-static int uart_do_autoconfig(struct uart_state *state)
+static int uart_do_autoconfig(struct tty_struct *tty,struct uart_state *state)
 {
 {
 	struct uart_port *uport = state->uart_port;
 	struct uart_port *uport = state->uart_port;
 	struct tty_port *port = &state->port;
 	struct tty_port *port = &state->port;
@@ -980,7 +980,7 @@ static int uart_do_autoconfig(struct uart_state *state)
 
 
 	ret = -EBUSY;
 	ret = -EBUSY;
 	if (tty_port_users(port) == 1) {
 	if (tty_port_users(port) == 1) {
-		uart_shutdown(state);
+		uart_shutdown(tty, state);
 
 
 		/*
 		/*
 		 * If we already have a port type configured,
 		 * If we already have a port type configured,
@@ -999,7 +999,7 @@ static int uart_do_autoconfig(struct uart_state *state)
 		 */
 		 */
 		uport->ops->config_port(uport, flags);
 		uport->ops->config_port(uport, flags);
 
 
-		ret = uart_startup(state, 1);
+		ret = uart_startup(tty, state, 1);
 	}
 	}
 	mutex_unlock(&port->mutex);
 	mutex_unlock(&port->mutex);
 	return ret;
 	return ret;
@@ -1122,11 +1122,11 @@ uart_ioctl(struct tty_struct *tty, struct file *filp, unsigned int cmd,
 		break;
 		break;
 
 
 	case TIOCSSERIAL:
 	case TIOCSSERIAL:
-		ret = uart_set_info(state, uarg);
+		ret = uart_set_info(tty, state, uarg);
 		break;
 		break;
 
 
 	case TIOCSERCONFIG:
 	case TIOCSERCONFIG:
-		ret = uart_do_autoconfig(state);
+		ret = uart_do_autoconfig(tty, state);
 		break;
 		break;
 
 
 	case TIOCSERGWILD: /* obsolete */
 	case TIOCSERGWILD: /* obsolete */
@@ -1172,7 +1172,7 @@ uart_ioctl(struct tty_struct *tty, struct file *filp, unsigned int cmd,
 	 */
 	 */
 	switch (cmd) {
 	switch (cmd) {
 	case TIOCSERGETLSR: /* Get line status register */
 	case TIOCSERGETLSR: /* Get line status register */
-		ret = uart_get_lsr_info(state, uarg);
+		ret = uart_get_lsr_info(tty, state, uarg);
 		break;
 		break;
 
 
 	default: {
 	default: {
@@ -1194,7 +1194,7 @@ static void uart_set_ldisc(struct tty_struct *tty)
 	struct uart_port *uport = state->uart_port;
 	struct uart_port *uport = state->uart_port;
 
 
 	if (uport->ops->set_ldisc)
 	if (uport->ops->set_ldisc)
-		uport->ops->set_ldisc(uport);
+		uport->ops->set_ldisc(uport, tty->termios->c_line);
 }
 }
 
 
 static void uart_set_termios(struct tty_struct *tty,
 static void uart_set_termios(struct tty_struct *tty,
@@ -1219,7 +1219,7 @@ static void uart_set_termios(struct tty_struct *tty,
 		return;
 		return;
 	}
 	}
 
 
-	uart_change_speed(state, old_termios);
+	uart_change_speed(tty, state, old_termios);
 
 
 	/* Handle transition to B0 status */
 	/* Handle transition to B0 status */
 	if ((old_termios->c_cflag & CBAUD) && !(cflag & CBAUD))
 	if ((old_termios->c_cflag & CBAUD) && !(cflag & CBAUD))
@@ -1272,8 +1272,9 @@ static void uart_close(struct tty_struct *tty, struct file *filp)
 	struct uart_state *state = tty->driver_data;
 	struct uart_state *state = tty->driver_data;
 	struct tty_port *port;
 	struct tty_port *port;
 	struct uart_port *uport;
 	struct uart_port *uport;
+	unsigned long flags;
 
 
-	BUG_ON(!kernel_locked());
+	BUG_ON(!tty_locked());
 
 
 	if (!state)
 	if (!state)
 		return;
 		return;
@@ -1284,9 +1285,12 @@ static void uart_close(struct tty_struct *tty, struct file *filp)
 	pr_debug("uart_close(%d) called\n", uport->line);
 	pr_debug("uart_close(%d) called\n", uport->line);
 
 
 	mutex_lock(&port->mutex);
 	mutex_lock(&port->mutex);
+	spin_lock_irqsave(&port->lock, flags);
 
 
-	if (tty_hung_up_p(filp))
+	if (tty_hung_up_p(filp)) {
+		spin_unlock_irqrestore(&port->lock, flags);
 		goto done;
 		goto done;
+	}
 
 
 	if ((tty->count == 1) && (port->count != 1)) {
 	if ((tty->count == 1) && (port->count != 1)) {
 		/*
 		/*
@@ -1305,8 +1309,10 @@ static void uart_close(struct tty_struct *tty, struct file *filp)
 		       tty->name, port->count);
 		       tty->name, port->count);
 		port->count = 0;
 		port->count = 0;
 	}
 	}
-	if (port->count)
+	if (port->count) {
+		spin_unlock_irqrestore(&port->lock, flags);
 		goto done;
 		goto done;
+	}
 
 
 	/*
 	/*
 	 * Now we wait for the transmit buffer to clear; and we notify
 	 * Now we wait for the transmit buffer to clear; and we notify
@@ -1314,9 +1320,18 @@ static void uart_close(struct tty_struct *tty, struct file *filp)
 	 * setting tty->closing.
 	 * setting tty->closing.
 	 */
 	 */
 	tty->closing = 1;
 	tty->closing = 1;
+	spin_unlock_irqrestore(&port->lock, flags);
 
 
-	if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE)
-		tty_wait_until_sent(tty, msecs_to_jiffies(port->closing_wait));
+	if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE) {
+		/*
+		 * hack: open-coded tty_wait_until_sent to avoid
+		 * recursive tty_lock
+		 */
+		long timeout = msecs_to_jiffies(port->closing_wait);
+		if (wait_event_interruptible_timeout(tty->write_wait,
+				!tty_chars_in_buffer(tty), timeout) >= 0)
+			__uart_wait_until_sent(uport, timeout);
+	}
 
 
 	/*
 	/*
 	 * At this point, we stop accepting input.  To do this, we
 	 * At this point, we stop accepting input.  To do this, we
@@ -1332,45 +1347,47 @@ static void uart_close(struct tty_struct *tty, struct file *filp)
 		 * has completely drained; this is especially
 		 * has completely drained; this is especially
 		 * important if there is a transmit FIFO!
 		 * important if there is a transmit FIFO!
 		 */
 		 */
-		uart_wait_until_sent(tty, uport->timeout);
+		__uart_wait_until_sent(uport, uport->timeout);
 	}
 	}
 
 
-	uart_shutdown(state);
+	uart_shutdown(tty, state);
 	uart_flush_buffer(tty);
 	uart_flush_buffer(tty);
 
 
 	tty_ldisc_flush(tty);
 	tty_ldisc_flush(tty);
 
 
-	tty->closing = 0;
 	tty_port_tty_set(port, NULL);
 	tty_port_tty_set(port, NULL);
+	spin_lock_irqsave(&port->lock, flags);
+	tty->closing = 0;
 
 
 	if (port->blocked_open) {
 	if (port->blocked_open) {
+		spin_unlock_irqrestore(&port->lock, flags);
 		if (port->close_delay)
 		if (port->close_delay)
 			msleep_interruptible(port->close_delay);
 			msleep_interruptible(port->close_delay);
+		spin_lock_irqsave(&port->lock, flags);
 	} else if (!uart_console(uport)) {
 	} else if (!uart_console(uport)) {
+		spin_unlock_irqrestore(&port->lock, flags);
 		uart_change_pm(state, 3);
 		uart_change_pm(state, 3);
+		spin_lock_irqsave(&port->lock, flags);
 	}
 	}
 
 
 	/*
 	/*
 	 * Wake up anyone trying to open this port.
 	 * Wake up anyone trying to open this port.
 	 */
 	 */
 	clear_bit(ASYNCB_NORMAL_ACTIVE, &port->flags);
 	clear_bit(ASYNCB_NORMAL_ACTIVE, &port->flags);
+	spin_unlock_irqrestore(&port->lock, flags);
 	wake_up_interruptible(&port->open_wait);
 	wake_up_interruptible(&port->open_wait);
 
 
 done:
 done:
 	mutex_unlock(&port->mutex);
 	mutex_unlock(&port->mutex);
 }
 }
 
 
-static void uart_wait_until_sent(struct tty_struct *tty, int timeout)
+static void __uart_wait_until_sent(struct uart_port *port, int timeout)
 {
 {
-	struct uart_state *state = tty->driver_data;
-	struct uart_port *port = state->uart_port;
 	unsigned long char_time, expire;
 	unsigned long char_time, expire;
 
 
 	if (port->type == PORT_UNKNOWN || port->fifosize == 0)
 	if (port->type == PORT_UNKNOWN || port->fifosize == 0)
 		return;
 		return;
 
 
-	lock_kernel();
-
 	/*
 	/*
 	 * Set the check interval to be 1/5 of the estimated time to
 	 * Set the check interval to be 1/5 of the estimated time to
 	 * send a single character, and make it at least 1.  The check
 	 * send a single character, and make it at least 1.  The check
@@ -1416,7 +1433,16 @@ static void uart_wait_until_sent(struct tty_struct *tty, int timeout)
 			break;
 			break;
 	}
 	}
 	set_current_state(TASK_RUNNING); /* might not be needed */
 	set_current_state(TASK_RUNNING); /* might not be needed */
-	unlock_kernel();
+}
+
+static void uart_wait_until_sent(struct tty_struct *tty, int timeout)
+{
+	struct uart_state *state = tty->driver_data;
+	struct uart_port *port = state->uart_port;
+
+	tty_lock();
+	__uart_wait_until_sent(port, timeout);
+	tty_unlock();
 }
 }
 
 
 /*
 /*
@@ -1429,16 +1455,19 @@ static void uart_hangup(struct tty_struct *tty)
 {
 {
 	struct uart_state *state = tty->driver_data;
 	struct uart_state *state = tty->driver_data;
 	struct tty_port *port = &state->port;
 	struct tty_port *port = &state->port;
+	unsigned long flags;
 
 
-	BUG_ON(!kernel_locked());
+	BUG_ON(!tty_locked());
 	pr_debug("uart_hangup(%d)\n", state->uart_port->line);
 	pr_debug("uart_hangup(%d)\n", state->uart_port->line);
 
 
 	mutex_lock(&port->mutex);
 	mutex_lock(&port->mutex);
 	if (port->flags & ASYNC_NORMAL_ACTIVE) {
 	if (port->flags & ASYNC_NORMAL_ACTIVE) {
 		uart_flush_buffer(tty);
 		uart_flush_buffer(tty);
-		uart_shutdown(state);
+		uart_shutdown(tty, state);
+		spin_lock_irqsave(&port->lock, flags);
 		port->count = 0;
 		port->count = 0;
 		clear_bit(ASYNCB_NORMAL_ACTIVE, &port->flags);
 		clear_bit(ASYNCB_NORMAL_ACTIVE, &port->flags);
+		spin_unlock_irqrestore(&port->lock, flags);
 		tty_port_tty_set(port, NULL);
 		tty_port_tty_set(port, NULL);
 		wake_up_interruptible(&port->open_wait);
 		wake_up_interruptible(&port->open_wait);
 		wake_up_interruptible(&port->delta_msr_wait);
 		wake_up_interruptible(&port->delta_msr_wait);
@@ -1446,15 +1475,19 @@ static void uart_hangup(struct tty_struct *tty)
 	mutex_unlock(&port->mutex);
 	mutex_unlock(&port->mutex);
 }
 }
 
 
-/*
- * Copy across the serial console cflag setting into the termios settings
- * for the initial open of the port.  This allows continuity between the
- * kernel settings, and the settings init adopts when it opens the port
- * for the first time.
+/**
+ *	uart_update_termios	-	update the terminal hw settings
+ *	@tty: tty associated with UART
+ *	@state: UART to update
+ *
+ *	Copy across the serial console cflag setting into the termios settings
+ *	for the initial open of the port.  This allows continuity between the
+ *	kernel settings, and the settings init adopts when it opens the port
+ *	for the first time.
  */
  */
-static void uart_update_termios(struct uart_state *state)
+static void uart_update_termios(struct tty_struct *tty,
+						struct uart_state *state)
 {
 {
-	struct tty_struct *tty = state->port.tty;
 	struct uart_port *port = state->uart_port;
 	struct uart_port *port = state->uart_port;
 
 
 	if (uart_console(port) && port->cons->cflag) {
 	if (uart_console(port) && port->cons->cflag) {
@@ -1471,7 +1504,7 @@ static void uart_update_termios(struct uart_state *state)
 		/*
 		/*
 		 * Make termios settings take effect.
 		 * Make termios settings take effect.
 		 */
 		 */
-		uart_change_speed(state, NULL);
+		uart_change_speed(tty, state, NULL);
 
 
 		/*
 		/*
 		 * And finally enable the RTS and DTR signals.
 		 * And finally enable the RTS and DTR signals.
@@ -1481,90 +1514,37 @@ static void uart_update_termios(struct uart_state *state)
 	}
 	}
 }
 }
 
 
-/*
- * Block the open until the port is ready.  We must be called with
- * the per-port semaphore held.
- */
-static int
-uart_block_til_ready(struct file *filp, struct uart_state *state)
+static int uart_carrier_raised(struct tty_port *port)
 {
 {
-	DECLARE_WAITQUEUE(wait, current);
+	struct uart_state *state = container_of(port, struct uart_state, port);
 	struct uart_port *uport = state->uart_port;
 	struct uart_port *uport = state->uart_port;
-	struct tty_port *port = &state->port;
-	unsigned int mctrl;
-
-	port->blocked_open++;
-	port->count--;
-
-	add_wait_queue(&port->open_wait, &wait);
-	while (1) {
-		set_current_state(TASK_INTERRUPTIBLE);
-
-		/*
-		 * If we have been hung up, tell userspace/restart open.
-		 */
-		if (tty_hung_up_p(filp) || port->tty == NULL)
-			break;
-
-		/*
-		 * If the port has been closed, tell userspace/restart open.
-		 */
-		if (!(port->flags & ASYNC_INITIALIZED))
-			break;
+	int mctrl;
+	spin_lock_irq(&uport->lock);
+	uport->ops->enable_ms(uport);
+	mctrl = uport->ops->get_mctrl(uport);
+	spin_unlock_irq(&uport->lock);
+	if (mctrl & TIOCM_CAR)
+		return 1;
+	return 0;
+}
 
 
-		/*
-		 * If non-blocking mode is set, or CLOCAL mode is set,
-		 * we don't want to wait for the modem status lines to
-		 * indicate that the port is ready.
-		 *
-		 * Also, if the port is not enabled/configured, we want
-		 * to allow the open to succeed here.  Note that we will
-		 * have set TTY_IO_ERROR for a non-existant port.
-		 */
-		if ((filp->f_flags & O_NONBLOCK) ||
-		    (port->tty->termios->c_cflag & CLOCAL) ||
-		    (port->tty->flags & (1 << TTY_IO_ERROR)))
-			break;
+static void uart_dtr_rts(struct tty_port *port, int onoff)
+{
+	struct uart_state *state = container_of(port, struct uart_state, port);
+	struct uart_port *uport = state->uart_port;
 
 
-		/*
-		 * Set DTR to allow modem to know we're waiting.  Do
-		 * not set RTS here - we want to make sure we catch
-		 * the data from the modem.
-		 */
-		if (port->tty->termios->c_cflag & CBAUD)
-			uart_set_mctrl(uport, TIOCM_DTR);
+	if (onoff) {
+		uart_set_mctrl(uport, TIOCM_DTR | TIOCM_RTS);
 
 
 		/*
 		/*
-		 * and wait for the carrier to indicate that the
-		 * modem is ready for us.
+		 * If this is the first open to succeed,
+		 * adjust things to suit.
 		 */
 		 */
-		spin_lock_irq(&uport->lock);
-		uport->ops->enable_ms(uport);
-		mctrl = uport->ops->get_mctrl(uport);
-		spin_unlock_irq(&uport->lock);
-		if (mctrl & TIOCM_CAR)
-			break;
-
-		mutex_unlock(&port->mutex);
-		schedule();
-		mutex_lock(&port->mutex);
-
-		if (signal_pending(current))
-			break;
+		if (!test_and_set_bit(ASYNCB_NORMAL_ACTIVE, &port->flags))
+			uart_update_termios(port->tty, state);
 	}
 	}
-	set_current_state(TASK_RUNNING);
-	remove_wait_queue(&port->open_wait, &wait);
-
-	port->count++;
-	port->blocked_open--;
-
-	if (signal_pending(current))
-		return -ERESTARTSYS;
-
-	if (!port->tty || tty_hung_up_p(filp))
-		return -EAGAIN;
-
-	return 0;
+	else
+		uart_clear_mctrl(uport, TIOCM_DTR | TIOCM_RTS);
 }
 }
 
 
 static struct uart_state *uart_get(struct uart_driver *drv, int line)
 static struct uart_state *uart_get(struct uart_driver *drv, int line)
@@ -1611,7 +1591,7 @@ static int uart_open(struct tty_struct *tty, struct file *filp)
 	struct tty_port *port;
 	struct tty_port *port;
 	int retval, line = tty->index;
 	int retval, line = tty->index;
 
 
-	BUG_ON(!kernel_locked());
+	BUG_ON(!tty_locked());
 	pr_debug("uart_open(%d) called\n", line);
 	pr_debug("uart_open(%d) called\n", line);
 
 
 	/*
 	/*
@@ -1668,23 +1648,14 @@ static int uart_open(struct tty_struct *tty, struct file *filp)
 	/*
 	/*
 	 * Start up the serial port.
 	 * Start up the serial port.
 	 */
 	 */
-	retval = uart_startup(state, 0);
+	retval = uart_startup(tty, state, 0);
 
 
 	/*
 	/*
 	 * If we succeeded, wait until the port is ready.
 	 * If we succeeded, wait until the port is ready.
 	 */
 	 */
-	if (retval == 0)
-		retval = uart_block_til_ready(filp, state);
 	mutex_unlock(&port->mutex);
 	mutex_unlock(&port->mutex);
-
-	/*
-	 * If this is the first open to succeed, adjust things to suit.
-	 */
-	if (retval == 0 && !(port->flags & ASYNC_NORMAL_ACTIVE)) {
-		set_bit(ASYNCB_NORMAL_ACTIVE, &port->flags);
-
-		uart_update_termios(state);
-	}
+	if (retval == 0)
+		retval = tty_port_block_til_ready(port, tty, filp);
 
 
 fail:
 fail:
 	return retval;
 	return retval;
@@ -2010,9 +1981,13 @@ int uart_suspend_port(struct uart_driver *drv, struct uart_port *uport)
 	struct tty_port *port = &state->port;
 	struct tty_port *port = &state->port;
 	struct device *tty_dev;
 	struct device *tty_dev;
 	struct uart_match match = {uport, drv};
 	struct uart_match match = {uport, drv};
+	struct tty_struct *tty;
 
 
 	mutex_lock(&port->mutex);
 	mutex_lock(&port->mutex);
 
 
+	/* Must be inside the mutex lock until we convert to tty_port */
+	tty = port->tty;
+
 	tty_dev = device_find_child(uport->dev, &match, serial_match_port);
 	tty_dev = device_find_child(uport->dev, &match, serial_match_port);
 	if (device_may_wakeup(tty_dev)) {
 	if (device_may_wakeup(tty_dev)) {
 		enable_irq_wake(uport->irq);
 		enable_irq_wake(uport->irq);
@@ -2105,9 +2080,12 @@ int uart_resume_port(struct uart_driver *drv, struct uart_port *uport)
 		ops->set_mctrl(uport, 0);
 		ops->set_mctrl(uport, 0);
 		spin_unlock_irq(&uport->lock);
 		spin_unlock_irq(&uport->lock);
 		if (console_suspend_enabled || !uart_console(uport)) {
 		if (console_suspend_enabled || !uart_console(uport)) {
+			/* Protected by port mutex for now */
+			struct tty_struct *tty = port->tty;
 			ret = ops->startup(uport);
 			ret = ops->startup(uport);
 			if (ret == 0) {
 			if (ret == 0) {
-				uart_change_speed(state, NULL);
+				if (tty)
+					uart_change_speed(tty, state, NULL);
 				spin_lock_irq(&uport->lock);
 				spin_lock_irq(&uport->lock);
 				ops->set_mctrl(uport, uport->mctrl);
 				ops->set_mctrl(uport, uport->mctrl);
 				ops->start_tx(uport);
 				ops->start_tx(uport);
@@ -2119,7 +2097,7 @@ int uart_resume_port(struct uart_driver *drv, struct uart_port *uport)
 				 * Clear the "initialized" flag so we won't try
 				 * Clear the "initialized" flag so we won't try
 				 * to call the low level drivers shutdown method.
 				 * to call the low level drivers shutdown method.
 				 */
 				 */
-				uart_shutdown(state);
+				uart_shutdown(tty, state);
 			}
 			}
 		}
 		}
 
 
@@ -2312,6 +2290,11 @@ static const struct tty_operations uart_ops = {
 #endif
 #endif
 };
 };
 
 
+static const struct tty_port_operations uart_port_ops = {
+	.carrier_raised = uart_carrier_raised,
+	.dtr_rts	= uart_dtr_rts,
+};
+
 /**
 /**
  *	uart_register_driver - register a driver with the uart core layer
  *	uart_register_driver - register a driver with the uart core layer
  *	@drv: low level driver structure
  *	@drv: low level driver structure
@@ -2368,6 +2351,7 @@ int uart_register_driver(struct uart_driver *drv)
 		struct tty_port *port = &state->port;
 		struct tty_port *port = &state->port;
 
 
 		tty_port_init(port);
 		tty_port_init(port);
+		port->ops = &uart_port_ops;
 		port->close_delay     = 500;	/* .5 seconds */
 		port->close_delay     = 500;	/* .5 seconds */
 		port->closing_wait    = 30000;	/* 30 seconds */
 		port->closing_wait    = 30000;	/* 30 seconds */
 		tasklet_init(&state->tlet, uart_tasklet_action,
 		tasklet_init(&state->tlet, uart_tasklet_action,

+ 3 - 3
drivers/serial/timbuart.c

@@ -423,7 +423,7 @@ static struct uart_driver timbuart_driver = {
 	.nr = 1
 	.nr = 1
 };
 };
 
 
-static int timbuart_probe(struct platform_device *dev)
+static int __devinit timbuart_probe(struct platform_device *dev)
 {
 {
 	int err, irq;
 	int err, irq;
 	struct timbuart_port *uart;
 	struct timbuart_port *uart;
@@ -489,7 +489,7 @@ static int timbuart_probe(struct platform_device *dev)
 	return err;
 	return err;
 }
 }
 
 
-static int timbuart_remove(struct platform_device *dev)
+static int __devexit timbuart_remove(struct platform_device *dev)
 {
 {
 	struct timbuart_port *uart = platform_get_drvdata(dev);
 	struct timbuart_port *uart = platform_get_drvdata(dev);
 
 
@@ -507,7 +507,7 @@ static struct platform_driver timbuart_platform_driver = {
 		.owner	= THIS_MODULE,
 		.owner	= THIS_MODULE,
 	},
 	},
 	.probe		= timbuart_probe,
 	.probe		= timbuart_probe,
-	.remove		= timbuart_remove,
+	.remove		= __devexit_p(timbuart_remove),
 };
 };
 
 
 /*--------------------------------------------------------------------------*/
 /*--------------------------------------------------------------------------*/

+ 3 - 9
drivers/usb/class/cdc-acm.c

@@ -636,19 +636,13 @@ static void acm_tty_unregister(struct acm *acm)
 
 
 static int acm_tty_chars_in_buffer(struct tty_struct *tty);
 static int acm_tty_chars_in_buffer(struct tty_struct *tty);
 
 
-static void acm_port_down(struct acm *acm, int drain)
+static void acm_port_down(struct acm *acm)
 {
 {
 	int i, nr = acm->rx_buflimit;
 	int i, nr = acm->rx_buflimit;
 	mutex_lock(&open_mutex);
 	mutex_lock(&open_mutex);
 	if (acm->dev) {
 	if (acm->dev) {
 		usb_autopm_get_interface(acm->control);
 		usb_autopm_get_interface(acm->control);
 		acm_set_control(acm, acm->ctrlout = 0);
 		acm_set_control(acm, acm->ctrlout = 0);
-		/* try letting the last writes drain naturally */
-		if (drain) {
-			wait_event_interruptible_timeout(acm->drain_wait,
-				(ACM_NW == acm_wb_is_avail(acm)) || !acm->dev,
-					ACM_CLOSE_TIMEOUT * HZ);
-		}
 		usb_kill_urb(acm->ctrlurb);
 		usb_kill_urb(acm->ctrlurb);
 		for (i = 0; i < ACM_NW; i++)
 		for (i = 0; i < ACM_NW; i++)
 			usb_kill_urb(acm->wb[i].urb);
 			usb_kill_urb(acm->wb[i].urb);
@@ -664,7 +658,7 @@ static void acm_tty_hangup(struct tty_struct *tty)
 {
 {
 	struct acm *acm = tty->driver_data;
 	struct acm *acm = tty->driver_data;
 	tty_port_hangup(&acm->port);
 	tty_port_hangup(&acm->port);
-	acm_port_down(acm, 0);
+	acm_port_down(acm);
 }
 }
 
 
 static void acm_tty_close(struct tty_struct *tty, struct file *filp)
 static void acm_tty_close(struct tty_struct *tty, struct file *filp)
@@ -685,7 +679,7 @@ static void acm_tty_close(struct tty_struct *tty, struct file *filp)
 		mutex_unlock(&open_mutex);
 		mutex_unlock(&open_mutex);
 		return;
 		return;
 	}
 	}
-	acm_port_down(acm, 0);
+	acm_port_down(acm);
 	tty_port_close_end(&acm->port, tty);
 	tty_port_close_end(&acm->port, tty);
 	tty_port_tty_set(&acm->port, NULL);
 	tty_port_tty_set(&acm->port, NULL);
 }
 }

+ 9 - 5
drivers/usb/serial/digi_acceleport.c

@@ -609,8 +609,10 @@ static void digi_wakeup_write_lock(struct work_struct *work)
 static void digi_wakeup_write(struct usb_serial_port *port)
 static void digi_wakeup_write(struct usb_serial_port *port)
 {
 {
 	struct tty_struct *tty = tty_port_tty_get(&port->port);
 	struct tty_struct *tty = tty_port_tty_get(&port->port);
-	tty_wakeup(tty);
-	tty_kref_put(tty);
+	if (tty) {
+		tty_wakeup(tty);
+		tty_kref_put(tty);
+	}
 }
 }
 
 
 
 
@@ -1682,7 +1684,7 @@ static int digi_read_inb_callback(struct urb *urb)
 		priv->dp_throttle_restart = 1;
 		priv->dp_throttle_restart = 1;
 
 
 	/* receive data */
 	/* receive data */
-	if (opcode == DIGI_CMD_RECEIVE_DATA) {
+	if (tty && opcode == DIGI_CMD_RECEIVE_DATA) {
 		/* get flag from port_status */
 		/* get flag from port_status */
 		flag = 0;
 		flag = 0;
 
 
@@ -1763,10 +1765,12 @@ static int digi_read_oob_callback(struct urb *urb)
 			return -1;
 			return -1;
 
 
 		tty = tty_port_tty_get(&port->port);
 		tty = tty_port_tty_get(&port->port);
+
 		rts = 0;
 		rts = 0;
-		rts = tty->termios->c_cflag & CRTSCTS;
+		if (tty)
+			rts = tty->termios->c_cflag & CRTSCTS;
 		
 		
-		if (opcode == DIGI_CMD_READ_INPUT_SIGNALS) {
+		if (tty && opcode == DIGI_CMD_READ_INPUT_SIGNALS) {
 			spin_lock(&priv->dp_port_lock);
 			spin_lock(&priv->dp_port_lock);
 			/* convert from digi flags to termiox flags */
 			/* convert from digi flags to termiox flags */
 			if (val & DIGI_READ_INPUT_SIGNALS_CTS) {
 			if (val & DIGI_READ_INPUT_SIGNALS_CTS) {

+ 3 - 1
drivers/video/console/fbcon.c

@@ -283,7 +283,8 @@ static inline int fbcon_is_inactive(struct vc_data *vc, struct fb_info *info)
 	struct fbcon_ops *ops = info->fbcon_par;
 	struct fbcon_ops *ops = info->fbcon_par;
 
 
 	return (info->state != FBINFO_STATE_RUNNING ||
 	return (info->state != FBINFO_STATE_RUNNING ||
-		vc->vc_mode != KD_TEXT || ops->graphics);
+		vc->vc_mode != KD_TEXT || ops->graphics) &&
+		!vt_force_oops_output(vc);
 }
 }
 
 
 static inline int get_color(struct vc_data *vc, struct fb_info *info,
 static inline int get_color(struct vc_data *vc, struct fb_info *info,
@@ -1073,6 +1074,7 @@ static void fbcon_init(struct vc_data *vc, int init)
 	if (p->userfont)
 	if (p->userfont)
 		charcnt = FNTCHARCNT(p->fontdata);
 		charcnt = FNTCHARCNT(p->fontdata);
 
 
+	vc->vc_panic_force_write = !!(info->flags & FBINFO_CAN_FORCE_OUTPUT);
 	vc->vc_can_do_color = (fb_get_color_depth(&info->var, &info->fix)!=1);
 	vc->vc_can_do_color = (fb_get_color_depth(&info->var, &info->fix)!=1);
 	vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800;
 	vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800;
 	if (charcnt == 256) {
 	if (charcnt == 256) {

+ 0 - 2
drivers/video/console/vgacon.c

@@ -1108,7 +1108,6 @@ static int vgacon_do_font_op(struct vgastate *state,char *arg,int set,int ch512)
 		charmap += 4 * cmapsz;
 		charmap += 4 * cmapsz;
 #endif
 #endif
 
 
-	unlock_kernel();
 	spin_lock_irq(&vga_lock);
 	spin_lock_irq(&vga_lock);
 	/* First, the Sequencer */
 	/* First, the Sequencer */
 	vga_wseq(state->vgabase, VGA_SEQ_RESET, 0x1);
 	vga_wseq(state->vgabase, VGA_SEQ_RESET, 0x1);
@@ -1192,7 +1191,6 @@ static int vgacon_do_font_op(struct vgastate *state,char *arg,int set,int ch512)
 		vga_wattr(state->vgabase, VGA_AR_ENABLE_DISPLAY, 0);	
 		vga_wattr(state->vgabase, VGA_AR_ENABLE_DISPLAY, 0);	
 	}
 	}
 	spin_unlock_irq(&vga_lock);
 	spin_unlock_irq(&vga_lock);
-	lock_kernel();
 	return 0;
 	return 0;
 }
 }
 
 

+ 1 - 0
fs/compat_ioctl.c

@@ -946,6 +946,7 @@ COMPATIBLE_IOCTL(TIOCGPGRP)
 COMPATIBLE_IOCTL(TIOCGPTN)
 COMPATIBLE_IOCTL(TIOCGPTN)
 COMPATIBLE_IOCTL(TIOCSPTLCK)
 COMPATIBLE_IOCTL(TIOCSPTLCK)
 COMPATIBLE_IOCTL(TIOCSERGETLSR)
 COMPATIBLE_IOCTL(TIOCSERGETLSR)
+COMPATIBLE_IOCTL(TIOCSIG)
 #ifdef TCGETS2
 #ifdef TCGETS2
 COMPATIBLE_IOCTL(TCGETS2)
 COMPATIBLE_IOCTL(TCGETS2)
 COMPATIBLE_IOCTL(TCSETS2)
 COMPATIBLE_IOCTL(TCSETS2)

Some files were not shown because too many files changed in this diff