Commit b3a5e648 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'tty-5.2-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty

Pull tty/serial updates from Greg KH:
 "Here is the "big" set of tty/serial driver patches for 5.2-rc1.

  It's really pretty small, not much happening in this portion of the
  kernel at the moment. When the "highlight" is the movement of the
  documentation from .txt to .rst files, it's a good merge window.

  There's a number of small fixes and updates over the various serial
  drivers, and a new "tty null" driver for those embedded systems that
  like to make things even smaller and not break things.

  All of these have been in linux-next for a while with no reported
  issues"

* tag 'tty-5.2-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty: (45 commits)
  tty: serial: add driver for the SiFive UART
  dt-bindings: serial: add documentation for the SiFive UART driver
  serial: uartps: Add support for cts-override
  dt-bindings: xilinx-uartps: Add support for cts-override
  serial: milbeaut_usio: Fix error handling in probe and remove
  tty: rocket: deprecate the rp_ioctl
  tty: rocket: Remove RCPK_GET_STRUCT ioctl
  tty: update obsolete termios comment
  tty: serial_core: fix error code returned by uart_register_driver()
  serial: 8250-mtk: modify baudrate setting
  serial: 8250-mtk: add follow control
  docs: serial: convert docs to ReST and rename to *.rst
  serial: 8250_exar: Adjust IOT2000 matching
  TTY: serial_core, add ->install
  serial: Fix using plain integer instead of Null pointer
  tty:serial_core: Spelling mistake
  tty: Add NULL TTY driver
  tty: vt: keyboard: Allow Unicode compose base char
  Revert "tty: fix NULL pointer issue when tty_port ops is not set"
  serial: Add Milbeaut serial control
  ...
parents 132d68d3 45c054d0
...@@ -12,6 +12,11 @@ Required properties: ...@@ -12,6 +12,11 @@ Required properties:
See ../clocks/clock-bindings.txt for details. See ../clocks/clock-bindings.txt for details.
Optional properties:
- cts-override : Override the CTS modem status signal. This signal will
always be reported as active instead of being obtained from the modem status
register. Define this if your serial port does not use this pin
Example: Example:
uart@e0000000 { uart@e0000000 {
compatible = "cdns,uart-r1p8"; compatible = "cdns,uart-r1p8";
......
...@@ -12,6 +12,8 @@ Required properties: ...@@ -12,6 +12,8 @@ Required properties:
- reg: I2C address of the SC16IS7xx device. - reg: I2C address of the SC16IS7xx device.
- interrupts: Should contain the UART interrupt - interrupts: Should contain the UART interrupt
- clocks: Reference to the IC source clock. - clocks: Reference to the IC source clock.
OR (when there is no clock provider visible to the platform)
- clock-frequency: The source clock frequency for the IC.
Optional properties: Optional properties:
- gpio-controller: Marks the device node as a GPIO controller. - gpio-controller: Marks the device node as a GPIO controller.
......
SiFive asynchronous serial interface (UART)
Required properties:
- compatible: should be something similar to
"sifive,<chip>-uart" for the UART as integrated
on a particular chip, and "sifive,uart<version>" for the
general UART IP block programming model. Supported
compatible strings as of the date of this writing are:
"sifive,fu540-c000-uart" for the SiFive UART v0 as
integrated onto the SiFive FU540 chip, or "sifive,uart0"
for the SiFive UART v0 IP block with no chip integration
tweaks (if any)
- reg: address and length of the register space
- interrupts: Should contain the UART interrupt identifier
- clocks: Should contain a clock identifier for the UART's parent clock
UART HDL that corresponds to the IP block version numbers can be found
here:
https://github.com/sifive/sifive-blocks/tree/master/src/main/scala/devices/uart
Example:
uart0: serial@10010000 {
compatible = "sifive,fu540-c000-uart", "sifive,uart0";
interrupt-parent = <&plic0>;
interrupts = <80>;
reg = <0x0 0x10010000 0x0 0x1000>;
clocks = <&prci PRCI_CLK_TLCLK>;
};
...@@ -7,7 +7,17 @@ Required properties: ...@@ -7,7 +7,17 @@ Required properties:
- reg: offset and length of the register set for the device - reg: offset and length of the register set for the device
- interrupts: exactly one interrupt specifier - interrupts: exactly one interrupt specifier
- clocks: phandles to input clocks. - clock-names: Should contain following entries:
"enable" for UART module enable clock,
"uart" for UART clock,
"source" for UART source (parent) clock.
- clocks: Should contain a clock specifier for each entry in clock-names.
UART clock and source clock are optional properties, but enable clock
is required.
Optional properties:
- dma-names: Should contain "rx" for receive and "tx" for transmit channels.
- dmas: A list of dma specifiers, one for each entry in dma-names.
Example: Example:
uart0: serial@0 { uart0: serial@0 {
...@@ -15,5 +25,8 @@ Example: ...@@ -15,5 +25,8 @@ Example:
"sprd,sc9836-uart"; "sprd,sc9836-uart";
reg = <0x0 0x100>; reg = <0x0 0x100>;
interrupts = <GIC_SPI 2 IRQ_TYPE_LEVEL_HIGH>; interrupts = <GIC_SPI 2 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&ext_26m>; dma-names = "rx", "tx";
dmas = <&ap_dma 19>, <&ap_dma 20>;
clock-names = "enable", "uart", "source";
clocks = <&clk_ap_apb_gates 9>, <&clk_uart0>, <&ext_26m>;
}; };
================
Cyclades-Z notes
================
The Cyclades-Z must have firmware loaded onto the card before it will The Cyclades-Z must have firmware loaded onto the card before it will
operate. This operation should be performed during system startup, operate. This operation should be performed during system startup,
The firmware, loader program and the latest device driver code are The firmware, loader program and the latest device driver code are
available from Cyclades at available from Cyclades at
ftp://ftp.cyclades.com/pub/cyclades/cyclades-z/linux/
ftp://ftp.cyclades.com/pub/cyclades/cyclades-z/linux/
====================
Low Level Serial API Low Level Serial API
-------------------- ====================
This document is meant as a brief overview of some aspects of the new serial This document is meant as a brief overview of some aspects of the new serial
...@@ -44,7 +44,7 @@ are described in the uart_ops listing below.) ...@@ -44,7 +44,7 @@ are described in the uart_ops listing below.)
There are two locks. A per-port spinlock, and an overall semaphore. There are two locks. A per-port spinlock, and an overall semaphore.
From the core driver perspective, the port->lock locks the following From the core driver perspective, the port->lock locks the following
data: data::
port->mctrl port->mctrl
port->icount port->icount
...@@ -75,41 +75,51 @@ hardware. ...@@ -75,41 +75,51 @@ hardware.
return TIOCSER_TEMT. return TIOCSER_TEMT.
Locking: none. Locking: none.
Interrupts: caller dependent. Interrupts: caller dependent.
This call must not sleep This call must not sleep
set_mctrl(port, mctrl) set_mctrl(port, mctrl)
This function sets the modem control lines for port described This function sets the modem control lines for port described
by 'port' to the state described by mctrl. The relevant bits by 'port' to the state described by mctrl. The relevant bits
of mctrl are: of mctrl are:
- TIOCM_RTS RTS signal. - TIOCM_RTS RTS signal.
- TIOCM_DTR DTR signal. - TIOCM_DTR DTR signal.
- TIOCM_OUT1 OUT1 signal. - TIOCM_OUT1 OUT1 signal.
- TIOCM_OUT2 OUT2 signal. - TIOCM_OUT2 OUT2 signal.
- TIOCM_LOOP Set the port into loopback mode. - TIOCM_LOOP Set the port into loopback mode.
If the appropriate bit is set, the signal should be driven If the appropriate bit is set, the signal should be driven
active. If the bit is clear, the signal should be driven active. If the bit is clear, the signal should be driven
inactive. inactive.
Locking: port->lock taken. Locking: port->lock taken.
Interrupts: locally disabled. Interrupts: locally disabled.
This call must not sleep This call must not sleep
get_mctrl(port) get_mctrl(port)
Returns the current state of modem control inputs. The state Returns the current state of modem control inputs. The state
of the outputs should not be returned, since the core keeps of the outputs should not be returned, since the core keeps
track of their state. The state information should include: track of their state. The state information should include:
- TIOCM_CAR state of DCD signal - TIOCM_CAR state of DCD signal
- TIOCM_CTS state of CTS signal - TIOCM_CTS state of CTS signal
- TIOCM_DSR state of DSR signal - TIOCM_DSR state of DSR signal
- TIOCM_RI state of RI signal - TIOCM_RI state of RI signal
The bit is set if the signal is currently driven active. If The bit is set if the signal is currently driven active. If
the port does not support CTS, DCD or DSR, the driver should the port does not support CTS, DCD or DSR, the driver should
indicate that the signal is permanently active. If RI is indicate that the signal is permanently active. If RI is
not available, the signal should not be indicated as active. not available, the signal should not be indicated as active.
Locking: port->lock taken. Locking: port->lock taken.
Interrupts: locally disabled. Interrupts: locally disabled.
This call must not sleep This call must not sleep
stop_tx(port) stop_tx(port)
...@@ -121,14 +131,18 @@ hardware. ...@@ -121,14 +131,18 @@ hardware.
possible. possible.
Locking: port->lock taken. Locking: port->lock taken.
Interrupts: locally disabled. Interrupts: locally disabled.
This call must not sleep This call must not sleep
start_tx(port) start_tx(port)
Start transmitting characters. Start transmitting characters.
Locking: port->lock taken. Locking: port->lock taken.
Interrupts: locally disabled. Interrupts: locally disabled.
This call must not sleep This call must not sleep
throttle(port) throttle(port)
...@@ -144,6 +158,7 @@ hardware. ...@@ -144,6 +158,7 @@ hardware.
Notify the serial driver that characters can now be sent to the serial Notify the serial driver that characters can now be sent to the serial
port without fear of overrunning the input buffers of the line port without fear of overrunning the input buffers of the line
disciplines. disciplines.
This will be called only if hardware assisted flow control is enabled. This will be called only if hardware assisted flow control is enabled.
Locking: serialized with .throttle() and termios modification by the Locking: serialized with .throttle() and termios modification by the
...@@ -159,6 +174,7 @@ hardware. ...@@ -159,6 +174,7 @@ hardware.
Do not transmit if ch == '\0' (__DISABLED_CHAR). Do not transmit if ch == '\0' (__DISABLED_CHAR).
Locking: none. Locking: none.
Interrupts: caller dependent. Interrupts: caller dependent.
stop_rx(port) stop_rx(port)
...@@ -166,7 +182,9 @@ hardware. ...@@ -166,7 +182,9 @@ hardware.
being closed. being closed.
Locking: port->lock taken. Locking: port->lock taken.
Interrupts: locally disabled. Interrupts: locally disabled.
This call must not sleep This call must not sleep
enable_ms(port) enable_ms(port)
...@@ -177,7 +195,9 @@ hardware. ...@@ -177,7 +195,9 @@ hardware.
called. called.
Locking: port->lock taken. Locking: port->lock taken.
Interrupts: locally disabled. Interrupts: locally disabled.
This call must not sleep This call must not sleep
break_ctl(port,ctl) break_ctl(port,ctl)
...@@ -196,6 +216,7 @@ hardware. ...@@ -196,6 +216,7 @@ hardware.
This method will only be called when the port is initially opened. This method will only be called when the port is initially opened.
Locking: port_sem taken. Locking: port_sem taken.
Interrupts: globally disabled. Interrupts: globally disabled.
shutdown(port) shutdown(port)
...@@ -210,6 +231,7 @@ hardware. ...@@ -210,6 +231,7 @@ hardware.
this port. this port.
Locking: port_sem taken. Locking: port_sem taken.
Interrupts: caller dependent. Interrupts: caller dependent.
flush_buffer(port) flush_buffer(port)
...@@ -220,7 +242,9 @@ hardware. ...@@ -220,7 +242,9 @@ hardware.
buffer is cleared. buffer is cleared.
Locking: port->lock taken. Locking: port->lock taken.
Interrupts: locally disabled. Interrupts: locally disabled.
This call must not sleep This call must not sleep
set_termios(port,termios,oldtermios) set_termios(port,termios,oldtermios)
...@@ -228,29 +252,46 @@ hardware. ...@@ -228,29 +252,46 @@ hardware.
bits. Update read_status_mask and ignore_status_mask to indicate bits. Update read_status_mask and ignore_status_mask to indicate
the types of events we are interested in receiving. Relevant the types of events we are interested in receiving. Relevant
termios->c_cflag bits are: termios->c_cflag bits are:
CSIZE - word size
CSTOPB - 2 stop bits CSIZE
PARENB - parity enable - word size
PARODD - odd parity (when PARENB is in force) CSTOPB
CREAD - enable reception of characters (if not set, - 2 stop bits
PARENB
- parity enable
PARODD
- odd parity (when PARENB is in force)
CREAD
- enable reception of characters (if not set,
still receive characters from the port, but still receive characters from the port, but
throw them away. throw them away.
CRTSCTS - if set, enable CTS status change reporting CRTSCTS
CLOCAL - if not set, enable modem status change - if set, enable CTS status change reporting
CLOCAL
- if not set, enable modem status change
reporting. reporting.
Relevant termios->c_iflag bits are: Relevant termios->c_iflag bits are:
INPCK - enable frame and parity error events to be
INPCK
- enable frame and parity error events to be
passed to the TTY layer. passed to the TTY layer.
BRKINT BRKINT / PARMRK
PARMRK - both of these enable break events to be - both of these enable break events to be
passed to the TTY layer. passed to the TTY layer.
IGNPAR - ignore parity and framing errors IGNPAR
IGNBRK - ignore break errors, If IGNPAR is also - ignore parity and framing errors
IGNBRK
- ignore break errors, If IGNPAR is also
set, ignore overrun errors as well. set, ignore overrun errors as well.
The interaction of the iflag bits is as follows (parity error The interaction of the iflag bits is as follows (parity error
given as an example): given as an example):
=============== ======= ====== =============================
Parity error INPCK IGNPAR Parity error INPCK IGNPAR
=============== ======= ====== =============================
n/a 0 n/a character received, marked as n/a 0 n/a character received, marked as
TTY_NORMAL TTY_NORMAL
None 1 n/a character received, marked as None 1 n/a character received, marked as
...@@ -258,16 +299,19 @@ hardware. ...@@ -258,16 +299,19 @@ hardware.
Yes 1 0 character received, marked as Yes 1 0 character received, marked as
TTY_PARITY TTY_PARITY
Yes 1 1 character discarded Yes 1 1 character discarded
=============== ======= ====== =============================
Other flags may be used (eg, xon/xoff characters) if your Other flags may be used (eg, xon/xoff characters) if your
hardware supports hardware "soft" flow control. hardware supports hardware "soft" flow control.
Locking: caller holds tty_port->mutex Locking: caller holds tty_port->mutex
Interrupts: caller dependent. Interrupts: caller dependent.
This call must not sleep This call must not sleep
set_ldisc(port,termios) set_ldisc(port,termios)
Notifier for discipline change. See Documentation/serial/tty.txt. Notifier for discipline change. See Documentation/serial/tty.rst.
Locking: caller holds tty_port->mutex Locking: caller holds tty_port->mutex
...@@ -283,6 +327,7 @@ hardware. ...@@ -283,6 +327,7 @@ hardware.
will occur even if CONFIG_PM is not set. will occur even if CONFIG_PM is not set.
Locking: none. Locking: none.
Interrupts: caller dependent. Interrupts: caller dependent.
type(port) type(port)
...@@ -291,6 +336,7 @@ hardware. ...@@ -291,6 +336,7 @@ hardware.
substituted. substituted.
Locking: none. Locking: none.
Interrupts: caller dependent. Interrupts: caller dependent.
release_port(port) release_port(port)
...@@ -298,6 +344,7 @@ hardware. ...@@ -298,6 +344,7 @@ hardware.
the port. the port.
Locking: none. Locking: none.
Interrupts: caller dependent. Interrupts: caller dependent.
request_port(port) request_port(port)
...@@ -306,6 +353,7 @@ hardware. ...@@ -306,6 +353,7 @@ hardware.
returns, and it should return -EBUSY on failure. returns, and it should return -EBUSY on failure.
Locking: none. Locking: none.
Interrupts: caller dependent. Interrupts: caller dependent.
config_port(port,type) config_port(port,type)
...@@ -321,6 +369,7 @@ hardware. ...@@ -321,6 +369,7 @@ hardware.
internally hard wired (eg, system on a chip implementations). internally hard wired (eg, system on a chip implementations).
Locking: none. Locking: none.
Interrupts: caller dependent. Interrupts: caller dependent.
verify_port(port,serinfo) verify_port(port,serinfo)
...@@ -328,6 +377,7 @@ hardware. ...@@ -328,6 +377,7 @@ hardware.
suitable for this port type. suitable for this port type.
Locking: none. Locking: none.
Interrupts: caller dependent. Interrupts: caller dependent.
ioctl(port,cmd,arg) ioctl(port,cmd,arg)
...@@ -335,6 +385,7 @@ hardware. ...@@ -335,6 +385,7 @@ hardware.
using the standard numbering system found in <asm/ioctl.h> using the standard numbering system found in <asm/ioctl.h>
Locking: none. Locking: none.
Interrupts: caller dependent. Interrupts: caller dependent.
poll_init(port) poll_init(port)
...@@ -343,6 +394,7 @@ hardware. ...@@ -343,6 +394,7 @@ hardware.
this should not request interrupts. this should not request interrupts.
Locking: tty_mutex and tty_port->mutex taken. Locking: tty_mutex and tty_port->mutex taken.
Interrupts: n/a. Interrupts: n/a.
poll_put_char(port,ch) poll_put_char(port,ch)
...@@ -350,7 +402,9 @@ hardware. ...@@ -350,7 +402,9 @@ hardware.
port. It can and should block until there is space in the TX FIFO. port. It can and should block until there is space in the TX FIFO.
Locking: none. Locking: none.
Interrupts: caller dependent. Interrupts: caller dependent.
This call must not sleep This call must not sleep
poll_get_char(port) poll_get_char(port)
...@@ -359,7 +413,9 @@ hardware. ...@@ -359,7 +413,9 @@ hardware.
the function should return NO_POLL_CHAR immediately. the function should return NO_POLL_CHAR immediately.
Locking: none. Locking: none.
Interrupts: caller dependent. Interrupts: caller dependent.
This call must not sleep This call must not sleep
Other functions Other functions
...@@ -370,6 +426,7 @@ uart_update_timeout(port,cflag,baud) ...@@ -370,6 +426,7 @@ uart_update_timeout(port,cflag,baud)
number of bits, parity, stop bits and baud rate. number of bits, parity, stop bits and baud rate.
Locking: caller is expected to take port->lock Locking: caller is expected to take port->lock
Interrupts: n/a Interrupts: n/a
uart_get_baud_rate(port,termios,old,min,max) uart_get_baud_rate(port,termios,old,min,max)
...@@ -385,6 +442,7 @@ uart_get_baud_rate(port,termios,old,min,max) ...@@ -385,6 +442,7 @@ uart_get_baud_rate(port,termios,old,min,max)
Note: min..max must always allow 9600 baud to be selected. Note: min..max must always allow 9600 baud to be selected.
Locking: caller dependent. Locking: caller dependent.
Interrupts: n/a Interrupts: n/a
uart_get_divisor(port,baud) uart_get_divisor(port,baud)
...@@ -395,6 +453,7 @@ uart_get_divisor(port,baud) ...@@ -395,6 +453,7 @@ uart_get_divisor(port,baud)
custom divisor instead. custom divisor instead.
Locking: caller dependent. Locking: caller dependent.
Interrupts: n/a Interrupts: n/a
uart_match_port(port1,port2) uart_match_port(port1,port2)
...@@ -402,6 +461,7 @@ uart_match_port(port1,port2) ...@@ -402,6 +461,7 @@ uart_match_port(port1,port2)
uart_port structures describe the same port. uart_port structures describe the same port.
Locking: n/a Locking: n/a
Interrupts: n/a Interrupts: n/a
uart_write_wakeup(port) uart_write_wakeup(port)
...@@ -409,6 +469,7 @@ uart_write_wakeup(port) ...@@ -409,6 +469,7 @@ uart_write_wakeup(port)
characters in the transmit buffer have dropped below a threshold. characters in the transmit buffer have dropped below a threshold.
Locking: port->lock should be held. Locking: port->lock should be held.
Interrupts: n/a Interrupts: n/a
uart_register_driver(drv) uart_register_driver(drv)
...@@ -419,6 +480,7 @@ uart_register_driver(drv) ...@@ -419,6 +480,7 @@ uart_register_driver(drv)
registered using uart_add_one_port after this call has succeeded. registered using uart_add_one_port after this call has succeeded.
Locking: none Locking: none
Interrupts: enabled Interrupts: enabled
uart_unregister_driver() uart_unregister_driver()
...@@ -427,15 +489,16 @@ uart_unregister_driver() ...@@ -427,15 +489,16 @@ uart_unregister_driver()
uart_remove_one_port() if it registered them with uart_add_one_port(). uart_remove_one_port() if it registered them with uart_add_one_port().
Locking: none Locking: none
Interrupts: enabled Interrupts: enabled
uart_suspend_port() **uart_suspend_port()**
uart_resume_port() **uart_resume_port()**
uart_add_one_port() **uart_add_one_port()**
uart_remove_one_port() **uart_remove_one_port()**
Other notes Other notes
----------- -----------
...@@ -444,7 +507,7 @@ It is intended some day to drop the 'unused' entries from uart_port, and ...@@ -444,7 +507,7 @@ It is intended some day to drop the 'unused' entries from uart_port, and
allow low level drivers to register their own individual uart_port's with allow low level drivers to register their own individual uart_port's with
the core. This will allow drivers to use uart_port as a pointer to a the core. This will allow drivers to use uart_port as a pointer to a
structure containing both the uart_port entry with their own extensions, structure containing both the uart_port entry with their own extensions,
thus: thus::
struct my_port { struct my_port {
struct uart_port port; struct uart_port port;
...@@ -459,14 +522,14 @@ Some helpers are provided in order to set/get modem control lines via GPIO. ...@@ -459,14 +522,14 @@ Some helpers are provided in order to set/get modem control lines via GPIO.
mctrl_gpio_init(port, idx): mctrl_gpio_init(port, idx):
This will get the {cts,rts,...}-gpios from device tree if they are This will get the {cts,rts,...}-gpios from device tree if they are
present and request them, set direction etc, and return an present and request them, set direction etc, and return an
allocated structure. devm_* functions are used, so there's no need allocated structure. `devm_*` functions are used, so there's no need
to call mctrl_gpio_free(). to call mctrl_gpio_free().
As this sets up the irq handling make sure to not handle changes to the As this sets up the irq handling make sure to not handle changes to the
gpio input lines in your driver, too. gpio input lines in your driver, too.
mctrl_gpio_free(dev, gpios): mctrl_gpio_free(dev, gpios):
This will free the requested gpios in mctrl_gpio_init(). This will free the requested gpios in mctrl_gpio_init().
As devm_* functions are used, there's generally no need to call As `devm_*` functions are used, there's generally no need to call
this function. this function.
mctrl_gpio_to_gpiod(gpios, gidx) mctrl_gpio_to_gpiod(gpios, gidx)
......
:orphan:
==========================
Support for Serial devices
==========================
.. toctree::
:maxdepth: 1
driver
tty
Serial drivers
==============
.. toctree::
:maxdepth: 1
cyclades_z
moxa-smartio
n_gsm
rocket
serial-iso7816
serial-rs485
.. only:: subproject and html
Indices
=======
* :ref:`genindex`
============================================================================= =============================================================
MOXA Smartio/Industio Family Device Driver Installation Guide MOXA Smartio/Industio Family Device Driver Installation Guide
for Linux Kernel 2.4.x, 2.6.x =============================================================
Copyright (C) 2008, Moxa Inc.
============================================================================= .. note::
This file is outdated. It needs some care in order to make it
updated to Kernel 5.0 and upper
Copyright (C) 2008, Moxa Inc.
Date: 01/21/2008 Date: 01/21/2008
Content .. Content
1. Introduction 1. Introduction
2. System Requirement 2. System Requirement
3. Installation 3. Installation
3.1 Hardware installation 3.1 Hardware installation
3.2 Driver files 3.2 Driver files
3.3 Device naming convention 3.3 Device naming convention
...@@ -17,12 +23,12 @@ Content ...@@ -17,12 +23,12 @@ Content
3.5 Static driver configuration for Linux kernel 2.4.x and 2.6.x. 3.5 Static driver configuration for Linux kernel 2.4.x and 2.6.x.
3.6 Custom configuration 3.6 Custom configuration
3.7 Verify driver installation 3.7 Verify driver installation
4. Utilities 4. Utilities
5. Setserial 5. Setserial
6. Troubleshooting 6. Troubleshooting
-----------------------------------------------------------------------------
1. Introduction 1. Introduction
^^^^^^^^^^^^^^^
The Smartio/Industio/UPCI family Linux driver supports following multiport The Smartio/Industio/UPCI family Linux driver supports following multiport
boards. boards.
...@@ -43,7 +49,7 @@ Content ...@@ -43,7 +49,7 @@ Content
C104H, C104HS, C104H, C104HS,
CI-104J, CI-104JS, CI-104J, CI-104JS,
CI-134, CI-134I, CI-134IS, CI-134, CI-134I, CI-134IS,
(C114HI, CT-114I, C104P) (C114HI, CT-114I, C104P),
POS-104UL, POS-104UL,
CB-114, CB-114,
CB-134I CB-134I
...@@ -63,13 +69,17 @@ Content ...@@ -63,13 +69,17 @@ Content
occurs, please contact Moxa at support@moxa.com.tw. occurs, please contact Moxa at support@moxa.com.tw.
In addition to device driver, useful utilities are also provided in this In addition to device driver, useful utilities are also provided in this
version. They are version. They are:
- msdiag Diagnostic program for displaying installed Moxa
- msdiag
Diagnostic program for displaying installed Moxa
Smartio/Industio boards. Smartio/Industio boards.
- msmon Monitor program to observe data count and line status signals. - msmon
Monitor program to observe data count and line status signals.
- msterm A simple terminal program which is useful in testing serial - msterm A simple terminal program which is useful in testing serial
ports. ports.
- io-irq.exe Configuration program to setup ISA boards. Please note that - io-irq.exe
Configuration program to setup ISA boards. Please note that
this program can only be executed under DOS. this program can only be executed under DOS.
All the drivers and utilities are published in form of source code under All the drivers and utilities are published in form of source code under
...@@ -84,65 +94,63 @@ Content ...@@ -84,65 +94,63 @@ Content
please refer to hardware installation procedure in the User's Manual. please refer to hardware installation procedure in the User's Manual.
We assume the user should be familiar with following documents. We assume the user should be familiar with following documents.
- Serial-HOWTO - Serial-HOWTO
- Kernel-HOWTO - Kernel-HOWTO
-----------------------------------------------------------------------------
2. System Requirement 2. System Requirement
^^^^^^^^^^^^^^^^^^^^^
- Hardware platform: Intel x86 machine - Hardware platform: Intel x86 machine
- Kernel version: 2.4.x or 2.6.x - Kernel version: 2.4.x or 2.6.x
- gcc version 2.72 or later - gcc version 2.72 or later
- Maximum 4 boards can be installed in combination - Maximum 4 boards can be installed in combination
-----------------------------------------------------------------------------
3. Installation 3. Installation
^^^^^^^^^^^^^^^
3.1 Hardware installation 3.1 Hardware installation
3.2 Driver files =========================
3.3 Device naming convention
3.4 Module driver configuration
3.5 Static driver configuration for Linux kernel 2.4.x, 2.6.x.
3.6 Custom configuration
3.7 Verify driver installation
3.1 Hardware installation
There are two types of buses, ISA and PCI, for Smartio/Industio There are two types of buses, ISA and PCI, for Smartio/Industio
family multiport board. family multiport board.
ISA board ISA board
--------- ---------
You'll have to configure CAP address, I/O address, Interrupt Vector You'll have to configure CAP address, I/O address, Interrupt Vector
as well as IRQ before installing this driver. Please refer to hardware as well as IRQ before installing this driver. Please refer to hardware
installation procedure in User's Manual before proceed any further. installation procedure in User's Manual before proceed any further.
Please make sure the JP1 is open after the ISA board is set properly. Please make sure the JP1 is open after the ISA board is set properly.
PCI/UPCI board PCI/UPCI board
-------------- --------------
You may need to adjust IRQ usage in BIOS to avoid from IRQ conflict You may need to adjust IRQ usage in BIOS to avoid from IRQ conflict
with other ISA devices. Please refer to hardware installation with other ISA devices. Please refer to hardware installation
procedure in User's Manual in advance. procedure in User's Manual in advance.
PCI IRQ Sharing PCI IRQ Sharing
----------- ---------------
Each port within the same multiport board shares the same IRQ. Up to Each port within the same multiport board shares the same IRQ. Up to
4 Moxa Smartio/Industio PCI Family multiport boards can be installed 4 Moxa Smartio/Industio PCI Family multiport boards can be installed
together on one system and they can share the same IRQ. together on one system and they can share the same IRQ.
3.2 Driver files 3.2 Driver files
================
The driver file may be obtained from ftp, CD-ROM or floppy disk. The The driver file may be obtained from ftp, CD-ROM or floppy disk. The
first step, anyway, is to copy driver file "mxser.tgz" into specified first step, anyway, is to copy driver file "mxser.tgz" into specified
directory. e.g. /moxa. The execute commands as below. directory. e.g. /moxa. The execute commands as below::
# cd / # cd /
# mkdir moxa # mkdir moxa
# cd /moxa # cd /moxa
# tar xvf /dev/fd0 # tar xvf /dev/fd0
or or::
# cd / # cd /
# mkdir moxa # mkdir moxa
...@@ -151,39 +159,44 @@ Content ...@@ -151,39 +159,44 @@ Content
# tar xvfz mxser.tgz # tar xvfz mxser.tgz
3.3 Device naming convention 3.3 Device naming convention
============================
You may find all the driver and utilities files in /moxa/mxser. You may find all the driver and utilities files in /moxa/mxser.
Following installation procedure depends on the model you'd like to Following installation procedure depends on the model you'd like to
run the driver. If you prefer module driver, please refer to 3.4. run the driver. If you prefer module driver, please refer to 3.4.
If static driver is required, please refer to 3.5. If static driver is required, please refer to 3.5.
Dialin and callout port Dialin and callout port
----------------------- -----------------------
This driver remains traditional serial device properties. There are This driver remains traditional serial device properties. There are
two special file name for each serial port. One is dial-in port two special file name for each serial port. One is dial-in port
which is named "ttyMxx". For callout port, the naming convention which is named "ttyMxx". For callout port, the naming convention
is "cumxx". is "cumxx".
Device naming when more than 2 boards installed Device naming when more than 2 boards installed
----------------------------------------------- -----------------------------------------------
Naming convention for each Smartio/Industio multiport board is Naming convention for each Smartio/Industio multiport board is
pre-defined as below. pre-defined as below.
============ =============== ==============
Board Num. Dial-in Port Callout port Board Num. Dial-in Port Callout port
1st board ttyM0 - ttyM7 cum0 - cum7 1st board ttyM0 - ttyM7 cum0 - cum7
2nd board ttyM8 - ttyM15 cum8 - cum15 2nd board ttyM8 - ttyM15 cum8 - cum15
3rd board ttyM16 - ttyM23 cum16 - cum23 3rd board ttyM16 - ttyM23 cum16 - cum23
4th board ttyM24 - ttym31 cum24 - cum31 4th board ttyM24 - ttym31 cum24 - cum31
============ =============== ==============
.. note::
!!!!!!!!!!!!!!!!!!!! NOTE !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Under Kernel 2.6 and upper, the cum Device is Obsolete. So use ttyM*
Under Kernel 2.6 the cum Device is Obsolete. So use ttyM*
device instead. device instead.
!!!!!!!!!!!!!!!!!!!! NOTE !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
Board sequence Board sequence
-------------- --------------
This driver will activate ISA boards according to the parameter set This driver will activate ISA boards according to the parameter set
in the driver. After all specified ISA board activated, PCI board in the driver. After all specified ISA board activated, PCI board
will be installed in the system automatically driven. will be installed in the system automatically driven.
...@@ -191,18 +204,24 @@ Content ...@@ -191,18 +204,24 @@ Content
For PCI boards, their sequence will be after ISA boards and C168H/PCI For PCI boards, their sequence will be after ISA boards and C168H/PCI
has higher priority than C104H/PCI boards. has higher priority than C104H/PCI boards.
3.4 Module driver configuration 3.4 Module driver configuration
===============================
Module driver is easiest way to install. If you prefer static driver Module driver is easiest way to install. If you prefer static driver
installation, please skip this paragraph. installation, please skip this paragraph.
------------- Prepare to use the MOXA driver-------------------- ------------- Prepare to use the MOXA driver --------------------
3.4.1 Create tty device with correct major number
3.4.1 Create tty device with correct major number
-------------------------------------------------
Before using MOXA driver, your system must have the tty devices Before using MOXA driver, your system must have the tty devices
which are created with driver's major number. We offer one shell which are created with driver's major number. We offer one shell
script "msmknod" to simplify the procedure. script "msmknod" to simplify the procedure.
This step is only needed to be executed once. But you still This step is only needed to be executed once. But you still
need to do this procedure when: need to do this procedure when:
a. You change the driver's major number. Please refer the "3.7" a. You change the driver's major number. Please refer the "3.7"
section. section.
b. Your total installed MOXA boards number is changed. Maybe you b. Your total installed MOXA boards number is changed. Maybe you
...@@ -210,7 +229,8 @@ Content ...@@ -210,7 +229,8 @@ Content
c. You want to change the tty name. This needs to modify the c. You want to change the tty name. This needs to modify the
shell script "msmknod" shell script "msmknod"
The procedure is: The procedure is::
# cd /moxa/mxser/driver # cd /moxa/mxser/driver
# ./msmknod # ./msmknod
...@@ -223,7 +243,9 @@ Content ...@@ -223,7 +243,9 @@ Content
Msmknod will delete any special files occupying the same device Msmknod will delete any special files occupying the same device
naming. naming.
3.4.2 Build the MOXA driver and utilities 3.4.2 Build the MOXA driver and utilities
-----------------------------------------
Before using the MOXA driver and utilities, you need compile the Before using the MOXA driver and utilities, you need compile the
all the source code. This step is only need to be executed once. all the source code. This step is only need to be executed once.
But you still re-compile the source code if you modify the source But you still re-compile the source code if you modify the source
...@@ -234,19 +256,23 @@ Content ...@@ -234,19 +256,23 @@ Content
# make clean; make install # make clean; make install
!!!!!!!!!! NOTE !!!!!!!!!!!!!!!!! ..note::
For Red Hat 9, Red Hat Enterprise Linux AS3/ES3/WS3 & Fedora Core1: For Red Hat 9, Red Hat Enterprise Linux AS3/ES3/WS3 & Fedora Core1:
# make clean; make installsp1 # make clean; make installsp1
For Red Hat Enterprise Linux AS4/ES4/WS4: For Red Hat Enterprise Linux AS4/ES4/WS4:
# make clean; make installsp2 # make clean; make installsp2
!!!!!!!!!! NOTE !!!!!!!!!!!!!!!!!
The driver files "mxser.o" and utilities will be properly compiled The driver files "mxser.o" and utilities will be properly compiled
and copied to system directories respectively. and copied to system directories respectively.
------------- Load MOXA driver-------------------- ------------- Load MOXA driver--------------------
3.4.3 Load the MOXA driver
3.4.3 Load the MOXA driver
--------------------------
::
# modprobe mxser <argument> # modprobe mxser <argument>
...@@ -255,11 +281,16 @@ Content ...@@ -255,11 +281,16 @@ Content
<argument> is needed. Please refer to section "3.4.5" for more <argument> is needed. Please refer to section "3.4.5" for more
information. information.
------------- Load MOXA driver on boot --------------------
3.4.4 Load the mxser driver
---------------------------
------------- Load MOXA driver on boot -------------------- For the above description, you may manually execute
3.4.4 For the above description, you may manually execute
"modprobe mxser" to activate this driver and run "modprobe mxser" to activate this driver and run
"rmmod mxser" to remove it. "rmmod mxser" to remove it.
However, it's better to have a boot time configuration to However, it's better to have a boot time configuration to
eliminate manual operation. Boot time configuration can be eliminate manual operation. Boot time configuration can be
achieved by rc file. We offer one "rc.mxser" file to simplify achieved by rc file. We offer one "rc.mxser" file to simplify
...@@ -272,7 +303,7 @@ Content ...@@ -272,7 +303,7 @@ Content
encountered, please try to modify again. If the modification is encountered, please try to modify again. If the modification is
completed, follow the below step. completed, follow the below step.
Run following command for setting rc files. Run following command for setting rc files::
# cd /moxa/mxser/driver # cd /moxa/mxser/driver
# cp ./rc.mxser /etc/rc.d # cp ./rc.mxser /etc/rc.d
...@@ -280,28 +311,38 @@ Content ...@@ -280,28 +311,38 @@ Content
Check "rc.serial" is existed or not. If "rc.serial" doesn't exist, Check "rc.serial" is existed or not. If "rc.serial" doesn't exist,
create it by vi, run "chmod 755 rc.serial" to change the permission. create it by vi, run "chmod 755 rc.serial" to change the permission.
Add "/etc/rc.d/rc.mxser" in last line,
Add "/etc/rc.d/rc.mxser" in last line.
Reboot and check if moxa.o activated by "lsmod" command. Reboot and check if moxa.o activated by "lsmod" command.
3.4.5. If you'd like to drive Smartio/Industio ISA boards in the system, 3.4.5. specify CAP address
--------------------------
If you'd like to drive Smartio/Industio ISA boards in the system,
you'll have to add parameter to specify CAP address of given you'll have to add parameter to specify CAP address of given
board while activating "mxser.o". The format for parameters are board while activating "mxser.o". The format for parameters are
as follows. as follows.::
modprobe mxser ioaddr=0x???,0x???,0x???,0x??? modprobe mxser ioaddr=0x???,0x???,0x???,0x???
| | | | | | | |
| | | +- 4th ISA board | | | +- 4th ISA board
| | +------ 3rd ISA board | | +------ 3rd ISA board
| +------------ 2nd ISA board | +------------ 2nd ISA board
+------------------- 1st ISA board +-------------------1st ISA board
3.5 Static driver configuration for Linux kernel 2.4.x and 2.6.x 3.5 Static driver configuration for Linux kernel 2.4.x and 2.6.x
================================================================
Note: To use static driver, you must install the linux kernel Note:
To use static driver, you must install the linux kernel
source package. source package.
3.5.1 Backup the built-in driver in the kernel. 3.5.1 Backup the built-in driver in the kernel
----------------------------------------------
::
# cd /usr/src/linux/drivers/char # cd /usr/src/linux/drivers/char
# mv mxser.c mxser.c.old # mv mxser.c mxser.c.old
...@@ -309,12 +350,17 @@ Content ...@@ -309,12 +350,17 @@ Content
# cd /usr/src # cd /usr/src
# ln -s linux-2.4 linux # ln -s linux-2.4 linux
3.5.2 Create link 3.5.2 Create link
-----------------
::
# cd /usr/src/linux/drivers/char # cd /usr/src/linux/drivers/char
# ln -s /moxa/mxser/driver/mxser.c mxser.c # ln -s /moxa/mxser/driver/mxser.c mxser.c
3.5.3 Add CAP address list for ISA boards. For PCI boards user, 3.5.3 Add CAP address list for ISA boards.
please skip this step. ------------------------------------------
For PCI boards user, please skip this step.
In module mode, the CAP address for ISA board is given by In module mode, the CAP address for ISA board is given by
parameter. In static driver configuration, you'll have to parameter. In static driver configuration, you'll have to
...@@ -322,24 +368,27 @@ Content ...@@ -322,24 +368,27 @@ Content
install any ISA boards, you may skip to next portion. install any ISA boards, you may skip to next portion.
The instructions to modify driver source code are as The instructions to modify driver source code are as
below. below.
a. # cd /moxa/mxser/driver
a. run::
# cd /moxa/mxser/driver
# vi mxser.c # vi mxser.c
b. Find the array mxserBoardCAP[] as below.
static int mxserBoardCAP[] b. Find the array mxserBoardCAP[] as below::
= {0x00, 0x00, 0x00, 0x00};
static int mxserBoardCAP[] = {0x00, 0x00, 0x00, 0x00};
c. Change the address within this array using vi. For c. Change the address within this array using vi. For
example, to driver 2 ISA boards with CAP address example, to driver 2 ISA boards with CAP address
0x280 and 0x180 as 1st and 2nd board. Just to change 0x280 and 0x180 as 1st and 2nd board. Just to change
the source code as follows. the source code as follows::
static int mxserBoardCAP[] static int mxserBoardCAP[] = {0x280, 0x180, 0x00, 0x00};
= {0x280, 0x180, 0x00, 0x00};
3.5.4 Setup kernel configuration 3.5.4 Setup kernel configuration
--------------------------------
Configure the kernel: Configure the kernel::
# cd /usr/src/linux # cd /usr/src/linux
# make menuconfig # make menuconfig
...@@ -349,16 +398,22 @@ Content ...@@ -349,16 +398,22 @@ Content
SmartIO support] driver with "[*]" for built-in (not "[M]"), then SmartIO support] driver with "[*]" for built-in (not "[M]"), then
select [Exit] to exit this program. select [Exit] to exit this program.
3.5.5 Rebuild kernel 3.5.5 Rebuild kernel
--------------------
The following are for Linux kernel rebuilding, for your The following are for Linux kernel rebuilding, for your
reference only. reference only.
For appropriate details, please refer to the Linux document.
a. cd /usr/src/linux For appropriate details, please refer to the Linux document:
b. make clean /* take a few minutes */
c. make dep /* take a few minutes */ a. Run the following commands::
d. make bzImage /* take probably 10-20 minutes */
e. make install /* copy boot image to correct position */ cd /usr/src/linux
make clean # take a few minutes
make dep # take a few minutes
make bzImage # take probably 10-20 minutes
make install # copy boot image to correct position
f. Please make sure the boot kernel (vmlinuz) is in the f. Please make sure the boot kernel (vmlinuz) is in the
correct position. correct position.
g. If you use 'lilo' utility, you should check /etc/lilo.conf g. If you use 'lilo' utility, you should check /etc/lilo.conf
...@@ -371,25 +426,35 @@ Content ...@@ -371,25 +426,35 @@ Content
directory /usr/src/linux. directory /usr/src/linux.
3.5.6 Make tty device and special file 3.5.6 Make tty device and special file
--------------------------------------
::
# cd /moxa/mxser/driver # cd /moxa/mxser/driver
# ./msmknod # ./msmknod
3.5.7 Make utility 3.5.7 Make utility
------------------
::
# cd /moxa/mxser/utility # cd /moxa/mxser/utility
# make clean; make install # make clean; make install
3.5.8 Reboot 3.5.8 Reboot
------------
3.6 Custom configuration 3.6 Custom configuration
========================
Although this driver already provides you default configuration, you Although this driver already provides you default configuration, you
still can change the device name and major number. The instruction to still can change the device name and major number. The instruction to
change these parameters are shown as below. change these parameters are shown as below.
Change Device name a. Change Device name
------------------
If you'd like to use other device names instead of default naming If you'd like to use other device names instead of default naming
convention, all you have to do is to modify the internal code convention, all you have to do is to modify the internal code
within the shell script "msmknod". First, you have to open "msmknod" within the shell script "msmknod". First, you have to open "msmknod"
...@@ -397,32 +462,45 @@ Content ...@@ -397,32 +462,45 @@ Content
to the device name you desired. "msmknod" creates the device names to the device name you desired. "msmknod" creates the device names
you need next time executed. you need next time executed.
Change Major number b. Change Major number
-------------------
If major number 30 and 35 had been occupied, you may have to select If major number 30 and 35 had been occupied, you may have to select
2 free major numbers for this driver. There are 3 steps to change 2 free major numbers for this driver. There are 3 steps to change
major numbers. major numbers.
3.6.1 Find free major numbers 3.6.1 Find free major numbers
-----------------------------
In /proc/devices, you may find all the major numbers occupied In /proc/devices, you may find all the major numbers occupied
in the system. Please select 2 major numbers that are available. in the system. Please select 2 major numbers that are available.
e.g. 40, 45. e.g. 40, 45.
3.6.2 Create special files
3.6.2 Create special files
--------------------------
Run /moxa/mxser/driver/msmknod to create special files with Run /moxa/mxser/driver/msmknod to create special files with
specified major numbers. specified major numbers.
3.6.3 Modify driver with new major number
3.6.3 Modify driver with new major number
-----------------------------------------
Run vi to open /moxa/mxser/driver/mxser.c. Locate the line Run vi to open /moxa/mxser/driver/mxser.c. Locate the line
contains "MXSERMAJOR". Change the content as below. contains "MXSERMAJOR". Change the content as below::
#define MXSERMAJOR 40 #define MXSERMAJOR 40
#define MXSERCUMAJOR 45 #define MXSERCUMAJOR 45
3.6.4 Run "make clean; make install" in /moxa/mxser/driver. 3.6.4 Run "make clean; make install" in /moxa/mxser/driver.
3.7 Verify driver installation 3.7 Verify driver installation
==============================
You may refer to /var/log/messages to check the latest status You may refer to /var/log/messages to check the latest status
log reported by this driver whenever it's activated. log reported by this driver whenever it's activated.
-----------------------------------------------------------------------------
4. Utilities 4. Utilities
^^^^^^^^^^^^
There are 3 utilities contained in this driver. They are msdiag, msmon and There are 3 utilities contained in this driver. They are msdiag, msmon and
msterm. These 3 utilities are released in form of source code. They should msterm. These 3 utilities are released in form of source code. They should
be compiled into executable file and copied into /usr/bin. be compiled into executable file and copied into /usr/bin.
...@@ -430,16 +508,19 @@ Content ...@@ -430,16 +508,19 @@ Content
Before using these utilities, please load driver (refer 3.4 & 3.5) and Before using these utilities, please load driver (refer 3.4 & 3.5) and
make sure you had run the "msmknod" utility. make sure you had run the "msmknod" utility.
msdiag - Diagnostic msdiag - Diagnostic
-------------------- ===================
This utility provides the function to display what Moxa Smartio/Industio This utility provides the function to display what Moxa Smartio/Industio
board found by driver in the system. board found by driver in the system.
msmon - Port Monitoring msmon - Port Monitoring
----------------------- =======================
This utility gives the user a quick view about all the MOXA ports' This utility gives the user a quick view about all the MOXA ports'
activities. One can easily learn each port's total received/transmitted activities. One can easily learn each port's total received/transmitted
(Rx/Tx) character count since the time when the monitoring is started. (Rx/Tx) character count since the time when the monitoring is started.
Rx/Tx throughputs per second are also reported in interval basis (e.g. Rx/Tx throughputs per second are also reported in interval basis (e.g.
the last 5 seconds) and in average basis (since the time the monitoring the last 5 seconds) and in average basis (since the time the monitoring
is started). You can reset all ports' count by <HOME> key. <+> <-> is started). You can reset all ports' count by <HOME> key. <+> <->
...@@ -447,19 +528,21 @@ Content ...@@ -447,19 +528,21 @@ Content
on the port, that cursor stay, to view the port's communication on the port, that cursor stay, to view the port's communication
parameters, signal status, and input/output queue. parameters, signal status, and input/output queue.
msterm - Terminal Emulation msterm - Terminal Emulation
--------------------------- ===========================
This utility provides data sending and receiving ability of all tty ports, This utility provides data sending and receiving ability of all tty ports,
especially for MOXA ports. It is quite useful for testing simple especially for MOXA ports. It is quite useful for testing simple
application, for example, sending AT command to a modem connected to the application, for example, sending AT command to a modem connected to the
port or used as a terminal for login purpose. Note that this is only a port or used as a terminal for login purpose. Note that this is only a
dumb terminal emulation without handling full screen operation. dumb terminal emulation without handling full screen operation.
-----------------------------------------------------------------------------
5. Setserial 5. Setserial
^^^^^^^^^^^^
Supported Setserial parameters are listed as below. Supported Setserial parameters are listed as below.
============== =========================================================
uart set UART type(16450-->disable FIFO, 16550A-->enable FIFO) uart set UART type(16450-->disable FIFO, 16550A-->enable FIFO)
close_delay set the amount of time(in 1/100 of a second) that DTR close_delay set the amount of time(in 1/100 of a second) that DTR
should be kept low while being closed. should be kept low while being closed.
...@@ -475,49 +558,58 @@ Content ...@@ -475,49 +558,58 @@ Content
application requests 38.4kb. application requests 38.4kb.
divisor This option set the custom division. divisor This option set the custom division.
baud_base This option set the base baud rate. baud_base This option set the base baud rate.
============== =========================================================
-----------------------------------------------------------------------------
6. Troubleshooting 6. Troubleshooting
^^^^^^^^^^^^^^^^^^
The boot time error messages and solutions are stated as clearly as The boot time error messages and solutions are stated as clearly as
possible. If all the possible solutions fail, please contact our technical possible. If all the possible solutions fail, please contact our technical
support team to get more help. support team to get more help.
Error msg: More than 4 Moxa Smartio/Industio family boards found. Fifth board Error msg:
More than 4 Moxa Smartio/Industio family boards found. Fifth board
and after are ignored. and after are ignored.
Solution: Solution:
To avoid this problem, please unplug fifth and after board, because Moxa To avoid this problem, please unplug fifth and after board, because Moxa
driver supports up to 4 boards. driver supports up to 4 boards.
Error msg: Request_irq fail, IRQ(?) may be conflict with another device. Error msg:
Request_irq fail, IRQ(?) may be conflict with another device.
Solution: Solution:
Other PCI or ISA devices occupy the assigned IRQ. If you are not sure Other PCI or ISA devices occupy the assigned IRQ. If you are not sure
which device causes the situation, please check /proc/interrupts to find which device causes the situation, please check /proc/interrupts to find
free IRQ and simply change another free IRQ for Moxa board. free IRQ and simply change another free IRQ for Moxa board.
Error msg: Board #: C1xx Series(CAP=xxx) interrupt number invalid. Error msg:
Board #: C1xx Series(CAP=xxx) interrupt number invalid.
Solution: Solution:
Each port within the same multiport board shares the same IRQ. Please set Each port within the same multiport board shares the same IRQ. Please set
one IRQ (IRQ doesn't equal to zero) for one Moxa board. one IRQ (IRQ doesn't equal to zero) for one Moxa board.
Error msg: No interrupt vector be set for Moxa ISA board(CAP=xxx). Error msg:
No interrupt vector be set for Moxa ISA board(CAP=xxx).
Solution: Solution:
Moxa ISA board needs an interrupt vector.Please refer to user's manual Moxa ISA board needs an interrupt vector.Please refer to user's manual
"Hardware Installation" chapter to set interrupt vector. "Hardware Installation" chapter to set interrupt vector.
Error msg: Couldn't install MOXA Smartio/Industio family driver! Error msg:
Couldn't install MOXA Smartio/Industio family driver!
Solution: Solution:
Load Moxa driver fail, the major number may conflict with other devices. Load Moxa driver fail, the major number may conflict with other devices.
Please refer to previous section 3.7 to change a free major number for Please refer to previous section 3.7 to change a free major number for
Moxa driver. Moxa driver.
Error msg: Couldn't install MOXA Smartio/Industio family callout driver! Error msg:
Couldn't install MOXA Smartio/Industio family callout driver!
Solution: Solution:
Load Moxa callout driver fail, the callout device major number may Load Moxa callout driver fail, the callout device major number may
conflict with other devices. Please refer to previous section 3.7 to conflict with other devices. Please refer to previous section 3.7 to
change a free callout device major number for Moxa driver. change a free callout device major number for Moxa driver.
-----------------------------------------------------------------------------
n_gsm.c GSM 0710 tty multiplexor HOWTO ==============================
=================================================== GSM 0710 tty multiplexor HOWTO
==============================
This line discipline implements the GSM 07.10 multiplexing protocol This line discipline implements the GSM 07.10 multiplexing protocol
detailed in the following 3GPP document : detailed in the following 3GPP document:
http://www.3gpp.org/ftp/Specs/archive/07_series/07.10/0710-720.zip
http://www.3gpp.org/ftp/Specs/archive/07_series/07.10/0710-720.zip
This document give some hints on how to use this driver with GPRS and 3G This document give some hints on how to use this driver with GPRS and 3G
modems connected to a physical serial port. modems connected to a physical serial port.
How to use it How to use it
------------- -------------
1- initialize the modem in 0710 mux mode (usually AT+CMUX= command) through 1. initialize the modem in 0710 mux mode (usually AT+CMUX= command) through
its serial port. Depending on the modem used, you can pass more or less its serial port. Depending on the modem used, you can pass more or less
parameters to this command, parameters to this command,
2- switch the serial line to using the n_gsm line discipline by using 2. switch the serial line to using the n_gsm line discipline by using
TIOCSETD ioctl, TIOCSETD ioctl,
3- configure the mux using GSMIOC_GETCONF / GSMIOC_SETCONF ioctl, 3. configure the mux using GSMIOC_GETCONF / GSMIOC_SETCONF ioctl,
Major parts of the initialization program : Major parts of the initialization program :
(a good starting point is util-linux-ng/sys-utils/ldattach.c) (a good starting point is util-linux-ng/sys-utils/ldattach.c)::
#include <linux/gsmmux.h>
#define N_GSM0710 21 /* GSM 0710 Mux */ #include <linux/gsmmux.h>
#define DEFAULT_SPEED B115200 #define N_GSM0710 21 /* GSM 0710 Mux */
#define SERIAL_PORT /dev/ttyS0 #define DEFAULT_SPEED B115200
#define SERIAL_PORT /dev/ttyS0
int ldisc = N_GSM0710; int ldisc = N_GSM0710;
struct gsm_config c; struct gsm_config c;
...@@ -60,37 +63,41 @@ Major parts of the initialization program : ...@@ -60,37 +63,41 @@ Major parts of the initialization program :
daemon(0,0); daemon(0,0);
pause(); pause();
4- create the devices corresponding to the "virtual" serial ports (take care, 4. create the devices corresponding to the "virtual" serial ports (take care,
each modem has its configuration and some DLC have dedicated functions, each modem has its configuration and some DLC have dedicated functions,
for example GPS), starting with minor 1 (DLC0 is reserved for the management for example GPS), starting with minor 1 (DLC0 is reserved for the management
of the mux) of the mux)::
MAJOR=`cat /proc/devices |grep gsmtty | awk '{print $1}` MAJOR=`cat /proc/devices |grep gsmtty | awk '{print $1}`
for i in `seq 1 4`; do for i in `seq 1 4`; do
mknod /dev/ttygsm$i c $MAJOR $i mknod /dev/ttygsm$i c $MAJOR $i
done done
5. use these devices as plain serial ports.
5- use these devices as plain serial ports. for example, it's possible:
for example, it's possible :
- and to use gnokii to send / receive SMS on ttygsm1
- to use ppp to establish a datalink on ttygsm2
6- first close all virtual ports before closing the physical port. - and to use gnokii to send / receive SMS on ttygsm1
- to use ppp to establish a datalink on ttygsm2
Note that after closing the physical port the modem is still in multiplexing 6. first close all virtual ports before closing the physical port.
mode. This may prevent a successful re-opening of the port later. To avoid
this situation either reset the modem if your hardware allows that or send Note that after closing the physical port the modem is still in multiplexing
a disconnect command frame manually before initializing the multiplexing mode mode. This may prevent a successful re-opening of the port later. To avoid
for the second time. The byte sequence for the disconnect command frame is: this situation either reset the modem if your hardware allows that or send
0xf9, 0x03, 0xef, 0x03, 0xc3, 0x16, 0xf9. a disconnect command frame manually before initializing the multiplexing mode
for the second time. The byte sequence for the disconnect command frame is::
0xf9, 0x03, 0xef, 0x03, 0xc3, 0x16, 0xf9.
Additional Documentation Additional Documentation
------------------------ ------------------------
More practical details on the protocol and how it's supported by industrial More practical details on the protocol and how it's supported by industrial
modems can be found in the following documents : modems can be found in the following documents :
http://www.telit.com/module/infopool/download.php?id=616
http://www.u-blox.com/images/downloads/Product_Docs/LEON-G100-G200-MuxImplementation_ApplicationNote_%28GSM%20G1-CS-10002%29.pdf - http://www.telit.com/module/infopool/download.php?id=616
http://www.sierrawireless.com/Support/Downloads/AirPrime/WMP_Series/~/media/Support_Downloads/AirPrime/Application_notes/CMUX_Feature_Application_Note-Rev004.ashx - http://www.u-blox.com/images/downloads/Product_Docs/LEON-G100-G200-MuxImplementation_ApplicationNote_%28GSM%20G1-CS-10002%29.pdf
http://wm.sim.com/sim/News/photo/2010721161442.pdf - http://www.sierrawireless.com/Support/Downloads/AirPrime/WMP_Series/~/media/Support_Downloads/AirPrime/Application_notes/CMUX_Feature_Application_Note-Rev004.ashx
- http://wm.sim.com/sim/News/photo/2010721161442.pdf
11-03-08 - Eric Bénard - <eric@eukrea.com> 11-03-08 - Eric Bénard - <eric@eukrea.com>
================================================
Comtrol(tm) RocketPort(R)/RocketModem(TM) Series Comtrol(tm) RocketPort(R)/RocketModem(TM) Series
Device Driver for the Linux Operating System ================================================
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- Device Driver for the Linux Operating System
============================================
PRODUCT OVERVIEW Product overview
---------------- ----------------
This driver provides a loadable kernel driver for the Comtrol RocketPort This driver provides a loadable kernel driver for the Comtrol RocketPort
...@@ -29,22 +31,24 @@ information on how to set the DIP switches. ...@@ -29,22 +31,24 @@ information on how to set the DIP switches.
You pass the I/O port to the driver using the following module parameters: You pass the I/O port to the driver using the following module parameters:
board1 : I/O port for the first ISA board board1:
board2 : I/O port for the second ISA board I/O port for the first ISA board
board3 : I/O port for the third ISA board board2:
board4 : I/O port for the fourth ISA board I/O port for the second ISA board
board3:
I/O port for the third ISA board
board4:
I/O port for the fourth ISA board
There is a set of utilities and scripts provided with the external driver There is a set of utilities and scripts provided with the external driver
( downloadable from http://www.comtrol.com ) that ease the configuration and (downloadable from http://www.comtrol.com) that ease the configuration and
setup of the ISA cards. setup of the ISA cards.
The RocketModem II PCI boards require firmware to be loaded into the card The RocketModem II PCI boards require firmware to be loaded into the card
before it will function. The driver has only been tested as a module for this before it will function. The driver has only been tested as a module for this
board. board.
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- Installation Procedures
INSTALLATION PROCEDURES
----------------------- -----------------------
RocketPort/RocketModem PCI cards require no driver configuration, they are RocketPort/RocketModem PCI cards require no driver configuration, they are
...@@ -62,24 +66,24 @@ in the system log at /var/log/messages. ...@@ -62,24 +66,24 @@ in the system log at /var/log/messages.
If installed as a module, the module must be loaded. This can be done If installed as a module, the module must be loaded. This can be done
manually by entering "modprobe rocket". To have the module loaded automatically manually by entering "modprobe rocket". To have the module loaded automatically
upon system boot, edit a /etc/modprobe.d/*.conf file and add the line upon system boot, edit a `/etc/modprobe.d/*.conf` file and add the line
"alias char-major-46 rocket". "alias char-major-46 rocket".
In order to use the ports, their device names (nodes) must be created with mknod. In order to use the ports, their device names (nodes) must be created with mknod.
This is only required once, the system will retain the names once created. To This is only required once, the system will retain the names once created. To
create the RocketPort/RocketModem device names, use the command create the RocketPort/RocketModem device names, use the command
"mknod /dev/ttyRx c 46 x" where x is the port number starting at zero. For example: "mknod /dev/ttyRx c 46 x" where x is the port number starting at zero.
>mknod /dev/ttyR0 c 46 0 For example::
>mknod /dev/ttyR1 c 46 1
>mknod /dev/ttyR2 c 46 2
The Linux script MAKEDEV will create the first 16 ttyRx device names (nodes) > mknod /dev/ttyR0 c 46 0
for you: > mknod /dev/ttyR1 c 46 1
> mknod /dev/ttyR2 c 46 2
>/dev/MAKEDEV ttyR The Linux script MAKEDEV will create the first 16 ttyRx device names (nodes)
for you::
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- >/dev/MAKEDEV ttyR
ISA Rocketport Boards ISA Rocketport Boards
--------------------- ---------------------
...@@ -89,7 +93,7 @@ card before installing and using it. This is done by setting a set of DIP ...@@ -89,7 +93,7 @@ card before installing and using it. This is done by setting a set of DIP
switches on the Rocketport board. switches on the Rocketport board.
SETTING THE I/O ADDRESS Setting the I/O address
----------------------- -----------------------
Before installing RocketPort(R) or RocketPort RA boards, you must find Before installing RocketPort(R) or RocketPort RA boards, you must find
...@@ -130,9 +134,7 @@ the first 4 bytes of that range are used by the first board. You would ...@@ -130,9 +134,7 @@ the first 4 bytes of that range are used by the first board. You would
need to set the second, third, or fourth board to one of the next available need to set the second, third, or fourth board to one of the next available
blocks such as 0x180. blocks such as 0x180.
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- RocketPort and RocketPort RA SW1 Settings::
RocketPort and RocketPort RA SW1 Settings:
+-------------------------------+ +-------------------------------+
| 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 |
...@@ -140,30 +142,28 @@ RocketPort and RocketPort RA SW1 Settings: ...@@ -140,30 +142,28 @@ RocketPort and RocketPort RA SW1 Settings:
| Unused| Card | I/O Port Block| | Unused| Card | I/O Port Block|
+-------------------------------+ +-------------------------------+
DIP Switches DIP Switches DIP Switches DIP Switches
7 8 6 5 7 8 6 5
=================== =================== =================== ===================
On On UNUSED, MUST BE ON. On On First Card <==== Default On On UNUSED, MUST BE ON. On On First Card <==== Default
On Off Second Card On Off Second Card
Off On Third Card Off On Third Card
Off Off Fourth Card Off Off Fourth Card
DIP Switches I/O Address Range DIP Switches I/O Address Range
4 3 2 1 Used by the First Card 4 3 2 1 Used by the First Card
===================================== =====================================
On Off On Off 100-143 On Off On Off 100-143
On Off Off On 140-183 On Off Off On 140-183
On Off Off Off 180-1C3 <==== Default On Off Off Off 180-1C3 <==== Default
Off On On Off 200-243 Off On On Off 200-243
Off On Off On 240-283 Off On Off On 240-283
Off On Off Off 280-2C3 Off On Off Off 280-2C3
Off Off On Off 300-343 Off Off On Off 300-343
Off Off Off On 340-383 Off Off Off On 340-383
Off Off Off Off 380-3C3 Off Off Off Off 380-3C3
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- Reporting Bugs
REPORTING BUGS
-------------- --------------
For technical support, please provide the following For technical support, please provide the following
...@@ -171,19 +171,15 @@ information: Driver version, kernel release, distribution of ...@@ -171,19 +171,15 @@ information: Driver version, kernel release, distribution of
kernel, and type of board you are using. Error messages and log kernel, and type of board you are using. Error messages and log
printouts port configuration details are especially helpful. printouts port configuration details are especially helpful.
USA USA:
Phone: (612) 494-4100 :Phone: (612) 494-4100
FAX: (612) 494-4199 :FAX: (612) 494-4199
email: support@comtrol.com :email: support@comtrol.com
Comtrol Europe Comtrol Europe:
Phone: +44 (0) 1 869 323-220 :Phone: +44 (0) 1 869 323-220
FAX: +44 (0) 1 869 323-211 :FAX: +44 (0) 1 869 323-211
email: support@comtrol.co.uk :email: support@comtrol.co.uk
Web: http://www.comtrol.com Web: http://www.comtrol.com
FTP: ftp.comtrol.com FTP: ftp.comtrol.com
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
ISO7816 SERIAL COMMUNICATIONS =============================
ISO7816 Serial Communications
=============================
1. INTRODUCTION 1. Introduction
===============
ISO/IEC7816 is a series of standards specifying integrated circuit cards (ICC) ISO/IEC7816 is a series of standards specifying integrated circuit cards (ICC)
also known as smart cards. also known as smart cards.
2. HARDWARE-RELATED CONSIDERATIONS 2. Hardware-related considerations
==================================
Some CPUs/UARTs (e.g., Microchip AT91) contain a built-in mode capable of Some CPUs/UARTs (e.g., Microchip AT91) contain a built-in mode capable of
handling communication with a smart card. handling communication with a smart card.
...@@ -15,7 +19,8 @@ ...@@ -15,7 +19,8 @@
available at user-level to allow switching from one mode to the other, and available at user-level to allow switching from one mode to the other, and
vice versa. vice versa.
3. DATA STRUCTURES ALREADY AVAILABLE IN THE KERNEL 3. Data Structures Already Available in the Kernel
==================================================
The Linux kernel provides the serial_iso7816 structure (see [1]) to handle The Linux kernel provides the serial_iso7816 structure (see [1]) to handle
ISO7816 communications. This data structure is used to set and configure ISO7816 communications. This data structure is used to set and configure
...@@ -27,10 +32,11 @@ ...@@ -27,10 +32,11 @@
to TIOCGISO7816 and TIOCSISO7816 ioctls (see below). The iso7816_config to TIOCGISO7816 and TIOCSISO7816 ioctls (see below). The iso7816_config
callback receives a pointer to struct serial_iso7816. callback receives a pointer to struct serial_iso7816.
4. USAGE FROM USER-LEVEL 4. Usage from user-level
========================
From user-level, ISO7816 configuration can be get/set using the previous From user-level, ISO7816 configuration can be get/set using the previous
ioctls. For instance, to set ISO7816 you can use the following code: ioctls. For instance, to set ISO7816 you can use the following code::
#include <linux/serial.h> #include <linux/serial.h>
...@@ -78,6 +84,7 @@ ...@@ -78,6 +84,7 @@
/* Error handling. See errno. */ /* Error handling. See errno. */
} }
5. REFERENCES 5. References
=============
[1] include/uapi/linux/serial.h [1] include/uapi/linux/serial.h
RS485 SERIAL COMMUNICATIONS ===========================
RS485 Serial Communications
===========================
1. INTRODUCTION 1. Introduction
===============
EIA-485, also known as TIA/EIA-485 or RS-485, is a standard defining the EIA-485, also known as TIA/EIA-485 or RS-485, is a standard defining the
electrical characteristics of drivers and receivers for use in balanced electrical characteristics of drivers and receivers for use in balanced
...@@ -9,7 +12,8 @@ ...@@ -9,7 +12,8 @@
because it can be used effectively over long distances and in electrically because it can be used effectively over long distances and in electrically
noisy environments. noisy environments.
2. HARDWARE-RELATED CONSIDERATIONS 2. Hardware-related Considerations
==================================
Some CPUs/UARTs (e.g., Atmel AT91 or 16C950 UART) contain a built-in Some CPUs/UARTs (e.g., Atmel AT91 or 16C950 UART) contain a built-in
half-duplex mode capable of automatically controlling line direction by half-duplex mode capable of automatically controlling line direction by
...@@ -22,7 +26,8 @@ ...@@ -22,7 +26,8 @@
available at user-level to allow switching from one mode to the other, and available at user-level to allow switching from one mode to the other, and
vice versa. vice versa.
3. DATA STRUCTURES ALREADY AVAILABLE IN THE KERNEL 3. Data Structures Already Available in the Kernel
==================================================
The Linux kernel provides the serial_rs485 structure (see [1]) to handle The Linux kernel provides the serial_rs485 structure (see [1]) to handle
RS485 communications. This data structure is used to set and configure RS485 RS485 communications. This data structure is used to set and configure RS485
...@@ -38,10 +43,11 @@ ...@@ -38,10 +43,11 @@
to TIOCSRS485 and TIOCGRS485 ioctls (see below). The rs485_config callback to TIOCSRS485 and TIOCGRS485 ioctls (see below). The rs485_config callback
receives a pointer to struct serial_rs485. receives a pointer to struct serial_rs485.
4. USAGE FROM USER-LEVEL 4. Usage from user-level
========================
From user-level, RS485 configuration can be get/set using the previous From user-level, RS485 configuration can be get/set using the previous
ioctls. For instance, to set RS485 you can use the following code: ioctls. For instance, to set RS485 you can use the following code::
#include <linux/serial.h> #include <linux/serial.h>
...@@ -89,7 +95,9 @@ ...@@ -89,7 +95,9 @@
/* Error handling. See errno. */ /* Error handling. See errno. */
} }
5. REFERENCES 5. References
=============
[1] include/uapi/linux/serial.h [1] include/uapi/linux/serial.h
[2] Documentation/devicetree/bindings/serial/rs485.txt [2] Documentation/devicetree/bindings/serial/rs485.txt
=================
The Lockronomicon The Lockronomicon
=================
Your guide to the ancient and twisted locking policies of the tty layer and Your guide to the ancient and twisted locking policies of the tty layer and
the warped logic behind them. Beware all ye who read on. the warped logic behind them. Beware all ye who read on.
...@@ -34,9 +35,11 @@ about to enter and exit although this detail matters not). ...@@ -34,9 +35,11 @@ about to enter and exit although this detail matters not).
Line Discipline Methods Line Discipline Methods
----------------------- -----------------------
TTY side interfaces: TTY side interfaces
^^^^^^^^^^^^^^^^^^^
open() - Called when the line discipline is attached to ======================= =======================================================
open() Called when the line discipline is attached to
the terminal. No other call into the line the terminal. No other call into the line
discipline for this tty will occur until it discipline for this tty will occur until it
completes successfully. Should initialize any completes successfully. Should initialize any
...@@ -47,66 +50,69 @@ open() - Called when the line discipline is attached to ...@@ -47,66 +50,69 @@ open() - Called when the line discipline is attached to
Returning an error will prevent the ldisc from Returning an error will prevent the ldisc from
being attached. Can sleep. being attached. Can sleep.
close() - This is called on a terminal when the line close() This is called on a terminal when the line
discipline is being unplugged. At the point of discipline is being unplugged. At the point of
execution no further users will enter the execution no further users will enter the
ldisc code for this tty. Can sleep. ldisc code for this tty. Can sleep.
hangup() - Called when the tty line is hung up. hangup() Called when the tty line is hung up.
The line discipline should cease I/O to the tty. The line discipline should cease I/O to the tty.
No further calls into the ldisc code will occur. No further calls into the ldisc code will occur.
The return value is ignored. Can sleep. The return value is ignored. Can sleep.
read() - (optional) A process requests reading data from read() (optional) A process requests reading data from
the line. Multiple read calls may occur in parallel the line. Multiple read calls may occur in parallel
and the ldisc must deal with serialization issues. and the ldisc must deal with serialization issues.
If not defined, the process will receive an EIO If not defined, the process will receive an EIO
error. May sleep. error. May sleep.
write() - (optional) A process requests writing data to the write() (optional) A process requests writing data to the
line. Multiple write calls are serialized by the line. Multiple write calls are serialized by the
tty layer for the ldisc. If not defined, the tty layer for the ldisc. If not defined, the
process will receive an EIO error. May sleep. process will receive an EIO error. May sleep.
flush_buffer() - (optional) May be called at any point between flush_buffer() (optional) May be called at any point between
open and close, and instructs the line discipline open and close, and instructs the line discipline
to empty its input buffer. to empty its input buffer.
set_termios() - (optional) Called on termios structure changes. set_termios() (optional) Called on termios structure changes.
The caller passes the old termios data and the The caller passes the old termios data and the
current data is in the tty. Called under the current data is in the tty. Called under the
termios semaphore so allowed to sleep. Serialized termios semaphore so allowed to sleep. Serialized
against itself only. against itself only.
poll() - (optional) Check the status for the poll/select poll() (optional) Check the status for the poll/select
calls. Multiple poll calls may occur in parallel. calls. Multiple poll calls may occur in parallel.
May sleep. May sleep.
ioctl() - (optional) Called when an ioctl is handed to the ioctl() (optional) Called when an ioctl is handed to the
tty layer that might be for the ldisc. Multiple tty layer that might be for the ldisc. Multiple
ioctl calls may occur in parallel. May sleep. ioctl calls may occur in parallel. May sleep.
compat_ioctl() - (optional) Called when a 32 bit ioctl is handed compat_ioctl() (optional) Called when a 32 bit ioctl is handed
to the tty layer that might be for the ldisc. to the tty layer that might be for the ldisc.
Multiple ioctl calls may occur in parallel. Multiple ioctl calls may occur in parallel.
May sleep. May sleep.
======================= =======================================================
Driver Side Interfaces: Driver Side Interfaces
^^^^^^^^^^^^^^^^^^^^^^
receive_buf() - (optional) Called by the low-level driver to hand ======================= =======================================================
receive_buf() (optional) Called by the low-level driver to hand
a buffer of received bytes to the ldisc for a buffer of received bytes to the ldisc for
processing. The number of bytes is guaranteed not processing. The number of bytes is guaranteed not
to exceed the current value of tty->receive_room. to exceed the current value of tty->receive_room.
All bytes must be processed. All bytes must be processed.
receive_buf2() - (optional) Called by the low-level driver to hand receive_buf2() (optional) Called by the low-level driver to hand
a buffer of received bytes to the ldisc for a buffer of received bytes to the ldisc for
processing. Returns the number of bytes processed. processing. Returns the number of bytes processed.
If both receive_buf() and receive_buf2() are If both receive_buf() and receive_buf2() are
defined, receive_buf2() should be preferred. defined, receive_buf2() should be preferred.
write_wakeup() - May be called at any point between open and close. write_wakeup() May be called at any point between open and close.
The TTY_DO_WRITE_WAKEUP flag indicates if a call The TTY_DO_WRITE_WAKEUP flag indicates if a call
is needed but always races versus calls. Thus the is needed but always races versus calls. Thus the
ldisc must be careful about setting order and to ldisc must be careful about setting order and to
...@@ -117,17 +123,20 @@ write_wakeup() - May be called at any point between open and close. ...@@ -117,17 +123,20 @@ write_wakeup() - May be called at any point between open and close.
is permitted to call the driver write method from is permitted to call the driver write method from
this function. In such a situation defer it. this function. In such a situation defer it.
dcd_change() - Report to the tty line the current DCD pin status dcd_change() Report to the tty line the current DCD pin status
changes and the relative timestamp. The timestamp changes and the relative timestamp. The timestamp
cannot be NULL. cannot be NULL.
======================= =======================================================
Driver Access Driver Access
^^^^^^^^^^^^^
Line discipline methods can call the following methods of the underlying Line discipline methods can call the following methods of the underlying
hardware driver through the function pointers within the tty->driver hardware driver through the function pointers within the tty->driver
structure: structure:
======================= =======================================================
write() Write a block of characters to the tty device. write() Write a block of characters to the tty device.
Returns the number of characters accepted. The Returns the number of characters accepted. The
character buffer passed to this method is already character buffer passed to this method is already
...@@ -189,13 +198,16 @@ wait_until_sent() Waits until the device has written out all of the ...@@ -189,13 +198,16 @@ wait_until_sent() Waits until the device has written out all of the
characters in its transmitter FIFO. characters in its transmitter FIFO.
send_xchar() Send a high-priority XON/XOFF character to the device. send_xchar() Send a high-priority XON/XOFF character to the device.
======================= =======================================================
Flags Flags
^^^^^
Line discipline methods have access to tty->flags field containing the Line discipline methods have access to tty->flags field containing the
following interesting flags: following interesting flags:
======================= =======================================================
TTY_THROTTLED Driver input is throttled. The ldisc should call TTY_THROTTLED Driver input is throttled. The ldisc should call
tty->driver->unthrottle() in order to resume tty->driver->unthrottle() in order to resume
reception when it is ready to process more data. reception when it is ready to process more data.
...@@ -212,28 +224,30 @@ TTY_OTHER_CLOSED Device is a pty and the other side has closed. ...@@ -212,28 +224,30 @@ TTY_OTHER_CLOSED Device is a pty and the other side has closed.
TTY_NO_WRITE_SPLIT Prevent driver from splitting up writes into TTY_NO_WRITE_SPLIT Prevent driver from splitting up writes into
smaller chunks. smaller chunks.
======================= =======================================================
Locking Locking
^^^^^^^
Callers to the line discipline functions from the tty layer are required to Callers to the line discipline functions from the tty layer are required to
take line discipline locks. The same is true of calls from the driver side take line discipline locks. The same is true of calls from the driver side
but not yet enforced. but not yet enforced.
Three calls are now provided Three calls are now provided::
ldisc = tty_ldisc_ref(tty); ldisc = tty_ldisc_ref(tty);
takes a handle to the line discipline in the tty and returns it. If no ldisc takes a handle to the line discipline in the tty and returns it. If no ldisc
is currently attached or the ldisc is being closed and re-opened at this is currently attached or the ldisc is being closed and re-opened at this
point then NULL is returned. While this handle is held the ldisc will not point then NULL is returned. While this handle is held the ldisc will not
change or go away. change or go away::
tty_ldisc_deref(ldisc) tty_ldisc_deref(ldisc)
Returns the ldisc reference and allows the ldisc to be closed. Returning the Returns the ldisc reference and allows the ldisc to be closed. Returning the
reference takes away your right to call the ldisc functions until you take reference takes away your right to call the ldisc functions until you take
a new reference. a new reference::
ldisc = tty_ldisc_ref_wait(tty); ldisc = tty_ldisc_ref_wait(tty);
...@@ -253,61 +267,62 @@ code calling its own functions must be careful in this case. ...@@ -253,61 +267,62 @@ code calling its own functions must be careful in this case.
Driver Interface Driver Interface
---------------- ----------------
open() - Called when a device is opened. May sleep ======================= =======================================================
open() Called when a device is opened. May sleep
close() - Called when a device is closed. At the point of close() Called when a device is closed. At the point of
return from this call the driver must make no return from this call the driver must make no
further ldisc calls of any kind. May sleep further ldisc calls of any kind. May sleep
write() - Called to write bytes to the device. May not write() Called to write bytes to the device. May not
sleep. May occur in parallel in special cases. sleep. May occur in parallel in special cases.
Because this includes panic paths drivers generally Because this includes panic paths drivers generally
shouldn't try and do clever locking here. shouldn't try and do clever locking here.
put_char() - Stuff a single character onto the queue. The put_char() Stuff a single character onto the queue. The
driver is guaranteed following up calls to driver is guaranteed following up calls to
flush_chars. flush_chars.
flush_chars() - Ask the kernel to write put_char queue flush_chars() Ask the kernel to write put_char queue
write_room() - Return the number of characters that can be stuffed write_room() Return the number of characters that can be stuffed
into the port buffers without overflow (or less). into the port buffers without overflow (or less).
The ldisc is responsible for being intelligent The ldisc is responsible for being intelligent
about multi-threading of write_room/write calls about multi-threading of write_room/write calls
ioctl() - Called when an ioctl may be for the driver ioctl() Called when an ioctl may be for the driver
set_termios() - Called on termios change, serialized against set_termios() Called on termios change, serialized against
itself by a semaphore. May sleep. itself by a semaphore. May sleep.
set_ldisc() - Notifier for discipline change. At the point this set_ldisc() Notifier for discipline change. At the point this
is done the discipline is not yet usable. Can now is done the discipline is not yet usable. Can now
sleep (I think) sleep (I think)
throttle() - Called by the ldisc to ask the driver to do flow throttle() Called by the ldisc to ask the driver to do flow
control. Serialization including with unthrottle control. Serialization including with unthrottle
is the job of the ldisc layer. is the job of the ldisc layer.
unthrottle() - Called by the ldisc to ask the driver to stop flow unthrottle() Called by the ldisc to ask the driver to stop flow
control. control.
stop() - Ldisc notifier to the driver to stop output. As with stop() Ldisc notifier to the driver to stop output. As with
throttle the serializations with start() are down throttle the serializations with start() are down
to the ldisc layer. to the ldisc layer.
start() - Ldisc notifier to the driver to start output. start() Ldisc notifier to the driver to start output.
hangup() - Ask the tty driver to cause a hangup initiated hangup() Ask the tty driver to cause a hangup initiated
from the host side. [Can sleep ??] from the host side. [Can sleep ??]
break_ctl() - Send RS232 break. Can sleep. Can get called in break_ctl() Send RS232 break. Can sleep. Can get called in
parallel, driver must serialize (for now), and parallel, driver must serialize (for now), and
with write calls. with write calls.
wait_until_sent() - Wait for characters to exit the hardware queue wait_until_sent() Wait for characters to exit the hardware queue
of the driver. Can sleep of the driver. Can sleep
send_xchar() - Send XON/XOFF and if possible jump the queue with send_xchar() Send XON/XOFF and if possible jump the queue with
it in order to get fast flow control responses. it in order to get fast flow control responses.
Cannot sleep ?? Cannot sleep ??
======================= =======================================================
...@@ -10466,7 +10466,7 @@ F: include/uapi/linux/meye.h ...@@ -10466,7 +10466,7 @@ F: include/uapi/linux/meye.h
MOXA SMARTIO/INDUSTIO/INTELLIO SERIAL CARD MOXA SMARTIO/INDUSTIO/INTELLIO SERIAL CARD
M: Jiri Slaby <jirislaby@gmail.com> M: Jiri Slaby <jirislaby@gmail.com>
S: Maintained S: Maintained
F: Documentation/serial/moxa-smartio F: Documentation/serial/moxa-smartio.rst
F: drivers/tty/mxser.* F: drivers/tty/mxser.*
MR800 AVERMEDIA USB FM RADIO DRIVER MR800 AVERMEDIA USB FM RADIO DRIVER
...@@ -13376,7 +13376,7 @@ ROCKETPORT DRIVER ...@@ -13376,7 +13376,7 @@ ROCKETPORT DRIVER
P: Comtrol Corp. P: Comtrol Corp.
W: http://www.comtrol.com W: http://www.comtrol.com
S: Maintained S: Maintained
F: Documentation/serial/rocket.txt F: Documentation/serial/rocket.rst
F: drivers/tty/rocket* F: drivers/tty/rocket*
ROCKETPORT EXPRESS/INFINITY DRIVER ROCKETPORT EXPRESS/INFINITY DRIVER
......
...@@ -75,7 +75,7 @@ struct ports_driver_data { ...@@ -75,7 +75,7 @@ struct ports_driver_data {
/* All the console devices handled by this driver */ /* All the console devices handled by this driver */
struct list_head consoles; struct list_head consoles;
}; };
static struct ports_driver_data pdrvdata; static struct ports_driver_data pdrvdata = { .next_vtermno = 1};
static DEFINE_SPINLOCK(pdrvdata_lock); static DEFINE_SPINLOCK(pdrvdata_lock);
static DECLARE_COMPLETION(early_console_added); static DECLARE_COMPLETION(early_console_added);
...@@ -1394,6 +1394,7 @@ static int add_port(struct ports_device *portdev, u32 id) ...@@ -1394,6 +1394,7 @@ static int add_port(struct ports_device *portdev, u32 id)
port->async_queue = NULL; port->async_queue = NULL;
port->cons.ws.ws_row = port->cons.ws.ws_col = 0; port->cons.ws.ws_row = port->cons.ws.ws_col = 0;
port->cons.vtermno = 0;
port->host_connected = port->guest_connected = false; port->host_connected = port->guest_connected = false;
port->stats = (struct port_stats) { 0 }; port->stats = (struct port_stats) { 0 };
......
# SPDX-License-Identifier: GPL-2.0
config TTY config TTY
bool "Enable TTY" if EXPERT bool "Enable TTY" if EXPERT
default y default y
...@@ -83,7 +84,6 @@ config HW_CONSOLE ...@@ -83,7 +84,6 @@ config HW_CONSOLE
config VT_HW_CONSOLE_BINDING config VT_HW_CONSOLE_BINDING
bool "Support for binding and unbinding console drivers" bool "Support for binding and unbinding console drivers"
depends on HW_CONSOLE depends on HW_CONSOLE
default n
---help--- ---help---
The virtual terminal is the device that interacts with the physical The virtual terminal is the device that interacts with the physical
terminal through console drivers. On these systems, at least one terminal through console drivers. On these systems, at least one
...@@ -175,7 +175,7 @@ config ROCKETPORT ...@@ -175,7 +175,7 @@ config ROCKETPORT
This driver supports Comtrol RocketPort and RocketModem PCI boards. This driver supports Comtrol RocketPort and RocketModem PCI boards.
These boards provide 2, 4, 8, 16, or 32 high-speed serial ports or These boards provide 2, 4, 8, 16, or 32 high-speed serial ports or
modems. For information about the RocketPort/RocketModem boards modems. For information about the RocketPort/RocketModem boards
and this driver read <file:Documentation/serial/rocket.txt>. and this driver read <file:Documentation/serial/rocket.rst>.
To compile this driver as a module, choose M here: the To compile this driver as a module, choose M here: the
module will be called rocket. module will be called rocket.
...@@ -193,7 +193,7 @@ config CYCLADES ...@@ -193,7 +193,7 @@ config CYCLADES
your Linux box, for instance in order to become a dial-in server. your Linux box, for instance in order to become a dial-in server.
For information about the Cyclades-Z card, read For information about the Cyclades-Z card, read
<file:Documentation/serial/README.cycladesZ>. <file:Documentation/serial/cyclades_z.rst>.
To compile this driver as a module, choose M here: the To compile this driver as a module, choose M here: the
module will be called cyclades. module will be called cyclades.
...@@ -312,7 +312,6 @@ config N_GSM ...@@ -312,7 +312,6 @@ config N_GSM
config TRACE_ROUTER config TRACE_ROUTER
tristate "Trace data router for MIPI P1149.7 cJTAG standard" tristate "Trace data router for MIPI P1149.7 cJTAG standard"
depends on TRACE_SINK depends on TRACE_SINK
default n
help help
The trace router uses the Linux tty line discipline framework to The trace router uses the Linux tty line discipline framework to
route trace data coming from a tty port (say UART for example) to route trace data coming from a tty port (say UART for example) to
...@@ -328,7 +327,6 @@ config TRACE_ROUTER ...@@ -328,7 +327,6 @@ config TRACE_ROUTER
config TRACE_SINK config TRACE_SINK
tristate "Trace data sink for MIPI P1149.7 cJTAG standard" tristate "Trace data sink for MIPI P1149.7 cJTAG standard"
default n
help help
The trace sink uses the Linux line discipline framework to receive The trace sink uses the Linux line discipline framework to receive
trace data coming from the trace router line discipline driver trace data coming from the trace router line discipline driver
...@@ -376,6 +374,20 @@ config PPC_EARLY_DEBUG_EHV_BC_HANDLE ...@@ -376,6 +374,20 @@ config PPC_EARLY_DEBUG_EHV_BC_HANDLE
there simply will be no early console output. This is true also there simply will be no early console output. This is true also
if you don't boot under a hypervisor at all. if you don't boot under a hypervisor at all.
config NULL_TTY
tristate "NULL TTY driver"
help
Say Y here if you want a NULL TTY which simply discards messages.
This is useful to allow userspace applications which expect a console
device to work without modifications even when no console is
available or desired.
In order to use this driver, you should redirect the console to this
TTY, or boot the kernel with console=ttynull.
If unsure, say N.
config GOLDFISH_TTY config GOLDFISH_TTY
tristate "Goldfish TTY Driver" tristate "Goldfish TTY Driver"
depends on GOLDFISH depends on GOLDFISH
......
...@@ -25,6 +25,7 @@ obj-$(CONFIG_ISI) += isicom.o ...@@ -25,6 +25,7 @@ obj-$(CONFIG_ISI) += isicom.o
obj-$(CONFIG_MOXA_INTELLIO) += moxa.o obj-$(CONFIG_MOXA_INTELLIO) += moxa.o
obj-$(CONFIG_MOXA_SMARTIO) += mxser.o obj-$(CONFIG_MOXA_SMARTIO) += mxser.o
obj-$(CONFIG_NOZOMI) += nozomi.o obj-$(CONFIG_NOZOMI) += nozomi.o
obj-$(CONFIG_NULL_TTY) += ttynull.o
obj-$(CONFIG_ROCKETPORT) += rocket.o obj-$(CONFIG_ROCKETPORT) += rocket.o
obj-$(CONFIG_SYNCLINK_GT) += synclink_gt.o obj-$(CONFIG_SYNCLINK_GT) += synclink_gt.o
obj-$(CONFIG_SYNCLINKMP) += synclinkmp.o obj-$(CONFIG_SYNCLINKMP) += synclinkmp.o
......
# SPDX-License-Identifier: GPL-2.0
if TTY if TTY
config HVC_DRIVER config HVC_DRIVER
...@@ -24,7 +25,6 @@ config HVC_CONSOLE ...@@ -24,7 +25,6 @@ config HVC_CONSOLE
config HVC_OLD_HVSI config HVC_OLD_HVSI
bool "Old driver for pSeries serial port (/dev/hvsi*)" bool "Old driver for pSeries serial port (/dev/hvsi*)"
depends on HVC_CONSOLE depends on HVC_CONSOLE
default n
config HVC_OPAL config HVC_OPAL
bool "OPAL Console support" bool "OPAL Console support"
...@@ -73,7 +73,6 @@ config HVC_UDBG ...@@ -73,7 +73,6 @@ config HVC_UDBG
bool "udbg based fake hypervisor console" bool "udbg based fake hypervisor console"
depends on PPC depends on PPC
select HVC_DRIVER select HVC_DRIVER
default n
help help
This is meant to be used during HW bring up or debugging when This is meant to be used during HW bring up or debugging when
no other console mechanism exist but udbg, to get you a quick no other console mechanism exist but udbg, to get you a quick
......
# SPDX-License-Identifier: GPL-2.0
# #
# Makefile for the IPWireless driver # Makefile for the IPWireless driver
# #
......
...@@ -114,6 +114,10 @@ static int ipwireless_probe(struct pcmcia_device *p_dev, void *priv_data) ...@@ -114,6 +114,10 @@ static int ipwireless_probe(struct pcmcia_device *p_dev, void *priv_data)
ipw->common_memory = ioremap(p_dev->resource[2]->start, ipw->common_memory = ioremap(p_dev->resource[2]->start,
resource_size(p_dev->resource[2])); resource_size(p_dev->resource[2]));
if (!ipw->common_memory) {
ret = -ENOMEM;
goto exit1;
}
if (!request_mem_region(p_dev->resource[2]->start, if (!request_mem_region(p_dev->resource[2]->start,
resource_size(p_dev->resource[2]), resource_size(p_dev->resource[2]),
IPWIRELESS_PCCARD_NAME)) { IPWIRELESS_PCCARD_NAME)) {
...@@ -134,6 +138,10 @@ static int ipwireless_probe(struct pcmcia_device *p_dev, void *priv_data) ...@@ -134,6 +138,10 @@ static int ipwireless_probe(struct pcmcia_device *p_dev, void *priv_data)
ipw->attr_memory = ioremap(p_dev->resource[3]->start, ipw->attr_memory = ioremap(p_dev->resource[3]->start,
resource_size(p_dev->resource[3])); resource_size(p_dev->resource[3]));
if (!ipw->attr_memory) {
ret = -ENOMEM;
goto exit3;
}
if (!request_mem_region(p_dev->resource[3]->start, if (!request_mem_region(p_dev->resource[3]->start,
resource_size(p_dev->resource[3]), resource_size(p_dev->resource[3]),
IPWIRELESS_PCCARD_NAME)) { IPWIRELESS_PCCARD_NAME)) {
......
...@@ -550,9 +550,9 @@ static ssize_t process_output_block(struct tty_struct *tty, ...@@ -550,9 +550,9 @@ static ssize_t process_output_block(struct tty_struct *tty,
mutex_lock(&ldata->output_lock); mutex_lock(&ldata->output_lock);
space = tty_write_room(tty); space = tty_write_room(tty);
if (!space) { if (space <= 0) {
mutex_unlock(&ldata->output_lock); mutex_unlock(&ldata->output_lock);
return 0; return space;
} }
if (nr > space) if (nr > space)
nr = space; nr = space;
......
...@@ -1283,23 +1283,29 @@ static int rp_ioctl(struct tty_struct *tty, ...@@ -1283,23 +1283,29 @@ static int rp_ioctl(struct tty_struct *tty,
return -ENXIO; return -ENXIO;
switch (cmd) { switch (cmd) {
case RCKP_GET_STRUCT:
if (copy_to_user(argp, info, sizeof (struct r_port)))
ret = -EFAULT;
break;
case RCKP_GET_CONFIG: case RCKP_GET_CONFIG:
dev_warn_ratelimited(tty->dev,
"RCKP_GET_CONFIG option is deprecated\n");
ret = get_config(info, argp); ret = get_config(info, argp);
break; break;
case RCKP_SET_CONFIG: case RCKP_SET_CONFIG:
dev_warn_ratelimited(tty->dev,
"RCKP_SET_CONFIG option is deprecated\n");
ret = set_config(tty, info, argp); ret = set_config(tty, info, argp);
break; break;
case RCKP_GET_PORTS: case RCKP_GET_PORTS:
dev_warn_ratelimited(tty->dev,
"RCKP_GET_PORTS option is deprecated\n");
ret = get_ports(info, argp); ret = get_ports(info, argp);
break; break;
case RCKP_RESET_RM2: case RCKP_RESET_RM2:
dev_warn_ratelimited(tty->dev,
"RCKP_RESET_RM2 option is deprecated\n");
ret = reset_rm2(info, argp); ret = reset_rm2(info, argp);
break; break;
case RCKP_GET_VERSION: case RCKP_GET_VERSION:
dev_warn_ratelimited(tty->dev,
"RCKP_GET_VERSION option is deprecated\n");
ret = get_version(info, argp); ret = get_version(info, argp);
break; break;
default: default:
......
...@@ -71,7 +71,6 @@ struct rocket_version { ...@@ -71,7 +71,6 @@ struct rocket_version {
/* /*
* Rocketport ioctls -- "RP" * Rocketport ioctls -- "RP"
*/ */
#define RCKP_GET_STRUCT 0x00525001
#define RCKP_GET_CONFIG 0x00525002 #define RCKP_GET_CONFIG 0x00525002
#define RCKP_SET_CONFIG 0x00525003 #define RCKP_SET_CONFIG 0x00525003
#define RCKP_GET_PORTS 0x00525004 #define RCKP_GET_PORTS 0x00525004
......
# SPDX-License-Identifier: GPL-2.0
# #
# Serial bus device driver configuration # Serial bus device driver configuration
# #
......
# SPDX-License-Identifier: GPL-2.0
serdev-objs := core.o serdev-objs := core.o
obj-$(CONFIG_SERIAL_DEV_BUS) += serdev.o obj-$(CONFIG_SERIAL_DEV_BUS) += serdev.o
......
...@@ -361,12 +361,15 @@ static const struct exar8250_platform iot2040_platform = { ...@@ -361,12 +361,15 @@ static const struct exar8250_platform iot2040_platform = {
.register_gpio = iot2040_register_gpio, .register_gpio = iot2040_register_gpio,
}; };
/*
* For SIMATIC IOT2000, only IOT2040 and its variants have the Exar device,
* IOT2020 doesn't have. Therefore it is sufficient to match on the common
* board name after the device was found.
*/
static const struct dmi_system_id exar_platforms[] = { static const struct dmi_system_id exar_platforms[] = {
{ {
.matches = { .matches = {
DMI_EXACT_MATCH(DMI_BOARD_NAME, "SIMATIC IOT2000"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "SIMATIC IOT2000"),
DMI_EXACT_MATCH(DMI_BOARD_ASSET_TAG,
"6ES7647-0AA00-1YA2"),
}, },
.driver_data = (void *)&iot2040_platform, .driver_data = (void *)&iot2040_platform,
}, },
......
...@@ -303,7 +303,8 @@ static void fintek_8250_goto_highspeed(struct uart_8250_port *uart, ...@@ -303,7 +303,8 @@ static void fintek_8250_goto_highspeed(struct uart_8250_port *uart,
} }
} }
void fintek_8250_set_termios(struct uart_port *port, struct ktermios *termios, static void fintek_8250_set_termios(struct uart_port *port,
struct ktermios *termios,
struct ktermios *old) struct ktermios *old)
{ {
struct fintek_8250 *pdata = port->private_data; struct fintek_8250 *pdata = port->private_data;
......
// SPDX-License-Identifier: GPL-2.0
#include <linux/device.h> #include <linux/device.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
......
...@@ -21,15 +21,32 @@ ...@@ -21,15 +21,32 @@
#include "8250.h" #include "8250.h"
#define UART_MTK_HIGHS 0x09 /* Highspeed register */ #define MTK_UART_HIGHS 0x09 /* Highspeed register */
#define UART_MTK_SAMPLE_COUNT 0x0a /* Sample count register */ #define MTK_UART_SAMPLE_COUNT 0x0a /* Sample count register */
#define UART_MTK_SAMPLE_POINT 0x0b /* Sample point register */ #define MTK_UART_SAMPLE_POINT 0x0b /* Sample point register */
#define MTK_UART_RATE_FIX 0x0d /* UART Rate Fix Register */ #define MTK_UART_RATE_FIX 0x0d /* UART Rate Fix Register */
#define MTK_UART_ESCAPE_DAT 0x10 /* Escape Character register */
#define MTK_UART_ESCAPE_EN 0x11 /* Escape Enable register */
#define MTK_UART_DMA_EN 0x13 /* DMA Enable register */ #define MTK_UART_DMA_EN 0x13 /* DMA Enable register */
#define MTK_UART_RXTRI_AD 0x14 /* RX Trigger address */
#define MTK_UART_FRACDIV_L 0x15 /* Fractional divider LSB address */
#define MTK_UART_FRACDIV_M 0x16 /* Fractional divider MSB address */
#define MTK_UART_IER_XOFFI 0x20 /* Enable XOFF character interrupt */
#define MTK_UART_IER_RTSI 0x40 /* Enable RTS Modem status interrupt */
#define MTK_UART_IER_CTSI 0x80 /* Enable CTS Modem status interrupt */
#define MTK_UART_EFR_EN 0x10 /* Enable enhancement feature */
#define MTK_UART_EFR_RTS 0x40 /* Enable hardware rx flow control */
#define MTK_UART_EFR_CTS 0x80 /* Enable hardware tx flow control */
#define MTK_UART_EFR_NO_SW_FC 0x0 /* no sw flow control */
#define MTK_UART_EFR_XON1_XOFF1 0xa /* XON1/XOFF1 as sw flow control */
#define MTK_UART_EFR_XON2_XOFF2 0x5 /* XON2/XOFF2 as sw flow control */
#define MTK_UART_EFR_SW_FC_MASK 0xf /* Enable CTS Modem status interrupt */
#define MTK_UART_EFR_HW_FC (MTK_UART_EFR_RTS | MTK_UART_EFR_CTS)
#define MTK_UART_DMA_EN_TX 0x2 #define MTK_UART_DMA_EN_TX 0x2
#define MTK_UART_DMA_EN_RX 0x5 #define MTK_UART_DMA_EN_RX 0x5
#define MTK_UART_ESCAPE_CHAR 0x77 /* Escape char added under sw fc */
#define MTK_UART_TX_SIZE UART_XMIT_SIZE #define MTK_UART_TX_SIZE UART_XMIT_SIZE
#define MTK_UART_RX_SIZE 0x8000 #define MTK_UART_RX_SIZE 0x8000
#define MTK_UART_TX_TRIGGER 1 #define MTK_UART_TX_TRIGGER 1
...@@ -46,6 +63,7 @@ enum dma_rx_status { ...@@ -46,6 +63,7 @@ enum dma_rx_status {
struct mtk8250_data { struct mtk8250_data {
int line; int line;
unsigned int rx_pos; unsigned int rx_pos;
unsigned int clk_count;
struct clk *uart_clk; struct clk *uart_clk;
struct clk *bus_clk; struct clk *bus_clk;
struct uart_8250_dma *dma; struct uart_8250_dma *dma;
...@@ -54,6 +72,13 @@ struct mtk8250_data { ...@@ -54,6 +72,13 @@ struct mtk8250_data {
#endif #endif
}; };
/* flow control mode */
enum {
MTK_UART_FC_NONE,
MTK_UART_FC_SW,
MTK_UART_FC_HW,
};
#ifdef CONFIG_SERIAL_8250_DMA #ifdef CONFIG_SERIAL_8250_DMA
static void mtk8250_rx_dma(struct uart_8250_port *up); static void mtk8250_rx_dma(struct uart_8250_port *up);
...@@ -192,13 +217,89 @@ static void mtk8250_shutdown(struct uart_port *port) ...@@ -192,13 +217,89 @@ static void mtk8250_shutdown(struct uart_port *port)
return serial8250_do_shutdown(port); return serial8250_do_shutdown(port);
} }
static void mtk8250_disable_intrs(struct uart_8250_port *up, int mask)
{
serial_out(up, UART_IER, serial_in(up, UART_IER) & (~mask));
}
static void mtk8250_enable_intrs(struct uart_8250_port *up, int mask)
{
serial_out(up, UART_IER, serial_in(up, UART_IER) | mask);
}
static void mtk8250_set_flow_ctrl(struct uart_8250_port *up, int mode)
{
struct uart_port *port = &up->port;
int lcr = serial_in(up, UART_LCR);
serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
serial_out(up, UART_EFR, UART_EFR_ECB);
serial_out(up, UART_LCR, lcr);
lcr = serial_in(up, UART_LCR);
switch (mode) {
case MTK_UART_FC_NONE:
serial_out(up, MTK_UART_ESCAPE_DAT, MTK_UART_ESCAPE_CHAR);
serial_out(up, MTK_UART_ESCAPE_EN, 0x00);
serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
serial_out(up, UART_EFR, serial_in(up, UART_EFR) &
(~(MTK_UART_EFR_HW_FC | MTK_UART_EFR_SW_FC_MASK)));
serial_out(up, UART_LCR, lcr);
mtk8250_disable_intrs(up, MTK_UART_IER_XOFFI |
MTK_UART_IER_RTSI | MTK_UART_IER_CTSI);
break;
case MTK_UART_FC_HW:
serial_out(up, MTK_UART_ESCAPE_DAT, MTK_UART_ESCAPE_CHAR);
serial_out(up, MTK_UART_ESCAPE_EN, 0x00);
serial_out(up, UART_MCR, UART_MCR_RTS);
serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
/*enable hw flow control*/
serial_out(up, UART_EFR, MTK_UART_EFR_HW_FC |
(serial_in(up, UART_EFR) &
(~(MTK_UART_EFR_HW_FC | MTK_UART_EFR_SW_FC_MASK))));
serial_out(up, UART_LCR, lcr);
mtk8250_disable_intrs(up, MTK_UART_IER_XOFFI);
mtk8250_enable_intrs(up, MTK_UART_IER_CTSI | MTK_UART_IER_RTSI);
break;
case MTK_UART_FC_SW: /*MTK software flow control */
serial_out(up, MTK_UART_ESCAPE_DAT, MTK_UART_ESCAPE_CHAR);
serial_out(up, MTK_UART_ESCAPE_EN, 0x01);
serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
/*enable sw flow control */
serial_out(up, UART_EFR, MTK_UART_EFR_XON1_XOFF1 |
(serial_in(up, UART_EFR) &
(~(MTK_UART_EFR_HW_FC | MTK_UART_EFR_SW_FC_MASK))));
serial_out(up, UART_XON1, START_CHAR(port->state->port.tty));
serial_out(up, UART_XOFF1, STOP_CHAR(port->state->port.tty));
serial_out(up, UART_LCR, lcr);
mtk8250_disable_intrs(up, MTK_UART_IER_CTSI|MTK_UART_IER_RTSI);
mtk8250_enable_intrs(up, MTK_UART_IER_XOFFI);
break;
default:
break;
}
}
static void static void
mtk8250_set_termios(struct uart_port *port, struct ktermios *termios, mtk8250_set_termios(struct uart_port *port, struct ktermios *termios,
struct ktermios *old) struct ktermios *old)
{ {
unsigned short fraction_L_mapping[] = {
0, 1, 0x5, 0x15, 0x55, 0x57, 0x57, 0x77, 0x7F, 0xFF, 0xFF
};
unsigned short fraction_M_mapping[] = {
0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 3
};
struct uart_8250_port *up = up_to_u8250p(port); struct uart_8250_port *up = up_to_u8250p(port);
unsigned int baud, quot, fraction;
unsigned long flags; unsigned long flags;
unsigned int baud, quot; int mode;
#ifdef CONFIG_SERIAL_8250_DMA #ifdef CONFIG_SERIAL_8250_DMA
if (up->dma) { if (up->dma) {
...@@ -214,7 +315,7 @@ mtk8250_set_termios(struct uart_port *port, struct ktermios *termios, ...@@ -214,7 +315,7 @@ mtk8250_set_termios(struct uart_port *port, struct ktermios *termios,
serial8250_do_set_termios(port, termios, old); serial8250_do_set_termios(port, termios, old);
/* /*
* Mediatek UARTs use an extra highspeed register (UART_MTK_HIGHS) * Mediatek UARTs use an extra highspeed register (MTK_UART_HIGHS)
* *
* We need to recalcualte the quot register, as the claculation depends * We need to recalcualte the quot register, as the claculation depends
* on the vaule in the highspeed register. * on the vaule in the highspeed register.
...@@ -230,18 +331,11 @@ mtk8250_set_termios(struct uart_port *port, struct ktermios *termios, ...@@ -230,18 +331,11 @@ mtk8250_set_termios(struct uart_port *port, struct ktermios *termios,
port->uartclk / 16 / UART_DIV_MAX, port->uartclk / 16 / UART_DIV_MAX,
port->uartclk); port->uartclk);
if (baud <= 115200) { if (baud < 115200) {
serial_port_out(port, UART_MTK_HIGHS, 0x0); serial_port_out(port, MTK_UART_HIGHS, 0x0);
quot = uart_get_divisor(port, baud); quot = uart_get_divisor(port, baud);
} else if (baud <= 576000) {
serial_port_out(port, UART_MTK_HIGHS, 0x2);
/* Set to next lower baudrate supported */
if ((baud == 500000) || (baud == 576000))
baud = 460800;
quot = DIV_ROUND_UP(port->uartclk, 4 * baud);
} else { } else {
serial_port_out(port, UART_MTK_HIGHS, 0x3); serial_port_out(port, MTK_UART_HIGHS, 0x3);
quot = DIV_ROUND_UP(port->uartclk, 256 * baud); quot = DIV_ROUND_UP(port->uartclk, 256 * baud);
} }
...@@ -258,18 +352,40 @@ mtk8250_set_termios(struct uart_port *port, struct ktermios *termios, ...@@ -258,18 +352,40 @@ mtk8250_set_termios(struct uart_port *port, struct ktermios *termios,
/* reset DLAB */ /* reset DLAB */
serial_port_out(port, UART_LCR, up->lcr); serial_port_out(port, UART_LCR, up->lcr);
if (baud > 460800) { if (baud >= 115200) {
unsigned int tmp; unsigned int tmp;
tmp = DIV_ROUND_CLOSEST(port->uartclk, quot * baud); tmp = (port->uartclk / (baud * quot)) - 1;
serial_port_out(port, UART_MTK_SAMPLE_COUNT, tmp - 1); serial_port_out(port, MTK_UART_SAMPLE_COUNT, tmp);
serial_port_out(port, UART_MTK_SAMPLE_POINT, serial_port_out(port, MTK_UART_SAMPLE_POINT,
(tmp - 2) >> 1); (tmp >> 1) - 1);
/*count fraction to set fractoin register */
fraction = ((port->uartclk * 100) / baud / quot) % 100;
fraction = DIV_ROUND_CLOSEST(fraction, 10);
serial_port_out(port, MTK_UART_FRACDIV_L,
fraction_L_mapping[fraction]);
serial_port_out(port, MTK_UART_FRACDIV_M,
fraction_M_mapping[fraction]);
} else { } else {
serial_port_out(port, UART_MTK_SAMPLE_COUNT, 0x00); serial_port_out(port, MTK_UART_SAMPLE_COUNT, 0x00);
serial_port_out(port, UART_MTK_SAMPLE_POINT, 0xff); serial_port_out(port, MTK_UART_SAMPLE_POINT, 0xff);
serial_port_out(port, MTK_UART_FRACDIV_L, 0x00);
serial_port_out(port, MTK_UART_FRACDIV_M, 0x00);
} }
if ((termios->c_cflag & CRTSCTS) && (!(termios->c_iflag & CRTSCTS)))
mode = MTK_UART_FC_HW;
else if (termios->c_iflag & CRTSCTS)
mode = MTK_UART_FC_SW;
else
mode = MTK_UART_FC_NONE;
mtk8250_set_flow_ctrl(up, mode);
if (uart_console(port))
up->port.cons->cflag = termios->c_cflag;
spin_unlock_irqrestore(&port->lock, flags); spin_unlock_irqrestore(&port->lock, flags);
/* Don't rewrite B0 */ /* Don't rewrite B0 */
if (tty_termios_baud_rate(termios)) if (tty_termios_baud_rate(termios))
......
# SPDX-License-Identifier: GPL-2.0
# #
# The 8250/16550 serial drivers. You shouldn't be in this list unless # The 8250/16550 serial drivers. You shouldn't be in this list unless
# you somehow have an implicit or explicit dependency on SERIAL_8250. # you somehow have an implicit or explicit dependency on SERIAL_8250.
......
# SPDX-License-Identifier: GPL-2.0
# #
# Serial device configuration # Serial device configuration
# #
...@@ -369,7 +370,6 @@ config SERIAL_MAX310X ...@@ -369,7 +370,6 @@ config SERIAL_MAX310X
depends on SPI_MASTER depends on SPI_MASTER
select SERIAL_CORE select SERIAL_CORE
select REGMAP_SPI if SPI_MASTER select REGMAP_SPI if SPI_MASTER
default n
help help
This selects support for an advanced UART from Maxim (Dallas). This selects support for an advanced UART from Maxim (Dallas).
Supported ICs are MAX3107, MAX3108, MAX3109, MAX14830. Supported ICs are MAX3107, MAX3108, MAX3109, MAX14830.
...@@ -652,7 +652,6 @@ config SERIAL_MUX_CONSOLE ...@@ -652,7 +652,6 @@ config SERIAL_MUX_CONSOLE
config PDC_CONSOLE config PDC_CONSOLE
bool "PDC software console support" bool "PDC software console support"
depends on PARISC && !SERIAL_MUX && VT depends on PARISC && !SERIAL_MUX && VT
default n
help help
Saying Y here will enable the software based PDC console to be Saying Y here will enable the software based PDC console to be
used as the system console. This is useful for machines in used as the system console. This is useful for machines in
...@@ -1095,6 +1094,30 @@ config SERIAL_OMAP_CONSOLE ...@@ -1095,6 +1094,30 @@ config SERIAL_OMAP_CONSOLE
your boot loader about how to pass options to the kernel at your boot loader about how to pass options to the kernel at
boot time.) boot time.)
config SERIAL_SIFIVE
tristate "SiFive UART support"
depends on OF
select SERIAL_CORE
help
Select this option if you are building a kernel for a device that
contains a SiFive UART IP block. This type of UART is present on
SiFive FU540 SoCs, among others.
config SERIAL_SIFIVE_CONSOLE
bool "Console on SiFive UART"
depends on SERIAL_SIFIVE=y
select SERIAL_CORE_CONSOLE
help
Select this option if you would like to use a SiFive UART as the
system console.
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=ttySIFx". (Try "man bootparam" or see the documentation of
your boot loader about how to pass options to the kernel at
boot time.)
config SERIAL_LANTIQ config SERIAL_LANTIQ
bool "Lantiq serial driver" bool "Lantiq serial driver"
depends on LANTIQ depends on LANTIQ
...@@ -1109,7 +1132,6 @@ config SERIAL_QE ...@@ -1109,7 +1132,6 @@ config SERIAL_QE
depends on QUICC_ENGINE depends on QUICC_ENGINE
select SERIAL_CORE select SERIAL_CORE
select FW_LOADER select FW_LOADER
default n
help help
This driver supports the QE serial ports on Freescale embedded This driver supports the QE serial ports on Freescale embedded
PowerPC that contain a QUICC Engine. PowerPC that contain a QUICC Engine.
...@@ -1582,6 +1604,32 @@ config SERIAL_RDA_CONSOLE ...@@ -1582,6 +1604,32 @@ config SERIAL_RDA_CONSOLE
Say 'Y' here if you wish to use the RDA8810PL UART as the system Say 'Y' here if you wish to use the RDA8810PL UART as the system
console. Only earlycon is implemented currently. console. Only earlycon is implemented currently.
config SERIAL_MILBEAUT_USIO
tristate "Milbeaut USIO/UART serial port support"
depends on ARCH_MILBEAUT || (COMPILE_TEST && OF)
default ARCH_MILBEAUT
select SERIAL_CORE
help
This selects the USIO/UART IP found in Socionext Milbeaut SoCs.
config SERIAL_MILBEAUT_USIO_PORTS
int "Maximum number of CSIO/UART ports (1-8)"
range 1 8
depends on SERIAL_MILBEAUT_USIO
default "4"
config SERIAL_MILBEAUT_USIO_CONSOLE
bool "Support for console on MILBEAUT USIO/UART serial port"
depends on SERIAL_MILBEAUT_USIO=y
default y
select SERIAL_CORE_CONSOLE
select SERIAL_EARLYCON
help
Say 'Y' here if you wish to use a USIO/UART of Socionext Milbeaut
SoCs as the system console (the system console is the device which
receives all kernel messages and warnings and which allows logins in
single user mode).
endmenu endmenu
config SERIAL_MCTRL_GPIO config SERIAL_MCTRL_GPIO
......
...@@ -92,6 +92,8 @@ obj-$(CONFIG_SERIAL_PIC32) += pic32_uart.o ...@@ -92,6 +92,8 @@ obj-$(CONFIG_SERIAL_PIC32) += pic32_uart.o
obj-$(CONFIG_SERIAL_MPS2_UART) += mps2-uart.o obj-$(CONFIG_SERIAL_MPS2_UART) += mps2-uart.o
obj-$(CONFIG_SERIAL_OWL) += owl-uart.o obj-$(CONFIG_SERIAL_OWL) += owl-uart.o
obj-$(CONFIG_SERIAL_RDA) += rda-uart.o obj-$(CONFIG_SERIAL_RDA) += rda-uart.o
obj-$(CONFIG_SERIAL_MILBEAUT_USIO) += milbeaut_usio.o
obj-$(CONFIG_SERIAL_SIFIVE) += sifive.o
# GPIOLIB helpers for modem control lines # GPIOLIB helpers for modem control lines
obj-$(CONFIG_SERIAL_MCTRL_GPIO) += serial_mctrl_gpio.o obj-$(CONFIG_SERIAL_MCTRL_GPIO) += serial_mctrl_gpio.o
......
# SPDX-License-Identifier: GPL-2.0
# #
# Makefile for the Motorola 8xx FEC ethernet controller # Makefile for the Motorola 8xx FEC ethernet controller
# #
......
# SPDX-License-Identifier: GPL-2.0
# #
# Makefile for Jasmine adapter # Makefile for Jasmine adapter
# #
......
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2018 Socionext Inc.
*/
#if defined(CONFIG_SERIAL_MILBEAUT_USIO_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
#define SUPPORT_SYSRQ
#endif
#include <linux/clk.h>
#include <linux/console.h>
#include <linux/module.h>
#include <linux/of_irq.h>
#include <linux/platform_device.h>
#include <linux/serial_core.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#define USIO_NAME "mlb-usio-uart"
#define USIO_UART_DEV_NAME "ttyUSI"
static struct uart_port mlb_usio_ports[CONFIG_SERIAL_MILBEAUT_USIO_PORTS];
#define RX 0
#define TX 1
static int mlb_usio_irq[CONFIG_SERIAL_MILBEAUT_USIO_PORTS][2];
#define MLB_USIO_REG_SMR 0
#define MLB_USIO_REG_SCR 1
#define MLB_USIO_REG_ESCR 2
#define MLB_USIO_REG_SSR 3
#define MLB_USIO_REG_DR 4
#define MLB_USIO_REG_BGR 6
#define MLB_USIO_REG_FCR 12
#define MLB_USIO_REG_FBYTE 14
#define MLB_USIO_SMR_SOE BIT(0)
#define MLB_USIO_SMR_SBL BIT(3)
#define MLB_USIO_SCR_TXE BIT(0)
#define MLB_USIO_SCR_RXE BIT(1)
#define MLB_USIO_SCR_TBIE BIT(2)
#define MLB_USIO_SCR_TIE BIT(3)
#define MLB_USIO_SCR_RIE BIT(4)
#define MLB_USIO_SCR_UPCL BIT(7)
#define MLB_USIO_ESCR_L_8BIT 0
#define MLB_USIO_ESCR_L_5BIT 1
#define MLB_USIO_ESCR_L_6BIT 2
#define MLB_USIO_ESCR_L_7BIT 3
#define MLB_USIO_ESCR_P BIT(3)
#define MLB_USIO_ESCR_PEN BIT(4)
#define MLB_USIO_ESCR_FLWEN BIT(7)
#define MLB_USIO_SSR_TBI BIT(0)
#define MLB_USIO_SSR_TDRE BIT(1)
#define MLB_USIO_SSR_RDRF BIT(2)
#define MLB_USIO_SSR_ORE BIT(3)
#define MLB_USIO_SSR_FRE BIT(4)
#define MLB_USIO_SSR_PE BIT(5)
#define MLB_USIO_SSR_REC BIT(7)
#define MLB_USIO_SSR_BRK BIT(8)
#define MLB_USIO_FCR_FE1 BIT(0)
#define MLB_USIO_FCR_FE2 BIT(1)
#define MLB_USIO_FCR_FCL1 BIT(2)
#define MLB_USIO_FCR_FCL2 BIT(3)
#define MLB_USIO_FCR_FSET BIT(4)
#define MLB_USIO_FCR_FTIE BIT(9)
#define MLB_USIO_FCR_FDRQ BIT(10)
#define MLB_USIO_FCR_FRIIE BIT(11)
static void mlb_usio_stop_tx(struct uart_port *port)
{
writew(readw(port->membase + MLB_USIO_REG_FCR) & ~MLB_USIO_FCR_FTIE,
port->membase + MLB_USIO_REG_FCR);
writeb(readb(port->membase + MLB_USIO_REG_SCR) & ~MLB_USIO_SCR_TBIE,
port->membase + MLB_USIO_REG_SCR);
}
static void mlb_usio_tx_chars(struct uart_port *port)
{
struct circ_buf *xmit = &port->state->xmit;
int count;
writew(readw(port->membase + MLB_USIO_REG_FCR) & ~MLB_USIO_FCR_FTIE,
port->membase + MLB_USIO_REG_FCR);
writeb(readb(port->membase + MLB_USIO_REG_SCR) &
~(MLB_USIO_SCR_TIE | MLB_USIO_SCR_TBIE),
port->membase + MLB_USIO_REG_SCR);
if (port->x_char) {
writew(port->x_char, port->membase + MLB_USIO_REG_DR);
port->icount.tx++;
port->x_char = 0;
return;
}
if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
mlb_usio_stop_tx(port);
return;
}
count = port->fifosize -
(readw(port->membase + MLB_USIO_REG_FBYTE) & 0xff);
do {
writew(xmit->buf[xmit->tail], port->membase + MLB_USIO_REG_DR);
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
port->icount.tx++;
if (uart_circ_empty(xmit))
break;
} while (--count > 0);
writew(readw(port->membase + MLB_USIO_REG_FCR) & ~MLB_USIO_FCR_FDRQ,
port->membase + MLB_USIO_REG_FCR);
writeb(readb(port->membase + MLB_USIO_REG_SCR) | MLB_USIO_SCR_TBIE,
port->membase + MLB_USIO_REG_SCR);
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(port);
if (uart_circ_empty(xmit))
mlb_usio_stop_tx(port);
}
static void mlb_usio_start_tx(struct uart_port *port)
{
u16 fcr = readw(port->membase + MLB_USIO_REG_FCR);
writew(fcr | MLB_USIO_FCR_FTIE, port->membase + MLB_USIO_REG_FCR);
if (!(fcr & MLB_USIO_FCR_FDRQ))
return;
writeb(readb(port->membase + MLB_USIO_REG_SCR) | MLB_USIO_SCR_TBIE,
port->membase + MLB_USIO_REG_SCR);
if (readb(port->membase + MLB_USIO_REG_SSR) & MLB_USIO_SSR_TBI)
mlb_usio_tx_chars(port);
}
static void mlb_usio_stop_rx(struct uart_port *port)
{
writeb(readb(port->membase + MLB_USIO_REG_SCR) & ~MLB_USIO_SCR_RIE,
port->membase + MLB_USIO_REG_SCR);
}
static void mlb_usio_enable_ms(struct uart_port *port)
{
writeb(readb(port->membase + MLB_USIO_REG_SCR) |
MLB_USIO_SCR_RIE | MLB_USIO_SCR_RXE,
port->membase + MLB_USIO_REG_SCR);
}
static void mlb_usio_rx_chars(struct uart_port *port)
{
struct tty_port *ttyport = &port->state->port;
unsigned long flag = 0;
char ch = 0;
u8 status;
int max_count = 2;
while (max_count--) {
status = readb(port->membase + MLB_USIO_REG_SSR);
if (!(status & MLB_USIO_SSR_RDRF))
break;
if (!(status & (MLB_USIO_SSR_ORE | MLB_USIO_SSR_FRE |
MLB_USIO_SSR_PE))) {
ch = readw(port->membase + MLB_USIO_REG_DR);
flag = TTY_NORMAL;
port->icount.rx++;
if (uart_handle_sysrq_char(port, ch))
continue;
uart_insert_char(port, status, MLB_USIO_SSR_ORE,
ch, flag);
continue;
}
if (status & MLB_USIO_SSR_PE)
port->icount.parity++;
if (status & MLB_USIO_SSR_ORE)
port->icount.overrun++;
status &= port->read_status_mask;
if (status & MLB_USIO_SSR_BRK) {
flag = TTY_BREAK;
ch = 0;
} else
if (status & MLB_USIO_SSR_PE) {
flag = TTY_PARITY;
ch = 0;
} else
if (status & MLB_USIO_SSR_FRE) {
flag = TTY_FRAME;
ch = 0;
}
if (flag)
uart_insert_char(port, status, MLB_USIO_SSR_ORE,
ch, flag);
writeb(readb(port->membase + MLB_USIO_REG_SSR) |
MLB_USIO_SSR_REC,
port->membase + MLB_USIO_REG_SSR);
max_count = readw(port->membase + MLB_USIO_REG_FBYTE) >> 8;
writew(readw(port->membase + MLB_USIO_REG_FCR) |
MLB_USIO_FCR_FE2 | MLB_USIO_FCR_FRIIE,
port->membase + MLB_USIO_REG_FCR);
}
tty_flip_buffer_push(ttyport);
}
static irqreturn_t mlb_usio_rx_irq(int irq, void *dev_id)
{
struct uart_port *port = dev_id;
spin_lock(&port->lock);
mlb_usio_rx_chars(port);
spin_unlock(&port->lock);
return IRQ_HANDLED;
}
static irqreturn_t mlb_usio_tx_irq(int irq, void *dev_id)
{
struct uart_port *port = dev_id;
spin_lock(&port->lock);
if (readb(port->membase + MLB_USIO_REG_SSR) & MLB_USIO_SSR_TBI)
mlb_usio_tx_chars(port);
spin_unlock(&port->lock);
return IRQ_HANDLED;
}
static unsigned int mlb_usio_tx_empty(struct uart_port *port)
{
return (readb(port->membase + MLB_USIO_REG_SSR) & MLB_USIO_SSR_TBI) ?
TIOCSER_TEMT : 0;
}
static void mlb_usio_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
}
static unsigned int mlb_usio_get_mctrl(struct uart_port *port)
{
return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS;
}
static void mlb_usio_break_ctl(struct uart_port *port, int break_state)
{
}
static int mlb_usio_startup(struct uart_port *port)
{
const char *portname = to_platform_device(port->dev)->name;
unsigned long flags;
int ret, index = port->line;
unsigned char escr;
ret = request_irq(mlb_usio_irq[index][RX], mlb_usio_rx_irq,
0, portname, port);
if (ret)
return ret;
ret = request_irq(mlb_usio_irq[index][TX], mlb_usio_tx_irq,
0, portname, port);
if (ret) {
free_irq(mlb_usio_irq[index][RX], port);
return ret;
}
escr = readb(port->membase + MLB_USIO_REG_ESCR);
if (of_property_read_bool(port->dev->of_node, "auto-flow-control"))
escr |= MLB_USIO_ESCR_FLWEN;
spin_lock_irqsave(&port->lock, flags);
writeb(0, port->membase + MLB_USIO_REG_SCR);
writeb(escr, port->membase + MLB_USIO_REG_ESCR);
writeb(MLB_USIO_SCR_UPCL, port->membase + MLB_USIO_REG_SCR);
writeb(MLB_USIO_SSR_REC, port->membase + MLB_USIO_REG_SSR);
writew(0, port->membase + MLB_USIO_REG_FCR);
writew(MLB_USIO_FCR_FCL1 | MLB_USIO_FCR_FCL2,
port->membase + MLB_USIO_REG_FCR);
writew(MLB_USIO_FCR_FE1 | MLB_USIO_FCR_FE2 | MLB_USIO_FCR_FRIIE,
port->membase + MLB_USIO_REG_FCR);
writew(0, port->membase + MLB_USIO_REG_FBYTE);
writew(BIT(12), port->membase + MLB_USIO_REG_FBYTE);
writeb(MLB_USIO_SCR_TXE | MLB_USIO_SCR_RIE | MLB_USIO_SCR_TBIE |
MLB_USIO_SCR_RXE, port->membase + MLB_USIO_REG_SCR);
spin_unlock_irqrestore(&port->lock, flags);
return 0;
}
static void mlb_usio_shutdown(struct uart_port *port)
{
int index = port->line;
free_irq(mlb_usio_irq[index][RX], port);
free_irq(mlb_usio_irq[index][TX], port);
}
static void mlb_usio_set_termios(struct uart_port *port,
struct ktermios *termios, struct ktermios *old)
{
unsigned int escr, smr = MLB_USIO_SMR_SOE;
unsigned long flags, baud, quot;
switch (termios->c_cflag & CSIZE) {
case CS5:
escr = MLB_USIO_ESCR_L_5BIT;
break;
case CS6:
escr = MLB_USIO_ESCR_L_6BIT;
break;
case CS7:
escr = MLB_USIO_ESCR_L_7BIT;
break;
case CS8:
default:
escr = MLB_USIO_ESCR_L_8BIT;
break;
}
if (termios->c_cflag & CSTOPB)
smr |= MLB_USIO_SMR_SBL;
if (termios->c_cflag & PARENB) {
escr |= MLB_USIO_ESCR_PEN;
if (termios->c_cflag & PARODD)
escr |= MLB_USIO_ESCR_P;
}
/* Set hard flow control */
if (of_property_read_bool(port->dev->of_node, "auto-flow-control") ||
(termios->c_cflag & CRTSCTS))
escr |= MLB_USIO_ESCR_FLWEN;
baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk);
if (baud > 1)
quot = port->uartclk / baud - 1;
else
quot = 0;
spin_lock_irqsave(&port->lock, flags);
uart_update_timeout(port, termios->c_cflag, baud);
port->read_status_mask = MLB_USIO_SSR_ORE | MLB_USIO_SSR_RDRF |
MLB_USIO_SSR_TDRE;
if (termios->c_iflag & INPCK)
port->read_status_mask |= MLB_USIO_SSR_FRE | MLB_USIO_SSR_PE;
port->ignore_status_mask = 0;
if (termios->c_iflag & IGNPAR)
port->ignore_status_mask |= MLB_USIO_SSR_FRE | MLB_USIO_SSR_PE;
if ((termios->c_iflag & IGNBRK) && (termios->c_iflag & IGNPAR))
port->ignore_status_mask |= MLB_USIO_SSR_ORE;
if ((termios->c_cflag & CREAD) == 0)
port->ignore_status_mask |= MLB_USIO_SSR_RDRF;
writeb(0, port->membase + MLB_USIO_REG_SCR);
writeb(MLB_USIO_SCR_UPCL, port->membase + MLB_USIO_REG_SCR);
writeb(MLB_USIO_SSR_REC, port->membase + MLB_USIO_REG_SSR);
writew(0, port->membase + MLB_USIO_REG_FCR);
writeb(smr, port->membase + MLB_USIO_REG_SMR);
writeb(escr, port->membase + MLB_USIO_REG_ESCR);
writew(quot, port->membase + MLB_USIO_REG_BGR);
writew(0, port->membase + MLB_USIO_REG_FCR);
writew(MLB_USIO_FCR_FCL1 | MLB_USIO_FCR_FCL2 | MLB_USIO_FCR_FE1 |
MLB_USIO_FCR_FE2 | MLB_USIO_FCR_FRIIE,
port->membase + MLB_USIO_REG_FCR);
writew(0, port->membase + MLB_USIO_REG_FBYTE);
writew(BIT(12), port->membase + MLB_USIO_REG_FBYTE);
writeb(MLB_USIO_SCR_RIE | MLB_USIO_SCR_RXE | MLB_USIO_SCR_TBIE |
MLB_USIO_SCR_TXE, port->membase + MLB_USIO_REG_SCR);
spin_unlock_irqrestore(&port->lock, flags);
}
static const char *mlb_usio_type(struct uart_port *port)
{
return ((port->type == PORT_MLB_USIO) ? USIO_NAME : NULL);
}
static void mlb_usio_config_port(struct uart_port *port, int flags)
{
if (flags & UART_CONFIG_TYPE)
port->type = PORT_MLB_USIO;
}
static const struct uart_ops mlb_usio_ops = {
.tx_empty = mlb_usio_tx_empty,
.set_mctrl = mlb_usio_set_mctrl,
.get_mctrl = mlb_usio_get_mctrl,
.stop_tx = mlb_usio_stop_tx,
.start_tx = mlb_usio_start_tx,
.stop_rx = mlb_usio_stop_rx,
.enable_ms = mlb_usio_enable_ms,
.break_ctl = mlb_usio_break_ctl,
.startup = mlb_usio_startup,
.shutdown = mlb_usio_shutdown,
.set_termios = mlb_usio_set_termios,
.type = mlb_usio_type,
.config_port = mlb_usio_config_port,
};
#ifdef CONFIG_SERIAL_MILBEAUT_USIO_CONSOLE
static void mlb_usio_console_putchar(struct uart_port *port, int c)
{
while (!(readb(port->membase + MLB_USIO_REG_SSR) & MLB_USIO_SSR_TDRE))
cpu_relax();
writew(c, port->membase + MLB_USIO_REG_DR);
}
static void mlb_usio_console_write(struct console *co, const char *s,
unsigned int count)
{
struct uart_port *port = &mlb_usio_ports[co->index];
uart_console_write(port, s, count, mlb_usio_console_putchar);
}
static int __init mlb_usio_console_setup(struct console *co, char *options)
{
struct uart_port *port;
int baud = 115200;
int parity = 'n';
int flow = 'n';
int bits = 8;
if (co->index >= CONFIG_SERIAL_MILBEAUT_USIO_PORTS)
return -ENODEV;
port = &mlb_usio_ports[co->index];
if (!port->membase)
return -ENODEV;
if (options)
uart_parse_options(options, &baud, &parity, &bits, &flow);
if (of_property_read_bool(port->dev->of_node, "auto-flow-control"))
flow = 'r';
return uart_set_options(port, co, baud, parity, bits, flow);
}
static struct uart_driver mlb_usio_uart_driver;
static struct console mlb_usio_console = {
.name = USIO_UART_DEV_NAME,
.write = mlb_usio_console_write,
.device = uart_console_device,
.setup = mlb_usio_console_setup,
.flags = CON_PRINTBUFFER,
.index = -1,
.data = &mlb_usio_uart_driver,
};
static int __init mlb_usio_console_init(void)
{
register_console(&mlb_usio_console);
return 0;
}
console_initcall(mlb_usio_console_init);
static void mlb_usio_early_console_write(struct console *co, const char *s,
u_int count)
{
struct earlycon_device *dev = co->data;
uart_console_write(&dev->port, s, count, mlb_usio_console_putchar);
}
static int __init mlb_usio_early_console_setup(struct earlycon_device *device,
const char *opt)
{
if (!device->port.membase)
return -ENODEV;
device->con->write = mlb_usio_early_console_write;
return 0;
}
OF_EARLYCON_DECLARE(mlb_usio, "socionext,milbeaut-usio-uart",
mlb_usio_early_console_setup);
#define USIO_CONSOLE (&mlb_usio_console)
#else
#define USIO_CONSOLE NULL
#endif
static struct uart_driver mlb_usio_uart_driver = {
.owner = THIS_MODULE,
.driver_name = USIO_NAME,
.dev_name = USIO_UART_DEV_NAME,
.cons = USIO_CONSOLE,
.nr = CONFIG_SERIAL_MILBEAUT_USIO_PORTS,
};
static int mlb_usio_probe(struct platform_device *pdev)
{
struct clk *clk = devm_clk_get(&pdev->dev, NULL);
struct uart_port *port;
struct resource *res;
int index = 0;
int ret;
if (IS_ERR(clk)) {
dev_err(&pdev->dev, "Missing clock\n");
return PTR_ERR(clk);
}
ret = clk_prepare_enable(clk);
if (ret) {
dev_err(&pdev->dev, "Clock enable failed: %d\n", ret);
return ret;
}
of_property_read_u32(pdev->dev.of_node, "index", &index);
port = &mlb_usio_ports[index];
port->private_data = (void *)clk;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res == NULL) {
dev_err(&pdev->dev, "Missing regs\n");
ret = -ENODEV;
goto failed;
}
port->membase = devm_ioremap(&pdev->dev, res->start,
resource_size(res));
ret = platform_get_irq_byname(pdev, "rx");
mlb_usio_irq[index][RX] = ret;
ret = platform_get_irq_byname(pdev, "tx");
mlb_usio_irq[index][TX] = ret;
port->irq = mlb_usio_irq[index][RX];
port->uartclk = clk_get_rate(clk);
port->fifosize = 128;
port->iotype = UPIO_MEM32;
port->flags = UPF_BOOT_AUTOCONF | UPF_SPD_VHI;
port->line = index;
port->ops = &mlb_usio_ops;
port->dev = &pdev->dev;
ret = uart_add_one_port(&mlb_usio_uart_driver, port);
if (ret) {
dev_err(&pdev->dev, "Adding port failed: %d\n", ret);
goto failed;
}
return 0;
failed:
clk_disable_unprepare(clk);
return ret;
}
static int mlb_usio_remove(struct platform_device *pdev)
{
struct uart_port *port = &mlb_usio_ports[pdev->id];
struct clk *clk = port->private_data;
uart_remove_one_port(&mlb_usio_uart_driver, port);
clk_disable_unprepare(clk);
return 0;
}
static const struct of_device_id mlb_usio_dt_ids[] = {
{ .compatible = "socionext,milbeaut-usio-uart" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, mlb_usio_dt_ids);
static struct platform_driver mlb_usio_driver = {
.probe = mlb_usio_probe,
.remove = mlb_usio_remove,
.driver = {
.name = USIO_NAME,
.of_match_table = mlb_usio_dt_ids,
},
};
static int __init mlb_usio_init(void)
{
int ret = uart_register_driver(&mlb_usio_uart_driver);
if (ret) {
pr_err("%s: uart registration failed: %d\n", __func__, ret);
return ret;
}
ret = platform_driver_register(&mlb_usio_driver);
if (ret) {
uart_unregister_driver(&mlb_usio_uart_driver);
pr_err("%s: drv registration failed: %d\n", __func__, ret);
return ret;
}
return 0;
}
static void __exit mlb_usio_exit(void)
{
platform_driver_unregister(&mlb_usio_driver);
uart_unregister_driver(&mlb_usio_uart_driver);
}
module_init(mlb_usio_init);
module_exit(mlb_usio_exit);
MODULE_AUTHOR("SOCIONEXT");
MODULE_DESCRIPTION("MILBEAUT_USIO/UART Driver");
MODULE_LICENSE("GPL");
...@@ -14,9 +14,9 @@ ...@@ -14,9 +14,9 @@
#include <linux/device.h> #include <linux/device.h>
#include <linux/gpio/driver.h> #include <linux/gpio/driver.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of.h> #include <linux/property.h>
#include <linux/of_device.h>
#include <linux/regmap.h> #include <linux/regmap.h>
#include <linux/serial_core.h> #include <linux/serial_core.h>
#include <linux/serial.h> #include <linux/serial.h>
...@@ -1179,7 +1179,8 @@ static int sc16is7xx_probe(struct device *dev, ...@@ -1179,7 +1179,8 @@ static int sc16is7xx_probe(struct device *dev,
struct regmap *regmap, int irq, unsigned long flags) struct regmap *regmap, int irq, unsigned long flags)
{ {
struct sched_param sched_param = { .sched_priority = MAX_RT_PRIO / 2 }; struct sched_param sched_param = { .sched_priority = MAX_RT_PRIO / 2 };
unsigned long freq, *pfreq = dev_get_platdata(dev); unsigned long freq = 0, *pfreq = dev_get_platdata(dev);
u32 uartclk = 0;
int i, ret; int i, ret;
struct sc16is7xx_port *s; struct sc16is7xx_port *s;
...@@ -1193,10 +1194,17 @@ static int sc16is7xx_probe(struct device *dev, ...@@ -1193,10 +1194,17 @@ static int sc16is7xx_probe(struct device *dev,
return -ENOMEM; return -ENOMEM;
} }
/* Always ask for fixed clock rate from a property. */
device_property_read_u32(dev, "clock-frequency", &uartclk);
s->clk = devm_clk_get(dev, NULL); s->clk = devm_clk_get(dev, NULL);
if (IS_ERR(s->clk)) { if (IS_ERR(s->clk)) {
if (uartclk)
freq = uartclk;
if (pfreq) if (pfreq)
freq = *pfreq; freq = *pfreq;
if (freq)
dev_dbg(dev, "Clock frequency: %luHz\n", freq);
else else
return PTR_ERR(s->clk); return PTR_ERR(s->clk);
} else { } else {
...@@ -1384,13 +1392,9 @@ static int sc16is7xx_spi_probe(struct spi_device *spi) ...@@ -1384,13 +1392,9 @@ static int sc16is7xx_spi_probe(struct spi_device *spi)
return ret; return ret;
if (spi->dev.of_node) { if (spi->dev.of_node) {
const struct of_device_id *of_id = devtype = device_get_match_data(&spi->dev);
of_match_device(sc16is7xx_dt_ids, &spi->dev); if (!devtype)
if (!of_id)
return -ENODEV; return -ENODEV;
devtype = (struct sc16is7xx_devtype *)of_id->data;
} else { } else {
const struct spi_device_id *id_entry = spi_get_device_id(spi); const struct spi_device_id *id_entry = spi_get_device_id(spi);
...@@ -1426,7 +1430,7 @@ MODULE_DEVICE_TABLE(spi, sc16is7xx_spi_id_table); ...@@ -1426,7 +1430,7 @@ MODULE_DEVICE_TABLE(spi, sc16is7xx_spi_id_table);
static struct spi_driver sc16is7xx_spi_uart_driver = { static struct spi_driver sc16is7xx_spi_uart_driver = {
.driver = { .driver = {
.name = SC16IS7XX_NAME, .name = SC16IS7XX_NAME,
.of_match_table = of_match_ptr(sc16is7xx_dt_ids), .of_match_table = sc16is7xx_dt_ids,
}, },
.probe = sc16is7xx_spi_probe, .probe = sc16is7xx_spi_probe,
.remove = sc16is7xx_spi_remove, .remove = sc16is7xx_spi_remove,
...@@ -1445,13 +1449,9 @@ static int sc16is7xx_i2c_probe(struct i2c_client *i2c, ...@@ -1445,13 +1449,9 @@ static int sc16is7xx_i2c_probe(struct i2c_client *i2c,
struct regmap *regmap; struct regmap *regmap;
if (i2c->dev.of_node) { if (i2c->dev.of_node) {
const struct of_device_id *of_id = devtype = device_get_match_data(&i2c->dev);
of_match_device(sc16is7xx_dt_ids, &i2c->dev); if (!devtype)
if (!of_id)
return -ENODEV; return -ENODEV;
devtype = (struct sc16is7xx_devtype *)of_id->data;
} else { } else {
devtype = (struct sc16is7xx_devtype *)id->driver_data; devtype = (struct sc16is7xx_devtype *)id->driver_data;
flags = IRQF_TRIGGER_FALLING; flags = IRQF_TRIGGER_FALLING;
...@@ -1484,7 +1484,7 @@ MODULE_DEVICE_TABLE(i2c, sc16is7xx_i2c_id_table); ...@@ -1484,7 +1484,7 @@ MODULE_DEVICE_TABLE(i2c, sc16is7xx_i2c_id_table);
static struct i2c_driver sc16is7xx_i2c_uart_driver = { static struct i2c_driver sc16is7xx_i2c_uart_driver = {
.driver = { .driver = {
.name = SC16IS7XX_NAME, .name = SC16IS7XX_NAME,
.of_match_table = of_match_ptr(sc16is7xx_dt_ids), .of_match_table = sc16is7xx_dt_ids,
}, },
.probe = sc16is7xx_i2c_probe, .probe = sc16is7xx_i2c_probe,
.remove = sc16is7xx_i2c_remove, .remove = sc16is7xx_i2c_remove,
......
...@@ -130,9 +130,6 @@ static void uart_start(struct tty_struct *tty) ...@@ -130,9 +130,6 @@ static void uart_start(struct tty_struct *tty)
struct uart_port *port; struct uart_port *port;
unsigned long flags; unsigned long flags;
if (!state)
return;
port = uart_port_lock(state, flags); port = uart_port_lock(state, flags);
__uart_start(tty); __uart_start(tty);
uart_port_unlock(port, flags); uart_port_unlock(port, flags);
...@@ -730,9 +727,6 @@ static void uart_unthrottle(struct tty_struct *tty) ...@@ -730,9 +727,6 @@ static void uart_unthrottle(struct tty_struct *tty)
upstat_t mask = UPSTAT_SYNC_FIFO; upstat_t mask = UPSTAT_SYNC_FIFO;
struct uart_port *port; struct uart_port *port;
if (!state)
return;
port = uart_port_ref(state); port = uart_port_ref(state);
if (!port) if (!port)
return; return;
...@@ -1514,7 +1508,7 @@ static void uart_set_termios(struct tty_struct *tty, ...@@ -1514,7 +1508,7 @@ static void uart_set_termios(struct tty_struct *tty,
} }
uart_change_speed(tty, state, old_termios); uart_change_speed(tty, state, old_termios);
/* reload cflag from termios; port driver may have overriden flags */ /* reload cflag from termios; port driver may have overridden flags */
cflag = tty->termios.c_cflag; cflag = tty->termios.c_cflag;
/* Handle transition to B0 status */ /* Handle transition to B0 status */
...@@ -1747,6 +1741,16 @@ static void uart_dtr_rts(struct tty_port *port, int raise) ...@@ -1747,6 +1741,16 @@ static void uart_dtr_rts(struct tty_port *port, int raise)
uart_port_deref(uport); uart_port_deref(uport);
} }
static int uart_install(struct tty_driver *driver, struct tty_struct *tty)
{
struct uart_driver *drv = driver->driver_state;
struct uart_state *state = drv->state + tty->index;
tty->driver_data = state;
return tty_standard_install(driver, tty);
}
/* /*
* Calls to uart_open are serialised by the tty_lock in * Calls to uart_open are serialised by the tty_lock in
* drivers/tty/tty_io.c:tty_open() * drivers/tty/tty_io.c:tty_open()
...@@ -1759,11 +1763,8 @@ static void uart_dtr_rts(struct tty_port *port, int raise) ...@@ -1759,11 +1763,8 @@ static void uart_dtr_rts(struct tty_port *port, int raise)
*/ */
static int uart_open(struct tty_struct *tty, struct file *filp) static int uart_open(struct tty_struct *tty, struct file *filp)
{ {
struct uart_driver *drv = tty->driver->driver_state; struct uart_state *state = tty->driver_data;
int retval, line = tty->index; int retval;
struct uart_state *state = drv->state + line;
tty->driver_data = state;
retval = tty_port_open(&state->port, tty, filp); retval = tty_port_open(&state->port, tty, filp);
if (retval > 0) if (retval > 0)
...@@ -2448,6 +2449,7 @@ static void uart_poll_put_char(struct tty_driver *driver, int line, char ch) ...@@ -2448,6 +2449,7 @@ static void uart_poll_put_char(struct tty_driver *driver, int line, char ch)
#endif #endif
static const struct tty_operations uart_ops = { static const struct tty_operations uart_ops = {
.install = uart_install,
.open = uart_open, .open = uart_open,
.close = uart_close, .close = uart_close,
.write = uart_write, .write = uart_write,
...@@ -2505,7 +2507,7 @@ static const struct tty_port_operations uart_port_ops = { ...@@ -2505,7 +2507,7 @@ static const struct tty_port_operations uart_port_ops = {
int uart_register_driver(struct uart_driver *drv) int uart_register_driver(struct uart_driver *drv)
{ {
struct tty_driver *normal; struct tty_driver *normal;
int i, retval; int i, retval = -ENOMEM;
BUG_ON(drv->state); BUG_ON(drv->state);
...@@ -2557,7 +2559,7 @@ int uart_register_driver(struct uart_driver *drv) ...@@ -2557,7 +2559,7 @@ int uart_register_driver(struct uart_driver *drv)
out_kfree: out_kfree:
kfree(drv->state); kfree(drv->state);
out: out:
return -ENOMEM; return retval;
} }
/** /**
......
// SPDX-License-Identifier: GPL-2.0+
/*
* SiFive UART driver
* Copyright (C) 2018 Paul Walmsley <paul@pwsan.com>
* Copyright (C) 2018-2019 SiFive
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* Based partially on:
* - drivers/tty/serial/pxa.c
* - drivers/tty/serial/amba-pl011.c
* - drivers/tty/serial/uartlite.c
* - drivers/tty/serial/omap-serial.c
* - drivers/pwm/pwm-sifive.c
*
* See the following sources for further documentation:
* - Chapter 19 "Universal Asynchronous Receiver/Transmitter (UART)" of
* SiFive FE310-G000 v2p3
* - The tree/master/src/main/scala/devices/uart directory of
* https://github.com/sifive/sifive-blocks/
*
* The SiFive UART design is not 8250-compatible. The following common
* features are not supported:
* - Word lengths other than 8 bits
* - Break handling
* - Parity
* - Flow control
* - Modem signals (DSR, RI, etc.)
* On the other hand, the design is free from the baggage of the 8250
* programming model.
*/
#include <linux/clk.h>
#include <linux/console.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/platform_device.h>
#include <linux/serial_core.h>
#include <linux/serial_reg.h>
#include <linux/slab.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
/*
* Register offsets
*/
/* TXDATA */
#define SIFIVE_SERIAL_TXDATA_OFFS 0x0
#define SIFIVE_SERIAL_TXDATA_FULL_SHIFT 31
#define SIFIVE_SERIAL_TXDATA_FULL_MASK (1 << SIFIVE_SERIAL_TXDATA_FULL_SHIFT)
#define SIFIVE_SERIAL_TXDATA_DATA_SHIFT 0
#define SIFIVE_SERIAL_TXDATA_DATA_MASK (0xff << SIFIVE_SERIAL_TXDATA_DATA_SHIFT)
/* RXDATA */
#define SIFIVE_SERIAL_RXDATA_OFFS 0x4
#define SIFIVE_SERIAL_RXDATA_EMPTY_SHIFT 31
#define SIFIVE_SERIAL_RXDATA_EMPTY_MASK (1 << SIFIVE_SERIAL_RXDATA_EMPTY_SHIFT)
#define SIFIVE_SERIAL_RXDATA_DATA_SHIFT 0
#define SIFIVE_SERIAL_RXDATA_DATA_MASK (0xff << SIFIVE_SERIAL_RXDATA_DATA_SHIFT)
/* TXCTRL */
#define SIFIVE_SERIAL_TXCTRL_OFFS 0x8
#define SIFIVE_SERIAL_TXCTRL_TXCNT_SHIFT 16
#define SIFIVE_SERIAL_TXCTRL_TXCNT_MASK (0x7 << SIFIVE_SERIAL_TXCTRL_TXCNT_SHIFT)
#define SIFIVE_SERIAL_TXCTRL_NSTOP_SHIFT 1
#define SIFIVE_SERIAL_TXCTRL_NSTOP_MASK (1 << SIFIVE_SERIAL_TXCTRL_NSTOP_SHIFT)
#define SIFIVE_SERIAL_TXCTRL_TXEN_SHIFT 0
#define SIFIVE_SERIAL_TXCTRL_TXEN_MASK (1 << SIFIVE_SERIAL_TXCTRL_TXEN_SHIFT)
/* RXCTRL */
#define SIFIVE_SERIAL_RXCTRL_OFFS 0xC
#define SIFIVE_SERIAL_RXCTRL_RXCNT_SHIFT 16
#define SIFIVE_SERIAL_RXCTRL_RXCNT_MASK (0x7 << SIFIVE_SERIAL_TXCTRL_TXCNT_SHIFT)
#define SIFIVE_SERIAL_RXCTRL_RXEN_SHIFT 0
#define SIFIVE_SERIAL_RXCTRL_RXEN_MASK (1 << SIFIVE_SERIAL_RXCTRL_RXEN_SHIFT)
/* IE */
#define SIFIVE_SERIAL_IE_OFFS 0x10
#define SIFIVE_SERIAL_IE_RXWM_SHIFT 1
#define SIFIVE_SERIAL_IE_RXWM_MASK (1 << SIFIVE_SERIAL_IE_RXWM_SHIFT)
#define SIFIVE_SERIAL_IE_TXWM_SHIFT 0
#define SIFIVE_SERIAL_IE_TXWM_MASK (1 << SIFIVE_SERIAL_IE_TXWM_SHIFT)
/* IP */
#define SIFIVE_SERIAL_IP_OFFS 0x14
#define SIFIVE_SERIAL_IP_RXWM_SHIFT 1
#define SIFIVE_SERIAL_IP_RXWM_MASK (1 << SIFIVE_SERIAL_IP_RXWM_SHIFT)
#define SIFIVE_SERIAL_IP_TXWM_SHIFT 0
#define SIFIVE_SERIAL_IP_TXWM_MASK (1 << SIFIVE_SERIAL_IP_TXWM_SHIFT)
/* DIV */
#define SIFIVE_SERIAL_DIV_OFFS 0x18
#define SIFIVE_SERIAL_DIV_DIV_SHIFT 0
#define SIFIVE_SERIAL_DIV_DIV_MASK (0xffff << SIFIVE_SERIAL_IP_DIV_SHIFT)
/*
* Config macros
*/
/*
* SIFIVE_SERIAL_MAX_PORTS: maximum number of UARTs on a device that can
* host a serial console
*/
#define SIFIVE_SERIAL_MAX_PORTS 8
/*
* SIFIVE_DEFAULT_BAUD_RATE: default baud rate that the driver should
* configure itself to use
*/
#define SIFIVE_DEFAULT_BAUD_RATE 115200
/* SIFIVE_SERIAL_NAME: our driver's name that we pass to the operating system */
#define SIFIVE_SERIAL_NAME "sifive-serial"
/* SIFIVE_TTY_PREFIX: tty name prefix for SiFive serial ports */
#define SIFIVE_TTY_PREFIX "ttySIF"
/* SIFIVE_TX_FIFO_DEPTH: depth of the TX FIFO (in bytes) */
#define SIFIVE_TX_FIFO_DEPTH 8
/* SIFIVE_RX_FIFO_DEPTH: depth of the TX FIFO (in bytes) */
#define SIFIVE_RX_FIFO_DEPTH 8
#if (SIFIVE_TX_FIFO_DEPTH != SIFIVE_RX_FIFO_DEPTH)
#error Driver does not support configurations with different TX, RX FIFO sizes
#endif
/*
*
*/
/**
* sifive_serial_port - driver-specific data extension to struct uart_port
* @port: struct uart_port embedded in this struct
* @dev: struct device *
* @ier: shadowed copy of the interrupt enable register
* @clkin_rate: input clock to the UART IP block.
* @baud_rate: UART serial line rate (e.g., 115200 baud)
* @clk_notifier: clock rate change notifier for upstream clock changes
*
* Configuration data specific to this SiFive UART.
*/
struct sifive_serial_port {
struct uart_port port;
struct device *dev;
unsigned char ier;
unsigned long clkin_rate;
unsigned long baud_rate;
struct clk *clk;
struct notifier_block clk_notifier;
};
/*
* Structure container-of macros
*/
#define port_to_sifive_serial_port(p) (container_of((p), \
struct sifive_serial_port, \
port))
#define notifier_to_sifive_serial_port(nb) (container_of((nb), \
struct sifive_serial_port, \
clk_notifier))
/*
* Forward declarations
*/
static void sifive_serial_stop_tx(struct uart_port *port);
/*
* Internal functions
*/
/**
* __ssp_early_writel() - write to a SiFive serial port register (early)
* @port: pointer to a struct uart_port record
* @offs: register address offset from the IP block base address
* @v: value to write to the register
*
* Given a pointer @port to a struct uart_port record, write the value
* @v to the IP block register address offset @offs. This function is
* intended for early console use.
*
* Context: Intended to be used only by the earlyconsole code.
*/
static void __ssp_early_writel(u32 v, u16 offs, struct uart_port *port)
{
writel_relaxed(v, port->membase + offs);
}
/**
* __ssp_early_readl() - read from a SiFive serial port register (early)
* @port: pointer to a struct uart_port record
* @offs: register address offset from the IP block base address
*
* Given a pointer @port to a struct uart_port record, read the
* contents of the IP block register located at offset @offs from the
* IP block base and return it. This function is intended for early
* console use.
*
* Context: Intended to be called only by the earlyconsole code or by
* __ssp_readl() or __ssp_writel() (in this driver)
*
* Returns: the register value read from the UART.
*/
static u32 __ssp_early_readl(struct uart_port *port, u16 offs)
{
return readl_relaxed(port->membase + offs);
}
/**
* __ssp_writel() - write to a SiFive serial port register
* @v: value to write to the register
* @offs: register address offset from the IP block base address
* @ssp: pointer to a struct sifive_serial_port record
*
* Write the value @v to the IP block register located at offset @offs from the
* IP block base, given a pointer @ssp to a struct sifive_serial_port record.
*
* Context: Any context.
*/
static void __ssp_writel(u32 v, u16 offs, struct sifive_serial_port *ssp)
{
__ssp_early_writel(v, offs, &ssp->port);
}
/**
* __ssp_readl() - read from a SiFive serial port register
* @ssp: pointer to a struct sifive_serial_port record
* @offs: register address offset from the IP block base address
*
* Read the contents of the IP block register located at offset @offs from the
* IP block base, given a pointer @ssp to a struct sifive_serial_port record.
*
* Context: Any context.
*
* Returns: the value of the UART register
*/
static u32 __ssp_readl(struct sifive_serial_port *ssp, u16 offs)
{
return __ssp_early_readl(&ssp->port, offs);
}
/**
* sifive_serial_is_txfifo_full() - is the TXFIFO full?
* @ssp: pointer to a struct sifive_serial_port
*
* Read the transmit FIFO "full" bit, returning a non-zero value if the
* TX FIFO is full, or zero if space remains. Intended to be used to prevent
* writes to the TX FIFO when it's full.
*
* Returns: SIFIVE_SERIAL_TXDATA_FULL_MASK (non-zero) if the transmit FIFO
* is full, or 0 if space remains.
*/
static int sifive_serial_is_txfifo_full(struct sifive_serial_port *ssp)
{
return __ssp_readl(ssp, SIFIVE_SERIAL_TXDATA_OFFS) &
SIFIVE_SERIAL_TXDATA_FULL_MASK;
}
/**
* __ssp_transmit_char() - enqueue a byte to transmit onto the TX FIFO
* @ssp: pointer to a struct sifive_serial_port
* @ch: character to transmit
*
* Enqueue a byte @ch onto the transmit FIFO, given a pointer @ssp to the
* struct sifive_serial_port * to transmit on. Caller should first check to
* ensure that the TXFIFO has space; see sifive_serial_is_txfifo_full().
*
* Context: Any context.
*/
static void __ssp_transmit_char(struct sifive_serial_port *ssp, int ch)
{
__ssp_writel(ch, SIFIVE_SERIAL_TXDATA_OFFS, ssp);
}
/**
* __ssp_transmit_chars() - enqueue multiple bytes onto the TX FIFO
* @ssp: pointer to a struct sifive_serial_port
*
* Transfer up to a TX FIFO size's worth of characters from the Linux serial
* transmit buffer to the SiFive UART TX FIFO.
*
* Context: Any context. Expects @ssp->port.lock to be held by caller.
*/
static void __ssp_transmit_chars(struct sifive_serial_port *ssp)
{
struct circ_buf *xmit = &ssp->port.state->xmit;
int count;
if (ssp->port.x_char) {
__ssp_transmit_char(ssp, ssp->port.x_char);
ssp->port.icount.tx++;
ssp->port.x_char = 0;
return;
}
if (uart_circ_empty(xmit) || uart_tx_stopped(&ssp->port)) {
sifive_serial_stop_tx(&ssp->port);
return;
}
count = SIFIVE_TX_FIFO_DEPTH;
do {
__ssp_transmit_char(ssp, xmit->buf[xmit->tail]);
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
ssp->port.icount.tx++;
if (uart_circ_empty(xmit))
break;
} while (--count > 0);
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(&ssp->port);
if (uart_circ_empty(xmit))
sifive_serial_stop_tx(&ssp->port);
}
/**
* __ssp_enable_txwm() - enable transmit watermark interrupts
* @ssp: pointer to a struct sifive_serial_port
*
* Enable interrupt generation when the transmit FIFO watermark is reached
* on the SiFive UART referred to by @ssp.
*/
static void __ssp_enable_txwm(struct sifive_serial_port *ssp)
{
if (ssp->ier & SIFIVE_SERIAL_IE_TXWM_MASK)
return;
ssp->ier |= SIFIVE_SERIAL_IE_TXWM_MASK;
__ssp_writel(ssp->ier, SIFIVE_SERIAL_IE_OFFS, ssp);
}
/**
* __ssp_enable_rxwm() - enable receive watermark interrupts
* @ssp: pointer to a struct sifive_serial_port
*
* Enable interrupt generation when the receive FIFO watermark is reached
* on the SiFive UART referred to by @ssp.
*/
static void __ssp_enable_rxwm(struct sifive_serial_port *ssp)
{
if (ssp->ier & SIFIVE_SERIAL_IE_RXWM_MASK)
return;
ssp->ier |= SIFIVE_SERIAL_IE_RXWM_MASK;
__ssp_writel(ssp->ier, SIFIVE_SERIAL_IE_OFFS, ssp);
}
/**
* __ssp_disable_txwm() - disable transmit watermark interrupts
* @ssp: pointer to a struct sifive_serial_port
*
* Disable interrupt generation when the transmit FIFO watermark is reached
* on the UART referred to by @ssp.
*/
static void __ssp_disable_txwm(struct sifive_serial_port *ssp)
{
if (!(ssp->ier & SIFIVE_SERIAL_IE_TXWM_MASK))
return;
ssp->ier &= ~SIFIVE_SERIAL_IE_TXWM_MASK;
__ssp_writel(ssp->ier, SIFIVE_SERIAL_IE_OFFS, ssp);
}
/**
* __ssp_disable_rxwm() - disable receive watermark interrupts
* @ssp: pointer to a struct sifive_serial_port
*
* Disable interrupt generation when the receive FIFO watermark is reached
* on the UART referred to by @ssp.
*/
static void __ssp_disable_rxwm(struct sifive_serial_port *ssp)
{
if (!(ssp->ier & SIFIVE_SERIAL_IE_RXWM_MASK))
return;
ssp->ier &= ~SIFIVE_SERIAL_IE_RXWM_MASK;
__ssp_writel(ssp->ier, SIFIVE_SERIAL_IE_OFFS, ssp);
}
/**
* __ssp_receive_char() - receive a byte from the UART
* @ssp: pointer to a struct sifive_serial_port
* @is_empty: char pointer to return whether the RX FIFO is empty
*
* Try to read a byte from the SiFive UART RX FIFO, referenced by
* @ssp, and to return it. Also returns the RX FIFO empty bit in
* the char pointed to by @ch. The caller must pass the byte back to the
* Linux serial layer if needed.
*
* Returns: the byte read from the UART RX FIFO.
*/
static char __ssp_receive_char(struct sifive_serial_port *ssp, char *is_empty)
{
u32 v;
u8 ch;
v = __ssp_readl(ssp, SIFIVE_SERIAL_RXDATA_OFFS);
if (!is_empty)
WARN_ON(1);
else
*is_empty = (v & SIFIVE_SERIAL_RXDATA_EMPTY_MASK) >>
SIFIVE_SERIAL_RXDATA_EMPTY_SHIFT;
ch = (v & SIFIVE_SERIAL_RXDATA_DATA_MASK) >>
SIFIVE_SERIAL_RXDATA_DATA_SHIFT;
return ch;
}
/**
* __ssp_receive_chars() - receive multiple bytes from the UART
* @ssp: pointer to a struct sifive_serial_port
*
* Receive up to an RX FIFO's worth of bytes from the SiFive UART referred
* to by @ssp and pass them up to the Linux serial layer.
*
* Context: Expects ssp->port.lock to be held by caller.
*/
static void __ssp_receive_chars(struct sifive_serial_port *ssp)
{
unsigned char ch;
char is_empty;
int c;
for (c = SIFIVE_RX_FIFO_DEPTH; c > 0; --c) {
ch = __ssp_receive_char(ssp, &is_empty);
if (is_empty)
break;
ssp->port.icount.rx++;
uart_insert_char(&ssp->port, 0, 0, ch, TTY_NORMAL);
}
spin_unlock(&ssp->port.lock);
tty_flip_buffer_push(&ssp->port.state->port);
spin_lock(&ssp->port.lock);
}
/**
* __ssp_update_div() - calculate the divisor setting by the line rate
* @ssp: pointer to a struct sifive_serial_port
*
* Calculate the appropriate value of the clock divisor for the UART
* and target line rate referred to by @ssp and write it into the
* hardware.
*/
static void __ssp_update_div(struct sifive_serial_port *ssp)
{
u16 div;
div = DIV_ROUND_UP(ssp->clkin_rate, ssp->baud_rate) - 1;
__ssp_writel(div, SIFIVE_SERIAL_DIV_OFFS, ssp);
}
/**
* __ssp_update_baud_rate() - set the UART "baud rate"
* @ssp: pointer to a struct sifive_serial_port
* @rate: new target bit rate
*
* Calculate the UART divisor value for the target bit rate @rate for the
* SiFive UART described by @ssp and program it into the UART. There may
* be some error between the target bit rate and the actual bit rate implemented
* by the UART due to clock ratio granularity.
*/
static void __ssp_update_baud_rate(struct sifive_serial_port *ssp,
unsigned int rate)
{
if (ssp->baud_rate == rate)
return;
ssp->baud_rate = rate;
__ssp_update_div(ssp);
}
/**
* __ssp_set_stop_bits() - set the number of stop bits
* @ssp: pointer to a struct sifive_serial_port
* @nstop: 1 or 2 (stop bits)
*
* Program the SiFive UART referred to by @ssp to use @nstop stop bits.
*/
static void __ssp_set_stop_bits(struct sifive_serial_port *ssp, char nstop)
{
u32 v;
if (nstop < 1 || nstop > 2) {
WARN_ON(1);
return;
}
v = __ssp_readl(ssp, SIFIVE_SERIAL_TXCTRL_OFFS);
v &= ~SIFIVE_SERIAL_TXCTRL_NSTOP_MASK;
v |= (nstop - 1) << SIFIVE_SERIAL_TXCTRL_NSTOP_SHIFT;
__ssp_writel(v, SIFIVE_SERIAL_TXCTRL_OFFS, ssp);
}
/**
* __ssp_wait_for_xmitr() - wait for an empty slot on the TX FIFO
* @ssp: pointer to a struct sifive_serial_port
*
* Delay while the UART TX FIFO referred to by @ssp is marked as full.
*
* Context: Any context.
*/
static void __maybe_unused __ssp_wait_for_xmitr(struct sifive_serial_port *ssp)
{
while (sifive_serial_is_txfifo_full(ssp))
udelay(1); /* XXX Could probably be more intelligent here */
}
/*
* Linux serial API functions
*/
static void sifive_serial_stop_tx(struct uart_port *port)
{
struct sifive_serial_port *ssp = port_to_sifive_serial_port(port);
__ssp_disable_txwm(ssp);
}
static void sifive_serial_stop_rx(struct uart_port *port)
{
struct sifive_serial_port *ssp = port_to_sifive_serial_port(port);
__ssp_disable_rxwm(ssp);
}
static void sifive_serial_start_tx(struct uart_port *port)
{
struct sifive_serial_port *ssp = port_to_sifive_serial_port(port);
__ssp_enable_txwm(ssp);
}
static irqreturn_t sifive_serial_irq(int irq, void *dev_id)
{
struct sifive_serial_port *ssp = dev_id;
u32 ip;
spin_lock(&ssp->port.lock);
ip = __ssp_readl(ssp, SIFIVE_SERIAL_IP_OFFS);
if (!ip) {
spin_unlock(&ssp->port.lock);
return IRQ_NONE;
}
if (ip & SIFIVE_SERIAL_IP_RXWM_MASK)
__ssp_receive_chars(ssp);
if (ip & SIFIVE_SERIAL_IP_TXWM_MASK)
__ssp_transmit_chars(ssp);
spin_unlock(&ssp->port.lock);
return IRQ_HANDLED;
}
static unsigned int sifive_serial_tx_empty(struct uart_port *port)
{
return TIOCSER_TEMT;
}
static unsigned int sifive_serial_get_mctrl(struct uart_port *port)
{
return TIOCM_CAR | TIOCM_CTS | TIOCM_DSR;
}
static void sifive_serial_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
/* IP block does not support these signals */
}
static void sifive_serial_break_ctl(struct uart_port *port, int break_state)
{
/* IP block does not support sending a break */
}
static int sifive_serial_startup(struct uart_port *port)
{
struct sifive_serial_port *ssp = port_to_sifive_serial_port(port);
__ssp_enable_rxwm(ssp);
return 0;
}
static void sifive_serial_shutdown(struct uart_port *port)
{
struct sifive_serial_port *ssp = port_to_sifive_serial_port(port);
__ssp_disable_rxwm(ssp);
__ssp_disable_txwm(ssp);
}
/**
* sifive_serial_clk_notifier() - clock post-rate-change notifier
* @nb: pointer to the struct notifier_block, from the notifier code
* @event: event mask from the notifier code
* @data: pointer to the struct clk_notifier_data from the notifier code
*
* On the V0 SoC, the UART IP block is derived from the CPU clock source
* after a synchronous divide-by-two divider, so any CPU clock rate change
* requires the UART baud rate to be updated. This presumably could corrupt any
* serial word currently being transmitted or received. It would probably
* be better to stop receives and transmits, then complete the baud rate
* change, then re-enable them.
*/
static int sifive_serial_clk_notifier(struct notifier_block *nb,
unsigned long event, void *data)
{
struct clk_notifier_data *cnd = data;
struct sifive_serial_port *ssp = notifier_to_sifive_serial_port(nb);
if (event == POST_RATE_CHANGE && ssp->clkin_rate != cnd->new_rate) {
ssp->clkin_rate = cnd->new_rate;
__ssp_update_div(ssp);
}
return NOTIFY_OK;
}
static void sifive_serial_set_termios(struct uart_port *port,
struct ktermios *termios,
struct ktermios *old)
{
struct sifive_serial_port *ssp = port_to_sifive_serial_port(port);
unsigned long flags;
u32 v, old_v;
int rate;
char nstop;
if ((termios->c_cflag & CSIZE) != CS8)
dev_err_once(ssp->port.dev, "only 8-bit words supported\n");
if (termios->c_iflag & (INPCK | PARMRK))
dev_err_once(ssp->port.dev, "parity checking not supported\n");
if (termios->c_iflag & BRKINT)
dev_err_once(ssp->port.dev, "BREAK detection not supported\n");
/* Set number of stop bits */
nstop = (termios->c_cflag & CSTOPB) ? 2 : 1;
__ssp_set_stop_bits(ssp, nstop);
/* Set line rate */
rate = uart_get_baud_rate(port, termios, old, 0, ssp->clkin_rate / 16);
__ssp_update_baud_rate(ssp, rate);
spin_lock_irqsave(&ssp->port.lock, flags);
/* Update the per-port timeout */
uart_update_timeout(port, termios->c_cflag, rate);
ssp->port.read_status_mask = 0;
/* Ignore all characters if CREAD is not set */
v = __ssp_readl(ssp, SIFIVE_SERIAL_RXCTRL_OFFS);
old_v = v;
if ((termios->c_cflag & CREAD) == 0)
v &= SIFIVE_SERIAL_RXCTRL_RXEN_MASK;
else
v |= SIFIVE_SERIAL_RXCTRL_RXEN_MASK;
if (v != old_v)
__ssp_writel(v, SIFIVE_SERIAL_RXCTRL_OFFS, ssp);
spin_unlock_irqrestore(&ssp->port.lock, flags);
}
static void sifive_serial_release_port(struct uart_port *port)
{
}
static int sifive_serial_request_port(struct uart_port *port)
{
return 0;
}
static void sifive_serial_config_port(struct uart_port *port, int flags)
{
struct sifive_serial_port *ssp = port_to_sifive_serial_port(port);
ssp->port.type = PORT_SIFIVE_V0;
}
static int sifive_serial_verify_port(struct uart_port *port,
struct serial_struct *ser)
{
return -EINVAL;
}
static const char *sifive_serial_type(struct uart_port *port)
{
return port->type == PORT_SIFIVE_V0 ? "SiFive UART v0" : NULL;
}
/*
* Early console support
*/
#ifdef CONFIG_SERIAL_EARLYCON
static void early_sifive_serial_putc(struct uart_port *port, int c)
{
while (__ssp_early_readl(port, SIFIVE_SERIAL_TXDATA_OFFS) &
SIFIVE_SERIAL_TXDATA_FULL_MASK)
cpu_relax();
__ssp_early_writel(c, SIFIVE_SERIAL_TXDATA_OFFS, port);
}
static void early_sifive_serial_write(struct console *con, const char *s,
unsigned int n)
{
struct earlycon_device *dev = con->data;
struct uart_port *port = &dev->port;
uart_console_write(port, s, n, early_sifive_serial_putc);
}
static int __init early_sifive_serial_setup(struct earlycon_device *dev,
const char *options)
{
struct uart_port *port = &dev->port;
if (!port->membase)
return -ENODEV;
dev->con->write = early_sifive_serial_write;
return 0;
}
OF_EARLYCON_DECLARE(sifive, "sifive,uart0", early_sifive_serial_setup);
OF_EARLYCON_DECLARE(sifive, "sifive,fu540-c000-uart0",
early_sifive_serial_setup);
#endif /* CONFIG_SERIAL_EARLYCON */
/*
* Linux console interface
*/
#ifdef CONFIG_SERIAL_SIFIVE_CONSOLE
static struct sifive_serial_port *sifive_serial_console_ports[SIFIVE_SERIAL_MAX_PORTS];
static void sifive_serial_console_putchar(struct uart_port *port, int ch)
{
struct sifive_serial_port *ssp = port_to_sifive_serial_port(port);
__ssp_wait_for_xmitr(ssp);
__ssp_transmit_char(ssp, ch);
}
static void sifive_serial_console_write(struct console *co, const char *s,
unsigned int count)
{
struct sifive_serial_port *ssp = sifive_serial_console_ports[co->index];
unsigned long flags;
unsigned int ier;
int locked = 1;
if (!ssp)
return;
local_irq_save(flags);
if (ssp->port.sysrq)
locked = 0;
else if (oops_in_progress)
locked = spin_trylock(&ssp->port.lock);
else
spin_lock(&ssp->port.lock);
ier = __ssp_readl(ssp, SIFIVE_SERIAL_IE_OFFS);
__ssp_writel(0, SIFIVE_SERIAL_IE_OFFS, ssp);
uart_console_write(&ssp->port, s, count, sifive_serial_console_putchar);
__ssp_writel(ier, SIFIVE_SERIAL_IE_OFFS, ssp);
if (locked)
spin_unlock(&ssp->port.lock);
local_irq_restore(flags);
}
static int __init sifive_serial_console_setup(struct console *co, char *options)
{
struct sifive_serial_port *ssp;
int baud = SIFIVE_DEFAULT_BAUD_RATE;
int bits = 8;
int parity = 'n';
int flow = 'n';
if (co->index < 0 || co->index >= SIFIVE_SERIAL_MAX_PORTS)
return -ENODEV;
ssp = sifive_serial_console_ports[co->index];
if (!ssp)
return -ENODEV;
if (options)
uart_parse_options(options, &baud, &parity, &bits, &flow);
return uart_set_options(&ssp->port, co, baud, parity, bits, flow);
}
static struct uart_driver sifive_serial_uart_driver;
static struct console sifive_serial_console = {
.name = SIFIVE_TTY_PREFIX,
.write = sifive_serial_console_write,
.device = uart_console_device,
.setup = sifive_serial_console_setup,
.flags = CON_PRINTBUFFER,
.index = -1,
.data = &sifive_serial_uart_driver,
};
static int __init sifive_console_init(void)
{
register_console(&sifive_serial_console);
return 0;
}
console_initcall(sifive_console_init);
static void __ssp_add_console_port(struct sifive_serial_port *ssp)
{
sifive_serial_console_ports[ssp->port.line] = ssp;
}
static void __ssp_remove_console_port(struct sifive_serial_port *ssp)
{
sifive_serial_console_ports[ssp->port.line] = 0;
}
#define SIFIVE_SERIAL_CONSOLE (&sifive_serial_console)
#else
#define SIFIVE_SERIAL_CONSOLE NULL
static void __ssp_add_console_port(struct sifive_serial_port *ssp)
{}
static void __ssp_remove_console_port(struct sifive_serial_port *ssp)
{}
#endif
static const struct uart_ops sifive_serial_uops = {
.tx_empty = sifive_serial_tx_empty,
.set_mctrl = sifive_serial_set_mctrl,
.get_mctrl = sifive_serial_get_mctrl,
.stop_tx = sifive_serial_stop_tx,
.start_tx = sifive_serial_start_tx,
.stop_rx = sifive_serial_stop_rx,
.break_ctl = sifive_serial_break_ctl,
.startup = sifive_serial_startup,
.shutdown = sifive_serial_shutdown,
.set_termios = sifive_serial_set_termios,
.type = sifive_serial_type,
.release_port = sifive_serial_release_port,
.request_port = sifive_serial_request_port,
.config_port = sifive_serial_config_port,
.verify_port = sifive_serial_verify_port,
};
static struct uart_driver sifive_serial_uart_driver = {
.owner = THIS_MODULE,
.driver_name = SIFIVE_SERIAL_NAME,
.dev_name = SIFIVE_TTY_PREFIX,
.nr = SIFIVE_SERIAL_MAX_PORTS,
.cons = SIFIVE_SERIAL_CONSOLE,
};
static int sifive_serial_probe(struct platform_device *pdev)
{
struct sifive_serial_port *ssp;
struct resource *mem;
struct clk *clk;
void __iomem *base;
int irq, id, r;
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(&pdev->dev, "could not acquire interrupt\n");
return -EPROBE_DEFER;
}
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
base = devm_ioremap_resource(&pdev->dev, mem);
if (IS_ERR(base)) {
dev_err(&pdev->dev, "could not acquire device memory\n");
return PTR_ERR(base);
}
clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(clk)) {
dev_err(&pdev->dev, "unable to find controller clock\n");
return PTR_ERR(clk);
}
id = of_alias_get_id(pdev->dev.of_node, "serial");
if (id < 0) {
dev_err(&pdev->dev, "missing aliases entry\n");
return id;
}
#ifdef CONFIG_SERIAL_SIFIVE_CONSOLE
if (id > SIFIVE_SERIAL_MAX_PORTS) {
dev_err(&pdev->dev, "too many UARTs (%d)\n", id);
return -EINVAL;
}
#endif
ssp = devm_kzalloc(&pdev->dev, sizeof(*ssp), GFP_KERNEL);
if (!ssp)
return -ENOMEM;
ssp->port.dev = &pdev->dev;
ssp->port.type = PORT_SIFIVE_V0;
ssp->port.iotype = UPIO_MEM;
ssp->port.irq = irq;
ssp->port.fifosize = SIFIVE_TX_FIFO_DEPTH;
ssp->port.ops = &sifive_serial_uops;
ssp->port.line = id;
ssp->port.mapbase = mem->start;
ssp->port.membase = base;
ssp->dev = &pdev->dev;
ssp->clk = clk;
ssp->clk_notifier.notifier_call = sifive_serial_clk_notifier;
r = clk_notifier_register(ssp->clk, &ssp->clk_notifier);
if (r) {
dev_err(&pdev->dev, "could not register clock notifier: %d\n",
r);
goto probe_out1;
}
/* Set up clock divider */
ssp->clkin_rate = clk_get_rate(ssp->clk);
ssp->baud_rate = SIFIVE_DEFAULT_BAUD_RATE;
__ssp_update_div(ssp);
platform_set_drvdata(pdev, ssp);
/* Enable transmits and set the watermark level to 1 */
__ssp_writel((1 << SIFIVE_SERIAL_TXCTRL_TXCNT_SHIFT) |
SIFIVE_SERIAL_TXCTRL_TXEN_MASK,
SIFIVE_SERIAL_TXCTRL_OFFS, ssp);
/* Enable receives and set the watermark level to 0 */
__ssp_writel((0 << SIFIVE_SERIAL_RXCTRL_RXCNT_SHIFT) |
SIFIVE_SERIAL_RXCTRL_RXEN_MASK,
SIFIVE_SERIAL_RXCTRL_OFFS, ssp);
r = request_irq(ssp->port.irq, sifive_serial_irq, ssp->port.irqflags,
dev_name(&pdev->dev), ssp);
if (r) {
dev_err(&pdev->dev, "could not attach interrupt: %d\n", r);
goto probe_out2;
}
__ssp_add_console_port(ssp);
r = uart_add_one_port(&sifive_serial_uart_driver, &ssp->port);
if (r != 0) {
dev_err(&pdev->dev, "could not add uart: %d\n", r);
goto probe_out3;
}
return 0;
probe_out3:
__ssp_remove_console_port(ssp);
free_irq(ssp->port.irq, ssp);
probe_out2:
clk_notifier_unregister(ssp->clk, &ssp->clk_notifier);
probe_out1:
return r;
}
static int sifive_serial_remove(struct platform_device *dev)
{
struct sifive_serial_port *ssp = platform_get_drvdata(dev);
__ssp_remove_console_port(ssp);
uart_remove_one_port(&sifive_serial_uart_driver, &ssp->port);
free_irq(ssp->port.irq, ssp);
clk_notifier_unregister(ssp->clk, &ssp->clk_notifier);
return 0;
}
static const struct of_device_id sifive_serial_of_match[] = {
{ .compatible = "sifive,fu540-c000-uart0" },
{ .compatible = "sifive,uart0" },
{},
};
MODULE_DEVICE_TABLE(of, sifive_serial_of_match);
static struct platform_driver sifive_serial_platform_driver = {
.probe = sifive_serial_probe,
.remove = sifive_serial_remove,
.driver = {
.name = SIFIVE_SERIAL_NAME,
.of_match_table = of_match_ptr(sifive_serial_of_match),
},
};
static int __init sifive_serial_init(void)
{
int r;
r = uart_register_driver(&sifive_serial_uart_driver);
if (r)
goto init_out1;
r = platform_driver_register(&sifive_serial_platform_driver);
if (r)
goto init_out2;
return 0;
init_out2:
uart_unregister_driver(&sifive_serial_uart_driver);
init_out1:
return r;
}
static void __exit sifive_serial_exit(void)
{
platform_driver_unregister(&sifive_serial_platform_driver);
uart_unregister_driver(&sifive_serial_uart_driver);
}
module_init(sifive_serial_init);
module_exit(sifive_serial_exit);
MODULE_DESCRIPTION("SiFive UART serial driver");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Paul Walmsley <paul@pwsan.com>");
// SPDX-License-Identifier: GPL-2.0
/* /*
* C-Brick Serial Port (and console) driver for SGI Altix machines. * C-Brick Serial Port (and console) driver for SGI Altix machines.
* *
......
...@@ -10,6 +10,9 @@ ...@@ -10,6 +10,9 @@
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/console.h> #include <linux/console.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>
#include <linux/dma/sprd-dma.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/ioport.h> #include <linux/ioport.h>
#include <linux/kernel.h> #include <linux/kernel.h>
...@@ -75,6 +78,7 @@ ...@@ -75,6 +78,7 @@
/* control register 1 */ /* control register 1 */
#define SPRD_CTL1 0x001C #define SPRD_CTL1 0x001C
#define SPRD_DMA_EN BIT(15)
#define RX_HW_FLOW_CTL_THLD BIT(6) #define RX_HW_FLOW_CTL_THLD BIT(6)
#define RX_HW_FLOW_CTL_EN BIT(7) #define RX_HW_FLOW_CTL_EN BIT(7)
#define TX_HW_FLOW_CTL_EN BIT(8) #define TX_HW_FLOW_CTL_EN BIT(8)
...@@ -86,6 +90,7 @@ ...@@ -86,6 +90,7 @@
#define THLD_TX_EMPTY 0x40 #define THLD_TX_EMPTY 0x40
#define THLD_TX_EMPTY_SHIFT 8 #define THLD_TX_EMPTY_SHIFT 8
#define THLD_RX_FULL 0x40 #define THLD_RX_FULL 0x40
#define THLD_RX_FULL_MASK GENMASK(6, 0)
/* config baud rate register */ /* config baud rate register */
#define SPRD_CLKD0 0x0024 #define SPRD_CLKD0 0x0024
...@@ -100,15 +105,38 @@ ...@@ -100,15 +105,38 @@
#define SPRD_IMSR_TX_FIFO_EMPTY BIT(1) #define SPRD_IMSR_TX_FIFO_EMPTY BIT(1)
#define SPRD_IMSR_BREAK_DETECT BIT(7) #define SPRD_IMSR_BREAK_DETECT BIT(7)
#define SPRD_IMSR_TIMEOUT BIT(13) #define SPRD_IMSR_TIMEOUT BIT(13)
#define SPRD_DEFAULT_SOURCE_CLK 26000000
#define SPRD_RX_DMA_STEP 1
#define SPRD_RX_FIFO_FULL 1
#define SPRD_TX_FIFO_FULL 0x20
#define SPRD_UART_RX_SIZE (UART_XMIT_SIZE / 4)
struct sprd_uart_dma {
struct dma_chan *chn;
unsigned char *virt;
dma_addr_t phys_addr;
dma_cookie_t cookie;
u32 trans_len;
bool enable;
};
struct sprd_uart_port { struct sprd_uart_port {
struct uart_port port; struct uart_port port;
char name[16]; char name[16];
struct clk *clk;
struct sprd_uart_dma tx_dma;
struct sprd_uart_dma rx_dma;
dma_addr_t pos;
unsigned char *rx_buf_tail;
}; };
static struct sprd_uart_port *sprd_port[UART_NR_MAX]; static struct sprd_uart_port *sprd_port[UART_NR_MAX];
static int sprd_ports_num; static int sprd_ports_num;
static int sprd_start_dma_rx(struct uart_port *port);
static int sprd_tx_dma_config(struct uart_port *port);
static inline unsigned int serial_in(struct uart_port *port, static inline unsigned int serial_in(struct uart_port *port,
unsigned int offset) unsigned int offset)
{ {
...@@ -139,45 +167,389 @@ static void sprd_set_mctrl(struct uart_port *port, unsigned int mctrl) ...@@ -139,45 +167,389 @@ static void sprd_set_mctrl(struct uart_port *port, unsigned int mctrl)
/* nothing to do */ /* nothing to do */
} }
static void sprd_stop_tx(struct uart_port *port) static void sprd_stop_rx(struct uart_port *port)
{ {
struct sprd_uart_port *sp =
container_of(port, struct sprd_uart_port, port);
unsigned int ien, iclr; unsigned int ien, iclr;
if (sp->rx_dma.enable)
dmaengine_terminate_all(sp->rx_dma.chn);
iclr = serial_in(port, SPRD_ICLR); iclr = serial_in(port, SPRD_ICLR);
ien = serial_in(port, SPRD_IEN); ien = serial_in(port, SPRD_IEN);
iclr |= SPRD_IEN_TX_EMPTY; ien &= ~(SPRD_IEN_RX_FULL | SPRD_IEN_BREAK_DETECT);
ien &= ~SPRD_IEN_TX_EMPTY; iclr |= SPRD_IEN_RX_FULL | SPRD_IEN_BREAK_DETECT;
serial_out(port, SPRD_ICLR, iclr);
serial_out(port, SPRD_IEN, ien); serial_out(port, SPRD_IEN, ien);
serial_out(port, SPRD_ICLR, iclr);
} }
static void sprd_start_tx(struct uart_port *port) static void sprd_uart_dma_enable(struct uart_port *port, bool enable)
{ {
unsigned int ien; u32 val = serial_in(port, SPRD_CTL1);
ien = serial_in(port, SPRD_IEN); if (enable)
if (!(ien & SPRD_IEN_TX_EMPTY)) { val |= SPRD_DMA_EN;
ien |= SPRD_IEN_TX_EMPTY; else
serial_out(port, SPRD_IEN, ien); val &= ~SPRD_DMA_EN;
serial_out(port, SPRD_CTL1, val);
}
static void sprd_stop_tx_dma(struct uart_port *port)
{
struct sprd_uart_port *sp =
container_of(port, struct sprd_uart_port, port);
struct circ_buf *xmit = &port->state->xmit;
struct dma_tx_state state;
u32 trans_len;
dmaengine_pause(sp->tx_dma.chn);
dmaengine_tx_status(sp->tx_dma.chn, sp->tx_dma.cookie, &state);
if (state.residue) {
trans_len = state.residue - sp->tx_dma.phys_addr;
xmit->tail = (xmit->tail + trans_len) & (UART_XMIT_SIZE - 1);
port->icount.tx += trans_len;
dma_unmap_single(port->dev, sp->tx_dma.phys_addr,
sp->tx_dma.trans_len, DMA_TO_DEVICE);
} }
dmaengine_terminate_all(sp->tx_dma.chn);
sp->tx_dma.trans_len = 0;
} }
static void sprd_stop_rx(struct uart_port *port) static int sprd_tx_buf_remap(struct uart_port *port)
{
struct sprd_uart_port *sp =
container_of(port, struct sprd_uart_port, port);
struct circ_buf *xmit = &port->state->xmit;
sp->tx_dma.trans_len =
CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
sp->tx_dma.phys_addr = dma_map_single(port->dev,
(void *)&(xmit->buf[xmit->tail]),
sp->tx_dma.trans_len,
DMA_TO_DEVICE);
return dma_mapping_error(port->dev, sp->tx_dma.phys_addr);
}
static void sprd_complete_tx_dma(void *data)
{
struct uart_port *port = (struct uart_port *)data;
struct sprd_uart_port *sp =
container_of(port, struct sprd_uart_port, port);
struct circ_buf *xmit = &port->state->xmit;
unsigned long flags;
spin_lock_irqsave(&port->lock, flags);
dma_unmap_single(port->dev, sp->tx_dma.phys_addr,
sp->tx_dma.trans_len, DMA_TO_DEVICE);
xmit->tail = (xmit->tail + sp->tx_dma.trans_len) & (UART_XMIT_SIZE - 1);
port->icount.tx += sp->tx_dma.trans_len;
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(port);
if (uart_circ_empty(xmit) || sprd_tx_buf_remap(port) ||
sprd_tx_dma_config(port))
sp->tx_dma.trans_len = 0;
spin_unlock_irqrestore(&port->lock, flags);
}
static int sprd_uart_dma_submit(struct uart_port *port,
struct sprd_uart_dma *ud, u32 trans_len,
enum dma_transfer_direction direction,
dma_async_tx_callback callback)
{
struct dma_async_tx_descriptor *dma_des;
unsigned long flags;
flags = SPRD_DMA_FLAGS(SPRD_DMA_CHN_MODE_NONE,
SPRD_DMA_NO_TRG,
SPRD_DMA_FRAG_REQ,
SPRD_DMA_TRANS_INT);
dma_des = dmaengine_prep_slave_single(ud->chn, ud->phys_addr, trans_len,
direction, flags);
if (!dma_des)
return -ENODEV;
dma_des->callback = callback;
dma_des->callback_param = port;
ud->cookie = dmaengine_submit(dma_des);
if (dma_submit_error(ud->cookie))
return dma_submit_error(ud->cookie);
dma_async_issue_pending(ud->chn);
return 0;
}
static int sprd_tx_dma_config(struct uart_port *port)
{
struct sprd_uart_port *sp =
container_of(port, struct sprd_uart_port, port);
u32 burst = sp->tx_dma.trans_len > SPRD_TX_FIFO_FULL ?
SPRD_TX_FIFO_FULL : sp->tx_dma.trans_len;
int ret;
struct dma_slave_config cfg = {
.dst_addr = port->mapbase + SPRD_TXD,
.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE,
.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE,
.src_maxburst = burst,
};
ret = dmaengine_slave_config(sp->tx_dma.chn, &cfg);
if (ret < 0)
return ret;
return sprd_uart_dma_submit(port, &sp->tx_dma, sp->tx_dma.trans_len,
DMA_MEM_TO_DEV, sprd_complete_tx_dma);
}
static void sprd_start_tx_dma(struct uart_port *port)
{
struct sprd_uart_port *sp =
container_of(port, struct sprd_uart_port, port);
struct circ_buf *xmit = &port->state->xmit;
if (port->x_char) {
serial_out(port, SPRD_TXD, port->x_char);
port->icount.tx++;
port->x_char = 0;
return;
}
if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
sprd_stop_tx_dma(port);
return;
}
if (sp->tx_dma.trans_len)
return;
if (sprd_tx_buf_remap(port) || sprd_tx_dma_config(port))
sp->tx_dma.trans_len = 0;
}
static void sprd_rx_full_thld(struct uart_port *port, u32 thld)
{ {
u32 val = serial_in(port, SPRD_CTL2);
val &= ~THLD_RX_FULL_MASK;
val |= thld & THLD_RX_FULL_MASK;
serial_out(port, SPRD_CTL2, val);
}
static int sprd_rx_alloc_buf(struct sprd_uart_port *sp)
{
sp->rx_dma.virt = dma_alloc_coherent(sp->port.dev, SPRD_UART_RX_SIZE,
&sp->rx_dma.phys_addr, GFP_KERNEL);
if (!sp->rx_dma.virt)
return -ENOMEM;
return 0;
}
static void sprd_rx_free_buf(struct sprd_uart_port *sp)
{
if (sp->rx_dma.virt)
dma_free_coherent(sp->port.dev, SPRD_UART_RX_SIZE,
sp->rx_dma.virt, sp->rx_dma.phys_addr);
}
static int sprd_rx_dma_config(struct uart_port *port, u32 burst)
{
struct sprd_uart_port *sp =
container_of(port, struct sprd_uart_port, port);
struct dma_slave_config cfg = {
.src_addr = port->mapbase + SPRD_RXD,
.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE,
.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE,
.src_maxburst = burst,
};
return dmaengine_slave_config(sp->rx_dma.chn, &cfg);
}
static void sprd_uart_dma_rx(struct uart_port *port)
{
struct sprd_uart_port *sp =
container_of(port, struct sprd_uart_port, port);
struct tty_port *tty = &port->state->port;
port->icount.rx += sp->rx_dma.trans_len;
tty_insert_flip_string(tty, sp->rx_buf_tail, sp->rx_dma.trans_len);
tty_flip_buffer_push(tty);
}
static void sprd_uart_dma_irq(struct uart_port *port)
{
struct sprd_uart_port *sp =
container_of(port, struct sprd_uart_port, port);
struct dma_tx_state state;
enum dma_status status;
status = dmaengine_tx_status(sp->rx_dma.chn,
sp->rx_dma.cookie, &state);
if (status == DMA_ERROR)
sprd_stop_rx(port);
if (!state.residue && sp->pos == sp->rx_dma.phys_addr)
return;
if (!state.residue) {
sp->rx_dma.trans_len = SPRD_UART_RX_SIZE +
sp->rx_dma.phys_addr - sp->pos;
sp->pos = sp->rx_dma.phys_addr;
} else {
sp->rx_dma.trans_len = state.residue - sp->pos;
sp->pos = state.residue;
}
sprd_uart_dma_rx(port);
sp->rx_buf_tail += sp->rx_dma.trans_len;
}
static void sprd_complete_rx_dma(void *data)
{
struct uart_port *port = (struct uart_port *)data;
struct sprd_uart_port *sp =
container_of(port, struct sprd_uart_port, port);
struct dma_tx_state state;
enum dma_status status;
unsigned long flags;
spin_lock_irqsave(&port->lock, flags);
status = dmaengine_tx_status(sp->rx_dma.chn,
sp->rx_dma.cookie, &state);
if (status != DMA_COMPLETE) {
sprd_stop_rx(port);
spin_unlock_irqrestore(&port->lock, flags);
return;
}
if (sp->pos != sp->rx_dma.phys_addr) {
sp->rx_dma.trans_len = SPRD_UART_RX_SIZE +
sp->rx_dma.phys_addr - sp->pos;
sprd_uart_dma_rx(port);
sp->rx_buf_tail += sp->rx_dma.trans_len;
}
if (sprd_start_dma_rx(port))
sprd_stop_rx(port);
spin_unlock_irqrestore(&port->lock, flags);
}
static int sprd_start_dma_rx(struct uart_port *port)
{
struct sprd_uart_port *sp =
container_of(port, struct sprd_uart_port, port);
int ret;
if (!sp->rx_dma.enable)
return 0;
sp->pos = sp->rx_dma.phys_addr;
sp->rx_buf_tail = sp->rx_dma.virt;
sprd_rx_full_thld(port, SPRD_RX_FIFO_FULL);
ret = sprd_rx_dma_config(port, SPRD_RX_DMA_STEP);
if (ret)
return ret;
return sprd_uart_dma_submit(port, &sp->rx_dma, SPRD_UART_RX_SIZE,
DMA_DEV_TO_MEM, sprd_complete_rx_dma);
}
static void sprd_release_dma(struct uart_port *port)
{
struct sprd_uart_port *sp =
container_of(port, struct sprd_uart_port, port);
sprd_uart_dma_enable(port, false);
if (sp->rx_dma.enable)
dma_release_channel(sp->rx_dma.chn);
if (sp->tx_dma.enable)
dma_release_channel(sp->tx_dma.chn);
sp->tx_dma.enable = false;
sp->rx_dma.enable = false;
}
static void sprd_request_dma(struct uart_port *port)
{
struct sprd_uart_port *sp =
container_of(port, struct sprd_uart_port, port);
sp->tx_dma.enable = true;
sp->rx_dma.enable = true;
sp->tx_dma.chn = dma_request_chan(port->dev, "tx");
if (IS_ERR(sp->tx_dma.chn)) {
dev_err(port->dev, "request TX DMA channel failed, ret = %ld\n",
PTR_ERR(sp->tx_dma.chn));
sp->tx_dma.enable = false;
}
sp->rx_dma.chn = dma_request_chan(port->dev, "rx");
if (IS_ERR(sp->rx_dma.chn)) {
dev_err(port->dev, "request RX DMA channel failed, ret = %ld\n",
PTR_ERR(sp->rx_dma.chn));
sp->rx_dma.enable = false;
}
}
static void sprd_stop_tx(struct uart_port *port)
{
struct sprd_uart_port *sp = container_of(port, struct sprd_uart_port,
port);
unsigned int ien, iclr; unsigned int ien, iclr;
if (sp->tx_dma.enable) {
sprd_stop_tx_dma(port);
return;
}
iclr = serial_in(port, SPRD_ICLR); iclr = serial_in(port, SPRD_ICLR);
ien = serial_in(port, SPRD_IEN); ien = serial_in(port, SPRD_IEN);
ien &= ~(SPRD_IEN_RX_FULL | SPRD_IEN_BREAK_DETECT); iclr |= SPRD_IEN_TX_EMPTY;
iclr |= SPRD_IEN_RX_FULL | SPRD_IEN_BREAK_DETECT; ien &= ~SPRD_IEN_TX_EMPTY;
serial_out(port, SPRD_IEN, ien); serial_out(port, SPRD_IEN, ien);
serial_out(port, SPRD_ICLR, iclr); serial_out(port, SPRD_ICLR, iclr);
} }
static void sprd_start_tx(struct uart_port *port)
{
struct sprd_uart_port *sp = container_of(port, struct sprd_uart_port,
port);
unsigned int ien;
if (sp->tx_dma.enable) {
sprd_start_tx_dma(port);
return;
}
ien = serial_in(port, SPRD_IEN);
if (!(ien & SPRD_IEN_TX_EMPTY)) {
ien |= SPRD_IEN_TX_EMPTY;
serial_out(port, SPRD_IEN, ien);
}
}
/* The Sprd serial does not support this function. */ /* The Sprd serial does not support this function. */
static void sprd_break_ctl(struct uart_port *port, int break_state) static void sprd_break_ctl(struct uart_port *port, int break_state)
{ {
...@@ -218,9 +590,16 @@ static int handle_lsr_errors(struct uart_port *port, ...@@ -218,9 +590,16 @@ static int handle_lsr_errors(struct uart_port *port,
static inline void sprd_rx(struct uart_port *port) static inline void sprd_rx(struct uart_port *port)
{ {
struct sprd_uart_port *sp = container_of(port, struct sprd_uart_port,
port);
struct tty_port *tty = &port->state->port; struct tty_port *tty = &port->state->port;
unsigned int ch, flag, lsr, max_count = SPRD_TIMEOUT; unsigned int ch, flag, lsr, max_count = SPRD_TIMEOUT;
if (sp->rx_dma.enable) {
sprd_uart_dma_irq(port);
return;
}
while ((serial_in(port, SPRD_STS1) & SPRD_RX_FIFO_CNT_MASK) && while ((serial_in(port, SPRD_STS1) & SPRD_RX_FIFO_CNT_MASK) &&
max_count--) { max_count--) {
lsr = serial_in(port, SPRD_LSR); lsr = serial_in(port, SPRD_LSR);
...@@ -304,6 +683,25 @@ static irqreturn_t sprd_handle_irq(int irq, void *dev_id) ...@@ -304,6 +683,25 @@ static irqreturn_t sprd_handle_irq(int irq, void *dev_id)
return IRQ_HANDLED; return IRQ_HANDLED;
} }
static void sprd_uart_dma_startup(struct uart_port *port,
struct sprd_uart_port *sp)
{
int ret;
sprd_request_dma(port);
if (!(sp->rx_dma.enable || sp->tx_dma.enable))
return;
ret = sprd_start_dma_rx(port);
if (ret) {
sp->rx_dma.enable = false;
dma_release_channel(sp->rx_dma.chn);
dev_warn(port->dev, "fail to start RX dma mode\n");
}
sprd_uart_dma_enable(port, true);
}
static int sprd_startup(struct uart_port *port) static int sprd_startup(struct uart_port *port)
{ {
int ret = 0; int ret = 0;
...@@ -332,6 +730,9 @@ static int sprd_startup(struct uart_port *port) ...@@ -332,6 +730,9 @@ static int sprd_startup(struct uart_port *port)
/* allocate irq */ /* allocate irq */
sp = container_of(port, struct sprd_uart_port, port); sp = container_of(port, struct sprd_uart_port, port);
snprintf(sp->name, sizeof(sp->name), "sprd_serial%d", port->line); snprintf(sp->name, sizeof(sp->name), "sprd_serial%d", port->line);
sprd_uart_dma_startup(port, sp);
ret = devm_request_irq(port->dev, port->irq, sprd_handle_irq, ret = devm_request_irq(port->dev, port->irq, sprd_handle_irq,
IRQF_SHARED, sp->name, port); IRQF_SHARED, sp->name, port);
if (ret) { if (ret) {
...@@ -346,7 +747,9 @@ static int sprd_startup(struct uart_port *port) ...@@ -346,7 +747,9 @@ static int sprd_startup(struct uart_port *port)
/* enable interrupt */ /* enable interrupt */
spin_lock_irqsave(&port->lock, flags); spin_lock_irqsave(&port->lock, flags);
ien = serial_in(port, SPRD_IEN); ien = serial_in(port, SPRD_IEN);
ien |= SPRD_IEN_RX_FULL | SPRD_IEN_BREAK_DETECT | SPRD_IEN_TIMEOUT; ien |= SPRD_IEN_BREAK_DETECT | SPRD_IEN_TIMEOUT;
if (!sp->rx_dma.enable)
ien |= SPRD_IEN_RX_FULL;
serial_out(port, SPRD_IEN, ien); serial_out(port, SPRD_IEN, ien);
spin_unlock_irqrestore(&port->lock, flags); spin_unlock_irqrestore(&port->lock, flags);
...@@ -355,6 +758,7 @@ static int sprd_startup(struct uart_port *port) ...@@ -355,6 +758,7 @@ static int sprd_startup(struct uart_port *port)
static void sprd_shutdown(struct uart_port *port) static void sprd_shutdown(struct uart_port *port)
{ {
sprd_release_dma(port);
serial_out(port, SPRD_IEN, 0); serial_out(port, SPRD_IEN, 0);
serial_out(port, SPRD_ICLR, ~0); serial_out(port, SPRD_ICLR, ~0);
devm_free_irq(port->dev, port->irq, port); devm_free_irq(port->dev, port->irq, port);
...@@ -491,6 +895,22 @@ static int sprd_verify_port(struct uart_port *port, struct serial_struct *ser) ...@@ -491,6 +895,22 @@ static int sprd_verify_port(struct uart_port *port, struct serial_struct *ser)
return 0; return 0;
} }
static void sprd_pm(struct uart_port *port, unsigned int state,
unsigned int oldstate)
{
struct sprd_uart_port *sup =
container_of(port, struct sprd_uart_port, port);
switch (state) {
case UART_PM_STATE_ON:
clk_prepare_enable(sup->clk);
break;
case UART_PM_STATE_OFF:
clk_disable_unprepare(sup->clk);
break;
}
}
static const struct uart_ops serial_sprd_ops = { static const struct uart_ops serial_sprd_ops = {
.tx_empty = sprd_tx_empty, .tx_empty = sprd_tx_empty,
.get_mctrl = sprd_get_mctrl, .get_mctrl = sprd_get_mctrl,
...@@ -507,6 +927,7 @@ static const struct uart_ops serial_sprd_ops = { ...@@ -507,6 +927,7 @@ static const struct uart_ops serial_sprd_ops = {
.request_port = sprd_request_port, .request_port = sprd_request_port,
.config_port = sprd_config_port, .config_port = sprd_config_port,
.verify_port = sprd_verify_port, .verify_port = sprd_verify_port,
.pm = sprd_pm,
}; };
#ifdef CONFIG_SERIAL_SPRD_CONSOLE #ifdef CONFIG_SERIAL_SPRD_CONSOLE
...@@ -668,6 +1089,43 @@ static int sprd_remove(struct platform_device *dev) ...@@ -668,6 +1089,43 @@ static int sprd_remove(struct platform_device *dev)
if (!sprd_ports_num) if (!sprd_ports_num)
uart_unregister_driver(&sprd_uart_driver); uart_unregister_driver(&sprd_uart_driver);
sprd_rx_free_buf(sup);
return 0;
}
static int sprd_clk_init(struct uart_port *uport)
{
struct clk *clk_uart, *clk_parent;
struct sprd_uart_port *u = sprd_port[uport->line];
clk_uart = devm_clk_get(uport->dev, "uart");
if (IS_ERR(clk_uart)) {
dev_warn(uport->dev, "uart%d can't get uart clock\n",
uport->line);
clk_uart = NULL;
}
clk_parent = devm_clk_get(uport->dev, "source");
if (IS_ERR(clk_parent)) {
dev_warn(uport->dev, "uart%d can't get source clock\n",
uport->line);
clk_parent = NULL;
}
if (!clk_uart || clk_set_parent(clk_uart, clk_parent))
uport->uartclk = SPRD_DEFAULT_SOURCE_CLK;
else
uport->uartclk = clk_get_rate(clk_uart);
u->clk = devm_clk_get(uport->dev, "enable");
if (IS_ERR(u->clk)) {
if (PTR_ERR(u->clk) != -EPROBE_DEFER)
dev_err(uport->dev, "uart%d can't get enable clock\n",
uport->line);
return PTR_ERR(u->clk);
}
return 0; return 0;
} }
...@@ -675,7 +1133,6 @@ static int sprd_probe(struct platform_device *pdev) ...@@ -675,7 +1133,6 @@ static int sprd_probe(struct platform_device *pdev)
{ {
struct resource *res; struct resource *res;
struct uart_port *up; struct uart_port *up;
struct clk *clk;
int irq; int irq;
int index; int index;
int ret; int ret;
...@@ -704,9 +1161,9 @@ static int sprd_probe(struct platform_device *pdev) ...@@ -704,9 +1161,9 @@ static int sprd_probe(struct platform_device *pdev)
up->ops = &serial_sprd_ops; up->ops = &serial_sprd_ops;
up->flags = UPF_BOOT_AUTOCONF; up->flags = UPF_BOOT_AUTOCONF;
clk = devm_clk_get(&pdev->dev, NULL); ret = sprd_clk_init(up);
if (!IS_ERR_OR_NULL(clk)) if (ret)
up->uartclk = clk_get_rate(clk); return ret;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
up->membase = devm_ioremap_resource(&pdev->dev, res); up->membase = devm_ioremap_resource(&pdev->dev, res);
...@@ -722,6 +1179,14 @@ static int sprd_probe(struct platform_device *pdev) ...@@ -722,6 +1179,14 @@ static int sprd_probe(struct platform_device *pdev)
} }
up->irq = irq; up->irq = irq;
/*
* Allocate one dma buffer to prepare for receive transfer, in case
* memory allocation failure at runtime.
*/
ret = sprd_rx_alloc_buf(sprd_port[index]);
if (ret)
return ret;
if (!sprd_ports_num) { if (!sprd_ports_num) {
ret = uart_register_driver(&sprd_uart_driver); ret = uart_register_driver(&sprd_uart_driver);
if (ret < 0) { if (ret < 0) {
......
...@@ -1081,7 +1081,7 @@ static int qe_uart_verify_port(struct uart_port *port, ...@@ -1081,7 +1081,7 @@ static int qe_uart_verify_port(struct uart_port *port,
} }
/* UART operations /* UART operations
* *
* Details on these functions can be found in Documentation/serial/driver * Details on these functions can be found in Documentation/serial/driver.rst
*/ */
static const struct uart_ops qe_uart_pops = { static const struct uart_ops qe_uart_pops = {
.tx_empty = qe_uart_tx_empty, .tx_empty = qe_uart_tx_empty,
......
...@@ -193,6 +193,7 @@ struct cdns_uart { ...@@ -193,6 +193,7 @@ struct cdns_uart {
int id; int id;
struct notifier_block clk_rate_change_nb; struct notifier_block clk_rate_change_nb;
u32 quirks; u32 quirks;
bool cts_override;
}; };
struct cdns_platform_data { struct cdns_platform_data {
u32 quirks; u32 quirks;
...@@ -1000,6 +1001,11 @@ static void cdns_uart_config_port(struct uart_port *port, int flags) ...@@ -1000,6 +1001,11 @@ static void cdns_uart_config_port(struct uart_port *port, int flags)
*/ */
static unsigned int cdns_uart_get_mctrl(struct uart_port *port) static unsigned int cdns_uart_get_mctrl(struct uart_port *port)
{ {
struct cdns_uart *cdns_uart_data = port->private_data;
if (cdns_uart_data->cts_override)
return 0;
return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR; return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR;
} }
...@@ -1007,6 +1013,10 @@ static void cdns_uart_set_mctrl(struct uart_port *port, unsigned int mctrl) ...@@ -1007,6 +1013,10 @@ static void cdns_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
{ {
u32 val; u32 val;
u32 mode_reg; u32 mode_reg;
struct cdns_uart *cdns_uart_data = port->private_data;
if (cdns_uart_data->cts_override)
return;
val = readl(port->membase + CDNS_UART_MODEMCR); val = readl(port->membase + CDNS_UART_MODEMCR);
mode_reg = readl(port->membase + CDNS_UART_MR); mode_reg = readl(port->membase + CDNS_UART_MR);
...@@ -1665,6 +1675,8 @@ static int cdns_uart_probe(struct platform_device *pdev) ...@@ -1665,6 +1675,8 @@ static int cdns_uart_probe(struct platform_device *pdev)
console_port = NULL; console_port = NULL;
#endif #endif
cdns_uart_data->cts_override = of_property_read_bool(pdev->dev.of_node,
"cts-override");
return 0; return 0;
err_out_pm_disable: err_out_pm_disable:
......
...@@ -208,7 +208,7 @@ static struct sysrq_key_op sysrq_showlocks_op = { ...@@ -208,7 +208,7 @@ static struct sysrq_key_op sysrq_showlocks_op = {
#endif #endif
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
static DEFINE_SPINLOCK(show_lock); static DEFINE_RAW_SPINLOCK(show_lock);
static void showacpu(void *dummy) static void showacpu(void *dummy)
{ {
...@@ -218,10 +218,10 @@ static void showacpu(void *dummy) ...@@ -218,10 +218,10 @@ static void showacpu(void *dummy)
if (idle_cpu(smp_processor_id())) if (idle_cpu(smp_processor_id()))
return; return;
spin_lock_irqsave(&show_lock, flags); raw_spin_lock_irqsave(&show_lock, flags);
pr_info("CPU%d:\n", smp_processor_id()); pr_info("CPU%d:\n", smp_processor_id());
show_stack(NULL, NULL); show_stack(NULL, NULL);
spin_unlock_irqrestore(&show_lock, flags); raw_spin_unlock_irqrestore(&show_lock, flags);
} }
static void sysrq_showregs_othercpus(struct work_struct *dummy) static void sysrq_showregs_othercpus(struct work_struct *dummy)
......
...@@ -1173,7 +1173,7 @@ static struct tty_struct *tty_driver_lookup_tty(struct tty_driver *driver, ...@@ -1173,7 +1173,7 @@ static struct tty_struct *tty_driver_lookup_tty(struct tty_driver *driver,
* tty_init_termios - helper for termios setup * tty_init_termios - helper for termios setup
* @tty: the tty to set up * @tty: the tty to set up
* *
* Initialise the termios structures for this tty. Thus runs under * Initialise the termios structure for this tty. This runs under
* the tty_mutex currently so we can be relaxed about ordering. * the tty_mutex currently so we can be relaxed about ordering.
*/ */
......
...@@ -44,7 +44,7 @@ int __tty_check_change(struct tty_struct *tty, int sig) ...@@ -44,7 +44,7 @@ int __tty_check_change(struct tty_struct *tty, int sig)
tty_pgrp = tty->pgrp; tty_pgrp = tty->pgrp;
spin_unlock_irqrestore(&tty->ctrl_lock, flags); spin_unlock_irqrestore(&tty->ctrl_lock, flags);
if (tty_pgrp && pgrp != tty->pgrp) { if (tty_pgrp && pgrp != tty_pgrp) {
if (is_ignored(sig)) { if (is_ignored(sig)) {
if (sig == SIGTTIN) if (sig == SIGTTIN)
ret = -EIO; ret = -EIO;
...@@ -313,7 +313,7 @@ void disassociate_ctty(int on_exit) ...@@ -313,7 +313,7 @@ void disassociate_ctty(int on_exit)
read_unlock(&tasklist_lock); read_unlock(&tasklist_lock);
} }
/** /*
* *
* no_tty - Ensure the current process does not have a controlling tty * no_tty - Ensure the current process does not have a controlling tty
*/ */
......
...@@ -325,7 +325,7 @@ static void tty_port_shutdown(struct tty_port *port, struct tty_struct *tty) ...@@ -325,7 +325,7 @@ static void tty_port_shutdown(struct tty_port *port, struct tty_struct *tty)
if (tty && C_HUPCL(tty)) if (tty && C_HUPCL(tty))
tty_port_lower_dtr_rts(port); tty_port_lower_dtr_rts(port);
if (port->ops && port->ops->shutdown) if (port->ops->shutdown)
port->ops->shutdown(port); port->ops->shutdown(port);
} }
out: out:
...@@ -398,7 +398,7 @@ EXPORT_SYMBOL_GPL(tty_port_tty_wakeup); ...@@ -398,7 +398,7 @@ EXPORT_SYMBOL_GPL(tty_port_tty_wakeup);
*/ */
int tty_port_carrier_raised(struct tty_port *port) int tty_port_carrier_raised(struct tty_port *port)
{ {
if (!port->ops || !port->ops->carrier_raised) if (port->ops->carrier_raised == NULL)
return 1; return 1;
return port->ops->carrier_raised(port); return port->ops->carrier_raised(port);
} }
...@@ -414,7 +414,7 @@ EXPORT_SYMBOL(tty_port_carrier_raised); ...@@ -414,7 +414,7 @@ EXPORT_SYMBOL(tty_port_carrier_raised);
*/ */
void tty_port_raise_dtr_rts(struct tty_port *port) void tty_port_raise_dtr_rts(struct tty_port *port)
{ {
if (port->ops && port->ops->dtr_rts) if (port->ops->dtr_rts)
port->ops->dtr_rts(port, 1); port->ops->dtr_rts(port, 1);
} }
EXPORT_SYMBOL(tty_port_raise_dtr_rts); EXPORT_SYMBOL(tty_port_raise_dtr_rts);
...@@ -429,7 +429,7 @@ EXPORT_SYMBOL(tty_port_raise_dtr_rts); ...@@ -429,7 +429,7 @@ EXPORT_SYMBOL(tty_port_raise_dtr_rts);
*/ */
void tty_port_lower_dtr_rts(struct tty_port *port) void tty_port_lower_dtr_rts(struct tty_port *port)
{ {
if (port->ops && port->ops->dtr_rts) if (port->ops->dtr_rts)
port->ops->dtr_rts(port, 0); port->ops->dtr_rts(port, 0);
} }
EXPORT_SYMBOL(tty_port_lower_dtr_rts); EXPORT_SYMBOL(tty_port_lower_dtr_rts);
...@@ -684,7 +684,7 @@ int tty_port_open(struct tty_port *port, struct tty_struct *tty, ...@@ -684,7 +684,7 @@ int tty_port_open(struct tty_port *port, struct tty_struct *tty,
if (!tty_port_initialized(port)) { if (!tty_port_initialized(port)) {
clear_bit(TTY_IO_ERROR, &tty->flags); clear_bit(TTY_IO_ERROR, &tty->flags);
if (port->ops && port->ops->activate) { if (port->ops->activate) {
int retval = port->ops->activate(port, tty); int retval = port->ops->activate(port, tty);
if (retval) { if (retval) {
mutex_unlock(&port->mutex); mutex_unlock(&port->mutex);
......
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2019 Axis Communications AB
*
* Based on ttyprintk.c:
* Copyright (C) 2010 Samo Pogacnik
*/
#include <linux/console.h>
#include <linux/module.h>
#include <linux/tty.h>
static const struct tty_port_operations ttynull_port_ops;
static struct tty_driver *ttynull_driver;
static struct tty_port ttynull_port;
static int ttynull_open(struct tty_struct *tty, struct file *filp)
{
return tty_port_open(&ttynull_port, tty, filp);
}
static void ttynull_close(struct tty_struct *tty, struct file *filp)
{
tty_port_close(&ttynull_port, tty, filp);
}
static void ttynull_hangup(struct tty_struct *tty)
{
tty_port_hangup(&ttynull_port);
}
static int ttynull_write(struct tty_struct *tty, const unsigned char *buf,
int count)
{
return count;
}
static int ttynull_write_room(struct tty_struct *tty)
{
return 65536;
}
static const struct tty_operations ttynull_ops = {
.open = ttynull_open,
.close = ttynull_close,
.hangup = ttynull_hangup,
.write = ttynull_write,
.write_room = ttynull_write_room,
};
static struct tty_driver *ttynull_device(struct console *c, int *index)
{
*index = 0;
return ttynull_driver;
}
static struct console ttynull_console = {
.name = "ttynull",
.device = ttynull_device,
};
static int __init ttynull_init(void)
{
struct tty_driver *driver;
int ret;
driver = tty_alloc_driver(1,
TTY_DRIVER_RESET_TERMIOS |
TTY_DRIVER_REAL_RAW |
TTY_DRIVER_UNNUMBERED_NODE);
if (IS_ERR(driver))
return PTR_ERR(driver);
tty_port_init(&ttynull_port);
ttynull_port.ops = &ttynull_port_ops;
driver->driver_name = "ttynull";
driver->name = "ttynull";
driver->type = TTY_DRIVER_TYPE_CONSOLE;
driver->init_termios = tty_std_termios;
driver->init_termios.c_oflag = OPOST | OCRNL | ONOCR | ONLRET;
tty_set_operations(driver, &ttynull_ops);
tty_port_link_device(&ttynull_port, driver, 0);
ret = tty_register_driver(driver);
if (ret < 0) {
put_tty_driver(driver);
tty_port_destroy(&ttynull_port);
return ret;
}
ttynull_driver = driver;
register_console(&ttynull_console);
return 0;
}
static void __exit ttynull_exit(void)
{
unregister_console(&ttynull_console);
tty_unregister_driver(ttynull_driver);
put_tty_driver(ttynull_driver);
tty_port_destroy(&ttynull_port);
}
module_init(ttynull_init);
module_exit(ttynull_exit);
MODULE_LICENSE("GPL v2");
// SPDX-License-Identifier: GPL-2.0
/* vcc.c: sun4v virtual channel concentrator /* vcc.c: sun4v virtual channel concentrator
* *
* Copyright (C) 2017 Oracle. All rights reserved. * Copyright (C) 2017 Oracle. All rights reserved.
......
# SPDX-License-Identifier: GPL-2.0
consolemap_deftbl.c consolemap_deftbl.c
defkeymap.c defkeymap.c
...@@ -542,7 +542,7 @@ int con_set_unimap(struct vc_data *vc, ushort ct, struct unipair __user *list) ...@@ -542,7 +542,7 @@ int con_set_unimap(struct vc_data *vc, ushort ct, struct unipair __user *list)
if (!ct) if (!ct)
return 0; return 0;
unilist = memdup_user(list, ct * sizeof(struct unipair)); unilist = vmemdup_user(list, ct * sizeof(struct unipair));
if (IS_ERR(unilist)) if (IS_ERR(unilist))
return PTR_ERR(unilist); return PTR_ERR(unilist);
...@@ -641,7 +641,7 @@ int con_set_unimap(struct vc_data *vc, ushort ct, struct unipair __user *list) ...@@ -641,7 +641,7 @@ int con_set_unimap(struct vc_data *vc, ushort ct, struct unipair __user *list)
out_unlock: out_unlock:
console_unlock(); console_unlock();
kfree(unilist); kvfree(unilist);
return err; return err;
} }
...@@ -743,7 +743,7 @@ int con_get_unimap(struct vc_data *vc, ushort ct, ushort __user *uct, struct uni ...@@ -743,7 +743,7 @@ int con_get_unimap(struct vc_data *vc, ushort ct, ushort __user *uct, struct uni
struct uni_pagedir *p; struct uni_pagedir *p;
struct unipair *unilist; struct unipair *unilist;
unilist = kmalloc_array(ct, sizeof(struct unipair), GFP_KERNEL); unilist = kvmalloc_array(ct, sizeof(struct unipair), GFP_KERNEL);
if (!unilist) if (!unilist)
return -ENOMEM; return -ENOMEM;
...@@ -775,7 +775,7 @@ int con_get_unimap(struct vc_data *vc, ushort ct, ushort __user *uct, struct uni ...@@ -775,7 +775,7 @@ int con_get_unimap(struct vc_data *vc, ushort ct, ushort __user *uct, struct uni
if (copy_to_user(list, unilist, min(ect, ct) * sizeof(struct unipair))) if (copy_to_user(list, unilist, min(ect, ct) * sizeof(struct unipair)))
ret = -EFAULT; ret = -EFAULT;
put_user(ect, uct); put_user(ect, uct);
kfree(unilist); kvfree(unilist);
return ret ? ret : (ect <= ct) ? 0 : -ENOMEM; return ret ? ret : (ect <= ct) ? 0 : -ENOMEM;
} }
......
# SPDX-License-Identifier: GPL-2.0
# #
# Unicode table for IBM Codepage 437. Note that there are many more # Unicode table for IBM Codepage 437. Note that there are many more
# substitutions that could be conceived (for example, thick-line # substitutions that could be conceived (for example, thick-line
......
// SPDX-License-Identifier: GPL-2.0
/* Do not edit this file! It was automatically generated by */ /* Do not edit this file! It was automatically generated by */
/* loadkeys --mktable defkeymap.map > defkeymap.c */ /* loadkeys --mktable defkeymap.map > defkeymap.c */
......
# SPDX-License-Identifier: GPL-2.0
# Default kernel keymap. This uses 7 modifier combinations. # Default kernel keymap. This uses 7 modifier combinations.
keymaps 0-2,4-5,8,12 keymaps 0-2,4-5,8,12
# Change the above line into # Change the above line into
......
...@@ -123,6 +123,7 @@ static const int NR_TYPES = ARRAY_SIZE(max_vals); ...@@ -123,6 +123,7 @@ static const int NR_TYPES = ARRAY_SIZE(max_vals);
static struct input_handler kbd_handler; static struct input_handler kbd_handler;
static DEFINE_SPINLOCK(kbd_event_lock); static DEFINE_SPINLOCK(kbd_event_lock);
static DEFINE_SPINLOCK(led_lock); static DEFINE_SPINLOCK(led_lock);
static DEFINE_SPINLOCK(func_buf_lock); /* guard 'func_buf' and friends */
static unsigned long key_down[BITS_TO_LONGS(KEY_CNT)]; /* keyboard key bitmap */ static unsigned long key_down[BITS_TO_LONGS(KEY_CNT)]; /* keyboard key bitmap */
static unsigned char shift_down[NR_SHIFT]; /* shift state counters.. */ static unsigned char shift_down[NR_SHIFT]; /* shift state counters.. */
static bool dead_key_next; static bool dead_key_next;
...@@ -1449,7 +1450,7 @@ static void kbd_keycode(unsigned int keycode, int down, int hw_raw) ...@@ -1449,7 +1450,7 @@ static void kbd_keycode(unsigned int keycode, int down, int hw_raw)
KBD_UNICODE, &param); KBD_UNICODE, &param);
if (rc != NOTIFY_STOP) if (rc != NOTIFY_STOP)
if (down && !raw_mode) if (down && !raw_mode)
to_utf8(vc, keysym); k_unicode(vc, keysym, !down);
return; return;
} }
...@@ -1990,11 +1991,12 @@ int vt_do_kdgkb_ioctl(int cmd, struct kbsentry __user *user_kdgkb, int perm) ...@@ -1990,11 +1991,12 @@ int vt_do_kdgkb_ioctl(int cmd, struct kbsentry __user *user_kdgkb, int perm)
char *p; char *p;
u_char *q; u_char *q;
u_char __user *up; u_char __user *up;
int sz; int sz, fnw_sz;
int delta; int delta;
char *first_free, *fj, *fnw; char *first_free, *fj, *fnw;
int i, j, k; int i, j, k;
int ret; int ret;
unsigned long flags;
if (!capable(CAP_SYS_TTY_CONFIG)) if (!capable(CAP_SYS_TTY_CONFIG))
perm = 0; perm = 0;
...@@ -2037,7 +2039,14 @@ int vt_do_kdgkb_ioctl(int cmd, struct kbsentry __user *user_kdgkb, int perm) ...@@ -2037,7 +2039,14 @@ int vt_do_kdgkb_ioctl(int cmd, struct kbsentry __user *user_kdgkb, int perm)
goto reterr; goto reterr;
} }
fnw = NULL;
fnw_sz = 0;
/* race aginst other writers */
again:
spin_lock_irqsave(&func_buf_lock, flags);
q = func_table[i]; q = func_table[i];
/* fj pointer to next entry after 'q' */
first_free = funcbufptr + (funcbufsize - funcbufleft); first_free = funcbufptr + (funcbufsize - funcbufleft);
for (j = i+1; j < MAX_NR_FUNC && !func_table[j]; j++) for (j = i+1; j < MAX_NR_FUNC && !func_table[j]; j++)
; ;
...@@ -2045,10 +2054,12 @@ int vt_do_kdgkb_ioctl(int cmd, struct kbsentry __user *user_kdgkb, int perm) ...@@ -2045,10 +2054,12 @@ int vt_do_kdgkb_ioctl(int cmd, struct kbsentry __user *user_kdgkb, int perm)
fj = func_table[j]; fj = func_table[j];
else else
fj = first_free; fj = first_free;
/* buffer usage increase by new entry */
delta = (q ? -strlen(q) : 1) + strlen(kbs->kb_string); delta = (q ? -strlen(q) : 1) + strlen(kbs->kb_string);
if (delta <= funcbufleft) { /* it fits in current buf */ if (delta <= funcbufleft) { /* it fits in current buf */
if (j < MAX_NR_FUNC) { if (j < MAX_NR_FUNC) {
/* make enough space for new entry at 'fj' */
memmove(fj + delta, fj, first_free - fj); memmove(fj + delta, fj, first_free - fj);
for (k = j; k < MAX_NR_FUNC; k++) for (k = j; k < MAX_NR_FUNC; k++)
if (func_table[k]) if (func_table[k])
...@@ -2061,20 +2072,28 @@ int vt_do_kdgkb_ioctl(int cmd, struct kbsentry __user *user_kdgkb, int perm) ...@@ -2061,20 +2072,28 @@ int vt_do_kdgkb_ioctl(int cmd, struct kbsentry __user *user_kdgkb, int perm)
sz = 256; sz = 256;
while (sz < funcbufsize - funcbufleft + delta) while (sz < funcbufsize - funcbufleft + delta)
sz <<= 1; sz <<= 1;
if (fnw_sz != sz) {
spin_unlock_irqrestore(&func_buf_lock, flags);
kfree(fnw);
fnw = kmalloc(sz, GFP_KERNEL); fnw = kmalloc(sz, GFP_KERNEL);
if(!fnw) { fnw_sz = sz;
if (!fnw) {
ret = -ENOMEM; ret = -ENOMEM;
goto reterr; goto reterr;
} }
goto again;
}
if (!q) if (!q)
func_table[i] = fj; func_table[i] = fj;
/* copy data before insertion point to new location */
if (fj > funcbufptr) if (fj > funcbufptr)
memmove(fnw, funcbufptr, fj - funcbufptr); memmove(fnw, funcbufptr, fj - funcbufptr);
for (k = 0; k < j; k++) for (k = 0; k < j; k++)
if (func_table[k]) if (func_table[k])
func_table[k] = fnw + (func_table[k] - funcbufptr); func_table[k] = fnw + (func_table[k] - funcbufptr);
/* copy data after insertion point to new location */
if (first_free > fj) { if (first_free > fj) {
memmove(fnw + (fj - funcbufptr) + delta, fj, first_free - fj); memmove(fnw + (fj - funcbufptr) + delta, fj, first_free - fj);
for (k = j; k < MAX_NR_FUNC; k++) for (k = j; k < MAX_NR_FUNC; k++)
...@@ -2087,7 +2106,9 @@ int vt_do_kdgkb_ioctl(int cmd, struct kbsentry __user *user_kdgkb, int perm) ...@@ -2087,7 +2106,9 @@ int vt_do_kdgkb_ioctl(int cmd, struct kbsentry __user *user_kdgkb, int perm)
funcbufleft = funcbufleft - delta + sz - funcbufsize; funcbufleft = funcbufleft - delta + sz - funcbufsize;
funcbufsize = sz; funcbufsize = sz;
} }
/* finally insert item itself */
strcpy(func_table[i], kbs->kb_string); strcpy(func_table[i], kbs->kb_string);
spin_unlock_irqrestore(&func_buf_lock, flags);
break; break;
} }
ret = 0; ret = 0;
......
// SPDX-License-Identifier: GPL-2.0 // SPDX-License-Identifier: GPL-2.0
/* /*
* Provide access to virtual console memory. * Provide access to virtual console memory.
* /dev/vcs0: the screen as it is being viewed right now (possibly scrolled) * /dev/vcs: the screen as it is being viewed right now (possibly scrolled)
* /dev/vcsN: the screen of /dev/ttyN (1 <= N <= 63) * /dev/vcsN: the screen of /dev/ttyN (1 <= N <= 63)
* [minor: N] * [minor: N]
* *
......
...@@ -4180,8 +4180,6 @@ void do_blank_screen(int entering_gfx) ...@@ -4180,8 +4180,6 @@ void do_blank_screen(int entering_gfx)
return; return;
} }
if (blank_state != blank_normal_wait)
return;
blank_state = blank_off; blank_state = blank_off;
/* don't blank graphics */ /* don't blank graphics */
......
...@@ -45,7 +45,7 @@ struct device; ...@@ -45,7 +45,7 @@ struct device;
/* /*
* This structure describes all the operations that can be done on the * This structure describes all the operations that can be done on the
* physical hardware. See Documentation/serial/driver for details. * physical hardware. See Documentation/serial/driver.rst for details.
*/ */
struct uart_ops { struct uart_ops {
unsigned int (*tx_empty)(struct uart_port *); unsigned int (*tx_empty)(struct uart_port *);
......
...@@ -287,4 +287,10 @@ ...@@ -287,4 +287,10 @@
/* RDA UART */ /* RDA UART */
#define PORT_RDA 118 #define PORT_RDA 118
/* Socionext Milbeaut UART */
#define PORT_MLB_USIO 119
/* SiFive UART */
#define PORT_SIFIVE_V0 120
#endif /* _UAPILINUX_SERIAL_CORE_H */ #endif /* _UAPILINUX_SERIAL_CORE_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment