mirror of
				git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
				synced 2025-10-31 16:54:21 +00:00 
			
		
		
		
	TTY/Serial merge for 3.11-rc1
Here is the big TTY / Serial driver merge for 3.11-rc1. It's not all that big, nothing major changed in the tty api, which is a nice change, just a number of serial driver fixes and updates and new drivers, along with some n_tty fixes to help resolve some reported issues. All of these have been in the linux-next releases for a while, with the exception of the last revert patch, which was reported this past weekend by two different people as being needed. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.19 (GNU/Linux) iEYEABECAAYFAlHRtNIACgkQMUfUDdst+ymYgwCeKdyv9wRJ5cIWZt7Jz8ou3P/C 76YAoIvMv+fwoFBpyud/sC8eAKGTQ6KB =Sjk6 -----END PGP SIGNATURE----- Merge tag 'tty-3.11-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty Pull tty/serial updates from Greg KH: "Here is the big TTY / Serial driver merge for 3.11-rc1. It's not all that big, nothing major changed in the tty api, which is a nice change, just a number of serial driver fixes and updates and new drivers, along with some n_tty fixes to help resolve some reported issues. All of these have been in the linux-next releases for a while, with the exception of the last revert patch, which was reported this past weekend by two different people as being needed." * tag 'tty-3.11-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty: (51 commits) Revert "serial: 8250_pci: add support for another kind of NetMos Technology PCI 9835 Multi-I/O Controller" pch_uart: Add uart_clk selection for the MinnowBoard tty: atmel_serial: prepare clk before calling enable tty: Reset itty for other pty n_tty: Buffer work should not reschedule itself n_tty: Fix unsafe update of available buffer space n_tty: Untangle read completion variables n_tty: Encapsulate minimum_to_wake within N_TTY serial: omap: Fix device tree based PM runtime serial: imx: Fix serial clock unbalance serial/mpc52xx_uart: fix kernel panic when system reboot serial: mfd: Add sysrq support serial: imx: enable the clocks for console tty: serial: add Freescale lpuart driver support serial: imx: Improve Kconfig text serial: imx: Allow module build serial: imx: Fix warning when !CONFIG_SERIAL_IMX_CONSOLE tty/serial/sirf: fix error propagation in sirfsoc_uart_probe() serial: omap: fix potential NULL pointer dereference in serial_omap_runtime_suspend() tty: serial: Enable uartlite for ARM zynq ...
This commit is contained in:
		
						commit
						0de10f9ea6
					
				
					 57 changed files with 2144 additions and 797 deletions
				
			
		|  | @ -12,20 +12,20 @@ The second type has to be explicitly loaded and unloaded. This will be called | |||
| any time with each driver sharing the console with other drivers including | ||||
| the system driver. However, modular drivers cannot take over the console | ||||
| that is currently occupied by another modular driver. (Exception: Drivers that | ||||
| call take_over_console() will succeed in the takeover regardless of the type | ||||
| call do_take_over_console() will succeed in the takeover regardless of the type | ||||
| of driver occupying the consoles.) They can only take over the console that is | ||||
| occupied by the system driver. In the same token, if the modular driver is | ||||
| released by the console, the system driver will take over. | ||||
| 
 | ||||
| Modular drivers, from the programmer's point of view, has to call: | ||||
| 
 | ||||
| 	 take_over_console() - load and bind driver to console layer | ||||
| 	 give_up_console() - unbind and unload driver | ||||
| 	 do_take_over_console() - load and bind driver to console layer | ||||
| 	 give_up_console() - unload driver, it will only work if driver is fully unbond | ||||
| 
 | ||||
| In newer kernels, the following are also available: | ||||
| 
 | ||||
| 	 register_con_driver() | ||||
| 	 unregister_con_driver() | ||||
| 	 do_register_con_driver() | ||||
| 	 do_unregister_con_driver() | ||||
| 
 | ||||
| If sysfs is enabled, the contents of /sys/class/vtconsole can be | ||||
| examined. This shows the console backends currently registered by the | ||||
|  | @ -94,12 +94,12 @@ for more details). | |||
| Notes for developers: | ||||
| ===================== | ||||
| 
 | ||||
| take_over_console() is now broken up into: | ||||
| do_take_over_console() is now broken up into: | ||||
| 
 | ||||
|      register_con_driver() | ||||
|      bind_con_driver() - private function | ||||
|      do_register_con_driver() | ||||
|      do_bind_con_driver() - private function | ||||
| 
 | ||||
| give_up_console() is a wrapper to unregister_con_driver(), and a driver must | ||||
| give_up_console() is a wrapper to do_unregister_con_driver(), and a driver must | ||||
| be fully unbound for this call to succeed. con_is_bound() will check if the | ||||
| driver is bound or not. | ||||
| 
 | ||||
|  | @ -109,10 +109,10 @@ Guidelines for console driver writers: | |||
| In order for binding to and unbinding from the console to properly work, | ||||
| console drivers must follow these guidelines: | ||||
| 
 | ||||
| 1. All drivers, except system drivers, must call either register_con_driver() | ||||
|    or take_over_console(). register_con_driver() will just add the driver to | ||||
| 1. All drivers, except system drivers, must call either do_register_con_driver() | ||||
|    or do_take_over_console(). do_register_con_driver() will just add the driver to | ||||
|    the console's internal list. It won't take over the | ||||
|    console. take_over_console(), as it name implies, will also take over (or | ||||
|    console. do_take_over_console(), as it name implies, will also take over (or | ||||
|    bind to) the console. | ||||
| 
 | ||||
| 2. All resources allocated during con->con_init() must be released in | ||||
|  | @ -128,10 +128,10 @@ console drivers must follow these guidelines: | |||
|    rebind the driver to the console arrives. | ||||
| 
 | ||||
| 4. Upon exit of the driver, ensure that the driver is totally unbound. If the | ||||
|    condition is satisfied, then the driver must call unregister_con_driver() | ||||
|    condition is satisfied, then the driver must call do_unregister_con_driver() | ||||
|    or give_up_console(). | ||||
| 
 | ||||
| 5. unregister_con_driver() can also be called on conditions which make it | ||||
| 5. do_unregister_con_driver() can also be called on conditions which make it | ||||
|    impossible for the driver to service console requests.  This can happen | ||||
|    with the framebuffer console that suddenly lost all of its drivers. | ||||
| 
 | ||||
|  |  | |||
|  | @ -8,6 +8,8 @@ Required properties: | |||
| Optional properties: | ||||
| - fsl,uart-has-rtscts : Indicate the uart has rts and cts | ||||
| - fsl,irda-mode : Indicate the uart supports irda mode | ||||
| - fsl,dte-mode : Indicate the uart works in DTE mode. The uart works | ||||
|                   is DCE mode by default. | ||||
| 
 | ||||
| Example: | ||||
| 
 | ||||
|  | @ -16,4 +18,5 @@ serial@73fbc000 { | |||
| 	reg = <0x73fbc000 0x4000>; | ||||
| 	interrupts = <31>; | ||||
| 	fsl,uart-has-rtscts; | ||||
| 	fsl,dte-mode; | ||||
| }; | ||||
|  |  | |||
							
								
								
									
										14
									
								
								Documentation/devicetree/bindings/tty/serial/fsl-lpuart.txt
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								Documentation/devicetree/bindings/tty/serial/fsl-lpuart.txt
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,14 @@ | |||
| * Freescale low power universal asynchronous receiver/transmitter (lpuart) | ||||
| 
 | ||||
| Required properties: | ||||
| - compatible : Should be "fsl,<soc>-lpuart" | ||||
| - reg : Address and length of the register set for the device | ||||
| - interrupts : Should contain uart interrupt | ||||
| 
 | ||||
| Example: | ||||
| 
 | ||||
| uart0: serial@40027000 { | ||||
| 	       compatible = "fsl,vf610-lpuart"; | ||||
| 	       reg = <0x40027000 0x1000>; | ||||
| 	       interrupts = <0 61 0x00>; | ||||
|        }; | ||||
|  | @ -16,8 +16,6 @@ serial-rs485.txt | |||
| 	- info about RS485 structures and support in the kernel. | ||||
| specialix.txt | ||||
| 	- info on hardware/driver for specialix IO8+ multiport serial card. | ||||
| stallion.txt | ||||
| 	- info on using the Stallion multiport serial driver. | ||||
| sx.txt | ||||
| 	- info on the Specialix SX/SI multiport serial driver. | ||||
| tty.txt | ||||
|  |  | |||
|  | @ -1,392 +0,0 @@ | |||
| * NOTE - This is an unmaintained driver.  Lantronix, which bought Stallion | ||||
| technologies, is not active in driver maintenance, and they have no information | ||||
| on when or if they will have a 2.6 driver. | ||||
| 
 | ||||
| James Nelson <james4765@gmail.com> - 12-12-2004 | ||||
| 
 | ||||
| Stallion Multiport Serial Driver Readme | ||||
| --------------------------------------- | ||||
| 
 | ||||
| Copyright (C) 1994-1999,  Stallion Technologies. | ||||
| 
 | ||||
| Version:   5.5.1 | ||||
| Date:      28MAR99 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 1. INTRODUCTION | ||||
| 
 | ||||
| There are two drivers that work with the different families of Stallion | ||||
| multiport serial boards. One is for the Stallion smart boards - that is | ||||
| EasyIO, EasyConnection 8/32 and EasyConnection 8/64-PCI, the other for | ||||
| the true Stallion intelligent multiport boards - EasyConnection 8/64 | ||||
| (ISA, EISA), EasyConnection/RA-PCI, ONboard and Brumby. | ||||
| 
 | ||||
| If you are using any of the Stallion intelligent multiport boards (Brumby, | ||||
| ONboard, EasyConnection 8/64 (ISA, EISA), EasyConnection/RA-PCI) with | ||||
| Linux you will need to get the driver utility package.  This contains a | ||||
| firmware loader and the firmware images necessary to make the devices operate. | ||||
| 
 | ||||
| The Stallion Technologies ftp site, ftp.stallion.com, will always have | ||||
| the latest version of the driver utility package. | ||||
| 
 | ||||
| ftp://ftp.stallion.com/drivers/ata5/Linux/ata-linux-550.tar.gz | ||||
| 
 | ||||
| As of the printing of this document the latest version of the driver | ||||
| utility package is 5.5.0. If a later version is now available then you | ||||
| should use the latest version. | ||||
| 
 | ||||
| If you are using the EasyIO, EasyConnection 8/32 or EasyConnection 8/64-PCI | ||||
| boards then you don't need this package, although it does have a serial stats | ||||
| display program. | ||||
| 
 | ||||
| If you require DIP switch settings, or EISA configuration files, or any | ||||
| other information related to Stallion boards then have a look at Stallion's | ||||
| web pages at http://www.stallion.com. | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 2. INSTALLATION | ||||
| 
 | ||||
| The drivers can be used as loadable modules or compiled into the kernel. | ||||
| You can choose which when doing a "config" on the kernel. | ||||
| 
 | ||||
| All ISA, and EISA boards that you want to use need to be configured into | ||||
| the driver(s). All PCI boards will be automatically detected when you load | ||||
| the driver - so they do not need to be entered into the driver(s) | ||||
| configuration structure. Note that kernel PCI support is required to use PCI | ||||
| boards. | ||||
| 
 | ||||
| There are two methods of configuring ISA and EISA boards into the drivers. | ||||
| If using the driver as a loadable module then the simplest method is to pass | ||||
| the driver configuration as module arguments. The other method is to modify | ||||
| the driver source to add configuration lines for each board in use. | ||||
| 
 | ||||
| If you have pre-built Stallion driver modules then the module argument | ||||
| configuration method should be used. A lot of Linux distributions come with | ||||
| pre-built driver modules in /lib/modules/X.Y.Z/misc for the kernel in use. | ||||
| That makes things pretty simple to get going. | ||||
| 
 | ||||
| 
 | ||||
| 2.1 MODULE DRIVER CONFIGURATION: | ||||
| 
 | ||||
| The simplest configuration for modules is to use the module load arguments | ||||
| to configure any ISA or EISA boards. PCI boards are automatically | ||||
| detected, so do not need any additional configuration at all. | ||||
| 
 | ||||
| If using EasyIO, EasyConnection 8/32 ISA, or EasyConnection 8/63-PCI | ||||
| boards then use the "stallion" driver module, Otherwise if you are using | ||||
| an EasyConnection 8/64 ISA or EISA, EasyConnection/RA-PCI, ONboard, | ||||
| Brumby or original Stallion board then use the "istallion" driver module. | ||||
| 
 | ||||
| Typically to load up the smart board driver use: | ||||
| 
 | ||||
|     modprobe stallion | ||||
| 
 | ||||
| This will load the EasyIO and EasyConnection 8/32 driver. It will output a | ||||
| message to say that it loaded and print the driver version number. It will | ||||
| also print out whether it found the configured boards or not. These messages | ||||
| may not appear on the console, but typically are always logged to | ||||
| /var/adm/messages or /var/log/syslog files - depending on how the klogd and | ||||
| syslogd daemons are setup on your system. | ||||
| 
 | ||||
| To load the intelligent board driver use: | ||||
| 
 | ||||
|     modprobe istallion | ||||
| 
 | ||||
| It will output similar messages to the smart board driver. | ||||
| 
 | ||||
| If not using an auto-detectable board type (that is a PCI board) then you | ||||
| will also need to supply command line arguments to the modprobe command | ||||
| when loading the driver. The general form of the configuration argument is | ||||
| 
 | ||||
|     board?=<name>[,<ioaddr>[,<addr>][,<irq>]] | ||||
| 
 | ||||
| where: | ||||
| 
 | ||||
|     board?  -- specifies the arbitrary board number of this board, | ||||
|                can be in the range 0 to 3. | ||||
| 
 | ||||
|     name    -- textual name of this board. The board name is the common | ||||
|                board name, or any "shortened" version of that. The board | ||||
|                type number may also be used here. | ||||
| 
 | ||||
|     ioaddr  -- specifies the I/O address of this board. This argument is | ||||
|                optional, but should generally be specified. | ||||
| 
 | ||||
|     addr    -- optional second address argument. Some board types require | ||||
|                a second I/O address, some require a memory address. The | ||||
|                exact meaning of this argument depends on the board type. | ||||
| 
 | ||||
|     irq     -- optional IRQ line used by this board. | ||||
| 
 | ||||
| Up to 4 board configuration arguments can be specified on the load line. | ||||
| Here is some examples: | ||||
| 
 | ||||
|     modprobe stallion board0=easyio,0x2a0,5 | ||||
| 
 | ||||
| This configures an EasyIO board as board 0 at I/O address 0x2a0 and IRQ 5. | ||||
| 
 | ||||
|     modprobe istallion board3=ec8/64,0x2c0,0xcc000 | ||||
| 
 | ||||
| This configures an EasyConnection 8/64 ISA as board 3 at I/O address 0x2c0 at | ||||
| memory address 0xcc000. | ||||
| 
 | ||||
|     modprobe stallion board1=ec8/32-at,0x2a0,0x280,10 | ||||
| 
 | ||||
| This configures an EasyConnection 8/32 ISA board at primary I/O address 0x2a0, | ||||
| secondary address 0x280 and IRQ 10. | ||||
| 
 | ||||
| You will probably want to enter this module load and configuration information | ||||
| into your system startup scripts so that the drivers are loaded and configured | ||||
| on each system boot. Typically configuration files are put in the | ||||
| /etc/modprobe.d/ directory. | ||||
| 
 | ||||
| 
 | ||||
| 2.2 STATIC DRIVER CONFIGURATION: | ||||
| 
 | ||||
| For static driver configuration you need to modify the driver source code. | ||||
| Entering ISA and EISA boards into the driver(s) configuration structure | ||||
| involves editing the driver(s) source file. It's pretty easy if you follow | ||||
| the instructions below. Both drivers can support up to 4 boards. The smart | ||||
| card driver (the stallion.c driver) supports any combination of EasyIO and | ||||
| EasyConnection 8/32 boards (up to a total of 4). The intelligent driver | ||||
| supports any combination of ONboards, Brumbys, Stallions and EasyConnection | ||||
| 8/64 (ISA and EISA) boards (up to a total of 4). | ||||
| 
 | ||||
| To set up the driver(s) for the boards that you want to use you need to | ||||
| edit the appropriate driver file and add configuration entries. | ||||
| 
 | ||||
| If using EasyIO or EasyConnection 8/32 ISA boards, | ||||
|    In drivers/char/stallion.c: | ||||
|       - find the definition of the stl_brdconf array (of structures) | ||||
|         near the top of the file | ||||
|       - modify this to match the boards you are going to install | ||||
| 	(the comments before this structure should help) | ||||
|       - save and exit | ||||
| 
 | ||||
| If using ONboard, Brumby, Stallion or EasyConnection 8/64 (ISA or EISA) | ||||
| boards, | ||||
|    In drivers/char/istallion.c: | ||||
|       - find the definition of the stli_brdconf array (of structures) | ||||
|         near the top of the file | ||||
|       - modify this to match the boards you are going to install | ||||
| 	(the comments before this structure should help) | ||||
|       - save and exit | ||||
| 
 | ||||
| Once you have set up the board configurations then you are ready to build | ||||
| the kernel or modules. | ||||
| 
 | ||||
| When the new kernel is booted, or the loadable module loaded then the | ||||
| driver will emit some kernel trace messages about whether the configured | ||||
| boards were detected or not. Depending on how your system logger is set | ||||
| up these may come out on the console, or just be logged to | ||||
| /var/adm/messages or /var/log/syslog. You should check the messages to | ||||
| confirm that all is well. | ||||
| 
 | ||||
| 
 | ||||
| 2.3 SHARING INTERRUPTS | ||||
| 
 | ||||
| It is possible to share interrupts between multiple EasyIO and | ||||
| EasyConnection 8/32 boards in an EISA system. To do this you must be using | ||||
| static driver configuration, modifying the driver source code to add driver | ||||
| configuration. Then a couple of extra things are required: | ||||
| 
 | ||||
| 1. When entering the board resources into the stallion.c file you need to | ||||
|    mark the boards as using level triggered interrupts. Do this by replacing | ||||
|    the "0" entry at field position 6 (the last field) in the board | ||||
|    configuration structure with a "1". (This is the structure that defines | ||||
|    the board type, I/O locations, etc. for each board). All boards that are | ||||
|    sharing an interrupt must be set this way, and each board should have the | ||||
|    same interrupt number specified here as well. Now build the module or | ||||
|    kernel as you would normally. | ||||
| 
 | ||||
| 2. When physically installing the boards into the system you must enter | ||||
|    the system EISA configuration utility. You will need to install the EISA | ||||
|    configuration files for *all* the EasyIO and EasyConnection 8/32 boards | ||||
|    that are sharing interrupts. The Stallion EasyIO and EasyConnection 8/32 | ||||
|    EISA configuration files required are supplied by Stallion Technologies | ||||
|    on the EASY Utilities floppy diskette (usually supplied in the box with | ||||
|    the board when purchased. If not, you can pick it up from Stallion's FTP | ||||
|    site, ftp.stallion.com). You will need to edit the board resources to | ||||
|    choose level triggered interrupts, and make sure to set each board's | ||||
|    interrupt to the same IRQ number. | ||||
| 
 | ||||
| You must complete both the above steps for this to work. When you reboot | ||||
| or load the driver your EasyIO and EasyConnection 8/32 boards will be | ||||
| sharing interrupts. | ||||
| 
 | ||||
| 
 | ||||
| 2.4 USING HIGH SHARED MEMORY | ||||
| 
 | ||||
| The EasyConnection 8/64-EI, ONboard and Stallion boards are capable of | ||||
| using shared memory addresses above the usual 640K - 1Mb range. The ONboard | ||||
| ISA and the Stallion boards can be programmed to use memory addresses up to | ||||
| 16Mb (the ISA bus addressing limit), and the EasyConnection 8/64-EI and | ||||
| ONboard/E can be programmed for memory addresses up to 4Gb (the EISA bus | ||||
| addressing limit). | ||||
| 
 | ||||
| The higher than 1Mb memory addresses are fully supported by this driver. | ||||
| Just enter the address as you normally would for a lower than 1Mb address | ||||
| (in the driver's board configuration structure). | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 2.5 TROUBLE SHOOTING | ||||
| 
 | ||||
| If a board is not found by the driver but is actually in the system then the | ||||
| most likely problem is that the I/O address is wrong. Change the module load | ||||
| argument for the loadable module form. Or change it in the driver stallion.c | ||||
| or istallion.c configuration structure and rebuild the kernel or modules, or | ||||
| change it on the board. | ||||
| 
 | ||||
| On EasyIO and EasyConnection 8/32 boards the IRQ is software programmable, so | ||||
| if there is a conflict you may need to change the IRQ used for a board. There | ||||
| are no interrupts to worry about for ONboard, Brumby or EasyConnection 8/64 | ||||
| (ISA and EISA) boards. The memory region on EasyConnection 8/64 and | ||||
| ONboard boards is software programmable, but not on the Brumby boards. | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 3. USING THE DRIVERS | ||||
| 
 | ||||
| 3.1 INTELLIGENT DRIVER OPERATION | ||||
| 
 | ||||
| The intelligent boards also need to have their "firmware" code downloaded | ||||
| to them. This is done via a user level application supplied in the driver | ||||
| utility package called "stlload". Compile this program wherever you dropped | ||||
| the package files, by typing "make". In its simplest form you can then type | ||||
| 
 | ||||
|     ./stlload -i cdk.sys | ||||
| 
 | ||||
| in this directory and that will download board 0 (assuming board 0 is an | ||||
| EasyConnection 8/64 or EasyConnection/RA board). To download to an | ||||
| ONboard, Brumby or Stallion do: | ||||
| 
 | ||||
|     ./stlload -i 2681.sys | ||||
| 
 | ||||
| Normally you would want all boards to be downloaded as part of the standard | ||||
| system startup. To achieve this, add one of the lines above into the | ||||
| /etc/rc.d/rc.S or /etc/rc.d/rc.serial file. To download each board just add | ||||
| the "-b <brd-number>" option to the line. You will need to download code for | ||||
| every board. You should probably move the stlload program into a system | ||||
| directory, such as /usr/sbin. Also, the default location of the cdk.sys image | ||||
| file in the stlload down-loader is /usr/lib/stallion. Create that directory | ||||
| and put the cdk.sys and 2681.sys files in it. (It's a convenient place to put | ||||
| them anyway). As an example your /etc/rc.d/rc.S file might have the | ||||
| following lines added to it (if you had 3 boards): | ||||
| 
 | ||||
|     /usr/sbin/stlload -b 0 -i /usr/lib/stallion/cdk.sys | ||||
|     /usr/sbin/stlload -b 1 -i /usr/lib/stallion/2681.sys | ||||
|     /usr/sbin/stlload -b 2 -i /usr/lib/stallion/2681.sys | ||||
| 
 | ||||
| The image files cdk.sys and 2681.sys are specific to the board types. The | ||||
| cdk.sys will only function correctly on an EasyConnection 8/64 board. Similarly | ||||
| the 2681.sys image fill only operate on ONboard, Brumby and Stallion boards. | ||||
| If you load the wrong image file into a board it will fail to start up, and | ||||
| of course the ports will not be operational! | ||||
| 
 | ||||
| If you are using the modularized version of the driver you might want to put | ||||
| the modprobe calls in the startup script as well (before the download lines | ||||
| obviously). | ||||
| 
 | ||||
| 
 | ||||
| 3.2 USING THE SERIAL PORTS | ||||
| 
 | ||||
| Once the driver is installed you will need to setup some device nodes to | ||||
| access the serial ports. The simplest method is to use the /dev/MAKEDEV program. | ||||
| It will automatically create device entries for Stallion boards. This will | ||||
| create the normal serial port devices as /dev/ttyE# where# is the port number | ||||
| starting from 0. A bank of 64 minor device numbers is allocated to each board, | ||||
| so the first port on the second board is port 64,etc. A set of callout type | ||||
| devices may also be created. They are created as the devices /dev/cue# where # | ||||
| is the same as for the ttyE devices. | ||||
| 
 | ||||
| For the most part the Stallion driver tries to emulate the standard PC system | ||||
| COM ports and the standard Linux serial driver. The idea is that you should | ||||
| be able to use Stallion board ports and COM ports interchangeably without | ||||
| modifying anything but the device name. Anything that doesn't work like that | ||||
| should be considered a bug in this driver! | ||||
| 
 | ||||
| If you look at the driver code you will notice that it is fairly closely | ||||
| based on the Linux serial driver (linux/drivers/char/serial.c). This is | ||||
| intentional, obviously this is the easiest way to emulate its behavior! | ||||
| 
 | ||||
| Since this driver tries to emulate the standard serial ports as much as | ||||
| possible, most system utilities should work as they do for the standard | ||||
| COM ports. Most importantly "stty" works as expected and "setserial" can | ||||
| also be used (excepting the ability to auto-configure the I/O and IRQ | ||||
| addresses of boards). Higher baud rates are supported in the usual fashion | ||||
| through setserial or using the CBAUDEX extensions. Note that the EasyIO and | ||||
| EasyConnection (all types) support at least 57600 and 115200 baud. The newer | ||||
| EasyConnection XP modules and new EasyIO boards support 230400 and 460800 | ||||
| baud as well. The older boards including ONboard and Brumby support a | ||||
| maximum baud rate of 38400. | ||||
| 
 | ||||
| If you are unfamiliar with how to use serial ports, then get the Serial-HOWTO | ||||
| by Greg Hankins. It will explain everything you need to know! | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 4. NOTES | ||||
| 
 | ||||
| You can use both drivers at once if you have a mix of board types installed | ||||
| in a system. However to do this you will need to change the major numbers | ||||
| used by one of the drivers. Currently both drivers use major numbers 24, 25 | ||||
| and 28 for their devices. Change one driver to use some other major numbers, | ||||
| and then modify the mkdevnods script to make device nodes based on those new | ||||
| major numbers. For example, you could change the istallion.c driver to use | ||||
| major numbers 60, 61 and 62. You will also need to create device nodes with | ||||
| different names for the ports, for example ttyF# and cuf#. | ||||
| 
 | ||||
| The original Stallion board is no longer supported by Stallion Technologies. | ||||
| Although it is known to work with the istallion driver. | ||||
| 
 | ||||
| Finding a free physical memory address range can be a problem. The older | ||||
| boards like the Stallion and ONboard need large areas (64K or even 128K), so | ||||
| they can be very difficult to get into a system. If you have 16 Mb of RAM | ||||
| then you have no choice but to put them somewhere in the 640K -> 1Mb range. | ||||
| ONboards require 64K, so typically 0xd0000 is good, or 0xe0000 on some | ||||
| systems. If you have an original Stallion board, "V4.0" or Rev.O, then you | ||||
| need a 64K memory address space, so again 0xd0000 and 0xe0000 are good. | ||||
| Older Stallion boards are a much bigger problem. They need 128K of address | ||||
| space and must be on a 128K boundary. If you don't have a VGA card then | ||||
| 0xc0000 might be usable - there is really no other place you can put them | ||||
| below 1Mb. | ||||
| 
 | ||||
| Both the ONboard and old Stallion boards can use higher memory addresses as | ||||
| well, but you must have less than 16Mb of RAM to be able to use them. Usual | ||||
| high memory addresses used include 0xec0000 and 0xf00000. | ||||
| 
 | ||||
| The Brumby boards only require 16Kb of address space, so you can usually | ||||
| squeeze them in somewhere. Common addresses are 0xc8000, 0xcc000, or in | ||||
| the 0xd0000 range. EasyConnection 8/64 boards are even better, they only | ||||
| require 4Kb of address space, again usually 0xc8000, 0xcc000 or 0xd0000 | ||||
| are good. | ||||
| 
 | ||||
| If you are using an EasyConnection 8/64-EI or ONboard/E then usually the | ||||
| 0xd0000 or 0xe0000 ranges are the best options below 1Mb. If neither of | ||||
| them can be used then the high memory support to use the really high address | ||||
| ranges is the best option. Typically the 2Gb range is convenient for them, | ||||
| and gets them well out of the way. | ||||
| 
 | ||||
| The ports of the EasyIO-8M board do not have DCD or DTR signals. So these | ||||
| ports cannot be used as real modem devices. Generally, when using these | ||||
| ports you should only use the cueX devices. | ||||
| 
 | ||||
| The driver utility package contains a couple of very useful programs. One  | ||||
| is a serial port statistics collection and display program - very handy | ||||
| for solving serial port problems. The other is an extended option setting | ||||
| program that works with the intelligent boards. | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 5. DISCLAIMER | ||||
| 
 | ||||
| The information contained in this document is believed to be accurate and | ||||
| reliable. However, no responsibility is assumed by Stallion Technologies | ||||
| Pty. Ltd. for its use, nor any infringements of patents or other rights | ||||
| of third parties resulting from its use. Stallion Technologies reserves | ||||
| the right to modify the design of its products and will endeavour to change | ||||
| the information in manuals and accompanying documentation accordingly. | ||||
| 
 | ||||
|  | @ -4577,7 +4577,7 @@ F:	fs/jbd2/ | |||
| F:	include/linux/jbd2.h | ||||
| 
 | ||||
| JSM Neo PCI based serial card | ||||
| M:	Lucas Tavares <lucaskt@linux.vnet.ibm.com> | ||||
| M:	Thadeu Lima de Souza Cascardo <cascardo@linux.vnet.ibm.com> | ||||
| L:	linux-serial@vger.kernel.org | ||||
| S:	Maintained | ||||
| F:	drivers/tty/serial/jsm/ | ||||
|  |  | |||
|  | @ -61,7 +61,9 @@ locate_and_init_vga(void *(*sel_func)(void *, void *)) | |||
| 
 | ||||
| 	/* Set the VGA hose and init the new console. */ | ||||
| 	pci_vga_hose = hose; | ||||
| 	take_over_console(&vga_con, 0, MAX_NR_CONSOLES-1, 1); | ||||
| 	console_lock(); | ||||
| 	do_take_over_console(&vga_con, 0, MAX_NR_CONSOLES-1, 1); | ||||
| 	console_unlock(); | ||||
| } | ||||
| 
 | ||||
| void __init | ||||
|  |  | |||
|  | @ -117,7 +117,9 @@ common_shutdown_1(void *generic_ptr) | |||
| 		if (in_interrupt()) | ||||
| 			irq_exit(); | ||||
| 		/* This has the effect of resetting the VGA video origin.  */ | ||||
| 		take_over_console(&dummy_con, 0, MAX_NR_CONSOLES-1, 1); | ||||
| 		console_lock(); | ||||
| 		do_take_over_console(&dummy_con, 0, MAX_NR_CONSOLES-1, 1); | ||||
| 		console_unlock(); | ||||
| #endif | ||||
| 		pci_restore_srm_config(); | ||||
| 		set_hae(srm_hae); | ||||
|  |  | |||
|  | @ -257,7 +257,9 @@ static int __init bcm1480_pcibios_init(void) | |||
| 	register_pci_controller(&bcm1480_controller); | ||||
| 
 | ||||
| #ifdef CONFIG_VGA_CONSOLE | ||||
| 	take_over_console(&vga_con, 0, MAX_NR_CONSOLES-1, 1); | ||||
| 	console_lock(); | ||||
| 	do_take_over_console(&vga_con, 0, MAX_NR_CONSOLES-1, 1); | ||||
| 	console_unlock(); | ||||
| #endif | ||||
| 	return 0; | ||||
| } | ||||
|  |  | |||
|  | @ -283,7 +283,9 @@ static int __init sb1250_pcibios_init(void) | |||
| 	register_pci_controller(&sb1250_controller); | ||||
| 
 | ||||
| #ifdef CONFIG_VGA_CONSOLE | ||||
| 	take_over_console(&vga_con, 0, MAX_NR_CONSOLES - 1, 1); | ||||
| 	console_lock(); | ||||
| 	do_take_over_console(&vga_con, 0, MAX_NR_CONSOLES - 1, 1); | ||||
| 	console_unlock(); | ||||
| #endif | ||||
| 	return 0; | ||||
| } | ||||
|  |  | |||
|  | @ -156,7 +156,7 @@ void __init setup_arch(char **cmdline_p) | |||
| #endif | ||||
| 
 | ||||
| #if defined(CONFIG_VT) && defined(CONFIG_DUMMY_CONSOLE) | ||||
| 	conswitchp = &dummy_con;	/* we use take_over_console() later ! */ | ||||
| 	conswitchp = &dummy_con;	/* we use do_take_over_console() later ! */ | ||||
| #endif | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -299,4 +299,53 @@ struct mpc512x_psc_fifo { | |||
| #define rxdata_32 rxdata.rxdata_32 | ||||
| }; | ||||
| 
 | ||||
| struct mpc5125_psc { | ||||
| 	u8		mr1;			/* PSC + 0x00 */ | ||||
| 	u8		reserved0[3]; | ||||
| 	u8		mr2;			/* PSC + 0x04 */ | ||||
| 	u8		reserved1[3]; | ||||
| 	struct { | ||||
| 		u16		status;		/* PSC + 0x08 */ | ||||
| 		u8		reserved2[2]; | ||||
| 		u8		clock_select;	/* PSC + 0x0c */ | ||||
| 		u8		reserved3[3]; | ||||
| 	} sr_csr; | ||||
| 	u8		command;		/* PSC + 0x10 */ | ||||
| 	u8		reserved4[3]; | ||||
| 	union {					/* PSC + 0x14 */ | ||||
| 		u8		buffer_8; | ||||
| 		u16		buffer_16; | ||||
| 		u32		buffer_32; | ||||
| 	} buffer; | ||||
| 	struct { | ||||
| 		u8		ipcr;		/* PSC + 0x18 */ | ||||
| 		u8		reserved5[3]; | ||||
| 		u8		acr;		/* PSC + 0x1c */ | ||||
| 		u8		reserved6[3]; | ||||
| 	} ipcr_acr; | ||||
| 	struct { | ||||
| 		u16		isr;		/* PSC + 0x20 */ | ||||
| 		u8		reserved7[2]; | ||||
| 		u16		imr;		/* PSC + 0x24 */ | ||||
| 		u8		reserved8[2]; | ||||
| 	} isr_imr; | ||||
| 	u8		ctur;			/* PSC + 0x28 */ | ||||
| 	u8		reserved9[3]; | ||||
| 	u8		ctlr;			/* PSC + 0x2c */ | ||||
| 	u8		reserved10[3]; | ||||
| 	u32		ccr;			/* PSC + 0x30 */ | ||||
| 	u32		ac97slots;		/* PSC + 0x34 */ | ||||
| 	u32		ac97cmd;		/* PSC + 0x38 */ | ||||
| 	u32		ac97data;		/* PSC + 0x3c */ | ||||
| 	u8		reserved11[4]; | ||||
| 	u8		ip;			/* PSC + 0x44 */ | ||||
| 	u8		reserved12[3]; | ||||
| 	u8		op1;			/* PSC + 0x48 */ | ||||
| 	u8		reserved13[3]; | ||||
| 	u8		op0;			/* PSC + 0x4c */ | ||||
| 	u8		reserved14[3]; | ||||
| 	u32		sicr;			/* PSC + 0x50 */ | ||||
| 	u8		reserved15[4];	/* make eq. sizeof(mpc52xx_psc) */ | ||||
| }; | ||||
| 
 | ||||
| #endif  /* __ASM_MPC52xx_PSC_H__ */ | ||||
|  |  | |||
|  | @ -15,18 +15,6 @@ config DEVKMEM | |||
| 	  kind of kernel debugging operations. | ||||
| 	  When in doubt, say "N". | ||||
| 
 | ||||
| config STALDRV | ||||
| 	bool "Stallion multiport serial support" | ||||
| 	depends on SERIAL_NONSTANDARD | ||||
| 	help | ||||
| 	  Stallion cards give you many serial ports.  You would need something | ||||
| 	  like this to connect more than two modems to your Linux box, for | ||||
| 	  instance in order to become a dial-in server.  If you say Y here, | ||||
| 	  you will be asked for your specific card model in the next | ||||
| 	  questions.  Make sure to read <file:Documentation/serial/stallion.txt> | ||||
| 	  in this case.  If you have never heard about all this, it's safe to | ||||
| 	  say N. | ||||
| 
 | ||||
| config SGI_SNSC | ||||
| 	bool "SGI Altix system controller communication support" | ||||
| 	depends on (IA64_SGI_SN2 || IA64_GENERIC) | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| obj-$(CONFIG_TTY)		+= tty_io.o n_tty.o tty_ioctl.o tty_ldisc.o \
 | ||||
| 				   tty_buffer.o tty_port.o tty_mutex.o | ||||
| 				   tty_buffer.o tty_port.o tty_mutex.o tty_ldsem.o | ||||
| obj-$(CONFIG_LEGACY_PTYS)	+= pty.o | ||||
| obj-$(CONFIG_UNIX98_PTYS)	+= pty.o | ||||
| obj-$(CONFIG_AUDIT)		+= tty_audit.o | ||||
|  |  | |||
|  | @ -1328,7 +1328,7 @@ out_error: | |||
|  */ | ||||
| static	int __init hvc_iucv_config(char *val) | ||||
| { | ||||
| 	 return strict_strtoul(val, 10, &hvc_iucv_devices); | ||||
| 	 return kstrtoul(val, 10, &hvc_iucv_devices); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -89,6 +89,7 @@ struct n_tty_data { | |||
| 	int read_head; | ||||
| 	int read_tail; | ||||
| 	int read_cnt; | ||||
| 	int minimum_to_wake; | ||||
| 
 | ||||
| 	unsigned char *echo_buf; | ||||
| 	unsigned int echo_pos; | ||||
|  | @ -114,22 +115,25 @@ static inline int tty_put_user(struct tty_struct *tty, unsigned char x, | |||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  *	n_tty_set__room	-	receive space | ||||
|  *	n_tty_set_room	-	receive space | ||||
|  *	@tty: terminal | ||||
|  * | ||||
|  *	Called by the driver to find out how much data it is | ||||
|  *	permitted to feed to the line discipline without any being lost | ||||
|  *	and thus to manage flow control. Not serialized. Answers for the | ||||
|  *	"instant". | ||||
|  *	Updates tty->receive_room to reflect the currently available space | ||||
|  *	in the input buffer, and re-schedules the flip buffer work if space | ||||
|  *	just became available. | ||||
|  * | ||||
|  *	Locks: Concurrent update is protected with read_lock | ||||
|  */ | ||||
| 
 | ||||
| static void n_tty_set_room(struct tty_struct *tty) | ||||
| static int set_room(struct tty_struct *tty) | ||||
| { | ||||
| 	struct n_tty_data *ldata = tty->disc_data; | ||||
| 	int left; | ||||
| 	int old_left; | ||||
| 	unsigned long flags; | ||||
| 
 | ||||
| 	raw_spin_lock_irqsave(&ldata->read_lock, flags); | ||||
| 
 | ||||
| 	/* ldata->read_cnt is not read locked ? */ | ||||
| 	if (I_PARMRK(tty)) { | ||||
| 		/* Multiply read_cnt by 3, since each byte might take up to
 | ||||
| 		 * three times as many spaces when PARMRK is set (depending on | ||||
|  | @ -149,8 +153,15 @@ static void n_tty_set_room(struct tty_struct *tty) | |||
| 	old_left = tty->receive_room; | ||||
| 	tty->receive_room = left; | ||||
| 
 | ||||
| 	raw_spin_unlock_irqrestore(&ldata->read_lock, flags); | ||||
| 
 | ||||
| 	return left && !old_left; | ||||
| } | ||||
| 
 | ||||
| static void n_tty_set_room(struct tty_struct *tty) | ||||
| { | ||||
| 	/* Did this open up the receive buffer? We may need to flip */ | ||||
| 	if (left && !old_left) { | ||||
| 	if (set_room(tty)) { | ||||
| 		WARN_RATELIMIT(tty->port->itty == NULL, | ||||
| 				"scheduling with invalid itty\n"); | ||||
| 		/* see if ldisc has been killed - if so, this means that
 | ||||
|  | @ -647,8 +658,7 @@ static void process_echoes(struct tty_struct *tty) | |||
| 			if (no_space_left) | ||||
| 				break; | ||||
| 		} else { | ||||
| 			if (O_OPOST(tty) && | ||||
| 			    !(test_bit(TTY_HW_COOK_OUT, &tty->flags))) { | ||||
| 			if (O_OPOST(tty)) { | ||||
| 				int retval = do_output_char(c, tty, space); | ||||
| 				if (retval < 0) | ||||
| 					break; | ||||
|  | @ -1454,9 +1464,9 @@ static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp, | |||
| 			tty->ops->flush_chars(tty); | ||||
| 	} | ||||
| 
 | ||||
| 	n_tty_set_room(tty); | ||||
| 	set_room(tty); | ||||
| 
 | ||||
| 	if ((!ldata->icanon && (ldata->read_cnt >= tty->minimum_to_wake)) || | ||||
| 	if ((!ldata->icanon && (ldata->read_cnt >= ldata->minimum_to_wake)) || | ||||
| 		L_EXTPROC(tty)) { | ||||
| 		kill_fasync(&tty->fasync, SIGIO, POLL_IN); | ||||
| 		if (waitqueue_active(&tty->read_wait)) | ||||
|  | @ -1516,12 +1526,7 @@ static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old) | |||
| 		wake_up_interruptible(&tty->read_wait); | ||||
| 
 | ||||
| 	ldata->icanon = (L_ICANON(tty) != 0); | ||||
| 	if (test_bit(TTY_HW_COOK_IN, &tty->flags)) { | ||||
| 		ldata->raw = 1; | ||||
| 		ldata->real_raw = 1; | ||||
| 		n_tty_set_room(tty); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	if (I_ISTRIP(tty) || I_IUCLC(tty) || I_IGNCR(tty) || | ||||
| 	    I_ICRNL(tty) || I_INLCR(tty) || L_ICANON(tty) || | ||||
| 	    I_IXON(tty) || L_ISIG(tty) || L_ECHO(tty) || | ||||
|  | @ -1642,7 +1647,7 @@ static int n_tty_open(struct tty_struct *tty) | |||
| 	tty->disc_data = ldata; | ||||
| 	reset_buffer_flags(tty->disc_data); | ||||
| 	ldata->column = 0; | ||||
| 	tty->minimum_to_wake = 1; | ||||
| 	ldata->minimum_to_wake = 1; | ||||
| 	tty->closing = 0; | ||||
| 	/* indicate buffer work may resume */ | ||||
| 	clear_bit(TTY_LDISC_HALTED, &tty->flags); | ||||
|  | @ -1806,21 +1811,17 @@ do_it_again: | |||
| 	minimum = time = 0; | ||||
| 	timeout = MAX_SCHEDULE_TIMEOUT; | ||||
| 	if (!ldata->icanon) { | ||||
| 		time = (HZ / 10) * TIME_CHAR(tty); | ||||
| 		minimum = MIN_CHAR(tty); | ||||
| 		if (minimum) { | ||||
| 			time = (HZ / 10) * TIME_CHAR(tty); | ||||
| 			if (time) | ||||
| 				tty->minimum_to_wake = 1; | ||||
| 				ldata->minimum_to_wake = 1; | ||||
| 			else if (!waitqueue_active(&tty->read_wait) || | ||||
| 				 (tty->minimum_to_wake > minimum)) | ||||
| 				tty->minimum_to_wake = minimum; | ||||
| 				 (ldata->minimum_to_wake > minimum)) | ||||
| 				ldata->minimum_to_wake = minimum; | ||||
| 		} else { | ||||
| 			timeout = 0; | ||||
| 			if (time) { | ||||
| 				timeout = time; | ||||
| 				time = 0; | ||||
| 			} | ||||
| 			tty->minimum_to_wake = minimum = 1; | ||||
| 			timeout = (HZ / 10) * TIME_CHAR(tty); | ||||
| 			ldata->minimum_to_wake = minimum = 1; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
|  | @ -1860,9 +1861,9 @@ do_it_again: | |||
| 		   TASK_RUNNING. */ | ||||
| 		set_current_state(TASK_INTERRUPTIBLE); | ||||
| 
 | ||||
| 		if (((minimum - (b - buf)) < tty->minimum_to_wake) && | ||||
| 		if (((minimum - (b - buf)) < ldata->minimum_to_wake) && | ||||
| 		    ((minimum - (b - buf)) >= 1)) | ||||
| 			tty->minimum_to_wake = (minimum - (b - buf)); | ||||
| 			ldata->minimum_to_wake = (minimum - (b - buf)); | ||||
| 
 | ||||
| 		if (!input_available_p(tty, 0)) { | ||||
| 			if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) { | ||||
|  | @ -1881,7 +1882,6 @@ do_it_again: | |||
| 				retval = -ERESTARTSYS; | ||||
| 				break; | ||||
| 			} | ||||
| 			/* FIXME: does n_tty_set_room need locking ? */ | ||||
| 			n_tty_set_room(tty); | ||||
| 			timeout = schedule_timeout(timeout); | ||||
| 			continue; | ||||
|  | @ -1979,7 +1979,7 @@ do_it_again: | |||
| 	remove_wait_queue(&tty->read_wait, &wait); | ||||
| 
 | ||||
| 	if (!waitqueue_active(&tty->read_wait)) | ||||
| 		tty->minimum_to_wake = minimum; | ||||
| 		ldata->minimum_to_wake = minimum; | ||||
| 
 | ||||
| 	__set_current_state(TASK_RUNNING); | ||||
| 	size = b - buf; | ||||
|  | @ -2045,7 +2045,7 @@ static ssize_t n_tty_write(struct tty_struct *tty, struct file *file, | |||
| 			retval = -EIO; | ||||
| 			break; | ||||
| 		} | ||||
| 		if (O_OPOST(tty) && !(test_bit(TTY_HW_COOK_OUT, &tty->flags))) { | ||||
| 		if (O_OPOST(tty)) { | ||||
| 			while (nr > 0) { | ||||
| 				ssize_t num = process_output_block(tty, b, nr); | ||||
| 				if (num < 0) { | ||||
|  | @ -2111,6 +2111,7 @@ break_out: | |||
| static unsigned int n_tty_poll(struct tty_struct *tty, struct file *file, | ||||
| 							poll_table *wait) | ||||
| { | ||||
| 	struct n_tty_data *ldata = tty->disc_data; | ||||
| 	unsigned int mask = 0; | ||||
| 
 | ||||
| 	poll_wait(file, &tty->read_wait, wait); | ||||
|  | @ -2125,9 +2126,9 @@ static unsigned int n_tty_poll(struct tty_struct *tty, struct file *file, | |||
| 		mask |= POLLHUP; | ||||
| 	if (!(mask & (POLLHUP | POLLIN | POLLRDNORM))) { | ||||
| 		if (MIN_CHAR(tty) && !TIME_CHAR(tty)) | ||||
| 			tty->minimum_to_wake = MIN_CHAR(tty); | ||||
| 			ldata->minimum_to_wake = MIN_CHAR(tty); | ||||
| 		else | ||||
| 			tty->minimum_to_wake = 1; | ||||
| 			ldata->minimum_to_wake = 1; | ||||
| 	} | ||||
| 	if (tty->ops->write && !tty_is_writelocked(tty) && | ||||
| 			tty_chars_in_buffer(tty) < WAKEUP_CHARS && | ||||
|  | @ -2175,6 +2176,18 @@ static int n_tty_ioctl(struct tty_struct *tty, struct file *file, | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| static void n_tty_fasync(struct tty_struct *tty, int on) | ||||
| { | ||||
| 	struct n_tty_data *ldata = tty->disc_data; | ||||
| 
 | ||||
| 	if (!waitqueue_active(&tty->read_wait)) { | ||||
| 		if (on) | ||||
| 			ldata->minimum_to_wake = 1; | ||||
| 		else if (!tty->fasync) | ||||
| 			ldata->minimum_to_wake = N_TTY_BUF_SIZE; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| struct tty_ldisc_ops tty_ldisc_N_TTY = { | ||||
| 	.magic           = TTY_LDISC_MAGIC, | ||||
| 	.name            = "n_tty", | ||||
|  | @ -2188,7 +2201,8 @@ struct tty_ldisc_ops tty_ldisc_N_TTY = { | |||
| 	.set_termios     = n_tty_set_termios, | ||||
| 	.poll            = n_tty_poll, | ||||
| 	.receive_buf     = n_tty_receive_buf, | ||||
| 	.write_wakeup    = n_tty_write_wakeup | ||||
| 	.write_wakeup    = n_tty_write_wakeup, | ||||
| 	.fasync		 = n_tty_fasync, | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  |  | |||
|  | @ -4797,10 +4797,6 @@ static struct pci_device_id serial_pci_tbl[] = { | |||
| 		PCI_VENDOR_ID_IBM, 0x0299, | ||||
| 		0, 0, pbn_b0_bt_2_115200 }, | ||||
| 
 | ||||
| 	{	PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9835, | ||||
| 		0x1000, 0x0012, | ||||
| 		0, 0, pbn_b0_bt_2_115200 }, | ||||
| 
 | ||||
| 	{	PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9901, | ||||
| 		0xA000, 0x1000, | ||||
| 		0, 0, pbn_b0_1_115200 }, | ||||
|  |  | |||
|  | @ -12,9 +12,8 @@ config SERIAL_8250 | |||
| 	  here are those that are setting up dedicated Ethernet WWW/FTP | ||||
| 	  servers, or users that have one of the various bus mice instead of a | ||||
| 	  serial mouse and don't intend to use their machine's standard serial | ||||
| 	  port for anything.  (Note that the Cyclades and Stallion multi | ||||
| 	  serial port drivers do not need this driver built in for them to | ||||
| 	  work.) | ||||
| 	  port for anything.  (Note that the Cyclades multi serial port driver | ||||
| 	  does not need this driver built in for it to work.) | ||||
| 
 | ||||
| 	  To compile this driver as a module, choose M here: the | ||||
| 	  module will be called 8250. | ||||
|  |  | |||
|  | @ -551,7 +551,7 @@ config BFIN_UART3_CTSRTS | |||
| 	  Enable hardware flow control in the driver. | ||||
| 
 | ||||
| config SERIAL_IMX | ||||
| 	bool "IMX serial port support" | ||||
| 	tristate "IMX serial port support" | ||||
| 	depends on ARCH_MXC | ||||
| 	select SERIAL_CORE | ||||
| 	select RATIONAL | ||||
|  | @ -561,22 +561,21 @@ config SERIAL_IMX | |||
| 
 | ||||
| config SERIAL_IMX_CONSOLE | ||||
| 	bool "Console on IMX serial port" | ||||
| 	depends on SERIAL_IMX | ||||
| 	depends on SERIAL_IMX=y | ||||
| 	select SERIAL_CORE_CONSOLE | ||||
| 	help | ||||
| 	  If you have enabled the serial port on the Motorola IMX | ||||
| 	  If you have enabled the serial port on the Freescale IMX | ||||
| 	  CPU you can make it the console by answering Y to this option. | ||||
| 
 | ||||
| 	  Even if you say Y here, the currently visible virtual console | ||||
| 	  (/dev/tty0) will still be used as the system console by default, but | ||||
| 	  you can alter that using a kernel command line option such as | ||||
| 	  "console=ttySA0". (Try "man bootparam" or see the documentation of | ||||
| 	  your boot loader (lilo or loadlin) about how to pass options to the | ||||
| 	  kernel at boot time.) | ||||
| 	  "console=ttymxc0". (Try "man bootparam" or see the documentation of | ||||
| 	  your bootloader about how to pass options to the kernel at boot time.) | ||||
| 
 | ||||
| config SERIAL_UARTLITE | ||||
| 	tristate "Xilinx uartlite serial port support" | ||||
| 	depends on PPC32 || MICROBLAZE || MFD_TIMBERDALE | ||||
| 	depends on PPC32 || MICROBLAZE || MFD_TIMBERDALE || ARCH_ZYNQ | ||||
| 	select SERIAL_CORE | ||||
| 	help | ||||
| 	  Say Y here if you want to use the Xilinx uartlite serial controller. | ||||
|  | @ -1484,6 +1483,20 @@ config SERIAL_RP2_NR_UARTS | |||
| 	  If multiple cards are present, the default limit of 32 ports may | ||||
| 	  need to be increased. | ||||
| 
 | ||||
| config SERIAL_FSL_LPUART | ||||
| 	tristate "Freescale lpuart serial port support" | ||||
| 	select SERIAL_CORE | ||||
| 	help | ||||
| 	  Support for the on-chip lpuart on some Freescale SOCs. | ||||
| 
 | ||||
| config SERIAL_FSL_LPUART_CONSOLE | ||||
| 	bool "Console on Freescale lpuart serial port" | ||||
| 	depends on SERIAL_FSL_LPUART=y | ||||
| 	select SERIAL_CORE_CONSOLE | ||||
| 	help | ||||
| 	  If you have enabled the lpuart serial port on the Freescale SoCs, | ||||
| 	  you can make it the console by answering Y to this option. | ||||
| 
 | ||||
| endmenu | ||||
| 
 | ||||
| endif # TTY | ||||
|  |  | |||
|  | @ -85,3 +85,4 @@ obj-$(CONFIG_SERIAL_AR933X)   += ar933x_uart.o | |||
| obj-$(CONFIG_SERIAL_EFM32_UART) += efm32-uart.o | ||||
| obj-$(CONFIG_SERIAL_ARC)	+= arc_uart.o | ||||
| obj-$(CONFIG_SERIAL_RP2)	+= rp2.o | ||||
| obj-$(CONFIG_SERIAL_FSL_LPUART)	+= fsl_lpuart.o | ||||
|  |  | |||
|  | @ -604,7 +604,6 @@ static int altera_uart_remove(struct platform_device *pdev) | |||
| 
 | ||||
| 	if (port) { | ||||
| 		uart_remove_one_port(&altera_uart_driver, port); | ||||
| 		platform_set_drvdata(pdev, NULL); | ||||
| 		port->mapbase = 0; | ||||
| 	} | ||||
| 
 | ||||
|  |  | |||
|  | @ -79,13 +79,12 @@ struct vendor_data { | |||
| 	bool			dma_threshold; | ||||
| 	bool			cts_event_workaround; | ||||
| 
 | ||||
| 	unsigned int (*get_fifosize)(unsigned int periphid); | ||||
| 	unsigned int (*get_fifosize)(struct amba_device *dev); | ||||
| }; | ||||
| 
 | ||||
| static unsigned int get_fifosize_arm(unsigned int periphid) | ||||
| static unsigned int get_fifosize_arm(struct amba_device *dev) | ||||
| { | ||||
| 	unsigned int rev = (periphid >> 20) & 0xf; | ||||
| 	return rev < 3 ? 16 : 32; | ||||
| 	return amba_rev(dev) < 3 ? 16 : 32; | ||||
| } | ||||
| 
 | ||||
| static struct vendor_data vendor_arm = { | ||||
|  | @ -98,7 +97,7 @@ static struct vendor_data vendor_arm = { | |||
| 	.get_fifosize		= get_fifosize_arm, | ||||
| }; | ||||
| 
 | ||||
| static unsigned int get_fifosize_st(unsigned int periphid) | ||||
| static unsigned int get_fifosize_st(struct amba_device *dev) | ||||
| { | ||||
| 	return 64; | ||||
| } | ||||
|  | @ -2157,7 +2156,7 @@ static int pl011_probe(struct amba_device *dev, const struct amba_id *id) | |||
| 	uap->lcrh_rx = vendor->lcrh_rx; | ||||
| 	uap->lcrh_tx = vendor->lcrh_tx; | ||||
| 	uap->old_cr = 0; | ||||
| 	uap->fifosize = vendor->get_fifosize(dev->periphid); | ||||
| 	uap->fifosize = vendor->get_fifosize(dev); | ||||
| 	uap->port.dev = &dev->dev; | ||||
| 	uap->port.mapbase = dev->res.start; | ||||
| 	uap->port.membase = base; | ||||
|  |  | |||
|  | @ -1100,7 +1100,7 @@ static void atmel_serial_pm(struct uart_port *port, unsigned int state, | |||
| 		 * Enable the peripheral clock for this serial port. | ||||
| 		 * This is called on uart_open() or a resume event. | ||||
| 		 */ | ||||
| 		clk_enable(atmel_port->clk); | ||||
| 		clk_prepare_enable(atmel_port->clk); | ||||
| 
 | ||||
| 		/* re-enable interrupts if we disabled some on suspend */ | ||||
| 		UART_PUT_IER(port, atmel_port->backup_imr); | ||||
|  | @ -1114,7 +1114,7 @@ static void atmel_serial_pm(struct uart_port *port, unsigned int state, | |||
| 		 * Disable the peripheral clock for this serial port. | ||||
| 		 * This is called on uart_close() or a suspend event. | ||||
| 		 */ | ||||
| 		clk_disable(atmel_port->clk); | ||||
| 		clk_disable_unprepare(atmel_port->clk); | ||||
| 		break; | ||||
| 	default: | ||||
| 		printk(KERN_ERR "atmel_serial: unknown pm %d\n", state); | ||||
|  | @ -1458,9 +1458,10 @@ static void atmel_of_init_port(struct atmel_uart_port *atmel_port, | |||
| /*
 | ||||
|  * Configure the port from the platform device resource info. | ||||
|  */ | ||||
| static void atmel_init_port(struct atmel_uart_port *atmel_port, | ||||
| static int atmel_init_port(struct atmel_uart_port *atmel_port, | ||||
| 				      struct platform_device *pdev) | ||||
| { | ||||
| 	int ret; | ||||
| 	struct uart_port *port = &atmel_port->uart; | ||||
| 	struct atmel_uart_data *pdata = pdev->dev.platform_data; | ||||
| 
 | ||||
|  | @ -1496,9 +1497,19 @@ static void atmel_init_port(struct atmel_uart_port *atmel_port, | |||
| 	/* for console, the clock could already be configured */ | ||||
| 	if (!atmel_port->clk) { | ||||
| 		atmel_port->clk = clk_get(&pdev->dev, "usart"); | ||||
| 		clk_enable(atmel_port->clk); | ||||
| 		if (IS_ERR(atmel_port->clk)) { | ||||
| 			ret = PTR_ERR(atmel_port->clk); | ||||
| 			atmel_port->clk = NULL; | ||||
| 			return ret; | ||||
| 		} | ||||
| 		ret = clk_prepare_enable(atmel_port->clk); | ||||
| 		if (ret) { | ||||
| 			clk_put(atmel_port->clk); | ||||
| 			atmel_port->clk = NULL; | ||||
| 			return ret; | ||||
| 		} | ||||
| 		port->uartclk = clk_get_rate(atmel_port->clk); | ||||
| 		clk_disable(atmel_port->clk); | ||||
| 		clk_disable_unprepare(atmel_port->clk); | ||||
| 		/* only enable clock when USART is in use */ | ||||
| 	} | ||||
| 
 | ||||
|  | @ -1511,6 +1522,8 @@ static void atmel_init_port(struct atmel_uart_port *atmel_port, | |||
| 	} else { | ||||
| 		atmel_port->tx_done_mask = ATMEL_US_TXRDY; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| struct platform_device *atmel_default_console_device;	/* the serial console device */ | ||||
|  | @ -1601,6 +1614,7 @@ static void __init atmel_console_get_options(struct uart_port *port, int *baud, | |||
| 
 | ||||
| static int __init atmel_console_setup(struct console *co, char *options) | ||||
| { | ||||
| 	int ret; | ||||
| 	struct uart_port *port = &atmel_ports[co->index].uart; | ||||
| 	int baud = 115200; | ||||
| 	int bits = 8; | ||||
|  | @ -1612,7 +1626,9 @@ static int __init atmel_console_setup(struct console *co, char *options) | |||
| 		return -ENODEV; | ||||
| 	} | ||||
| 
 | ||||
| 	clk_enable(atmel_ports[co->index].clk); | ||||
| 	ret = clk_prepare_enable(atmel_ports[co->index].clk); | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	UART_PUT_IDR(port, -1); | ||||
| 	UART_PUT_CR(port, ATMEL_US_RSTSTA | ATMEL_US_RSTRX); | ||||
|  | @ -1645,6 +1661,7 @@ static struct console atmel_console = { | |||
|  */ | ||||
| static int __init atmel_console_init(void) | ||||
| { | ||||
| 	int ret; | ||||
| 	if (atmel_default_console_device) { | ||||
| 		struct atmel_uart_data *pdata = | ||||
| 			atmel_default_console_device->dev.platform_data; | ||||
|  | @ -1655,7 +1672,9 @@ static int __init atmel_console_init(void) | |||
| 		port->uart.line = id; | ||||
| 
 | ||||
| 		add_preferred_console(ATMEL_DEVICENAME, id, NULL); | ||||
| 		atmel_init_port(port, atmel_default_console_device); | ||||
| 		ret = atmel_init_port(port, atmel_default_console_device); | ||||
| 		if (ret) | ||||
| 			return ret; | ||||
| 		register_console(&atmel_console); | ||||
| 	} | ||||
| 
 | ||||
|  | @ -1786,7 +1805,9 @@ static int atmel_serial_probe(struct platform_device *pdev) | |||
| 	port->backup_imr = 0; | ||||
| 	port->uart.line = ret; | ||||
| 
 | ||||
| 	atmel_init_port(port, pdev); | ||||
| 	ret = atmel_init_port(port, pdev); | ||||
| 	if (ret) | ||||
| 		goto err; | ||||
| 
 | ||||
| 	pinctrl = devm_pinctrl_get_select_default(&pdev->dev); | ||||
| 	if (IS_ERR(pinctrl)) { | ||||
|  | @ -1812,9 +1833,9 @@ static int atmel_serial_probe(struct platform_device *pdev) | |||
| 			&& ATMEL_CONSOLE_DEVICE->flags & CON_ENABLED) { | ||||
| 		/*
 | ||||
| 		 * The serial core enabled the clock for us, so undo | ||||
| 		 * the clk_enable() in atmel_console_setup() | ||||
| 		 * the clk_prepare_enable() in atmel_console_setup() | ||||
| 		 */ | ||||
| 		clk_disable(port->clk); | ||||
| 		clk_disable_unprepare(port->clk); | ||||
| 	} | ||||
| #endif | ||||
| 
 | ||||
|  |  | |||
|  | @ -1384,7 +1384,7 @@ static int cpm_uart_probe(struct platform_device *ofdev) | |||
| 	if (index >= UART_NR) | ||||
| 		return -ENODEV; | ||||
| 
 | ||||
| 	dev_set_drvdata(&ofdev->dev, pinfo); | ||||
| 	platform_set_drvdata(ofdev, pinfo); | ||||
| 
 | ||||
| 	/* initialize the device pointer for the port */ | ||||
| 	pinfo->port.dev = &ofdev->dev; | ||||
|  | @ -1398,7 +1398,7 @@ static int cpm_uart_probe(struct platform_device *ofdev) | |||
| 
 | ||||
| static int cpm_uart_remove(struct platform_device *ofdev) | ||||
| { | ||||
| 	struct uart_cpm_port *pinfo = dev_get_drvdata(&ofdev->dev); | ||||
| 	struct uart_cpm_port *pinfo = platform_get_drvdata(ofdev); | ||||
| 	return uart_remove_one_port(&cpm_reg, &pinfo->port); | ||||
| } | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										874
									
								
								drivers/tty/serial/fsl_lpuart.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										874
									
								
								drivers/tty/serial/fsl_lpuart.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,874 @@ | |||
| /*
 | ||||
|  *  Freescale lpuart serial port driver | ||||
|  * | ||||
|  *  Copyright 2012-2013 Freescale Semiconductor, Inc. | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License as published by | ||||
|  * the Free Software Foundation; either version 2 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  */ | ||||
| 
 | ||||
| #if defined(CONFIG_SERIAL_FSL_LPUART_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) | ||||
| #define SUPPORT_SYSRQ | ||||
| #endif | ||||
| 
 | ||||
| #include <linux/module.h> | ||||
| #include <linux/io.h> | ||||
| #include <linux/irq.h> | ||||
| #include <linux/clk.h> | ||||
| #include <linux/of.h> | ||||
| #include <linux/of_device.h> | ||||
| #include <linux/console.h> | ||||
| #include <linux/serial_core.h> | ||||
| #include <linux/tty_flip.h> | ||||
| 
 | ||||
| /* All registers are 8-bit width */ | ||||
| #define UARTBDH			0x00 | ||||
| #define UARTBDL			0x01 | ||||
| #define UARTCR1			0x02 | ||||
| #define UARTCR2			0x03 | ||||
| #define UARTSR1			0x04 | ||||
| #define UARTCR3			0x06 | ||||
| #define UARTDR			0x07 | ||||
| #define UARTCR4			0x0a | ||||
| #define UARTCR5			0x0b | ||||
| #define UARTMODEM		0x0d | ||||
| #define UARTPFIFO		0x10 | ||||
| #define UARTCFIFO		0x11 | ||||
| #define UARTSFIFO		0x12 | ||||
| #define UARTTWFIFO		0x13 | ||||
| #define UARTTCFIFO		0x14 | ||||
| #define UARTRWFIFO		0x15 | ||||
| 
 | ||||
| #define UARTBDH_LBKDIE		0x80 | ||||
| #define UARTBDH_RXEDGIE		0x40 | ||||
| #define UARTBDH_SBR_MASK	0x1f | ||||
| 
 | ||||
| #define UARTCR1_LOOPS		0x80 | ||||
| #define UARTCR1_RSRC		0x20 | ||||
| #define UARTCR1_M		0x10 | ||||
| #define UARTCR1_WAKE		0x08 | ||||
| #define UARTCR1_ILT		0x04 | ||||
| #define UARTCR1_PE		0x02 | ||||
| #define UARTCR1_PT		0x01 | ||||
| 
 | ||||
| #define UARTCR2_TIE		0x80 | ||||
| #define UARTCR2_TCIE		0x40 | ||||
| #define UARTCR2_RIE		0x20 | ||||
| #define UARTCR2_ILIE		0x10 | ||||
| #define UARTCR2_TE		0x08 | ||||
| #define UARTCR2_RE		0x04 | ||||
| #define UARTCR2_RWU		0x02 | ||||
| #define UARTCR2_SBK		0x01 | ||||
| 
 | ||||
| #define UARTSR1_TDRE		0x80 | ||||
| #define UARTSR1_TC		0x40 | ||||
| #define UARTSR1_RDRF		0x20 | ||||
| #define UARTSR1_IDLE		0x10 | ||||
| #define UARTSR1_OR		0x08 | ||||
| #define UARTSR1_NF		0x04 | ||||
| #define UARTSR1_FE		0x02 | ||||
| #define UARTSR1_PE		0x01 | ||||
| 
 | ||||
| #define UARTCR3_R8		0x80 | ||||
| #define UARTCR3_T8		0x40 | ||||
| #define UARTCR3_TXDIR		0x20 | ||||
| #define UARTCR3_TXINV		0x10 | ||||
| #define UARTCR3_ORIE		0x08 | ||||
| #define UARTCR3_NEIE		0x04 | ||||
| #define UARTCR3_FEIE		0x02 | ||||
| #define UARTCR3_PEIE		0x01 | ||||
| 
 | ||||
| #define UARTCR4_MAEN1		0x80 | ||||
| #define UARTCR4_MAEN2		0x40 | ||||
| #define UARTCR4_M10		0x20 | ||||
| #define UARTCR4_BRFA_MASK	0x1f | ||||
| #define UARTCR4_BRFA_OFF	0 | ||||
| 
 | ||||
| #define UARTCR5_TDMAS		0x80 | ||||
| #define UARTCR5_RDMAS		0x20 | ||||
| 
 | ||||
| #define UARTMODEM_RXRTSE	0x08 | ||||
| #define UARTMODEM_TXRTSPOL	0x04 | ||||
| #define UARTMODEM_TXRTSE	0x02 | ||||
| #define UARTMODEM_TXCTSE	0x01 | ||||
| 
 | ||||
| #define UARTPFIFO_TXFE		0x80 | ||||
| #define UARTPFIFO_FIFOSIZE_MASK	0x7 | ||||
| #define UARTPFIFO_TXSIZE_OFF	4 | ||||
| #define UARTPFIFO_RXFE		0x08 | ||||
| #define UARTPFIFO_RXSIZE_OFF	0 | ||||
| 
 | ||||
| #define UARTCFIFO_TXFLUSH	0x80 | ||||
| #define UARTCFIFO_RXFLUSH	0x40 | ||||
| #define UARTCFIFO_RXOFE		0x04 | ||||
| #define UARTCFIFO_TXOFE		0x02 | ||||
| #define UARTCFIFO_RXUFE		0x01 | ||||
| 
 | ||||
| #define UARTSFIFO_TXEMPT	0x80 | ||||
| #define UARTSFIFO_RXEMPT	0x40 | ||||
| #define UARTSFIFO_RXOF		0x04 | ||||
| #define UARTSFIFO_TXOF		0x02 | ||||
| #define UARTSFIFO_RXUF		0x01 | ||||
| 
 | ||||
| #define DRIVER_NAME	"fsl-lpuart" | ||||
| #define DEV_NAME	"ttyLP" | ||||
| #define UART_NR		6 | ||||
| 
 | ||||
| struct lpuart_port { | ||||
| 	struct uart_port	port; | ||||
| 	struct clk		*clk; | ||||
| 	unsigned int		txfifo_size; | ||||
| 	unsigned int		rxfifo_size; | ||||
| }; | ||||
| 
 | ||||
| static struct of_device_id lpuart_dt_ids[] = { | ||||
| 	{ | ||||
| 		.compatible = "fsl,vf610-lpuart", | ||||
| 	}, | ||||
| 	{ /* sentinel */ } | ||||
| }; | ||||
| MODULE_DEVICE_TABLE(of, lpuart_dt_ids); | ||||
| 
 | ||||
| static void lpuart_stop_tx(struct uart_port *port) | ||||
| { | ||||
| 	unsigned char temp; | ||||
| 
 | ||||
| 	temp = readb(port->membase + UARTCR2); | ||||
| 	temp &= ~(UARTCR2_TIE | UARTCR2_TCIE); | ||||
| 	writeb(temp, port->membase + UARTCR2); | ||||
| } | ||||
| 
 | ||||
| static void lpuart_stop_rx(struct uart_port *port) | ||||
| { | ||||
| 	unsigned char temp; | ||||
| 
 | ||||
| 	temp = readb(port->membase + UARTCR2); | ||||
| 	writeb(temp & ~UARTCR2_RE, port->membase + UARTCR2); | ||||
| } | ||||
| 
 | ||||
| static void lpuart_enable_ms(struct uart_port *port) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| static inline void lpuart_transmit_buffer(struct lpuart_port *sport) | ||||
| { | ||||
| 	struct circ_buf *xmit = &sport->port.state->xmit; | ||||
| 
 | ||||
| 	while (!uart_circ_empty(xmit) && | ||||
| 		(readb(sport->port.membase + UARTTCFIFO) < sport->txfifo_size)) { | ||||
| 		writeb(xmit->buf[xmit->tail], sport->port.membase + UARTDR); | ||||
| 		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); | ||||
| 		sport->port.icount.tx++; | ||||
| 	} | ||||
| 
 | ||||
| 	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) | ||||
| 		uart_write_wakeup(&sport->port); | ||||
| 
 | ||||
| 	if (uart_circ_empty(xmit)) | ||||
| 		lpuart_stop_tx(&sport->port); | ||||
| } | ||||
| 
 | ||||
| static void lpuart_start_tx(struct uart_port *port) | ||||
| { | ||||
| 	struct lpuart_port *sport = container_of(port, struct lpuart_port, port); | ||||
| 	unsigned char temp; | ||||
| 
 | ||||
| 	temp = readb(port->membase + UARTCR2); | ||||
| 	writeb(temp | UARTCR2_TIE, port->membase + UARTCR2); | ||||
| 
 | ||||
| 	if (readb(port->membase + UARTSR1) & UARTSR1_TDRE) | ||||
| 		lpuart_transmit_buffer(sport); | ||||
| } | ||||
| 
 | ||||
| static irqreturn_t lpuart_txint(int irq, void *dev_id) | ||||
| { | ||||
| 	struct lpuart_port *sport = dev_id; | ||||
| 	struct circ_buf *xmit = &sport->port.state->xmit; | ||||
| 	unsigned long flags; | ||||
| 
 | ||||
| 	spin_lock_irqsave(&sport->port.lock, flags); | ||||
| 	if (sport->port.x_char) { | ||||
| 		writeb(sport->port.x_char, sport->port.membase + UARTDR); | ||||
| 		goto out; | ||||
| 	} | ||||
| 
 | ||||
| 	if (uart_circ_empty(xmit) || uart_tx_stopped(&sport->port)) { | ||||
| 		lpuart_stop_tx(&sport->port); | ||||
| 		goto out; | ||||
| 	} | ||||
| 
 | ||||
| 	lpuart_transmit_buffer(sport); | ||||
| 
 | ||||
| 	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) | ||||
| 		uart_write_wakeup(&sport->port); | ||||
| 
 | ||||
| out: | ||||
| 	spin_unlock_irqrestore(&sport->port.lock, flags); | ||||
| 	return IRQ_HANDLED; | ||||
| } | ||||
| 
 | ||||
| static irqreturn_t lpuart_rxint(int irq, void *dev_id) | ||||
| { | ||||
| 	struct lpuart_port *sport = dev_id; | ||||
| 	unsigned int flg, ignored = 0; | ||||
| 	struct tty_port *port = &sport->port.state->port; | ||||
| 	unsigned long flags; | ||||
| 	unsigned char rx, sr; | ||||
| 
 | ||||
| 	spin_lock_irqsave(&sport->port.lock, flags); | ||||
| 
 | ||||
| 	while (!(readb(sport->port.membase + UARTSFIFO) & UARTSFIFO_RXEMPT)) { | ||||
| 		flg = TTY_NORMAL; | ||||
| 		sport->port.icount.rx++; | ||||
| 		/*
 | ||||
| 		 * to clear the FE, OR, NF, FE, PE flags, | ||||
| 		 * read SR1 then read DR | ||||
| 		 */ | ||||
| 		sr = readb(sport->port.membase + UARTSR1); | ||||
| 		rx = readb(sport->port.membase + UARTDR); | ||||
| 
 | ||||
| 		if (uart_handle_sysrq_char(&sport->port, (unsigned char)rx)) | ||||
| 			continue; | ||||
| 
 | ||||
| 		if (sr & (UARTSR1_PE | UARTSR1_OR | UARTSR1_FE)) { | ||||
| 			if (sr & UARTSR1_PE) | ||||
| 				sport->port.icount.parity++; | ||||
| 			else if (sr & UARTSR1_FE) | ||||
| 				sport->port.icount.frame++; | ||||
| 
 | ||||
| 			if (sr & UARTSR1_OR) | ||||
| 				sport->port.icount.overrun++; | ||||
| 
 | ||||
| 			if (sr & sport->port.ignore_status_mask) { | ||||
| 				if (++ignored > 100) | ||||
| 					goto out; | ||||
| 				continue; | ||||
| 			} | ||||
| 
 | ||||
| 			sr &= sport->port.read_status_mask; | ||||
| 
 | ||||
| 			if (sr & UARTSR1_PE) | ||||
| 				flg = TTY_PARITY; | ||||
| 			else if (sr & UARTSR1_FE) | ||||
| 				flg = TTY_FRAME; | ||||
| 
 | ||||
| 			if (sr & UARTSR1_OR) | ||||
| 				flg = TTY_OVERRUN; | ||||
| 
 | ||||
| #ifdef SUPPORT_SYSRQ | ||||
| 			sport->port.sysrq = 0; | ||||
| #endif | ||||
| 		} | ||||
| 
 | ||||
| 		tty_insert_flip_char(port, rx, flg); | ||||
| 	} | ||||
| 
 | ||||
| out: | ||||
| 	spin_unlock_irqrestore(&sport->port.lock, flags); | ||||
| 
 | ||||
| 	tty_flip_buffer_push(port); | ||||
| 	return IRQ_HANDLED; | ||||
| } | ||||
| 
 | ||||
| static irqreturn_t lpuart_int(int irq, void *dev_id) | ||||
| { | ||||
| 	struct lpuart_port *sport = dev_id; | ||||
| 	unsigned char sts; | ||||
| 
 | ||||
| 	sts = readb(sport->port.membase + UARTSR1); | ||||
| 
 | ||||
| 	if (sts & UARTSR1_RDRF) | ||||
| 		lpuart_rxint(irq, dev_id); | ||||
| 
 | ||||
| 	if (sts & UARTSR1_TDRE && | ||||
| 		!(readb(sport->port.membase + UARTCR5) & UARTCR5_TDMAS)) | ||||
| 		lpuart_txint(irq, dev_id); | ||||
| 
 | ||||
| 	return IRQ_HANDLED; | ||||
| } | ||||
| 
 | ||||
| /* return TIOCSER_TEMT when transmitter is not busy */ | ||||
| static unsigned int lpuart_tx_empty(struct uart_port *port) | ||||
| { | ||||
| 	return (readb(port->membase + UARTSR1) & UARTSR1_TC) ? | ||||
| 		TIOCSER_TEMT : 0; | ||||
| } | ||||
| 
 | ||||
| static unsigned int lpuart_get_mctrl(struct uart_port *port) | ||||
| { | ||||
| 	unsigned int temp = 0; | ||||
| 	unsigned char reg; | ||||
| 
 | ||||
| 	reg = readb(port->membase + UARTMODEM); | ||||
| 	if (reg & UARTMODEM_TXCTSE) | ||||
| 		temp |= TIOCM_CTS; | ||||
| 
 | ||||
| 	if (reg & UARTMODEM_RXRTSE) | ||||
| 		temp |= TIOCM_RTS; | ||||
| 
 | ||||
| 	return temp; | ||||
| } | ||||
| 
 | ||||
| static void lpuart_set_mctrl(struct uart_port *port, unsigned int mctrl) | ||||
| { | ||||
| 	unsigned char temp; | ||||
| 
 | ||||
| 	temp = readb(port->membase + UARTMODEM) & | ||||
| 			~(UARTMODEM_RXRTSE | UARTMODEM_TXCTSE); | ||||
| 
 | ||||
| 	if (mctrl & TIOCM_RTS) | ||||
| 		temp |= UARTMODEM_RXRTSE; | ||||
| 
 | ||||
| 	if (mctrl & TIOCM_CTS) | ||||
| 		temp |= UARTMODEM_TXCTSE; | ||||
| 
 | ||||
| 	writeb(temp, port->membase + UARTMODEM); | ||||
| } | ||||
| 
 | ||||
| static void lpuart_break_ctl(struct uart_port *port, int break_state) | ||||
| { | ||||
| 	unsigned char temp; | ||||
| 
 | ||||
| 	temp = readb(port->membase + UARTCR2) & ~UARTCR2_SBK; | ||||
| 
 | ||||
| 	if (break_state != 0) | ||||
| 		temp |= UARTCR2_SBK; | ||||
| 
 | ||||
| 	writeb(temp, port->membase + UARTCR2); | ||||
| } | ||||
| 
 | ||||
| static void lpuart_setup_watermark(struct lpuart_port *sport) | ||||
| { | ||||
| 	unsigned char val, cr2; | ||||
| 
 | ||||
| 	cr2 = readb(sport->port.membase + UARTCR2); | ||||
| 	cr2 &= ~(UARTCR2_TIE | UARTCR2_TCIE | UARTCR2_TE | | ||||
| 			UARTCR2_RIE | UARTCR2_RE); | ||||
| 	writeb(cr2, sport->port.membase + UARTCR2); | ||||
| 
 | ||||
| 	/* determine FIFO size and enable FIFO mode */ | ||||
| 	val = readb(sport->port.membase + UARTPFIFO); | ||||
| 
 | ||||
| 	sport->txfifo_size = 0x1 << (((val >> UARTPFIFO_TXSIZE_OFF) & | ||||
| 		UARTPFIFO_FIFOSIZE_MASK) + 1); | ||||
| 
 | ||||
| 	sport->rxfifo_size = 0x1 << (((val >> UARTPFIFO_RXSIZE_OFF) & | ||||
| 		UARTPFIFO_FIFOSIZE_MASK) + 1); | ||||
| 
 | ||||
| 	writeb(val | UARTPFIFO_TXFE | UARTPFIFO_RXFE, | ||||
| 			sport->port.membase + UARTPFIFO); | ||||
| 
 | ||||
| 	/* flush Tx and Rx FIFO */ | ||||
| 	writeb(UARTCFIFO_TXFLUSH | UARTCFIFO_RXFLUSH, | ||||
| 			sport->port.membase + UARTCFIFO); | ||||
| 
 | ||||
| 	writeb(2, sport->port.membase + UARTTWFIFO); | ||||
| 	writeb(1, sport->port.membase + UARTRWFIFO); | ||||
| } | ||||
| 
 | ||||
| static int lpuart_startup(struct uart_port *port) | ||||
| { | ||||
| 	struct lpuart_port *sport = container_of(port, struct lpuart_port, port); | ||||
| 	int ret; | ||||
| 	unsigned long flags; | ||||
| 	unsigned char temp; | ||||
| 
 | ||||
| 	ret = devm_request_irq(port->dev, port->irq, lpuart_int, 0, | ||||
| 				DRIVER_NAME, sport); | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	spin_lock_irqsave(&sport->port.lock, flags); | ||||
| 
 | ||||
| 	lpuart_setup_watermark(sport); | ||||
| 
 | ||||
| 	temp = readb(sport->port.membase + UARTCR2); | ||||
| 	temp |= (UARTCR2_RIE | UARTCR2_TIE | UARTCR2_RE | UARTCR2_TE); | ||||
| 	writeb(temp, sport->port.membase + UARTCR2); | ||||
| 
 | ||||
| 	spin_unlock_irqrestore(&sport->port.lock, flags); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void lpuart_shutdown(struct uart_port *port) | ||||
| { | ||||
| 	struct lpuart_port *sport = container_of(port, struct lpuart_port, port); | ||||
| 	unsigned char temp; | ||||
| 	unsigned long flags; | ||||
| 
 | ||||
| 	spin_lock_irqsave(&port->lock, flags); | ||||
| 
 | ||||
| 	/* disable Rx/Tx and interrupts */ | ||||
| 	temp = readb(port->membase + UARTCR2); | ||||
| 	temp &= ~(UARTCR2_TE | UARTCR2_RE | | ||||
| 			UARTCR2_TIE | UARTCR2_TCIE | UARTCR2_RIE); | ||||
| 	writeb(temp, port->membase + UARTCR2); | ||||
| 
 | ||||
| 	spin_unlock_irqrestore(&port->lock, flags); | ||||
| 
 | ||||
| 	devm_free_irq(port->dev, port->irq, sport); | ||||
| } | ||||
| 
 | ||||
| static void | ||||
| lpuart_set_termios(struct uart_port *port, struct ktermios *termios, | ||||
| 		   struct ktermios *old) | ||||
| { | ||||
| 	struct lpuart_port *sport = container_of(port, struct lpuart_port, port); | ||||
| 	unsigned long flags; | ||||
| 	unsigned char cr1, old_cr1, old_cr2, cr4, bdh, modem; | ||||
| 	unsigned int  baud; | ||||
| 	unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8; | ||||
| 	unsigned int sbr, brfa; | ||||
| 
 | ||||
| 	cr1 = old_cr1 = readb(sport->port.membase + UARTCR1); | ||||
| 	old_cr2 = readb(sport->port.membase + UARTCR2); | ||||
| 	cr4 = readb(sport->port.membase + UARTCR4); | ||||
| 	bdh = readb(sport->port.membase + UARTBDH); | ||||
| 	modem = readb(sport->port.membase + UARTMODEM); | ||||
| 	/*
 | ||||
| 	 * only support CS8 and CS7, and for CS7 must enable PE. | ||||
| 	 * supported mode: | ||||
| 	 *  - (7,e/o,1) | ||||
| 	 *  - (8,n,1) | ||||
| 	 *  - (8,m/s,1) | ||||
| 	 *  - (8,e/o,1) | ||||
| 	 */ | ||||
| 	while ((termios->c_cflag & CSIZE) != CS8 && | ||||
| 		(termios->c_cflag & CSIZE) != CS7) { | ||||
| 		termios->c_cflag &= ~CSIZE; | ||||
| 		termios->c_cflag |= old_csize; | ||||
| 		old_csize = CS8; | ||||
| 	} | ||||
| 
 | ||||
| 	if ((termios->c_cflag & CSIZE) == CS8 || | ||||
| 		(termios->c_cflag & CSIZE) == CS7) | ||||
| 		cr1 = old_cr1 & ~UARTCR1_M; | ||||
| 
 | ||||
| 	if (termios->c_cflag & CMSPAR) { | ||||
| 		if ((termios->c_cflag & CSIZE) != CS8) { | ||||
| 			termios->c_cflag &= ~CSIZE; | ||||
| 			termios->c_cflag |= CS8; | ||||
| 		} | ||||
| 		cr1 |= UARTCR1_M; | ||||
| 	} | ||||
| 
 | ||||
| 	if (termios->c_cflag & CRTSCTS) { | ||||
| 		modem |= (UARTMODEM_RXRTSE | UARTMODEM_TXCTSE); | ||||
| 	} else { | ||||
| 		termios->c_cflag &= ~CRTSCTS; | ||||
| 		modem &= ~(UARTMODEM_RXRTSE | UARTMODEM_TXCTSE); | ||||
| 	} | ||||
| 
 | ||||
| 	if (termios->c_cflag & CSTOPB) | ||||
| 		termios->c_cflag &= ~CSTOPB; | ||||
| 
 | ||||
| 	/* parity must be enabled when CS7 to match 8-bits format */ | ||||
| 	if ((termios->c_cflag & CSIZE) == CS7) | ||||
| 		termios->c_cflag |= PARENB; | ||||
| 
 | ||||
| 	if ((termios->c_cflag & PARENB)) { | ||||
| 		if (termios->c_cflag & CMSPAR) { | ||||
| 			cr1 &= ~UARTCR1_PE; | ||||
| 			cr1 |= UARTCR1_M; | ||||
| 		} else { | ||||
| 			cr1 |= UARTCR1_PE; | ||||
| 			if ((termios->c_cflag & CSIZE) == CS8) | ||||
| 				cr1 |= UARTCR1_M; | ||||
| 			if (termios->c_cflag & PARODD) | ||||
| 				cr1 |= UARTCR1_PT; | ||||
| 			else | ||||
| 				cr1 &= ~UARTCR1_PT; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	/* ask the core to calculate the divisor */ | ||||
| 	baud = uart_get_baud_rate(port, termios, old, 50, port->uartclk / 16); | ||||
| 
 | ||||
| 	spin_lock_irqsave(&sport->port.lock, flags); | ||||
| 
 | ||||
| 	sport->port.read_status_mask = 0; | ||||
| 	if (termios->c_iflag & INPCK) | ||||
| 		sport->port.read_status_mask |=	(UARTSR1_FE | UARTSR1_PE); | ||||
| 	if (termios->c_iflag & (BRKINT | PARMRK)) | ||||
| 		sport->port.read_status_mask |= UARTSR1_FE; | ||||
| 
 | ||||
| 	/* characters to ignore */ | ||||
| 	sport->port.ignore_status_mask = 0; | ||||
| 	if (termios->c_iflag & IGNPAR) | ||||
| 		sport->port.ignore_status_mask |= UARTSR1_PE; | ||||
| 	if (termios->c_iflag & IGNBRK) { | ||||
| 		sport->port.ignore_status_mask |= UARTSR1_FE; | ||||
| 		/*
 | ||||
| 		 * if we're ignoring parity and break indicators, | ||||
| 		 * ignore overruns too (for real raw support). | ||||
| 		 */ | ||||
| 		if (termios->c_iflag & IGNPAR) | ||||
| 			sport->port.ignore_status_mask |= UARTSR1_OR; | ||||
| 	} | ||||
| 
 | ||||
| 	/* update the per-port timeout */ | ||||
| 	uart_update_timeout(port, termios->c_cflag, baud); | ||||
| 
 | ||||
| 	/* wait transmit engin complete */ | ||||
| 	while (!(readb(sport->port.membase + UARTSR1) & UARTSR1_TC)) | ||||
| 		barrier(); | ||||
| 
 | ||||
| 	/* disable transmit and receive */ | ||||
| 	writeb(old_cr2 & ~(UARTCR2_TE | UARTCR2_RE), | ||||
| 			sport->port.membase + UARTCR2); | ||||
| 
 | ||||
| 	sbr = sport->port.uartclk / (16 * baud); | ||||
| 	brfa = ((sport->port.uartclk - (16 * sbr * baud)) * 2) / baud; | ||||
| 	bdh &= ~UARTBDH_SBR_MASK; | ||||
| 	bdh |= (sbr >> 8) & 0x1F; | ||||
| 	cr4 &= ~UARTCR4_BRFA_MASK; | ||||
| 	brfa &= UARTCR4_BRFA_MASK; | ||||
| 	writeb(cr4 | brfa, sport->port.membase + UARTCR4); | ||||
| 	writeb(bdh, sport->port.membase + UARTBDH); | ||||
| 	writeb(sbr & 0xFF, sport->port.membase + UARTBDL); | ||||
| 	writeb(cr1, sport->port.membase + UARTCR1); | ||||
| 	writeb(modem, sport->port.membase + UARTMODEM); | ||||
| 
 | ||||
| 	/* restore control register */ | ||||
| 	writeb(old_cr2, sport->port.membase + UARTCR2); | ||||
| 
 | ||||
| 	spin_unlock_irqrestore(&sport->port.lock, flags); | ||||
| } | ||||
| 
 | ||||
| static const char *lpuart_type(struct uart_port *port) | ||||
| { | ||||
| 	return "FSL_LPUART"; | ||||
| } | ||||
| 
 | ||||
| static void lpuart_release_port(struct uart_port *port) | ||||
| { | ||||
| 	/* nothing to do */ | ||||
| } | ||||
| 
 | ||||
| static int lpuart_request_port(struct uart_port *port) | ||||
| { | ||||
| 	return  0; | ||||
| } | ||||
| 
 | ||||
| /* configure/autoconfigure the port */ | ||||
| static void lpuart_config_port(struct uart_port *port, int flags) | ||||
| { | ||||
| 	if (flags & UART_CONFIG_TYPE) | ||||
| 		port->type = PORT_LPUART; | ||||
| } | ||||
| 
 | ||||
| static int lpuart_verify_port(struct uart_port *port, struct serial_struct *ser) | ||||
| { | ||||
| 	int ret = 0; | ||||
| 
 | ||||
| 	if (ser->type != PORT_UNKNOWN && ser->type != PORT_LPUART) | ||||
| 		ret = -EINVAL; | ||||
| 	if (port->irq != ser->irq) | ||||
| 		ret = -EINVAL; | ||||
| 	if (ser->io_type != UPIO_MEM) | ||||
| 		ret = -EINVAL; | ||||
| 	if (port->uartclk / 16 != ser->baud_base) | ||||
| 		ret = -EINVAL; | ||||
| 	if (port->iobase != ser->port) | ||||
| 		ret = -EINVAL; | ||||
| 	if (ser->hub6 != 0) | ||||
| 		ret = -EINVAL; | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static struct uart_ops lpuart_pops = { | ||||
| 	.tx_empty	= lpuart_tx_empty, | ||||
| 	.set_mctrl	= lpuart_set_mctrl, | ||||
| 	.get_mctrl	= lpuart_get_mctrl, | ||||
| 	.stop_tx	= lpuart_stop_tx, | ||||
| 	.start_tx	= lpuart_start_tx, | ||||
| 	.stop_rx	= lpuart_stop_rx, | ||||
| 	.enable_ms	= lpuart_enable_ms, | ||||
| 	.break_ctl	= lpuart_break_ctl, | ||||
| 	.startup	= lpuart_startup, | ||||
| 	.shutdown	= lpuart_shutdown, | ||||
| 	.set_termios	= lpuart_set_termios, | ||||
| 	.type		= lpuart_type, | ||||
| 	.request_port	= lpuart_request_port, | ||||
| 	.release_port	= lpuart_release_port, | ||||
| 	.config_port	= lpuart_config_port, | ||||
| 	.verify_port	= lpuart_verify_port, | ||||
| }; | ||||
| 
 | ||||
| static struct lpuart_port *lpuart_ports[UART_NR]; | ||||
| 
 | ||||
| #ifdef CONFIG_SERIAL_FSL_LPUART_CONSOLE | ||||
| static void lpuart_console_putchar(struct uart_port *port, int ch) | ||||
| { | ||||
| 	while (!(readb(port->membase + UARTSR1) & UARTSR1_TDRE)) | ||||
| 		barrier(); | ||||
| 
 | ||||
| 	writeb(ch, port->membase + UARTDR); | ||||
| } | ||||
| 
 | ||||
| static void | ||||
| lpuart_console_write(struct console *co, const char *s, unsigned int count) | ||||
| { | ||||
| 	struct lpuart_port *sport = lpuart_ports[co->index]; | ||||
| 	unsigned char  old_cr2, cr2; | ||||
| 
 | ||||
| 	/* first save CR2 and then disable interrupts */ | ||||
| 	cr2 = old_cr2 = readb(sport->port.membase + UARTCR2); | ||||
| 	cr2 |= (UARTCR2_TE |  UARTCR2_RE); | ||||
| 	cr2 &= ~(UARTCR2_TIE | UARTCR2_TCIE | UARTCR2_RIE); | ||||
| 	writeb(cr2, sport->port.membase + UARTCR2); | ||||
| 
 | ||||
| 	uart_console_write(&sport->port, s, count, lpuart_console_putchar); | ||||
| 
 | ||||
| 	/* wait for transmitter finish complete and restore CR2 */ | ||||
| 	while (!(readb(sport->port.membase + UARTSR1) & UARTSR1_TC)) | ||||
| 		barrier(); | ||||
| 
 | ||||
| 	writeb(old_cr2, sport->port.membase + UARTCR2); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * if the port was already initialised (eg, by a boot loader), | ||||
|  * try to determine the current setup. | ||||
|  */ | ||||
| static void __init | ||||
| lpuart_console_get_options(struct lpuart_port *sport, int *baud, | ||||
| 			   int *parity, int *bits) | ||||
| { | ||||
| 	unsigned char cr, bdh, bdl, brfa; | ||||
| 	unsigned int sbr, uartclk, baud_raw; | ||||
| 
 | ||||
| 	cr = readb(sport->port.membase + UARTCR2); | ||||
| 	cr &= UARTCR2_TE | UARTCR2_RE; | ||||
| 	if (!cr) | ||||
| 		return; | ||||
| 
 | ||||
| 	/* ok, the port was enabled */ | ||||
| 
 | ||||
| 	cr = readb(sport->port.membase + UARTCR1); | ||||
| 
 | ||||
| 	*parity = 'n'; | ||||
| 	if (cr & UARTCR1_PE) { | ||||
| 		if (cr & UARTCR1_PT) | ||||
| 			*parity = 'o'; | ||||
| 		else | ||||
| 			*parity = 'e'; | ||||
| 	} | ||||
| 
 | ||||
| 	if (cr & UARTCR1_M) | ||||
| 		*bits = 9; | ||||
| 	else | ||||
| 		*bits = 8; | ||||
| 
 | ||||
| 	bdh = readb(sport->port.membase + UARTBDH); | ||||
| 	bdh &= UARTBDH_SBR_MASK; | ||||
| 	bdl = readb(sport->port.membase + UARTBDL); | ||||
| 	sbr = bdh; | ||||
| 	sbr <<= 8; | ||||
| 	sbr |= bdl; | ||||
| 	brfa = readb(sport->port.membase + UARTCR4); | ||||
| 	brfa &= UARTCR4_BRFA_MASK; | ||||
| 
 | ||||
| 	uartclk = clk_get_rate(sport->clk); | ||||
| 	/*
 | ||||
| 	 * baud = mod_clk/(16*(sbr[13]+(brfa)/32) | ||||
| 	 */ | ||||
| 	baud_raw = uartclk / (16 * (sbr + brfa / 32)); | ||||
| 
 | ||||
| 	if (*baud != baud_raw) | ||||
| 		printk(KERN_INFO "Serial: Console lpuart rounded baud rate" | ||||
| 				"from %d to %d\n", baud_raw, *baud); | ||||
| } | ||||
| 
 | ||||
| static int __init lpuart_console_setup(struct console *co, char *options) | ||||
| { | ||||
| 	struct lpuart_port *sport; | ||||
| 	int baud = 115200; | ||||
| 	int bits = 8; | ||||
| 	int parity = 'n'; | ||||
| 	int flow = 'n'; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * check whether an invalid uart number has been specified, and | ||||
| 	 * if so, search for the first available port that does have | ||||
| 	 * console support. | ||||
| 	 */ | ||||
| 	if (co->index == -1 || co->index >= ARRAY_SIZE(lpuart_ports)) | ||||
| 		co->index = 0; | ||||
| 
 | ||||
| 	sport = lpuart_ports[co->index]; | ||||
| 	if (sport == NULL) | ||||
| 		return -ENODEV; | ||||
| 
 | ||||
| 	if (options) | ||||
| 		uart_parse_options(options, &baud, &parity, &bits, &flow); | ||||
| 	else | ||||
| 		lpuart_console_get_options(sport, &baud, &parity, &bits); | ||||
| 
 | ||||
| 	lpuart_setup_watermark(sport); | ||||
| 
 | ||||
| 	return uart_set_options(&sport->port, co, baud, parity, bits, flow); | ||||
| } | ||||
| 
 | ||||
| static struct uart_driver lpuart_reg; | ||||
| static struct console lpuart_console = { | ||||
| 	.name		= DEV_NAME, | ||||
| 	.write		= lpuart_console_write, | ||||
| 	.device		= uart_console_device, | ||||
| 	.setup		= lpuart_console_setup, | ||||
| 	.flags		= CON_PRINTBUFFER, | ||||
| 	.index		= -1, | ||||
| 	.data		= &lpuart_reg, | ||||
| }; | ||||
| 
 | ||||
| #define LPUART_CONSOLE	(&lpuart_console) | ||||
| #else | ||||
| #define LPUART_CONSOLE	NULL | ||||
| #endif | ||||
| 
 | ||||
| static struct uart_driver lpuart_reg = { | ||||
| 	.owner		= THIS_MODULE, | ||||
| 	.driver_name	= DRIVER_NAME, | ||||
| 	.dev_name	= DEV_NAME, | ||||
| 	.nr		= ARRAY_SIZE(lpuart_ports), | ||||
| 	.cons		= LPUART_CONSOLE, | ||||
| }; | ||||
| 
 | ||||
| static int lpuart_probe(struct platform_device *pdev) | ||||
| { | ||||
| 	struct device_node *np = pdev->dev.of_node; | ||||
| 	struct lpuart_port *sport; | ||||
| 	struct resource *res; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	sport = devm_kzalloc(&pdev->dev, sizeof(*sport), GFP_KERNEL); | ||||
| 	if (!sport) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	pdev->dev.coherent_dma_mask = 0; | ||||
| 
 | ||||
| 	ret = of_alias_get_id(np, "serial"); | ||||
| 	if (ret < 0) { | ||||
| 		dev_err(&pdev->dev, "failed to get alias id, errno %d\n", ret); | ||||
| 		return ret; | ||||
| 	} | ||||
| 	sport->port.line = ret; | ||||
| 
 | ||||
| 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||||
| 	if (!res) | ||||
| 		return -ENODEV; | ||||
| 
 | ||||
| 	sport->port.mapbase = res->start; | ||||
| 	sport->port.membase = devm_ioremap_resource(&pdev->dev, res); | ||||
| 	if (IS_ERR(sport->port.membase)) | ||||
| 		return PTR_ERR(sport->port.membase); | ||||
| 
 | ||||
| 	sport->port.dev = &pdev->dev; | ||||
| 	sport->port.type = PORT_LPUART; | ||||
| 	sport->port.iotype = UPIO_MEM; | ||||
| 	sport->port.irq = platform_get_irq(pdev, 0); | ||||
| 	sport->port.ops = &lpuart_pops; | ||||
| 	sport->port.flags = UPF_BOOT_AUTOCONF; | ||||
| 
 | ||||
| 	sport->clk = devm_clk_get(&pdev->dev, "ipg"); | ||||
| 	if (IS_ERR(sport->clk)) { | ||||
| 		ret = PTR_ERR(sport->clk); | ||||
| 		dev_err(&pdev->dev, "failed to get uart clk: %d\n", ret); | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	ret = clk_prepare_enable(sport->clk); | ||||
| 	if (ret) { | ||||
| 		dev_err(&pdev->dev, "failed to enable uart clk: %d\n", ret); | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	sport->port.uartclk = clk_get_rate(sport->clk); | ||||
| 
 | ||||
| 	lpuart_ports[sport->port.line] = sport; | ||||
| 
 | ||||
| 	platform_set_drvdata(pdev, &sport->port); | ||||
| 
 | ||||
| 	ret = uart_add_one_port(&lpuart_reg, &sport->port); | ||||
| 	if (ret) { | ||||
| 		clk_disable_unprepare(sport->clk); | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int lpuart_remove(struct platform_device *pdev) | ||||
| { | ||||
| 	struct lpuart_port *sport = platform_get_drvdata(pdev); | ||||
| 
 | ||||
| 	uart_remove_one_port(&lpuart_reg, &sport->port); | ||||
| 
 | ||||
| 	clk_disable_unprepare(sport->clk); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| #ifdef CONFIG_PM_SLEEP | ||||
| static int lpuart_suspend(struct device *dev) | ||||
| { | ||||
| 	struct lpuart_port *sport = dev_get_drvdata(dev); | ||||
| 
 | ||||
| 	uart_suspend_port(&lpuart_reg, &sport->port); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int lpuart_resume(struct device *dev) | ||||
| { | ||||
| 	struct lpuart_port *sport = dev_get_drvdata(dev); | ||||
| 
 | ||||
| 	uart_resume_port(&lpuart_reg, &sport->port); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| static SIMPLE_DEV_PM_OPS(lpuart_pm_ops, lpuart_suspend, lpuart_resume); | ||||
| 
 | ||||
| static struct platform_driver lpuart_driver = { | ||||
| 	.probe		= lpuart_probe, | ||||
| 	.remove		= lpuart_remove, | ||||
| 	.driver		= { | ||||
| 		.name	= "fsl-lpuart", | ||||
| 		.owner	= THIS_MODULE, | ||||
| 		.of_match_table = lpuart_dt_ids, | ||||
| 		.pm	= &lpuart_pm_ops, | ||||
| 	}, | ||||
| }; | ||||
| 
 | ||||
| static int __init lpuart_serial_init(void) | ||||
| { | ||||
| 	int ret; | ||||
| 
 | ||||
| 	pr_info("serial: Freescale lpuart driver\n"); | ||||
| 
 | ||||
| 	ret = uart_register_driver(&lpuart_reg); | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	ret = platform_driver_register(&lpuart_driver); | ||||
| 	if (ret) | ||||
| 		uart_unregister_driver(&lpuart_reg); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void __exit lpuart_serial_exit(void) | ||||
| { | ||||
| 	platform_driver_unregister(&lpuart_driver); | ||||
| 	uart_unregister_driver(&lpuart_reg); | ||||
| } | ||||
| 
 | ||||
| module_init(lpuart_serial_init); | ||||
| module_exit(lpuart_serial_exit); | ||||
| 
 | ||||
| MODULE_DESCRIPTION("Freescale lpuart serial port driver"); | ||||
| MODULE_LICENSE("GPL v2"); | ||||
|  | @ -201,6 +201,7 @@ struct imx_port { | |||
| 	unsigned int		old_status; | ||||
| 	int			txirq, rxirq, rtsirq; | ||||
| 	unsigned int		have_rtscts:1; | ||||
| 	unsigned int		dte_mode:1; | ||||
| 	unsigned int		use_irda:1; | ||||
| 	unsigned int		irda_inv_rx:1; | ||||
| 	unsigned int		irda_inv_tx:1; | ||||
|  | @ -271,6 +272,7 @@ static inline int is_imx21_uart(struct imx_port *sport) | |||
| /*
 | ||||
|  * Save and restore functions for UCR1, UCR2 and UCR3 registers | ||||
|  */ | ||||
| #if defined(CONFIG_CONSOLE_POLL) || defined(CONFIG_SERIAL_IMX_CONSOLE) | ||||
| static void imx_port_ucrs_save(struct uart_port *port, | ||||
| 			       struct imx_port_ucrs *ucr) | ||||
| { | ||||
|  | @ -288,6 +290,7 @@ static void imx_port_ucrs_restore(struct uart_port *port, | |||
| 	writel(ucr->ucr2, port->membase + UCR2); | ||||
| 	writel(ucr->ucr3, port->membase + UCR3); | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| /*
 | ||||
|  * Handle any change of modem status signal since we were last called. | ||||
|  | @ -449,6 +452,13 @@ static void imx_start_tx(struct uart_port *port) | |||
| 		temp &= ~(UCR1_RRDYEN); | ||||
| 		writel(temp, sport->port.membase + UCR1); | ||||
| 	} | ||||
| 	/* Clear any pending ORE flag before enabling interrupt */ | ||||
| 	temp = readl(sport->port.membase + USR2); | ||||
| 	writel(temp | USR2_ORE, sport->port.membase + USR2); | ||||
| 
 | ||||
| 	temp = readl(sport->port.membase + UCR4); | ||||
| 	temp |= UCR4_OREN; | ||||
| 	writel(temp, sport->port.membase + UCR4); | ||||
| 
 | ||||
| 	temp = readl(sport->port.membase + UCR1); | ||||
| 	writel(temp | UCR1_TXMPTYEN, sport->port.membase + UCR1); | ||||
|  | @ -582,6 +592,7 @@ static irqreturn_t imx_int(int irq, void *dev_id) | |||
| { | ||||
| 	struct imx_port *sport = dev_id; | ||||
| 	unsigned int sts; | ||||
| 	unsigned int sts2; | ||||
| 
 | ||||
| 	sts = readl(sport->port.membase + USR1); | ||||
| 
 | ||||
|  | @ -598,6 +609,13 @@ static irqreturn_t imx_int(int irq, void *dev_id) | |||
| 	if (sts & USR1_AWAKE) | ||||
| 		writel(USR1_AWAKE, sport->port.membase + USR1); | ||||
| 
 | ||||
| 	sts2 = readl(sport->port.membase + USR2); | ||||
| 	if (sts2 & USR2_ORE) { | ||||
| 		dev_err(sport->port.dev, "Rx FIFO overrun\n"); | ||||
| 		sport->port.icount.overrun++; | ||||
| 		writel(sts2 | USR2_ORE, sport->port.membase + USR2); | ||||
| 	} | ||||
| 
 | ||||
| 	return IRQ_HANDLED; | ||||
| } | ||||
| 
 | ||||
|  | @ -684,6 +702,17 @@ static int imx_startup(struct uart_port *port) | |||
| 	int retval; | ||||
| 	unsigned long flags, temp; | ||||
| 
 | ||||
| 	if (!uart_console(port)) { | ||||
| 		retval = clk_prepare_enable(sport->clk_per); | ||||
| 		if (retval) | ||||
| 			goto error_out1; | ||||
| 		retval = clk_prepare_enable(sport->clk_ipg); | ||||
| 		if (retval) { | ||||
| 			clk_disable_unprepare(sport->clk_per); | ||||
| 			goto error_out1; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	imx_setup_ufcr(sport, 0); | ||||
| 
 | ||||
| 	/* disable the DREN bit (Data Ready interrupt enable) before
 | ||||
|  | @ -871,6 +900,11 @@ static void imx_shutdown(struct uart_port *port) | |||
| 
 | ||||
| 	writel(temp, sport->port.membase + UCR1); | ||||
| 	spin_unlock_irqrestore(&sport->port.lock, flags); | ||||
| 
 | ||||
| 	if (!uart_console(&sport->port)) { | ||||
| 		clk_disable_unprepare(sport->clk_per); | ||||
| 		clk_disable_unprepare(sport->clk_ipg); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static void | ||||
|  | @ -1007,6 +1041,8 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios, | |||
| 
 | ||||
| 	ufcr = readl(sport->port.membase + UFCR); | ||||
| 	ufcr = (ufcr & (~UFCR_RFDIV)) | UFCR_RFDIV_REG(div); | ||||
| 	if (sport->dte_mode) | ||||
| 		ufcr |= UFCR_DCEDTE; | ||||
| 	writel(ufcr, sport->port.membase + UFCR); | ||||
| 
 | ||||
| 	writel(num, sport->port.membase + UBIR); | ||||
|  | @ -1431,6 +1467,9 @@ static int serial_imx_probe_dt(struct imx_port *sport, | |||
| 	if (of_get_property(np, "fsl,irda-mode", NULL)) | ||||
| 		sport->use_irda = 1; | ||||
| 
 | ||||
| 	if (of_get_property(np, "fsl,dte-mode", NULL)) | ||||
| 		sport->dte_mode = 1; | ||||
| 
 | ||||
| 	sport->devdata = of_id->data; | ||||
| 
 | ||||
| 	return 0; | ||||
|  | @ -1544,6 +1583,11 @@ static int serial_imx_probe(struct platform_device *pdev) | |||
| 		goto deinit; | ||||
| 	platform_set_drvdata(pdev, sport); | ||||
| 
 | ||||
| 	if (!uart_console(&sport->port)) { | ||||
| 		clk_disable_unprepare(sport->clk_per); | ||||
| 		clk_disable_unprepare(sport->clk_ipg); | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| deinit: | ||||
| 	if (pdata && pdata->exit) | ||||
|  | @ -1565,9 +1609,6 @@ static int serial_imx_remove(struct platform_device *pdev) | |||
| 
 | ||||
| 	uart_remove_one_port(&imx_reg, &sport->port); | ||||
| 
 | ||||
| 	clk_disable_unprepare(sport->clk_per); | ||||
| 	clk_disable_unprepare(sport->clk_ipg); | ||||
| 
 | ||||
| 	if (pdata && pdata->exit) | ||||
| 		pdata->exit(pdev); | ||||
| 
 | ||||
|  |  | |||
|  | @ -21,6 +21,10 @@ | |||
|  *    be triggered | ||||
|  */ | ||||
| 
 | ||||
| #if defined(CONFIG_SERIAL_MFD_HSU_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) | ||||
| #define SUPPORT_SYSRQ | ||||
| #endif | ||||
| 
 | ||||
| #include <linux/module.h> | ||||
| #include <linux/init.h> | ||||
| #include <linux/console.h> | ||||
|  |  | |||
|  | @ -84,16 +84,6 @@ static void mpc52xx_uart_of_enumerate(void); | |||
| static irqreturn_t mpc52xx_uart_int(int irq, void *dev_id); | ||||
| static irqreturn_t mpc5xxx_uart_process_int(struct uart_port *port); | ||||
| 
 | ||||
| 
 | ||||
| /* Simple macro to test if a port is console or not. This one is taken
 | ||||
|  * for serial_core.c and maybe should be moved to serial_core.h ? */ | ||||
| #ifdef CONFIG_SERIAL_CORE_CONSOLE | ||||
| #define uart_console(port) \ | ||||
| 	((port)->cons && (port)->cons->index == (port)->line) | ||||
| #else | ||||
| #define uart_console(port)	(0) | ||||
| #endif | ||||
| 
 | ||||
| /* ======================================================================== */ | ||||
| /* PSC fifo operations for isolating differences between 52xx and 512x      */ | ||||
| /* ======================================================================== */ | ||||
|  | @ -122,6 +112,15 @@ struct psc_ops { | |||
| 	void		(*fifoc_uninit)(void); | ||||
| 	void		(*get_irq)(struct uart_port *, struct device_node *); | ||||
| 	irqreturn_t	(*handle_irq)(struct uart_port *port); | ||||
| 	u16		(*get_status)(struct uart_port *port); | ||||
| 	u8		(*get_ipcr)(struct uart_port *port); | ||||
| 	void		(*command)(struct uart_port *port, u8 cmd); | ||||
| 	void		(*set_mode)(struct uart_port *port, u8 mr1, u8 mr2); | ||||
| 	void		(*set_rts)(struct uart_port *port, int state); | ||||
| 	void		(*enable_ms)(struct uart_port *port); | ||||
| 	void		(*set_sicr)(struct uart_port *port, u32 val); | ||||
| 	void		(*set_imr)(struct uart_port *port, u16 val); | ||||
| 	u8		(*get_mr1)(struct uart_port *port); | ||||
| }; | ||||
| 
 | ||||
| /* setting the prescaler and divisor reg is common for all chips */ | ||||
|  | @ -134,6 +133,65 @@ static inline void mpc52xx_set_divisor(struct mpc52xx_psc __iomem *psc, | |||
| 	out_8(&psc->ctlr, divisor & 0xff); | ||||
| } | ||||
| 
 | ||||
| static u16 mpc52xx_psc_get_status(struct uart_port *port) | ||||
| { | ||||
| 	return in_be16(&PSC(port)->mpc52xx_psc_status); | ||||
| } | ||||
| 
 | ||||
| static u8 mpc52xx_psc_get_ipcr(struct uart_port *port) | ||||
| { | ||||
| 	return in_8(&PSC(port)->mpc52xx_psc_ipcr); | ||||
| } | ||||
| 
 | ||||
| static void mpc52xx_psc_command(struct uart_port *port, u8 cmd) | ||||
| { | ||||
| 	out_8(&PSC(port)->command, cmd); | ||||
| } | ||||
| 
 | ||||
| static void mpc52xx_psc_set_mode(struct uart_port *port, u8 mr1, u8 mr2) | ||||
| { | ||||
| 	out_8(&PSC(port)->command, MPC52xx_PSC_SEL_MODE_REG_1); | ||||
| 	out_8(&PSC(port)->mode, mr1); | ||||
| 	out_8(&PSC(port)->mode, mr2); | ||||
| } | ||||
| 
 | ||||
| static void mpc52xx_psc_set_rts(struct uart_port *port, int state) | ||||
| { | ||||
| 	if (state) | ||||
| 		out_8(&PSC(port)->op1, MPC52xx_PSC_OP_RTS); | ||||
| 	else | ||||
| 		out_8(&PSC(port)->op0, MPC52xx_PSC_OP_RTS); | ||||
| } | ||||
| 
 | ||||
| static void mpc52xx_psc_enable_ms(struct uart_port *port) | ||||
| { | ||||
| 	struct mpc52xx_psc __iomem *psc = PSC(port); | ||||
| 
 | ||||
| 	/* clear D_*-bits by reading them */ | ||||
| 	in_8(&psc->mpc52xx_psc_ipcr); | ||||
| 	/* enable CTS and DCD as IPC interrupts */ | ||||
| 	out_8(&psc->mpc52xx_psc_acr, MPC52xx_PSC_IEC_CTS | MPC52xx_PSC_IEC_DCD); | ||||
| 
 | ||||
| 	port->read_status_mask |= MPC52xx_PSC_IMR_IPC; | ||||
| 	out_be16(&psc->mpc52xx_psc_imr, port->read_status_mask); | ||||
| } | ||||
| 
 | ||||
| static void mpc52xx_psc_set_sicr(struct uart_port *port, u32 val) | ||||
| { | ||||
| 	out_be32(&PSC(port)->sicr, val); | ||||
| } | ||||
| 
 | ||||
| static void mpc52xx_psc_set_imr(struct uart_port *port, u16 val) | ||||
| { | ||||
| 	out_be16(&PSC(port)->mpc52xx_psc_imr, val); | ||||
| } | ||||
| 
 | ||||
| static u8 mpc52xx_psc_get_mr1(struct uart_port *port) | ||||
| { | ||||
| 	out_8(&PSC(port)->command, MPC52xx_PSC_SEL_MODE_REG_1); | ||||
| 	return in_8(&PSC(port)->mode); | ||||
| } | ||||
| 
 | ||||
| #ifdef CONFIG_PPC_MPC52xx | ||||
| #define FIFO_52xx(port) ((struct mpc52xx_psc_fifo __iomem *)(PSC(port)+1)) | ||||
| static void mpc52xx_psc_fifo_init(struct uart_port *port) | ||||
|  | @ -304,6 +362,15 @@ static struct psc_ops mpc52xx_psc_ops = { | |||
| 	.set_baudrate = mpc5200_psc_set_baudrate, | ||||
| 	.get_irq = mpc52xx_psc_get_irq, | ||||
| 	.handle_irq = mpc52xx_psc_handle_irq, | ||||
| 	.get_status = mpc52xx_psc_get_status, | ||||
| 	.get_ipcr = mpc52xx_psc_get_ipcr, | ||||
| 	.command = mpc52xx_psc_command, | ||||
| 	.set_mode = mpc52xx_psc_set_mode, | ||||
| 	.set_rts = mpc52xx_psc_set_rts, | ||||
| 	.enable_ms = mpc52xx_psc_enable_ms, | ||||
| 	.set_sicr = mpc52xx_psc_set_sicr, | ||||
| 	.set_imr = mpc52xx_psc_set_imr, | ||||
| 	.get_mr1 = mpc52xx_psc_get_mr1, | ||||
| }; | ||||
| 
 | ||||
| static struct psc_ops mpc5200b_psc_ops = { | ||||
|  | @ -325,6 +392,15 @@ static struct psc_ops mpc5200b_psc_ops = { | |||
| 	.set_baudrate = mpc5200b_psc_set_baudrate, | ||||
| 	.get_irq = mpc52xx_psc_get_irq, | ||||
| 	.handle_irq = mpc52xx_psc_handle_irq, | ||||
| 	.get_status = mpc52xx_psc_get_status, | ||||
| 	.get_ipcr = mpc52xx_psc_get_ipcr, | ||||
| 	.command = mpc52xx_psc_command, | ||||
| 	.set_mode = mpc52xx_psc_set_mode, | ||||
| 	.set_rts = mpc52xx_psc_set_rts, | ||||
| 	.enable_ms = mpc52xx_psc_enable_ms, | ||||
| 	.set_sicr = mpc52xx_psc_set_sicr, | ||||
| 	.set_imr = mpc52xx_psc_set_imr, | ||||
| 	.get_mr1 = mpc52xx_psc_get_mr1, | ||||
| }; | ||||
| 
 | ||||
| #endif /* CONFIG_MPC52xx */ | ||||
|  | @ -572,6 +648,246 @@ static void mpc512x_psc_get_irq(struct uart_port *port, struct device_node *np) | |||
| 	port->irqflags = IRQF_SHARED; | ||||
| 	port->irq = psc_fifoc_irq; | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| #ifdef CONFIG_PPC_MPC512x | ||||
| 
 | ||||
| #define PSC_5125(port) ((struct mpc5125_psc __iomem *)((port)->membase)) | ||||
| #define FIFO_5125(port) ((struct mpc512x_psc_fifo __iomem *)(PSC_5125(port)+1)) | ||||
| 
 | ||||
| static void mpc5125_psc_fifo_init(struct uart_port *port) | ||||
| { | ||||
| 	/* /32 prescaler */ | ||||
| 	out_8(&PSC_5125(port)->mpc52xx_psc_clock_select, 0xdd); | ||||
| 
 | ||||
| 	out_be32(&FIFO_5125(port)->txcmd, MPC512x_PSC_FIFO_RESET_SLICE); | ||||
| 	out_be32(&FIFO_5125(port)->txcmd, MPC512x_PSC_FIFO_ENABLE_SLICE); | ||||
| 	out_be32(&FIFO_5125(port)->txalarm, 1); | ||||
| 	out_be32(&FIFO_5125(port)->tximr, 0); | ||||
| 
 | ||||
| 	out_be32(&FIFO_5125(port)->rxcmd, MPC512x_PSC_FIFO_RESET_SLICE); | ||||
| 	out_be32(&FIFO_5125(port)->rxcmd, MPC512x_PSC_FIFO_ENABLE_SLICE); | ||||
| 	out_be32(&FIFO_5125(port)->rxalarm, 1); | ||||
| 	out_be32(&FIFO_5125(port)->rximr, 0); | ||||
| 
 | ||||
| 	out_be32(&FIFO_5125(port)->tximr, MPC512x_PSC_FIFO_ALARM); | ||||
| 	out_be32(&FIFO_5125(port)->rximr, MPC512x_PSC_FIFO_ALARM); | ||||
| } | ||||
| 
 | ||||
| static int mpc5125_psc_raw_rx_rdy(struct uart_port *port) | ||||
| { | ||||
| 	return !(in_be32(&FIFO_5125(port)->rxsr) & MPC512x_PSC_FIFO_EMPTY); | ||||
| } | ||||
| 
 | ||||
| static int mpc5125_psc_raw_tx_rdy(struct uart_port *port) | ||||
| { | ||||
| 	return !(in_be32(&FIFO_5125(port)->txsr) & MPC512x_PSC_FIFO_FULL); | ||||
| } | ||||
| 
 | ||||
| static int mpc5125_psc_rx_rdy(struct uart_port *port) | ||||
| { | ||||
| 	return in_be32(&FIFO_5125(port)->rxsr) & | ||||
| 	       in_be32(&FIFO_5125(port)->rximr) & MPC512x_PSC_FIFO_ALARM; | ||||
| } | ||||
| 
 | ||||
| static int mpc5125_psc_tx_rdy(struct uart_port *port) | ||||
| { | ||||
| 	return in_be32(&FIFO_5125(port)->txsr) & | ||||
| 	       in_be32(&FIFO_5125(port)->tximr) & MPC512x_PSC_FIFO_ALARM; | ||||
| } | ||||
| 
 | ||||
| static int mpc5125_psc_tx_empty(struct uart_port *port) | ||||
| { | ||||
| 	return in_be32(&FIFO_5125(port)->txsr) & MPC512x_PSC_FIFO_EMPTY; | ||||
| } | ||||
| 
 | ||||
| static void mpc5125_psc_stop_rx(struct uart_port *port) | ||||
| { | ||||
| 	unsigned long rx_fifo_imr; | ||||
| 
 | ||||
| 	rx_fifo_imr = in_be32(&FIFO_5125(port)->rximr); | ||||
| 	rx_fifo_imr &= ~MPC512x_PSC_FIFO_ALARM; | ||||
| 	out_be32(&FIFO_5125(port)->rximr, rx_fifo_imr); | ||||
| } | ||||
| 
 | ||||
| static void mpc5125_psc_start_tx(struct uart_port *port) | ||||
| { | ||||
| 	unsigned long tx_fifo_imr; | ||||
| 
 | ||||
| 	tx_fifo_imr = in_be32(&FIFO_5125(port)->tximr); | ||||
| 	tx_fifo_imr |= MPC512x_PSC_FIFO_ALARM; | ||||
| 	out_be32(&FIFO_5125(port)->tximr, tx_fifo_imr); | ||||
| } | ||||
| 
 | ||||
| static void mpc5125_psc_stop_tx(struct uart_port *port) | ||||
| { | ||||
| 	unsigned long tx_fifo_imr; | ||||
| 
 | ||||
| 	tx_fifo_imr = in_be32(&FIFO_5125(port)->tximr); | ||||
| 	tx_fifo_imr &= ~MPC512x_PSC_FIFO_ALARM; | ||||
| 	out_be32(&FIFO_5125(port)->tximr, tx_fifo_imr); | ||||
| } | ||||
| 
 | ||||
| static void mpc5125_psc_rx_clr_irq(struct uart_port *port) | ||||
| { | ||||
| 	out_be32(&FIFO_5125(port)->rxisr, in_be32(&FIFO_5125(port)->rxisr)); | ||||
| } | ||||
| 
 | ||||
| static void mpc5125_psc_tx_clr_irq(struct uart_port *port) | ||||
| { | ||||
| 	out_be32(&FIFO_5125(port)->txisr, in_be32(&FIFO_5125(port)->txisr)); | ||||
| } | ||||
| 
 | ||||
| static void mpc5125_psc_write_char(struct uart_port *port, unsigned char c) | ||||
| { | ||||
| 	out_8(&FIFO_5125(port)->txdata_8, c); | ||||
| } | ||||
| 
 | ||||
| static unsigned char mpc5125_psc_read_char(struct uart_port *port) | ||||
| { | ||||
| 	return in_8(&FIFO_5125(port)->rxdata_8); | ||||
| } | ||||
| 
 | ||||
| static void mpc5125_psc_cw_disable_ints(struct uart_port *port) | ||||
| { | ||||
| 	port->read_status_mask = | ||||
| 		in_be32(&FIFO_5125(port)->tximr) << 16 | | ||||
| 		in_be32(&FIFO_5125(port)->rximr); | ||||
| 	out_be32(&FIFO_5125(port)->tximr, 0); | ||||
| 	out_be32(&FIFO_5125(port)->rximr, 0); | ||||
| } | ||||
| 
 | ||||
| static void mpc5125_psc_cw_restore_ints(struct uart_port *port) | ||||
| { | ||||
| 	out_be32(&FIFO_5125(port)->tximr, | ||||
| 		(port->read_status_mask >> 16) & 0x7f); | ||||
| 	out_be32(&FIFO_5125(port)->rximr, port->read_status_mask & 0x7f); | ||||
| } | ||||
| 
 | ||||
| static inline void mpc5125_set_divisor(struct mpc5125_psc __iomem *psc, | ||||
| 		u8 prescaler, unsigned int divisor) | ||||
| { | ||||
| 	/* select prescaler */ | ||||
| 	out_8(&psc->mpc52xx_psc_clock_select, prescaler); | ||||
| 	out_8(&psc->ctur, divisor >> 8); | ||||
| 	out_8(&psc->ctlr, divisor & 0xff); | ||||
| } | ||||
| 
 | ||||
| static unsigned int mpc5125_psc_set_baudrate(struct uart_port *port, | ||||
| 					     struct ktermios *new, | ||||
| 					     struct ktermios *old) | ||||
| { | ||||
| 	unsigned int baud; | ||||
| 	unsigned int divisor; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Calculate with a /16 prescaler here. | ||||
| 	 */ | ||||
| 
 | ||||
| 	/* uartclk contains the ips freq */ | ||||
| 	baud = uart_get_baud_rate(port, new, old, | ||||
| 				  port->uartclk / (16 * 0xffff) + 1, | ||||
| 				  port->uartclk / 16); | ||||
| 	divisor = (port->uartclk + 8 * baud) / (16 * baud); | ||||
| 
 | ||||
| 	/* enable the /16 prescaler and set the divisor */ | ||||
| 	mpc5125_set_divisor(PSC_5125(port), 0xdd, divisor); | ||||
| 	return baud; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * MPC5125 have compatible PSC FIFO Controller. | ||||
|  * Special init not needed. | ||||
|  */ | ||||
| static u16 mpc5125_psc_get_status(struct uart_port *port) | ||||
| { | ||||
| 	return in_be16(&PSC_5125(port)->mpc52xx_psc_status); | ||||
| } | ||||
| 
 | ||||
| static u8 mpc5125_psc_get_ipcr(struct uart_port *port) | ||||
| { | ||||
| 	return in_8(&PSC_5125(port)->mpc52xx_psc_ipcr); | ||||
| } | ||||
| 
 | ||||
| static void mpc5125_psc_command(struct uart_port *port, u8 cmd) | ||||
| { | ||||
| 	out_8(&PSC_5125(port)->command, cmd); | ||||
| } | ||||
| 
 | ||||
| static void mpc5125_psc_set_mode(struct uart_port *port, u8 mr1, u8 mr2) | ||||
| { | ||||
| 	out_8(&PSC_5125(port)->mr1, mr1); | ||||
| 	out_8(&PSC_5125(port)->mr2, mr2); | ||||
| } | ||||
| 
 | ||||
| static void mpc5125_psc_set_rts(struct uart_port *port, int state) | ||||
| { | ||||
| 	if (state & TIOCM_RTS) | ||||
| 		out_8(&PSC_5125(port)->op1, MPC52xx_PSC_OP_RTS); | ||||
| 	else | ||||
| 		out_8(&PSC_5125(port)->op0, MPC52xx_PSC_OP_RTS); | ||||
| } | ||||
| 
 | ||||
| static void mpc5125_psc_enable_ms(struct uart_port *port) | ||||
| { | ||||
| 	struct mpc5125_psc __iomem *psc = PSC_5125(port); | ||||
| 
 | ||||
| 	/* clear D_*-bits by reading them */ | ||||
| 	in_8(&psc->mpc52xx_psc_ipcr); | ||||
| 	/* enable CTS and DCD as IPC interrupts */ | ||||
| 	out_8(&psc->mpc52xx_psc_acr, MPC52xx_PSC_IEC_CTS | MPC52xx_PSC_IEC_DCD); | ||||
| 
 | ||||
| 	port->read_status_mask |= MPC52xx_PSC_IMR_IPC; | ||||
| 	out_be16(&psc->mpc52xx_psc_imr, port->read_status_mask); | ||||
| } | ||||
| 
 | ||||
| static void mpc5125_psc_set_sicr(struct uart_port *port, u32 val) | ||||
| { | ||||
| 	out_be32(&PSC_5125(port)->sicr, val); | ||||
| } | ||||
| 
 | ||||
| static void mpc5125_psc_set_imr(struct uart_port *port, u16 val) | ||||
| { | ||||
| 	out_be16(&PSC_5125(port)->mpc52xx_psc_imr, val); | ||||
| } | ||||
| 
 | ||||
| static u8 mpc5125_psc_get_mr1(struct uart_port *port) | ||||
| { | ||||
| 	return in_8(&PSC_5125(port)->mr1); | ||||
| } | ||||
| 
 | ||||
| static struct psc_ops mpc5125_psc_ops = { | ||||
| 	.fifo_init = mpc5125_psc_fifo_init, | ||||
| 	.raw_rx_rdy = mpc5125_psc_raw_rx_rdy, | ||||
| 	.raw_tx_rdy = mpc5125_psc_raw_tx_rdy, | ||||
| 	.rx_rdy = mpc5125_psc_rx_rdy, | ||||
| 	.tx_rdy = mpc5125_psc_tx_rdy, | ||||
| 	.tx_empty = mpc5125_psc_tx_empty, | ||||
| 	.stop_rx = mpc5125_psc_stop_rx, | ||||
| 	.start_tx = mpc5125_psc_start_tx, | ||||
| 	.stop_tx = mpc5125_psc_stop_tx, | ||||
| 	.rx_clr_irq = mpc5125_psc_rx_clr_irq, | ||||
| 	.tx_clr_irq = mpc5125_psc_tx_clr_irq, | ||||
| 	.write_char = mpc5125_psc_write_char, | ||||
| 	.read_char = mpc5125_psc_read_char, | ||||
| 	.cw_disable_ints = mpc5125_psc_cw_disable_ints, | ||||
| 	.cw_restore_ints = mpc5125_psc_cw_restore_ints, | ||||
| 	.set_baudrate = mpc5125_psc_set_baudrate, | ||||
| 	.clock = mpc512x_psc_clock, | ||||
| 	.fifoc_init = mpc512x_psc_fifoc_init, | ||||
| 	.fifoc_uninit = mpc512x_psc_fifoc_uninit, | ||||
| 	.get_irq = mpc512x_psc_get_irq, | ||||
| 	.handle_irq = mpc512x_psc_handle_irq, | ||||
| 	.get_status = mpc5125_psc_get_status, | ||||
| 	.get_ipcr = mpc5125_psc_get_ipcr, | ||||
| 	.command = mpc5125_psc_command, | ||||
| 	.set_mode = mpc5125_psc_set_mode, | ||||
| 	.set_rts = mpc5125_psc_set_rts, | ||||
| 	.enable_ms = mpc5125_psc_enable_ms, | ||||
| 	.set_sicr = mpc5125_psc_set_sicr, | ||||
| 	.set_imr = mpc5125_psc_set_imr, | ||||
| 	.get_mr1 = mpc5125_psc_get_mr1, | ||||
| }; | ||||
| 
 | ||||
| static struct psc_ops mpc512x_psc_ops = { | ||||
| 	.fifo_init = mpc512x_psc_fifo_init, | ||||
|  | @ -595,8 +911,18 @@ static struct psc_ops mpc512x_psc_ops = { | |||
| 	.fifoc_uninit = mpc512x_psc_fifoc_uninit, | ||||
| 	.get_irq = mpc512x_psc_get_irq, | ||||
| 	.handle_irq = mpc512x_psc_handle_irq, | ||||
| 	.get_status = mpc52xx_psc_get_status, | ||||
| 	.get_ipcr = mpc52xx_psc_get_ipcr, | ||||
| 	.command = mpc52xx_psc_command, | ||||
| 	.set_mode = mpc52xx_psc_set_mode, | ||||
| 	.set_rts = mpc52xx_psc_set_rts, | ||||
| 	.enable_ms = mpc52xx_psc_enable_ms, | ||||
| 	.set_sicr = mpc52xx_psc_set_sicr, | ||||
| 	.set_imr = mpc52xx_psc_set_imr, | ||||
| 	.get_mr1 = mpc52xx_psc_get_mr1, | ||||
| }; | ||||
| #endif | ||||
| #endif /* CONFIG_PPC_MPC512x */ | ||||
| 
 | ||||
| 
 | ||||
| static const struct psc_ops *psc_ops; | ||||
| 
 | ||||
|  | @ -613,17 +939,14 @@ mpc52xx_uart_tx_empty(struct uart_port *port) | |||
| static void | ||||
| mpc52xx_uart_set_mctrl(struct uart_port *port, unsigned int mctrl) | ||||
| { | ||||
| 	if (mctrl & TIOCM_RTS) | ||||
| 		out_8(&PSC(port)->op1, MPC52xx_PSC_OP_RTS); | ||||
| 	else | ||||
| 		out_8(&PSC(port)->op0, MPC52xx_PSC_OP_RTS); | ||||
| 	psc_ops->set_rts(port, mctrl & TIOCM_RTS); | ||||
| } | ||||
| 
 | ||||
| static unsigned int | ||||
| mpc52xx_uart_get_mctrl(struct uart_port *port) | ||||
| { | ||||
| 	unsigned int ret = TIOCM_DSR; | ||||
| 	u8 status = in_8(&PSC(port)->mpc52xx_psc_ipcr); | ||||
| 	u8 status = psc_ops->get_ipcr(port); | ||||
| 
 | ||||
| 	if (!(status & MPC52xx_PSC_CTS)) | ||||
| 		ret |= TIOCM_CTS; | ||||
|  | @ -673,15 +996,7 @@ mpc52xx_uart_stop_rx(struct uart_port *port) | |||
| static void | ||||
| mpc52xx_uart_enable_ms(struct uart_port *port) | ||||
| { | ||||
| 	struct mpc52xx_psc __iomem *psc = PSC(port); | ||||
| 
 | ||||
| 	/* clear D_*-bits by reading them */ | ||||
| 	in_8(&psc->mpc52xx_psc_ipcr); | ||||
| 	/* enable CTS and DCD as IPC interrupts */ | ||||
| 	out_8(&psc->mpc52xx_psc_acr, MPC52xx_PSC_IEC_CTS | MPC52xx_PSC_IEC_DCD); | ||||
| 
 | ||||
| 	port->read_status_mask |= MPC52xx_PSC_IMR_IPC; | ||||
| 	out_be16(&psc->mpc52xx_psc_imr, port->read_status_mask); | ||||
| 	psc_ops->enable_ms(port); | ||||
| } | ||||
| 
 | ||||
| static void | ||||
|  | @ -691,9 +1006,9 @@ mpc52xx_uart_break_ctl(struct uart_port *port, int ctl) | |||
| 	spin_lock_irqsave(&port->lock, flags); | ||||
| 
 | ||||
| 	if (ctl == -1) | ||||
| 		out_8(&PSC(port)->command, MPC52xx_PSC_START_BRK); | ||||
| 		psc_ops->command(port, MPC52xx_PSC_START_BRK); | ||||
| 	else | ||||
| 		out_8(&PSC(port)->command, MPC52xx_PSC_STOP_BRK); | ||||
| 		psc_ops->command(port, MPC52xx_PSC_STOP_BRK); | ||||
| 
 | ||||
| 	spin_unlock_irqrestore(&port->lock, flags); | ||||
| } | ||||
|  | @ -701,7 +1016,6 @@ mpc52xx_uart_break_ctl(struct uart_port *port, int ctl) | |||
| static int | ||||
| mpc52xx_uart_startup(struct uart_port *port) | ||||
| { | ||||
| 	struct mpc52xx_psc __iomem *psc = PSC(port); | ||||
| 	int ret; | ||||
| 
 | ||||
| 	if (psc_ops->clock) { | ||||
|  | @ -717,15 +1031,15 @@ mpc52xx_uart_startup(struct uart_port *port) | |||
| 		return ret; | ||||
| 
 | ||||
| 	/* Reset/activate the port, clear and enable interrupts */ | ||||
| 	out_8(&psc->command, MPC52xx_PSC_RST_RX); | ||||
| 	out_8(&psc->command, MPC52xx_PSC_RST_TX); | ||||
| 	psc_ops->command(port, MPC52xx_PSC_RST_RX); | ||||
| 	psc_ops->command(port, MPC52xx_PSC_RST_TX); | ||||
| 
 | ||||
| 	out_be32(&psc->sicr, 0);	/* UART mode DCD ignored */ | ||||
| 	psc_ops->set_sicr(port, 0);	/* UART mode DCD ignored */ | ||||
| 
 | ||||
| 	psc_ops->fifo_init(port); | ||||
| 
 | ||||
| 	out_8(&psc->command, MPC52xx_PSC_TX_ENABLE); | ||||
| 	out_8(&psc->command, MPC52xx_PSC_RX_ENABLE); | ||||
| 	psc_ops->command(port, MPC52xx_PSC_TX_ENABLE); | ||||
| 	psc_ops->command(port, MPC52xx_PSC_RX_ENABLE); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
|  | @ -733,19 +1047,20 @@ mpc52xx_uart_startup(struct uart_port *port) | |||
| static void | ||||
| mpc52xx_uart_shutdown(struct uart_port *port) | ||||
| { | ||||
| 	struct mpc52xx_psc __iomem *psc = PSC(port); | ||||
| 
 | ||||
| 	/* Shut down the port.  Leave TX active if on a console port */ | ||||
| 	out_8(&psc->command, MPC52xx_PSC_RST_RX); | ||||
| 	psc_ops->command(port, MPC52xx_PSC_RST_RX); | ||||
| 	if (!uart_console(port)) | ||||
| 		out_8(&psc->command, MPC52xx_PSC_RST_TX); | ||||
| 		psc_ops->command(port, MPC52xx_PSC_RST_TX); | ||||
| 
 | ||||
| 	port->read_status_mask = 0; | ||||
| 	out_be16(&psc->mpc52xx_psc_imr, port->read_status_mask); | ||||
| 	psc_ops->set_imr(port, port->read_status_mask); | ||||
| 
 | ||||
| 	if (psc_ops->clock) | ||||
| 		psc_ops->clock(port, 0); | ||||
| 
 | ||||
| 	/* Disable interrupt */ | ||||
| 	psc_ops->cw_disable_ints(port); | ||||
| 
 | ||||
| 	/* Release interrupt */ | ||||
| 	free_irq(port->irq, port); | ||||
| } | ||||
|  | @ -754,7 +1069,6 @@ static void | |||
| mpc52xx_uart_set_termios(struct uart_port *port, struct ktermios *new, | ||||
| 			 struct ktermios *old) | ||||
| { | ||||
| 	struct mpc52xx_psc __iomem *psc = PSC(port); | ||||
| 	unsigned long flags; | ||||
| 	unsigned char mr1, mr2; | ||||
| 	unsigned int j; | ||||
|  | @ -818,13 +1132,11 @@ mpc52xx_uart_set_termios(struct uart_port *port, struct ktermios *new, | |||
| 			"Some chars may have been lost.\n"); | ||||
| 
 | ||||
| 	/* Reset the TX & RX */ | ||||
| 	out_8(&psc->command, MPC52xx_PSC_RST_RX); | ||||
| 	out_8(&psc->command, MPC52xx_PSC_RST_TX); | ||||
| 	psc_ops->command(port, MPC52xx_PSC_RST_RX); | ||||
| 	psc_ops->command(port, MPC52xx_PSC_RST_TX); | ||||
| 
 | ||||
| 	/* Send new mode settings */ | ||||
| 	out_8(&psc->command, MPC52xx_PSC_SEL_MODE_REG_1); | ||||
| 	out_8(&psc->mode, mr1); | ||||
| 	out_8(&psc->mode, mr2); | ||||
| 	psc_ops->set_mode(port, mr1, mr2); | ||||
| 	baud = psc_ops->set_baudrate(port, new, old); | ||||
| 
 | ||||
| 	/* Update the per-port timeout */ | ||||
|  | @ -834,8 +1146,8 @@ mpc52xx_uart_set_termios(struct uart_port *port, struct ktermios *new, | |||
| 		mpc52xx_uart_enable_ms(port); | ||||
| 
 | ||||
| 	/* Reenable TX & RX */ | ||||
| 	out_8(&psc->command, MPC52xx_PSC_TX_ENABLE); | ||||
| 	out_8(&psc->command, MPC52xx_PSC_RX_ENABLE); | ||||
| 	psc_ops->command(port, MPC52xx_PSC_TX_ENABLE); | ||||
| 	psc_ops->command(port, MPC52xx_PSC_RX_ENABLE); | ||||
| 
 | ||||
| 	/* We're all set, release the lock */ | ||||
| 	spin_unlock_irqrestore(&port->lock, flags); | ||||
|  | @ -963,7 +1275,7 @@ mpc52xx_uart_int_rx_chars(struct uart_port *port) | |||
| 		flag = TTY_NORMAL; | ||||
| 		port->icount.rx++; | ||||
| 
 | ||||
| 		status = in_be16(&PSC(port)->mpc52xx_psc_status); | ||||
| 		status = psc_ops->get_status(port); | ||||
| 
 | ||||
| 		if (status & (MPC52xx_PSC_SR_PE | | ||||
| 			      MPC52xx_PSC_SR_FE | | ||||
|  | @ -983,7 +1295,7 @@ mpc52xx_uart_int_rx_chars(struct uart_port *port) | |||
| 			} | ||||
| 
 | ||||
| 			/* Clear error condition */ | ||||
| 			out_8(&PSC(port)->command, MPC52xx_PSC_RST_ERR_STAT); | ||||
| 			psc_ops->command(port, MPC52xx_PSC_RST_ERR_STAT); | ||||
| 
 | ||||
| 		} | ||||
| 		tty_insert_flip_char(tport, ch, flag); | ||||
|  | @ -1066,7 +1378,7 @@ mpc5xxx_uart_process_int(struct uart_port *port) | |||
| 		if (psc_ops->tx_rdy(port)) | ||||
| 			keepgoing |= mpc52xx_uart_int_tx_chars(port); | ||||
| 
 | ||||
| 		status = in_8(&PSC(port)->mpc52xx_psc_ipcr); | ||||
| 		status = psc_ops->get_ipcr(port); | ||||
| 		if (status & MPC52xx_PSC_D_DCD) | ||||
| 			uart_handle_dcd_change(port, !(status & MPC52xx_PSC_DCD)); | ||||
| 
 | ||||
|  | @ -1107,14 +1419,12 @@ static void __init | |||
| mpc52xx_console_get_options(struct uart_port *port, | ||||
| 			    int *baud, int *parity, int *bits, int *flow) | ||||
| { | ||||
| 	struct mpc52xx_psc __iomem *psc = PSC(port); | ||||
| 	unsigned char mr1; | ||||
| 
 | ||||
| 	pr_debug("mpc52xx_console_get_options(port=%p)\n", port); | ||||
| 
 | ||||
| 	/* Read the mode registers */ | ||||
| 	out_8(&psc->command, MPC52xx_PSC_SEL_MODE_REG_1); | ||||
| 	mr1 = in_8(&psc->mode); | ||||
| 	mr1 = psc_ops->get_mr1(port); | ||||
| 
 | ||||
| 	/* CT{U,L}R are write-only ! */ | ||||
| 	*baud = CONFIG_SERIAL_MPC52xx_CONSOLE_BAUD; | ||||
|  | @ -1304,6 +1614,7 @@ static struct of_device_id mpc52xx_uart_of_match[] = { | |||
| #endif | ||||
| #ifdef CONFIG_PPC_MPC512x | ||||
| 	{ .compatible = "fsl,mpc5121-psc-uart", .data = &mpc512x_psc_ops, }, | ||||
| 	{ .compatible = "fsl,mpc5125-psc-uart", .data = &mpc5125_psc_ops, }, | ||||
| #endif | ||||
| 	{}, | ||||
| }; | ||||
|  | @ -1372,15 +1683,14 @@ static int mpc52xx_uart_of_probe(struct platform_device *op) | |||
| 	if (ret) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	dev_set_drvdata(&op->dev, (void *)port); | ||||
| 	platform_set_drvdata(op, (void *)port); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int | ||||
| mpc52xx_uart_of_remove(struct platform_device *op) | ||||
| { | ||||
| 	struct uart_port *port = dev_get_drvdata(&op->dev); | ||||
| 	dev_set_drvdata(&op->dev, NULL); | ||||
| 	struct uart_port *port = platform_get_drvdata(op); | ||||
| 
 | ||||
| 	if (port) | ||||
| 		uart_remove_one_port(&mpc52xx_uart_driver, port); | ||||
|  | @ -1392,7 +1702,7 @@ mpc52xx_uart_of_remove(struct platform_device *op) | |||
| static int | ||||
| mpc52xx_uart_of_suspend(struct platform_device *op, pm_message_t state) | ||||
| { | ||||
| 	struct uart_port *port = (struct uart_port *) dev_get_drvdata(&op->dev); | ||||
| 	struct uart_port *port = (struct uart_port *) platform_get_drvdata(op); | ||||
| 
 | ||||
| 	if (port) | ||||
| 		uart_suspend_port(&mpc52xx_uart_driver, port); | ||||
|  | @ -1403,7 +1713,7 @@ mpc52xx_uart_of_suspend(struct platform_device *op, pm_message_t state) | |||
| static int | ||||
| mpc52xx_uart_of_resume(struct platform_device *op) | ||||
| { | ||||
| 	struct uart_port *port = (struct uart_port *) dev_get_drvdata(&op->dev); | ||||
| 	struct uart_port *port = (struct uart_port *) platform_get_drvdata(op); | ||||
| 
 | ||||
| 	if (port) | ||||
| 		uart_resume_port(&mpc52xx_uart_driver, port); | ||||
|  |  | |||
|  | @ -204,7 +204,7 @@ static int of_platform_serial_probe(struct platform_device *ofdev) | |||
| 
 | ||||
| 	info->type = port_type; | ||||
| 	info->line = ret; | ||||
| 	dev_set_drvdata(&ofdev->dev, info); | ||||
| 	platform_set_drvdata(ofdev, info); | ||||
| 	return 0; | ||||
| out: | ||||
| 	kfree(info); | ||||
|  | @ -217,7 +217,7 @@ out: | |||
|  */ | ||||
| static int of_platform_serial_remove(struct platform_device *ofdev) | ||||
| { | ||||
| 	struct of_serial_info *info = dev_get_drvdata(&ofdev->dev); | ||||
| 	struct of_serial_info *info = platform_get_drvdata(ofdev); | ||||
| 	switch (info->type) { | ||||
| #ifdef CONFIG_SERIAL_8250 | ||||
| 	case PORT_8250 ... PORT_MAX_8250: | ||||
|  |  | |||
|  | @ -161,6 +161,7 @@ struct uart_omap_port { | |||
| 	u32			calc_latency; | ||||
| 	struct work_struct	qos_work; | ||||
| 	struct pinctrl		*pins; | ||||
| 	bool			is_suspending; | ||||
| }; | ||||
| 
 | ||||
| #define to_uart_omap_port(p)	((container_of((p), struct uart_omap_port, port))) | ||||
|  | @ -197,7 +198,7 @@ static int serial_omap_get_context_loss_count(struct uart_omap_port *up) | |||
| 	struct omap_uart_port_info *pdata = up->dev->platform_data; | ||||
| 
 | ||||
| 	if (!pdata || !pdata->get_context_loss_count) | ||||
| 		return 0; | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	return pdata->get_context_loss_count(up->dev); | ||||
| } | ||||
|  | @ -1289,6 +1290,22 @@ static struct uart_driver serial_omap_reg = { | |||
| }; | ||||
| 
 | ||||
| #ifdef CONFIG_PM_SLEEP | ||||
| static int serial_omap_prepare(struct device *dev) | ||||
| { | ||||
| 	struct uart_omap_port *up = dev_get_drvdata(dev); | ||||
| 
 | ||||
| 	up->is_suspending = true; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void serial_omap_complete(struct device *dev) | ||||
| { | ||||
| 	struct uart_omap_port *up = dev_get_drvdata(dev); | ||||
| 
 | ||||
| 	up->is_suspending = false; | ||||
| } | ||||
| 
 | ||||
| static int serial_omap_suspend(struct device *dev) | ||||
| { | ||||
| 	struct uart_omap_port *up = dev_get_drvdata(dev); | ||||
|  | @ -1307,7 +1324,10 @@ static int serial_omap_resume(struct device *dev) | |||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| #endif | ||||
| #else | ||||
| #define serial_omap_prepare NULL | ||||
| #define serial_omap_complete NULL | ||||
| #endif /* CONFIG_PM_SLEEP */ | ||||
| 
 | ||||
| static void omap_serial_fill_features_erratas(struct uart_omap_port *up) | ||||
| { | ||||
|  | @ -1482,6 +1502,9 @@ static int serial_omap_probe(struct platform_device *pdev) | |||
| 
 | ||||
| 	platform_set_drvdata(pdev, up); | ||||
| 	pm_runtime_enable(&pdev->dev); | ||||
| 	if (omap_up_info->autosuspend_timeout == 0) | ||||
| 		omap_up_info->autosuspend_timeout = -1; | ||||
| 	device_init_wakeup(up->dev, true); | ||||
| 	pm_runtime_use_autosuspend(&pdev->dev); | ||||
| 	pm_runtime_set_autosuspend_delay(&pdev->dev, | ||||
| 			omap_up_info->autosuspend_timeout); | ||||
|  | @ -1591,13 +1614,19 @@ static void serial_omap_restore_context(struct uart_omap_port *up) | |||
| static int serial_omap_runtime_suspend(struct device *dev) | ||||
| { | ||||
| 	struct uart_omap_port *up = dev_get_drvdata(dev); | ||||
| 	struct omap_uart_port_info *pdata = dev->platform_data; | ||||
| 
 | ||||
| 	if (!up) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	if (!pdata) | ||||
| 		return 0; | ||||
| 	/*
 | ||||
| 	* When using 'no_console_suspend', the console UART must not be | ||||
| 	* suspended. Since driver suspend is managed by runtime suspend, | ||||
| 	* preventing runtime suspend (by returning error) will keep device | ||||
| 	* active during suspend. | ||||
| 	*/ | ||||
| 	if (up->is_suspending && !console_suspend_enabled && | ||||
| 	    uart_console(&up->port)) | ||||
| 		return -EBUSY; | ||||
| 
 | ||||
| 	up->context_loss_cnt = serial_omap_get_context_loss_count(up); | ||||
| 
 | ||||
|  | @ -1626,7 +1655,7 @@ static int serial_omap_runtime_resume(struct device *dev) | |||
| 	int loss_cnt = serial_omap_get_context_loss_count(up); | ||||
| 
 | ||||
| 	if (loss_cnt < 0) { | ||||
| 		dev_err(dev, "serial_omap_get_context_loss_count failed : %d\n", | ||||
| 		dev_dbg(dev, "serial_omap_get_context_loss_count failed : %d\n", | ||||
| 			loss_cnt); | ||||
| 		serial_omap_restore_context(up); | ||||
| 	} else if (up->context_loss_cnt != loss_cnt) { | ||||
|  | @ -1643,6 +1672,8 @@ static const struct dev_pm_ops serial_omap_dev_pm_ops = { | |||
| 	SET_SYSTEM_SLEEP_PM_OPS(serial_omap_suspend, serial_omap_resume) | ||||
| 	SET_RUNTIME_PM_OPS(serial_omap_runtime_suspend, | ||||
| 				serial_omap_runtime_resume, NULL) | ||||
| 	.prepare        = serial_omap_prepare, | ||||
| 	.complete       = serial_omap_complete, | ||||
| }; | ||||
| 
 | ||||
| #if defined(CONFIG_OF) | ||||
|  |  | |||
|  | @ -217,6 +217,7 @@ enum { | |||
| #define FRI2_64_UARTCLK  64000000 /*  64.0000 MHz */ | ||||
| #define FRI2_48_UARTCLK  48000000 /*  48.0000 MHz */ | ||||
| #define NTC1_UARTCLK     64000000 /*  64.0000 MHz */ | ||||
| #define MINNOW_UARTCLK   50000000 /*  50.0000 MHz */ | ||||
| 
 | ||||
| struct pch_uart_buffer { | ||||
| 	unsigned char *buf; | ||||
|  | @ -398,6 +399,10 @@ static int pch_uart_get_uartclk(void) | |||
| 		    strstr(cmp, "nanoETXexpress-TT"))) | ||||
| 		return NTC1_UARTCLK; | ||||
| 
 | ||||
| 	cmp = dmi_get_system_info(DMI_BOARD_NAME); | ||||
| 	if (cmp && strstr(cmp, "MinnowBoard")) | ||||
| 		return MINNOW_UARTCLK; | ||||
| 
 | ||||
| 	return DEFAULT_UARTCLK; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -1811,7 +1811,13 @@ static int __init s3c24xx_serial_modinit(void) | |||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	return platform_driver_register(&samsung_serial_driver); | ||||
| 	ret = platform_driver_register(&samsung_serial_driver); | ||||
| 	if (ret < 0) { | ||||
| 		pr_err("Failed to register platform driver\n"); | ||||
| 		uart_unregister_driver(&s3c24xx_uart_drv); | ||||
| 	} | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static void __exit s3c24xx_serial_modexit(void) | ||||
|  |  | |||
|  | @ -696,7 +696,7 @@ static int sc26xx_probe(struct platform_device *dev) | |||
| 	if (err) | ||||
| 		goto out_remove_ports; | ||||
| 
 | ||||
| 	dev_set_drvdata(&dev->dev, up); | ||||
| 	platform_set_drvdata(dev, up); | ||||
| 	return 0; | ||||
| 
 | ||||
| out_remove_ports: | ||||
|  | @ -716,7 +716,7 @@ out_free_port: | |||
| 
 | ||||
| static int __exit sc26xx_driver_remove(struct platform_device *dev) | ||||
| { | ||||
| 	struct uart_sc26xx_port *up = dev_get_drvdata(&dev->dev); | ||||
| 	struct uart_sc26xx_port *up = platform_get_drvdata(dev); | ||||
| 
 | ||||
| 	free_irq(up->port[0].irq, up); | ||||
| 
 | ||||
|  | @ -728,7 +728,6 @@ static int __exit sc26xx_driver_remove(struct platform_device *dev) | |||
| 	kfree(up); | ||||
| 	sc26xx_port = NULL; | ||||
| 
 | ||||
| 	dev_set_drvdata(&dev->dev, NULL); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -50,12 +50,6 @@ static struct lock_class_key port_lock_key; | |||
| 
 | ||||
| #define HIGH_BITS_OFFSET	((sizeof(long)-sizeof(int))*8) | ||||
| 
 | ||||
| #ifdef CONFIG_SERIAL_CORE_CONSOLE | ||||
| #define uart_console(port)	((port)->cons && (port)->cons->index == (port)->line) | ||||
| #else | ||||
| #define uart_console(port)	(0) | ||||
| #endif | ||||
| 
 | ||||
| static void uart_change_speed(struct tty_struct *tty, struct uart_state *state, | ||||
| 					struct ktermios *old_termios); | ||||
| static void uart_wait_until_sent(struct tty_struct *tty, int timeout); | ||||
|  |  | |||
|  | @ -687,9 +687,10 @@ int sirfsoc_uart_probe(struct platform_device *pdev) | |||
| 
 | ||||
| 	if (sirfport->hw_flow_ctrl) { | ||||
| 		sirfport->p = pinctrl_get_select_default(&pdev->dev); | ||||
| 		ret = IS_ERR(sirfport->p); | ||||
| 		if (ret) | ||||
| 		if (IS_ERR(sirfport->p)) { | ||||
| 			ret = PTR_ERR(sirfport->p); | ||||
| 			goto err; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	sirfport->clk = clk_get(&pdev->dev, NULL); | ||||
|  |  | |||
|  | @ -577,7 +577,7 @@ static int hv_probe(struct platform_device *op) | |||
| 	if (err) | ||||
| 		goto out_remove_port; | ||||
| 
 | ||||
| 	dev_set_drvdata(&op->dev, port); | ||||
| 	platform_set_drvdata(op, port); | ||||
| 
 | ||||
| 	return 0; | ||||
| 
 | ||||
|  | @ -601,7 +601,7 @@ out_free_port: | |||
| 
 | ||||
| static int hv_remove(struct platform_device *dev) | ||||
| { | ||||
| 	struct uart_port *port = dev_get_drvdata(&dev->dev); | ||||
| 	struct uart_port *port = platform_get_drvdata(dev); | ||||
| 
 | ||||
| 	free_irq(port->irq, port); | ||||
| 
 | ||||
|  | @ -612,8 +612,6 @@ static int hv_remove(struct platform_device *dev) | |||
| 	kfree(port); | ||||
| 	sunhv_port = NULL; | ||||
| 
 | ||||
| 	dev_set_drvdata(&dev->dev, NULL); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -1037,7 +1037,7 @@ static int sab_probe(struct platform_device *op) | |||
| 	if (err) | ||||
| 		goto out3; | ||||
| 
 | ||||
| 	dev_set_drvdata(&op->dev, &up[0]); | ||||
| 	platform_set_drvdata(op, &up[0]); | ||||
| 
 | ||||
| 	inst++; | ||||
| 
 | ||||
|  | @ -1059,7 +1059,7 @@ out: | |||
| 
 | ||||
| static int sab_remove(struct platform_device *op) | ||||
| { | ||||
| 	struct uart_sunsab_port *up = dev_get_drvdata(&op->dev); | ||||
| 	struct uart_sunsab_port *up = platform_get_drvdata(op); | ||||
| 
 | ||||
| 	uart_remove_one_port(&sunsab_reg, &up[1].port); | ||||
| 	uart_remove_one_port(&sunsab_reg, &up[0].port); | ||||
|  | @ -1070,8 +1070,6 @@ static int sab_remove(struct platform_device *op) | |||
| 		   up[0].port.membase, | ||||
| 		   sizeof(union sab82532_async_regs)); | ||||
| 
 | ||||
| 	dev_set_drvdata(&op->dev, NULL); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -1454,7 +1454,7 @@ static int su_probe(struct platform_device *op) | |||
| 			kfree(up); | ||||
| 			return err; | ||||
| 		} | ||||
| 		dev_set_drvdata(&op->dev, up); | ||||
| 		platform_set_drvdata(op, up); | ||||
| 
 | ||||
| 		nr_inst++; | ||||
| 
 | ||||
|  | @ -1483,7 +1483,7 @@ static int su_probe(struct platform_device *op) | |||
| 	if (err) | ||||
| 		goto out_unmap; | ||||
| 
 | ||||
| 	dev_set_drvdata(&op->dev, up); | ||||
| 	platform_set_drvdata(op, up); | ||||
| 
 | ||||
| 	nr_inst++; | ||||
| 
 | ||||
|  | @ -1496,7 +1496,7 @@ out_unmap: | |||
| 
 | ||||
| static int su_remove(struct platform_device *op) | ||||
| { | ||||
| 	struct uart_sunsu_port *up = dev_get_drvdata(&op->dev); | ||||
| 	struct uart_sunsu_port *up = platform_get_drvdata(op); | ||||
| 	bool kbdms = false; | ||||
| 
 | ||||
| 	if (up->su_type == SU_PORT_MS || | ||||
|  | @ -1516,8 +1516,6 @@ static int su_remove(struct platform_device *op) | |||
| 	if (kbdms) | ||||
| 		kfree(up); | ||||
| 
 | ||||
| 	dev_set_drvdata(&op->dev, NULL); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -1495,7 +1495,7 @@ static int zs_probe(struct platform_device *op) | |||
| 		kbm_inst++; | ||||
| 	} | ||||
| 
 | ||||
| 	dev_set_drvdata(&op->dev, &up[0]); | ||||
| 	platform_set_drvdata(op, &up[0]); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
|  | @ -1512,7 +1512,7 @@ static void zs_remove_one(struct uart_sunzilog_port *up) | |||
| 
 | ||||
| static int zs_remove(struct platform_device *op) | ||||
| { | ||||
| 	struct uart_sunzilog_port *up = dev_get_drvdata(&op->dev); | ||||
| 	struct uart_sunzilog_port *up = platform_get_drvdata(op); | ||||
| 	struct zilog_layout __iomem *regs; | ||||
| 
 | ||||
| 	zs_remove_one(&up[0]); | ||||
|  | @ -1521,8 +1521,6 @@ static int zs_remove(struct platform_device *op) | |||
| 	regs = sunzilog_chip_regs[up[0].port.line / 2]; | ||||
| 	of_iounmap(&op->resource[0], regs, sizeof(struct zilog_layout)); | ||||
| 
 | ||||
| 	dev_set_drvdata(&op->dev, NULL); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -1451,7 +1451,7 @@ static int ucc_uart_probe(struct platform_device *ofdev) | |||
| 		goto out_np; | ||||
| 	} | ||||
| 
 | ||||
| 	dev_set_drvdata(&ofdev->dev, qe_port); | ||||
| 	platform_set_drvdata(ofdev, qe_port); | ||||
| 
 | ||||
| 	dev_info(&ofdev->dev, "UCC%u assigned to /dev/ttyQE%u\n", | ||||
| 		qe_port->ucc_num + 1, qe_port->port.line); | ||||
|  | @ -1471,13 +1471,12 @@ out_free: | |||
| 
 | ||||
| static int ucc_uart_remove(struct platform_device *ofdev) | ||||
| { | ||||
| 	struct uart_qe_port *qe_port = dev_get_drvdata(&ofdev->dev); | ||||
| 	struct uart_qe_port *qe_port = platform_get_drvdata(ofdev); | ||||
| 
 | ||||
| 	dev_info(&ofdev->dev, "removing /dev/ttyQE%u\n", qe_port->port.line); | ||||
| 
 | ||||
| 	uart_remove_one_port(&ucc_uart_driver, &qe_port->port); | ||||
| 
 | ||||
| 	dev_set_drvdata(&ofdev->dev, NULL); | ||||
| 	kfree(qe_port); | ||||
| 
 | ||||
| 	return 0; | ||||
|  | @ -1518,9 +1517,11 @@ static int __init ucc_uart_init(void) | |||
| 	} | ||||
| 
 | ||||
| 	ret = platform_driver_register(&ucc_uart_of_driver); | ||||
| 	if (ret) | ||||
| 	if (ret) { | ||||
| 		printk(KERN_ERR | ||||
| 		       "ucc-uart: could not register platform driver\n"); | ||||
| 		uart_unregister_driver(&ucc_uart_driver); | ||||
| 	} | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
|  |  | |||
|  | @ -648,7 +648,7 @@ static struct platform_driver vt8500_platform_driver = { | |||
| 	.driver = { | ||||
| 		.name = "vt8500_serial", | ||||
| 		.owner = THIS_MODULE, | ||||
| 		.of_match_table = of_match_ptr(wmt_dt_ids), | ||||
| 		.of_match_table = wmt_dt_ids, | ||||
| 	}, | ||||
| }; | ||||
| 
 | ||||
|  |  | |||
|  | @ -974,12 +974,11 @@ static int xuartps_probe(struct platform_device *pdev) | |||
| 		port->dev = &pdev->dev; | ||||
| 		port->uartclk = clk_get_rate(clk); | ||||
| 		port->private_data = clk; | ||||
| 		dev_set_drvdata(&pdev->dev, port); | ||||
| 		platform_set_drvdata(pdev, port); | ||||
| 		rc = uart_add_one_port(&xuartps_uart_driver, port); | ||||
| 		if (rc) { | ||||
| 			dev_err(&pdev->dev, | ||||
| 				"uart_add_one_port() failed; err=%i\n", rc); | ||||
| 			dev_set_drvdata(&pdev->dev, NULL); | ||||
| 			return rc; | ||||
| 		} | ||||
| 		return 0; | ||||
|  | @ -994,46 +993,17 @@ static int xuartps_probe(struct platform_device *pdev) | |||
|  **/ | ||||
| static int xuartps_remove(struct platform_device *pdev) | ||||
| { | ||||
| 	struct uart_port *port = dev_get_drvdata(&pdev->dev); | ||||
| 	struct uart_port *port = platform_get_drvdata(pdev); | ||||
| 	struct clk *clk = port->private_data; | ||||
| 	int rc; | ||||
| 
 | ||||
| 	/* Remove the xuartps port from the serial core */ | ||||
| 	rc = uart_remove_one_port(&xuartps_uart_driver, port); | ||||
| 	dev_set_drvdata(&pdev->dev, NULL); | ||||
| 	port->mapbase = 0; | ||||
| 	clk_disable_unprepare(clk); | ||||
| 	return rc; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * xuartps_suspend - suspend event | ||||
|  * @pdev: Pointer to the platform device structure | ||||
|  * @state: State of the device | ||||
|  * | ||||
|  * Returns 0 | ||||
|  **/ | ||||
| static int xuartps_suspend(struct platform_device *pdev, pm_message_t state) | ||||
| { | ||||
| 	/* Call the API provided in serial_core.c file which handles
 | ||||
| 	 * the suspend. | ||||
| 	 */ | ||||
| 	uart_suspend_port(&xuartps_uart_driver, &xuartps_port[pdev->id]); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * xuartps_resume - Resume after a previous suspend | ||||
|  * @pdev: Pointer to the platform device structure | ||||
|  * | ||||
|  * Returns 0 | ||||
|  **/ | ||||
| static int xuartps_resume(struct platform_device *pdev) | ||||
| { | ||||
| 	uart_resume_port(&xuartps_uart_driver, &xuartps_port[pdev->id]); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /* Match table for of_platform binding */ | ||||
| static struct of_device_id xuartps_of_match[] = { | ||||
| 	{ .compatible = "xlnx,xuartps", }, | ||||
|  | @ -1044,8 +1014,6 @@ MODULE_DEVICE_TABLE(of, xuartps_of_match); | |||
| static struct platform_driver xuartps_platform_driver = { | ||||
| 	.probe   = xuartps_probe,		/* Probe method */ | ||||
| 	.remove  = xuartps_remove,		/* Detach method */ | ||||
| 	.suspend = xuartps_suspend,		/* Suspend */ | ||||
| 	.resume  = xuartps_resume,		/* Resume after a suspend */ | ||||
| 	.driver  = { | ||||
| 		.owner = THIS_MODULE, | ||||
| 		.name = XUARTPS_NAME,		/* Driver name */ | ||||
|  |  | |||
|  | @ -932,7 +932,7 @@ static int sysrq_reset_seq_param_set(const char *buffer, | |||
| 	unsigned long val; | ||||
| 	int error; | ||||
| 
 | ||||
| 	error = strict_strtoul(buffer, 0, &val); | ||||
| 	error = kstrtoul(buffer, 0, &val); | ||||
| 	if (error < 0) | ||||
| 		return error; | ||||
| 
 | ||||
|  |  | |||
|  | @ -1618,6 +1618,8 @@ static void release_tty(struct tty_struct *tty, int idx) | |||
| 	tty_free_termios(tty); | ||||
| 	tty_driver_remove_tty(tty->driver, tty); | ||||
| 	tty->port->itty = NULL; | ||||
| 	if (tty->link) | ||||
| 		tty->link->port->itty = NULL; | ||||
| 	cancel_work_sync(&tty->port->buf.work); | ||||
| 
 | ||||
| 	if (tty->link) | ||||
|  | @ -2138,6 +2140,7 @@ static unsigned int tty_poll(struct file *filp, poll_table *wait) | |||
| static int __tty_fasync(int fd, struct file *filp, int on) | ||||
| { | ||||
| 	struct tty_struct *tty = file_tty(filp); | ||||
| 	struct tty_ldisc *ldisc; | ||||
| 	unsigned long flags; | ||||
| 	int retval = 0; | ||||
| 
 | ||||
|  | @ -2148,11 +2151,17 @@ static int __tty_fasync(int fd, struct file *filp, int on) | |||
| 	if (retval <= 0) | ||||
| 		goto out; | ||||
| 
 | ||||
| 	ldisc = tty_ldisc_ref(tty); | ||||
| 	if (ldisc) { | ||||
| 		if (ldisc->ops->fasync) | ||||
| 			ldisc->ops->fasync(tty, on); | ||||
| 		tty_ldisc_deref(ldisc); | ||||
| 	} | ||||
| 
 | ||||
| 	if (on) { | ||||
| 		enum pid_type type; | ||||
| 		struct pid *pid; | ||||
| 		if (!waitqueue_active(&tty->read_wait)) | ||||
| 			tty->minimum_to_wake = 1; | ||||
| 
 | ||||
| 		spin_lock_irqsave(&tty->ctrl_lock, flags); | ||||
| 		if (tty->pgrp) { | ||||
| 			pid = tty->pgrp; | ||||
|  | @ -2165,13 +2174,7 @@ static int __tty_fasync(int fd, struct file *filp, int on) | |||
| 		spin_unlock_irqrestore(&tty->ctrl_lock, flags); | ||||
| 		retval = __f_setown(filp, pid, type, 0); | ||||
| 		put_pid(pid); | ||||
| 		if (retval) | ||||
| 			goto out; | ||||
| 	} else { | ||||
| 		if (!tty->fasync && !waitqueue_active(&tty->read_wait)) | ||||
| 			tty->minimum_to_wake = N_TTY_BUF_SIZE; | ||||
| 	} | ||||
| 	retval = 0; | ||||
| out: | ||||
| 	return retval; | ||||
| } | ||||
|  |  | |||
							
								
								
									
										453
									
								
								drivers/tty/tty_ldsem.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										453
									
								
								drivers/tty/tty_ldsem.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,453 @@ | |||
| /*
 | ||||
|  * Ldisc rw semaphore | ||||
|  * | ||||
|  * The ldisc semaphore is semantically a rw_semaphore but which enforces | ||||
|  * an alternate policy, namely: | ||||
|  *   1) Supports lock wait timeouts | ||||
|  *   2) Write waiter has priority | ||||
|  *   3) Downgrading is not supported | ||||
|  * | ||||
|  * Implementation notes: | ||||
|  *   1) Upper half of semaphore count is a wait count (differs from rwsem | ||||
|  *	in that rwsem normalizes the upper half to the wait bias) | ||||
|  *   2) Lacks overflow checking | ||||
|  * | ||||
|  * The generic counting was copied and modified from include/asm-generic/rwsem.h | ||||
|  * by Paul Mackerras <paulus@samba.org>. | ||||
|  * | ||||
|  * The scheduling policy was copied and modified from lib/rwsem.c | ||||
|  * Written by David Howells (dhowells@redhat.com). | ||||
|  * | ||||
|  * This implementation incorporates the write lock stealing work of | ||||
|  * Michel Lespinasse <walken@google.com>. | ||||
|  * | ||||
|  * Copyright (C) 2013 Peter Hurley <peter@hurleysoftware.com> | ||||
|  * | ||||
|  * This file may be redistributed under the terms of the GNU General Public | ||||
|  * License v2. | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/list.h> | ||||
| #include <linux/spinlock.h> | ||||
| #include <linux/atomic.h> | ||||
| #include <linux/tty.h> | ||||
| #include <linux/sched.h> | ||||
| 
 | ||||
| 
 | ||||
| #ifdef CONFIG_DEBUG_LOCK_ALLOC | ||||
| # define __acq(l, s, t, r, c, n, i)		\ | ||||
| 				lock_acquire(&(l)->dep_map, s, t, r, c, n, i) | ||||
| # define __rel(l, n, i)				\ | ||||
| 				lock_release(&(l)->dep_map, n, i) | ||||
| # ifdef CONFIG_PROVE_LOCKING | ||||
| #  define lockdep_acquire(l, s, t, i)		__acq(l, s, t, 0, 2, NULL, i) | ||||
| #  define lockdep_acquire_nest(l, s, t, n, i)	__acq(l, s, t, 0, 2, n, i) | ||||
| #  define lockdep_acquire_read(l, s, t, i)	__acq(l, s, t, 1, 2, NULL, i) | ||||
| #  define lockdep_release(l, n, i)		__rel(l, n, i) | ||||
| # else | ||||
| #  define lockdep_acquire(l, s, t, i)		__acq(l, s, t, 0, 1, NULL, i) | ||||
| #  define lockdep_acquire_nest(l, s, t, n, i)	__acq(l, s, t, 0, 1, n, i) | ||||
| #  define lockdep_acquire_read(l, s, t, i)	__acq(l, s, t, 1, 1, NULL, i) | ||||
| #  define lockdep_release(l, n, i)		__rel(l, n, i) | ||||
| # endif | ||||
| #else | ||||
| # define lockdep_acquire(l, s, t, i)		do { } while (0) | ||||
| # define lockdep_acquire_nest(l, s, t, n, i)	do { } while (0) | ||||
| # define lockdep_acquire_read(l, s, t, i)	do { } while (0) | ||||
| # define lockdep_release(l, n, i)		do { } while (0) | ||||
| #endif | ||||
| 
 | ||||
| #ifdef CONFIG_LOCK_STAT | ||||
| # define lock_stat(_lock, stat)		lock_##stat(&(_lock)->dep_map, _RET_IP_) | ||||
| #else | ||||
| # define lock_stat(_lock, stat)		do { } while (0) | ||||
| #endif | ||||
| 
 | ||||
| 
 | ||||
| #if BITS_PER_LONG == 64 | ||||
| # define LDSEM_ACTIVE_MASK	0xffffffffL | ||||
| #else | ||||
| # define LDSEM_ACTIVE_MASK	0x0000ffffL | ||||
| #endif | ||||
| 
 | ||||
| #define LDSEM_UNLOCKED		0L | ||||
| #define LDSEM_ACTIVE_BIAS	1L | ||||
| #define LDSEM_WAIT_BIAS		(-LDSEM_ACTIVE_MASK-1) | ||||
| #define LDSEM_READ_BIAS		LDSEM_ACTIVE_BIAS | ||||
| #define LDSEM_WRITE_BIAS	(LDSEM_WAIT_BIAS + LDSEM_ACTIVE_BIAS) | ||||
| 
 | ||||
| struct ldsem_waiter { | ||||
| 	struct list_head list; | ||||
| 	struct task_struct *task; | ||||
| }; | ||||
| 
 | ||||
| static inline long ldsem_atomic_update(long delta, struct ld_semaphore *sem) | ||||
| { | ||||
| 	return atomic_long_add_return(delta, (atomic_long_t *)&sem->count); | ||||
| } | ||||
| 
 | ||||
| static inline int ldsem_cmpxchg(long *old, long new, struct ld_semaphore *sem) | ||||
| { | ||||
| 	long tmp = *old; | ||||
| 	*old = atomic_long_cmpxchg(&sem->count, *old, new); | ||||
| 	return *old == tmp; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Initialize an ldsem: | ||||
|  */ | ||||
| void __init_ldsem(struct ld_semaphore *sem, const char *name, | ||||
| 		  struct lock_class_key *key) | ||||
| { | ||||
| #ifdef CONFIG_DEBUG_LOCK_ALLOC | ||||
| 	/*
 | ||||
| 	 * Make sure we are not reinitializing a held semaphore: | ||||
| 	 */ | ||||
| 	debug_check_no_locks_freed((void *)sem, sizeof(*sem)); | ||||
| 	lockdep_init_map(&sem->dep_map, name, key, 0); | ||||
| #endif | ||||
| 	sem->count = LDSEM_UNLOCKED; | ||||
| 	sem->wait_readers = 0; | ||||
| 	raw_spin_lock_init(&sem->wait_lock); | ||||
| 	INIT_LIST_HEAD(&sem->read_wait); | ||||
| 	INIT_LIST_HEAD(&sem->write_wait); | ||||
| } | ||||
| 
 | ||||
| static void __ldsem_wake_readers(struct ld_semaphore *sem) | ||||
| { | ||||
| 	struct ldsem_waiter *waiter, *next; | ||||
| 	struct task_struct *tsk; | ||||
| 	long adjust, count; | ||||
| 
 | ||||
| 	/* Try to grant read locks to all readers on the read wait list.
 | ||||
| 	 * Note the 'active part' of the count is incremented by | ||||
| 	 * the number of readers before waking any processes up. | ||||
| 	 */ | ||||
| 	adjust = sem->wait_readers * (LDSEM_ACTIVE_BIAS - LDSEM_WAIT_BIAS); | ||||
| 	count = ldsem_atomic_update(adjust, sem); | ||||
| 	do { | ||||
| 		if (count > 0) | ||||
| 			break; | ||||
| 		if (ldsem_cmpxchg(&count, count - adjust, sem)) | ||||
| 			return; | ||||
| 	} while (1); | ||||
| 
 | ||||
| 	list_for_each_entry_safe(waiter, next, &sem->read_wait, list) { | ||||
| 		tsk = waiter->task; | ||||
| 		smp_mb(); | ||||
| 		waiter->task = NULL; | ||||
| 		wake_up_process(tsk); | ||||
| 		put_task_struct(tsk); | ||||
| 	} | ||||
| 	INIT_LIST_HEAD(&sem->read_wait); | ||||
| 	sem->wait_readers = 0; | ||||
| } | ||||
| 
 | ||||
| static inline int writer_trylock(struct ld_semaphore *sem) | ||||
| { | ||||
| 	/* only wake this writer if the active part of the count can be
 | ||||
| 	 * transitioned from 0 -> 1 | ||||
| 	 */ | ||||
| 	long count = ldsem_atomic_update(LDSEM_ACTIVE_BIAS, sem); | ||||
| 	do { | ||||
| 		if ((count & LDSEM_ACTIVE_MASK) == LDSEM_ACTIVE_BIAS) | ||||
| 			return 1; | ||||
| 		if (ldsem_cmpxchg(&count, count - LDSEM_ACTIVE_BIAS, sem)) | ||||
| 			return 0; | ||||
| 	} while (1); | ||||
| } | ||||
| 
 | ||||
| static void __ldsem_wake_writer(struct ld_semaphore *sem) | ||||
| { | ||||
| 	struct ldsem_waiter *waiter; | ||||
| 
 | ||||
| 	waiter = list_entry(sem->write_wait.next, struct ldsem_waiter, list); | ||||
| 	wake_up_process(waiter->task); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * handle the lock release when processes blocked on it that can now run | ||||
|  * - if we come here from up_xxxx(), then: | ||||
|  *   - the 'active part' of count (&0x0000ffff) reached 0 (but may have changed) | ||||
|  *   - the 'waiting part' of count (&0xffff0000) is -ve (and will still be so) | ||||
|  * - the spinlock must be held by the caller | ||||
|  * - woken process blocks are discarded from the list after having task zeroed | ||||
|  */ | ||||
| static void __ldsem_wake(struct ld_semaphore *sem) | ||||
| { | ||||
| 	if (!list_empty(&sem->write_wait)) | ||||
| 		__ldsem_wake_writer(sem); | ||||
| 	else if (!list_empty(&sem->read_wait)) | ||||
| 		__ldsem_wake_readers(sem); | ||||
| } | ||||
| 
 | ||||
| static void ldsem_wake(struct ld_semaphore *sem) | ||||
| { | ||||
| 	unsigned long flags; | ||||
| 
 | ||||
| 	raw_spin_lock_irqsave(&sem->wait_lock, flags); | ||||
| 	__ldsem_wake(sem); | ||||
| 	raw_spin_unlock_irqrestore(&sem->wait_lock, flags); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * wait for the read lock to be granted | ||||
|  */ | ||||
| static struct ld_semaphore __sched * | ||||
| down_read_failed(struct ld_semaphore *sem, long count, long timeout) | ||||
| { | ||||
| 	struct ldsem_waiter waiter; | ||||
| 	struct task_struct *tsk = current; | ||||
| 	long adjust = -LDSEM_ACTIVE_BIAS + LDSEM_WAIT_BIAS; | ||||
| 
 | ||||
| 	/* set up my own style of waitqueue */ | ||||
| 	raw_spin_lock_irq(&sem->wait_lock); | ||||
| 
 | ||||
| 	/* Try to reverse the lock attempt but if the count has changed
 | ||||
| 	 * so that reversing fails, check if there are are no waiters, | ||||
| 	 * and early-out if not */ | ||||
| 	do { | ||||
| 		if (ldsem_cmpxchg(&count, count + adjust, sem)) | ||||
| 			break; | ||||
| 		if (count > 0) { | ||||
| 			raw_spin_unlock_irq(&sem->wait_lock); | ||||
| 			return sem; | ||||
| 		} | ||||
| 	} while (1); | ||||
| 
 | ||||
| 	list_add_tail(&waiter.list, &sem->read_wait); | ||||
| 	sem->wait_readers++; | ||||
| 
 | ||||
| 	waiter.task = tsk; | ||||
| 	get_task_struct(tsk); | ||||
| 
 | ||||
| 	/* if there are no active locks, wake the new lock owner(s) */ | ||||
| 	if ((count & LDSEM_ACTIVE_MASK) == 0) | ||||
| 		__ldsem_wake(sem); | ||||
| 
 | ||||
| 	raw_spin_unlock_irq(&sem->wait_lock); | ||||
| 
 | ||||
| 	/* wait to be given the lock */ | ||||
| 	for (;;) { | ||||
| 		set_task_state(tsk, TASK_UNINTERRUPTIBLE); | ||||
| 
 | ||||
| 		if (!waiter.task) | ||||
| 			break; | ||||
| 		if (!timeout) | ||||
| 			break; | ||||
| 		timeout = schedule_timeout(timeout); | ||||
| 	} | ||||
| 
 | ||||
| 	__set_task_state(tsk, TASK_RUNNING); | ||||
| 
 | ||||
| 	if (!timeout) { | ||||
| 		/* lock timed out but check if this task was just
 | ||||
| 		 * granted lock ownership - if so, pretend there | ||||
| 		 * was no timeout; otherwise, cleanup lock wait */ | ||||
| 		raw_spin_lock_irq(&sem->wait_lock); | ||||
| 		if (waiter.task) { | ||||
| 			ldsem_atomic_update(-LDSEM_WAIT_BIAS, sem); | ||||
| 			list_del(&waiter.list); | ||||
| 			raw_spin_unlock_irq(&sem->wait_lock); | ||||
| 			put_task_struct(waiter.task); | ||||
| 			return NULL; | ||||
| 		} | ||||
| 		raw_spin_unlock_irq(&sem->wait_lock); | ||||
| 	} | ||||
| 
 | ||||
| 	return sem; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * wait for the write lock to be granted | ||||
|  */ | ||||
| static struct ld_semaphore __sched * | ||||
| down_write_failed(struct ld_semaphore *sem, long count, long timeout) | ||||
| { | ||||
| 	struct ldsem_waiter waiter; | ||||
| 	struct task_struct *tsk = current; | ||||
| 	long adjust = -LDSEM_ACTIVE_BIAS; | ||||
| 	int locked = 0; | ||||
| 
 | ||||
| 	/* set up my own style of waitqueue */ | ||||
| 	raw_spin_lock_irq(&sem->wait_lock); | ||||
| 
 | ||||
| 	/* Try to reverse the lock attempt but if the count has changed
 | ||||
| 	 * so that reversing fails, check if the lock is now owned, | ||||
| 	 * and early-out if so */ | ||||
| 	do { | ||||
| 		if (ldsem_cmpxchg(&count, count + adjust, sem)) | ||||
| 			break; | ||||
| 		if ((count & LDSEM_ACTIVE_MASK) == LDSEM_ACTIVE_BIAS) { | ||||
| 			raw_spin_unlock_irq(&sem->wait_lock); | ||||
| 			return sem; | ||||
| 		} | ||||
| 	} while (1); | ||||
| 
 | ||||
| 	list_add_tail(&waiter.list, &sem->write_wait); | ||||
| 
 | ||||
| 	waiter.task = tsk; | ||||
| 
 | ||||
| 	set_task_state(tsk, TASK_UNINTERRUPTIBLE); | ||||
| 	for (;;) { | ||||
| 		if (!timeout) | ||||
| 			break; | ||||
| 		raw_spin_unlock_irq(&sem->wait_lock); | ||||
| 		timeout = schedule_timeout(timeout); | ||||
| 		raw_spin_lock_irq(&sem->wait_lock); | ||||
| 		set_task_state(tsk, TASK_UNINTERRUPTIBLE); | ||||
| 		if ((locked = writer_trylock(sem))) | ||||
| 			break; | ||||
| 	} | ||||
| 
 | ||||
| 	if (!locked) | ||||
| 		ldsem_atomic_update(-LDSEM_WAIT_BIAS, sem); | ||||
| 	list_del(&waiter.list); | ||||
| 	raw_spin_unlock_irq(&sem->wait_lock); | ||||
| 
 | ||||
| 	__set_task_state(tsk, TASK_RUNNING); | ||||
| 
 | ||||
| 	/* lock wait may have timed out */ | ||||
| 	if (!locked) | ||||
| 		return NULL; | ||||
| 	return sem; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| static inline int __ldsem_down_read_nested(struct ld_semaphore *sem, | ||||
| 					   int subclass, long timeout) | ||||
| { | ||||
| 	long count; | ||||
| 
 | ||||
| 	lockdep_acquire_read(sem, subclass, 0, _RET_IP_); | ||||
| 
 | ||||
| 	count = ldsem_atomic_update(LDSEM_READ_BIAS, sem); | ||||
| 	if (count <= 0) { | ||||
| 		lock_stat(sem, contended); | ||||
| 		if (!down_read_failed(sem, count, timeout)) { | ||||
| 			lockdep_release(sem, 1, _RET_IP_); | ||||
| 			return 0; | ||||
| 		} | ||||
| 	} | ||||
| 	lock_stat(sem, acquired); | ||||
| 	return 1; | ||||
| } | ||||
| 
 | ||||
| static inline int __ldsem_down_write_nested(struct ld_semaphore *sem, | ||||
| 					    int subclass, long timeout) | ||||
| { | ||||
| 	long count; | ||||
| 
 | ||||
| 	lockdep_acquire(sem, subclass, 0, _RET_IP_); | ||||
| 
 | ||||
| 	count = ldsem_atomic_update(LDSEM_WRITE_BIAS, sem); | ||||
| 	if ((count & LDSEM_ACTIVE_MASK) != LDSEM_ACTIVE_BIAS) { | ||||
| 		lock_stat(sem, contended); | ||||
| 		if (!down_write_failed(sem, count, timeout)) { | ||||
| 			lockdep_release(sem, 1, _RET_IP_); | ||||
| 			return 0; | ||||
| 		} | ||||
| 	} | ||||
| 	lock_stat(sem, acquired); | ||||
| 	return 1; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /*
 | ||||
|  * lock for reading -- returns 1 if successful, 0 if timed out | ||||
|  */ | ||||
| int __sched ldsem_down_read(struct ld_semaphore *sem, long timeout) | ||||
| { | ||||
| 	might_sleep(); | ||||
| 	return __ldsem_down_read_nested(sem, 0, timeout); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * trylock for reading -- returns 1 if successful, 0 if contention | ||||
|  */ | ||||
| int ldsem_down_read_trylock(struct ld_semaphore *sem) | ||||
| { | ||||
| 	long count = sem->count; | ||||
| 
 | ||||
| 	while (count >= 0) { | ||||
| 		if (ldsem_cmpxchg(&count, count + LDSEM_READ_BIAS, sem)) { | ||||
| 			lockdep_acquire_read(sem, 0, 1, _RET_IP_); | ||||
| 			lock_stat(sem, acquired); | ||||
| 			return 1; | ||||
| 		} | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * lock for writing -- returns 1 if successful, 0 if timed out | ||||
|  */ | ||||
| int __sched ldsem_down_write(struct ld_semaphore *sem, long timeout) | ||||
| { | ||||
| 	might_sleep(); | ||||
| 	return __ldsem_down_write_nested(sem, 0, timeout); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * trylock for writing -- returns 1 if successful, 0 if contention | ||||
|  */ | ||||
| int ldsem_down_write_trylock(struct ld_semaphore *sem) | ||||
| { | ||||
| 	long count = sem->count; | ||||
| 
 | ||||
| 	while ((count & LDSEM_ACTIVE_MASK) == 0) { | ||||
| 		if (ldsem_cmpxchg(&count, count + LDSEM_WRITE_BIAS, sem)) { | ||||
| 			lockdep_acquire(sem, 0, 1, _RET_IP_); | ||||
| 			lock_stat(sem, acquired); | ||||
| 			return 1; | ||||
| 		} | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * release a read lock | ||||
|  */ | ||||
| void ldsem_up_read(struct ld_semaphore *sem) | ||||
| { | ||||
| 	long count; | ||||
| 
 | ||||
| 	lockdep_release(sem, 1, _RET_IP_); | ||||
| 
 | ||||
| 	count = ldsem_atomic_update(-LDSEM_READ_BIAS, sem); | ||||
| 	if (count < 0 && (count & LDSEM_ACTIVE_MASK) == 0) | ||||
| 		ldsem_wake(sem); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * release a write lock | ||||
|  */ | ||||
| void ldsem_up_write(struct ld_semaphore *sem) | ||||
| { | ||||
| 	long count; | ||||
| 
 | ||||
| 	lockdep_release(sem, 1, _RET_IP_); | ||||
| 
 | ||||
| 	count = ldsem_atomic_update(-LDSEM_WRITE_BIAS, sem); | ||||
| 	if (count < 0) | ||||
| 		ldsem_wake(sem); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| #ifdef CONFIG_DEBUG_LOCK_ALLOC | ||||
| 
 | ||||
| int ldsem_down_read_nested(struct ld_semaphore *sem, int subclass, long timeout) | ||||
| { | ||||
| 	might_sleep(); | ||||
| 	return __ldsem_down_read_nested(sem, subclass, timeout); | ||||
| } | ||||
| 
 | ||||
| int ldsem_down_write_nested(struct ld_semaphore *sem, int subclass, | ||||
| 			    long timeout) | ||||
| { | ||||
| 	might_sleep(); | ||||
| 	return __ldsem_down_write_nested(sem, subclass, timeout); | ||||
| } | ||||
| 
 | ||||
| #endif | ||||
|  | @ -3086,17 +3086,6 @@ err: | |||
| }; | ||||
| 
 | ||||
| 
 | ||||
| static int bind_con_driver(const struct consw *csw, int first, int last, | ||||
| 			   int deflt) | ||||
| { | ||||
| 	int ret; | ||||
| 
 | ||||
| 	console_lock(); | ||||
| 	ret = do_bind_con_driver(csw, first, last, deflt); | ||||
| 	console_unlock(); | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| #ifdef CONFIG_VT_HW_CONSOLE_BINDING | ||||
| static int con_is_graphics(const struct consw *csw, int first, int last) | ||||
| { | ||||
|  | @ -3114,34 +3103,6 @@ static int con_is_graphics(const struct consw *csw, int first, int last) | |||
| 	return retval; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * unbind_con_driver - unbind a console driver | ||||
|  * @csw: pointer to console driver to unregister | ||||
|  * @first: first in range of consoles that @csw should be unbound from | ||||
|  * @last: last in range of consoles that @csw should be unbound from | ||||
|  * @deflt: should next bound console driver be default after @csw is unbound? | ||||
|  * | ||||
|  * To unbind a driver from all possible consoles, pass 0 as @first and | ||||
|  * %MAX_NR_CONSOLES as @last. | ||||
|  * | ||||
|  * @deflt controls whether the console that ends up replacing @csw should be | ||||
|  * the default console. | ||||
|  * | ||||
|  * RETURNS: | ||||
|  * -ENODEV if @csw isn't a registered console driver or can't be unregistered | ||||
|  * or 0 on success. | ||||
|  */ | ||||
| int unbind_con_driver(const struct consw *csw, int first, int last, int deflt) | ||||
| { | ||||
| 	int retval; | ||||
| 
 | ||||
| 	console_lock(); | ||||
| 	retval = do_unbind_con_driver(csw, first, last, deflt); | ||||
| 	console_unlock(); | ||||
| 	return retval; | ||||
| } | ||||
| EXPORT_SYMBOL(unbind_con_driver); | ||||
| 
 | ||||
| /* unlocked version of unbind_con_driver() */ | ||||
| int do_unbind_con_driver(const struct consw *csw, int first, int last, int deflt) | ||||
| { | ||||
|  | @ -3262,8 +3223,11 @@ static int vt_bind(struct con_driver *con) | |||
| 		if (first == 0 && last == MAX_NR_CONSOLES -1) | ||||
| 			deflt = 1; | ||||
| 
 | ||||
| 		if (first != -1) | ||||
| 			bind_con_driver(csw, first, last, deflt); | ||||
| 		if (first != -1) { | ||||
| 			console_lock(); | ||||
| 			do_bind_con_driver(csw, first, last, deflt); | ||||
| 			console_unlock(); | ||||
| 		} | ||||
| 
 | ||||
| 		first = -1; | ||||
| 		last = -1; | ||||
|  | @ -3301,8 +3265,11 @@ static int vt_unbind(struct con_driver *con) | |||
| 		if (first == 0 && last == MAX_NR_CONSOLES -1) | ||||
| 			deflt = 1; | ||||
| 
 | ||||
| 		if (first != -1) | ||||
| 			unbind_con_driver(csw, first, last, deflt); | ||||
| 		if (first != -1) { | ||||
| 			console_lock(); | ||||
| 			do_unbind_con_driver(csw, first, last, deflt); | ||||
| 			console_unlock(); | ||||
| 		} | ||||
| 
 | ||||
| 		first = -1; | ||||
| 		last = -1; | ||||
|  | @ -3574,29 +3541,9 @@ err: | |||
| 	return retval; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * register_con_driver - register console driver to console layer | ||||
|  * @csw: console driver | ||||
|  * @first: the first console to take over, minimum value is 0 | ||||
|  * @last: the last console to take over, maximum value is MAX_NR_CONSOLES -1 | ||||
|  * | ||||
|  * DESCRIPTION: This function registers a console driver which can later | ||||
|  * bind to a range of consoles specified by @first and @last. It will | ||||
|  * also initialize the console driver by calling con_startup(). | ||||
|  */ | ||||
| int register_con_driver(const struct consw *csw, int first, int last) | ||||
| { | ||||
| 	int retval; | ||||
| 
 | ||||
| 	console_lock(); | ||||
| 	retval = do_register_con_driver(csw, first, last); | ||||
| 	console_unlock(); | ||||
| 	return retval; | ||||
| } | ||||
| EXPORT_SYMBOL(register_con_driver); | ||||
| 
 | ||||
| /**
 | ||||
|  * unregister_con_driver - unregister console driver from console layer | ||||
|  * do_unregister_con_driver - unregister console driver from console layer | ||||
|  * @csw: console driver | ||||
|  * | ||||
|  * DESCRIPTION: All drivers that registers to the console layer must | ||||
|  | @ -3606,17 +3553,6 @@ EXPORT_SYMBOL(register_con_driver); | |||
|  * | ||||
|  * The driver must unbind first prior to unregistration. | ||||
|  */ | ||||
| int unregister_con_driver(const struct consw *csw) | ||||
| { | ||||
| 	int retval; | ||||
| 
 | ||||
| 	console_lock(); | ||||
| 	retval = do_unregister_con_driver(csw); | ||||
| 	console_unlock(); | ||||
| 	return retval; | ||||
| } | ||||
| EXPORT_SYMBOL(unregister_con_driver); | ||||
| 
 | ||||
| int do_unregister_con_driver(const struct consw *csw) | ||||
| { | ||||
| 	int i, retval = -ENODEV; | ||||
|  | @ -3654,7 +3590,7 @@ EXPORT_SYMBOL_GPL(do_unregister_con_driver); | |||
|  *	when a driver wants to take over some existing consoles | ||||
|  *	and become default driver for newly opened ones. | ||||
|  * | ||||
|  *	take_over_console is basically a register followed by unbind | ||||
|  *	do_take_over_console is basically a register followed by unbind | ||||
|  */ | ||||
| int do_take_over_console(const struct consw *csw, int first, int last, int deflt) | ||||
| { | ||||
|  | @ -3675,30 +3611,6 @@ int do_take_over_console(const struct consw *csw, int first, int last, int deflt | |||
| } | ||||
| EXPORT_SYMBOL_GPL(do_take_over_console); | ||||
| 
 | ||||
| /*
 | ||||
|  *	If we support more console drivers, this function is used | ||||
|  *	when a driver wants to take over some existing consoles | ||||
|  *	and become default driver for newly opened ones. | ||||
|  * | ||||
|  *	take_over_console is basically a register followed by unbind | ||||
|  */ | ||||
| int take_over_console(const struct consw *csw, int first, int last, int deflt) | ||||
| { | ||||
| 	int err; | ||||
| 
 | ||||
| 	err = register_con_driver(csw, first, last); | ||||
| 	/*
 | ||||
| 	 * If we get an busy error we still want to bind the console driver | ||||
| 	 * and return success, as we may have unbound the console driver | ||||
| 	 * but not unregistered it. | ||||
| 	 */ | ||||
| 	if (err == -EBUSY) | ||||
| 		err = 0; | ||||
| 	if (!err) | ||||
| 		bind_con_driver(csw, first, last, deflt); | ||||
| 
 | ||||
| 	return err; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * give_up_console is a wrapper to unregister_con_driver. It will only | ||||
|  | @ -3706,7 +3618,9 @@ int take_over_console(const struct consw *csw, int first, int last, int deflt) | |||
|  */ | ||||
| void give_up_console(const struct consw *csw) | ||||
| { | ||||
| 	unregister_con_driver(csw); | ||||
| 	console_lock(); | ||||
| 	do_unregister_con_driver(csw); | ||||
| 	console_unlock(); | ||||
| } | ||||
| 
 | ||||
| static int __init vtconsole_class_init(void) | ||||
|  | @ -4262,6 +4176,5 @@ EXPORT_SYMBOL(console_blanked); | |||
| EXPORT_SYMBOL(vc_cons); | ||||
| EXPORT_SYMBOL(global_cursor_default); | ||||
| #ifndef VT_SINGLE_DRIVER | ||||
| EXPORT_SYMBOL(take_over_console); | ||||
| EXPORT_SYMBOL(give_up_console); | ||||
| #endif | ||||
|  |  | |||
|  | @ -208,7 +208,7 @@ sisusbcon_init(struct vc_data *c, int init) | |||
| 	struct sisusb_usb_data *sisusb; | ||||
| 	int cols, rows; | ||||
| 
 | ||||
| 	/* This is called by take_over_console(),
 | ||||
| 	/* This is called by do_take_over_console(),
 | ||||
| 	 * ie by us/under our control. It is | ||||
| 	 * only called after text mode and fonts | ||||
| 	 * are set up/restored. | ||||
|  | @ -273,7 +273,7 @@ sisusbcon_deinit(struct vc_data *c) | |||
| 	struct sisusb_usb_data *sisusb; | ||||
| 	int i; | ||||
| 
 | ||||
| 	/* This is called by take_over_console()
 | ||||
| 	/* This is called by do_take_over_console()
 | ||||
| 	 * and others, ie not under our control. | ||||
| 	 */ | ||||
| 
 | ||||
|  | @ -1490,8 +1490,9 @@ sisusb_console_init(struct sisusb_usb_data *sisusb, int first, int last) | |||
| 	mutex_unlock(&sisusb->lock); | ||||
| 
 | ||||
| 	/* Now grab the desired console(s) */ | ||||
| 	ret = take_over_console(&sisusb_con, first - 1, last - 1, 0); | ||||
| 
 | ||||
| 	console_lock(); | ||||
| 	ret = do_take_over_console(&sisusb_con, first - 1, last - 1, 0); | ||||
| 	console_unlock(); | ||||
| 	if (!ret) | ||||
| 		sisusb->haveconsole = 1; | ||||
| 	else { | ||||
|  | @ -1535,11 +1536,14 @@ sisusb_console_exit(struct sisusb_usb_data *sisusb) | |||
| 
 | ||||
| 	if (sisusb->haveconsole) { | ||||
| 		for (i = 0; i < MAX_NR_CONSOLES; i++) | ||||
| 			if (sisusb->havethisconsole[i]) | ||||
| 				take_over_console(&sisusb_dummy_con, i, i, 0); | ||||
| 			if (sisusb->havethisconsole[i]) { | ||||
| 				console_lock(); | ||||
| 				do_take_over_console(&sisusb_dummy_con, i, i, 0); | ||||
| 				console_unlock(); | ||||
| 				/* At this point, con_deinit for all our
 | ||||
| 				 * consoles is executed by take_over_console(). | ||||
| 				 * consoles is executed by do_take_over_console(). | ||||
| 				 */ | ||||
| 			} | ||||
| 		sisusb->haveconsole = 0; | ||||
| 	} | ||||
| 
 | ||||
|  |  | |||
|  | @ -556,34 +556,6 @@ static int do_fbcon_takeover(int show_logo) | |||
| 	return err; | ||||
| } | ||||
| 
 | ||||
| static int fbcon_takeover(int show_logo) | ||||
| { | ||||
| 	int err, i; | ||||
| 
 | ||||
| 	if (!num_registered_fb) | ||||
| 		return -ENODEV; | ||||
| 
 | ||||
| 	if (!show_logo) | ||||
| 		logo_shown = FBCON_LOGO_DONTSHOW; | ||||
| 
 | ||||
| 	for (i = first_fb_vc; i <= last_fb_vc; i++) | ||||
| 		con2fb_map[i] = info_idx; | ||||
| 
 | ||||
| 	err = take_over_console(&fb_con, first_fb_vc, last_fb_vc, | ||||
| 				fbcon_is_default); | ||||
| 
 | ||||
| 	if (err) { | ||||
| 		for (i = first_fb_vc; i <= last_fb_vc; i++) { | ||||
| 			con2fb_map[i] = -1; | ||||
| 		} | ||||
| 		info_idx = -1; | ||||
| 	} else { | ||||
| 		fbcon_has_console_bind = 1; | ||||
| 	} | ||||
| 
 | ||||
| 	return err; | ||||
| } | ||||
| 
 | ||||
| #ifdef MODULE | ||||
| static void fbcon_prepare_logo(struct vc_data *vc, struct fb_info *info, | ||||
| 			       int cols, int rows, int new_cols, int new_rows) | ||||
|  | @ -901,7 +873,7 @@ static int set_con2fb_map(int unit, int newidx, int user) | |||
| /*
 | ||||
|  *  Low Level Operations | ||||
|  */ | ||||
| /* NOTE: fbcon cannot be __init: it may be called from take_over_console later */ | ||||
| /* NOTE: fbcon cannot be __init: it may be called from do_take_over_console later */ | ||||
| static int var_to_display(struct display *disp, | ||||
| 			  struct fb_var_screeninfo *var, | ||||
| 			  struct fb_info *info) | ||||
|  | @ -3543,8 +3515,9 @@ static void fbcon_start(void) | |||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		do_fbcon_takeover(0); | ||||
| 		console_unlock(); | ||||
| 		fbcon_takeover(0); | ||||
| 
 | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  | @ -3648,8 +3621,8 @@ static void __exit fb_console_exit(void) | |||
| 	fbcon_deinit_device(); | ||||
| 	device_destroy(fb_class, MKDEV(0, 0)); | ||||
| 	fbcon_exit(); | ||||
| 	do_unregister_con_driver(&fb_con); | ||||
| 	console_unlock(); | ||||
| 	unregister_con_driver(&fb_con); | ||||
| }	 | ||||
| 
 | ||||
| module_exit(fb_console_exit); | ||||
|  |  | |||
|  | @ -585,10 +585,14 @@ static const struct consw mda_con = { | |||
| 
 | ||||
| int __init mda_console_init(void) | ||||
| { | ||||
| 	int err; | ||||
| 
 | ||||
| 	if (mda_first_vc > mda_last_vc) | ||||
| 		return 1; | ||||
| 
 | ||||
| 	return take_over_console(&mda_con, mda_first_vc-1, mda_last_vc-1, 0); | ||||
| 	console_lock(); | ||||
| 	err = do_take_over_console(&mda_con, mda_first_vc-1, mda_last_vc-1, 0); | ||||
| 	console_unlock(); | ||||
| 	return err; | ||||
| } | ||||
| 
 | ||||
| static void __exit mda_console_exit(void) | ||||
|  |  | |||
|  | @ -297,7 +297,7 @@ static void newport_exit(void) | |||
| 		newport_set_def_font(i, NULL); | ||||
| } | ||||
| 
 | ||||
| /* Can't be __init, take_over_console may call it later */ | ||||
| /* Can't be __init, do_take_over_console may call it later */ | ||||
| static const char *newport_startup(void) | ||||
| { | ||||
| 	int i; | ||||
|  | @ -746,6 +746,7 @@ static int newport_probe(struct gio_device *dev, | |||
| 			 const struct gio_device_id *id) | ||||
| { | ||||
| 	unsigned long newport_addr; | ||||
| 	int err; | ||||
| 
 | ||||
| 	if (!dev->resource.start) | ||||
| 		return -EINVAL; | ||||
|  | @ -759,8 +760,10 @@ static int newport_probe(struct gio_device *dev, | |||
| 
 | ||||
| 	npregs = (struct newport_regs *)/* ioremap cannot fail */ | ||||
| 		ioremap(newport_addr, sizeof(struct newport_regs)); | ||||
| 
 | ||||
| 	return take_over_console(&newport_con, 0, MAX_NR_CONSOLES - 1, 1); | ||||
| 	console_lock(); | ||||
| 	err = do_take_over_console(&newport_con, 0, MAX_NR_CONSOLES - 1, 1); | ||||
| 	console_unlock(); | ||||
| 	return err; | ||||
| } | ||||
| 
 | ||||
| static void newport_remove(struct gio_device *dev) | ||||
|  |  | |||
|  | @ -372,6 +372,7 @@ static const struct consw sti_con = { | |||
| 
 | ||||
| static int __init sticonsole_init(void) | ||||
| { | ||||
|     int err; | ||||
|     /* already initialized ? */ | ||||
|     if (sticon_sti) | ||||
| 	 return 0; | ||||
|  | @ -382,7 +383,10 @@ static int __init sticonsole_init(void) | |||
| 
 | ||||
|     if (conswitchp == &dummy_con) { | ||||
| 	printk(KERN_INFO "sticon: Initializing STI text console.\n"); | ||||
| 	return take_over_console(&sti_con, 0, MAX_NR_CONSOLES - 1, 1); | ||||
| 	console_lock(); | ||||
| 	err = do_take_over_console(&sti_con, 0, MAX_NR_CONSOLES - 1, 1); | ||||
| 	console_unlock(); | ||||
| 	return err; | ||||
|     } | ||||
|     return 0; | ||||
| } | ||||
|  |  | |||
|  | @ -75,10 +75,7 @@ extern const struct consw newport_con;	/* SGI Newport console  */ | |||
| extern const struct consw prom_con;	/* SPARC PROM console */ | ||||
| 
 | ||||
| int con_is_bound(const struct consw *csw); | ||||
| int register_con_driver(const struct consw *csw, int first, int last); | ||||
| int unregister_con_driver(const struct consw *csw); | ||||
| int do_unregister_con_driver(const struct consw *csw); | ||||
| int take_over_console(const struct consw *sw, int first, int last, int deflt); | ||||
| int do_take_over_console(const struct consw *sw, int first, int last, int deflt); | ||||
| void give_up_console(const struct consw *sw); | ||||
| #ifdef CONFIG_HW_CONSOLE | ||||
|  |  | |||
|  | @ -31,6 +31,13 @@ | |||
| #include <linux/sysrq.h> | ||||
| #include <uapi/linux/serial_core.h> | ||||
| 
 | ||||
| #ifdef CONFIG_SERIAL_CORE_CONSOLE | ||||
| #define uart_console(port) \ | ||||
| 	((port)->cons && (port)->cons->index == (port)->line) | ||||
| #else | ||||
| #define uart_console(port)      (0) | ||||
| #endif | ||||
| 
 | ||||
| struct uart_port; | ||||
| struct serial_struct; | ||||
| struct device; | ||||
|  |  | |||
|  | @ -272,7 +272,6 @@ struct tty_struct { | |||
| #define N_TTY_BUF_SIZE 4096 | ||||
| 
 | ||||
| 	unsigned char closing:1; | ||||
| 	unsigned short minimum_to_wake; | ||||
| 	unsigned char *write_buf; | ||||
| 	int write_cnt; | ||||
| 	/* If the tty has a pending do_SAK, queue it here - akpm */ | ||||
|  | @ -309,8 +308,6 @@ struct tty_file_private { | |||
| #define TTY_LDISC 		9	/* Line discipline attached */ | ||||
| #define TTY_LDISC_CHANGING 	10	/* Line discipline changing */ | ||||
| #define TTY_LDISC_OPEN	 	11	/* Line discipline is open */ | ||||
| #define TTY_HW_COOK_OUT 	14	/* Hardware can do output cooking */ | ||||
| #define TTY_HW_COOK_IN 		15	/* Hardware can do input cooking */ | ||||
| #define TTY_PTY_LOCK 		16	/* pty private */ | ||||
| #define TTY_NO_WRITE_SPLIT 	17	/* Preserve write boundaries to driver */ | ||||
| #define TTY_HUPPED 		18	/* Post driver->hangup() */ | ||||
|  |  | |||
|  | @ -100,6 +100,11 @@ | |||
|  *	seek to perform this action quickly but should wait until | ||||
|  *	any pending driver I/O is completed. | ||||
|  * | ||||
|  * void (*fasync)(struct tty_struct *, int on) | ||||
|  * | ||||
|  *	Notify line discipline when signal-driven I/O is enabled or | ||||
|  *	disabled. | ||||
|  * | ||||
|  * void (*dcd_change)(struct tty_struct *tty, unsigned int status) | ||||
|  * | ||||
|  *	Tells the discipline that the DCD pin has changed its status. | ||||
|  | @ -110,6 +115,52 @@ | |||
| #include <linux/wait.h> | ||||
| #include <linux/wait.h> | ||||
| 
 | ||||
| 
 | ||||
| /*
 | ||||
|  * the semaphore definition | ||||
|  */ | ||||
| struct ld_semaphore { | ||||
| 	long			count; | ||||
| 	raw_spinlock_t		wait_lock; | ||||
| 	unsigned int		wait_readers; | ||||
| 	struct list_head	read_wait; | ||||
| 	struct list_head	write_wait; | ||||
| #ifdef CONFIG_DEBUG_LOCK_ALLOC | ||||
| 	struct lockdep_map	dep_map; | ||||
| #endif | ||||
| }; | ||||
| 
 | ||||
| extern void __init_ldsem(struct ld_semaphore *sem, const char *name, | ||||
| 			 struct lock_class_key *key); | ||||
| 
 | ||||
| #define init_ldsem(sem)						\ | ||||
| do {								\ | ||||
| 	static struct lock_class_key __key;			\ | ||||
| 								\ | ||||
| 	__init_ldsem((sem), #sem, &__key);			\ | ||||
| } while (0) | ||||
| 
 | ||||
| 
 | ||||
| extern int ldsem_down_read(struct ld_semaphore *sem, long timeout); | ||||
| extern int ldsem_down_read_trylock(struct ld_semaphore *sem); | ||||
| extern int ldsem_down_write(struct ld_semaphore *sem, long timeout); | ||||
| extern int ldsem_down_write_trylock(struct ld_semaphore *sem); | ||||
| extern void ldsem_up_read(struct ld_semaphore *sem); | ||||
| extern void ldsem_up_write(struct ld_semaphore *sem); | ||||
| 
 | ||||
| #ifdef CONFIG_DEBUG_LOCK_ALLOC | ||||
| extern int ldsem_down_read_nested(struct ld_semaphore *sem, int subclass, | ||||
| 				  long timeout); | ||||
| extern int ldsem_down_write_nested(struct ld_semaphore *sem, int subclass, | ||||
| 				   long timeout); | ||||
| #else | ||||
| # define ldsem_down_read_nested(sem, subclass, timeout)		\ | ||||
| 		ldsem_down_read(sem, timeout) | ||||
| # define ldsem_down_write_nested(sem, subclass, timeout)	\ | ||||
| 		ldsem_down_write(sem, timeout) | ||||
| #endif | ||||
| 
 | ||||
| 
 | ||||
| struct tty_ldisc_ops { | ||||
| 	int	magic; | ||||
| 	char	*name; | ||||
|  | @ -143,6 +194,7 @@ struct tty_ldisc_ops { | |||
| 			       char *fp, int count); | ||||
| 	void	(*write_wakeup)(struct tty_struct *); | ||||
| 	void	(*dcd_change)(struct tty_struct *, unsigned int); | ||||
| 	void	(*fasync)(struct tty_struct *tty, int on); | ||||
| 
 | ||||
| 	struct  module *owner; | ||||
| 
 | ||||
|  |  | |||
|  | @ -133,8 +133,6 @@ void change_console(struct vc_data *new_vc); | |||
| void reset_vc(struct vc_data *vc); | ||||
| extern int do_unbind_con_driver(const struct consw *csw, int first, int last, | ||||
| 			     int deflt); | ||||
| extern int unbind_con_driver(const struct consw *csw, int first, int last, | ||||
| 			     int deflt); | ||||
| int vty_init(const struct file_operations *console_fops); | ||||
| 
 | ||||
| static inline bool vt_force_oops_output(struct vc_data *vc) | ||||
|  |  | |||
|  | @ -226,4 +226,7 @@ | |||
| /* Rocketport EXPRESS/INFINITY */ | ||||
| #define PORT_RP2	102 | ||||
| 
 | ||||
| /* Freescale lpuart */ | ||||
| #define PORT_LPUART	103 | ||||
| 
 | ||||
| #endif /* _UAPILINUX_SERIAL_CORE_H */ | ||||
|  |  | |||
		Loading…
	
	Add table
		
		Reference in a new issue
	
	 Linus Torvalds
						Linus Torvalds