TTY/Serial driver patches for 4.19-rc1

Here is the big tty and serial driver pull request for 4.19-rc1.
 
 It's not all that big, just a number of small serial driver updates and
 fixes, along with some better vt handling for unicode characters for
 those using braille terminals.
 
 Full details are in the shortlog.
 
 All of these patches have been in linux-next for a long time with no
 reported issues.
 
 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
 -----BEGIN PGP SIGNATURE-----
 
 iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCW3g/5Q8cZ3JlZ0Brcm9h
 aC5jb20ACgkQMUfUDdst+ynZDwCdETeD4sIqt06hXeG4ADiVORb3gLgAnjJTbl9Y
 reffAFDRWrwD42SvTi1X
 =7tX/
 -----END PGP SIGNATURE-----

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

Pull tty/serial driver updates from Greg KH:
 "Here is the big tty and serial driver pull request for 4.19-rc1.

  It's not all that big, just a number of small serial driver updates
  and fixes, along with some better vt handling for unicode characters
  for those using braille terminals.

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

* tag 'tty-4.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty: (73 commits)
  tty: serial: 8250: Revert NXP SC16C2552 workaround
  serial: 8250_exar: Read INT0 from slave device, too
  tty: rocket: Fix possible buffer overwrite on register_PCI
  serial: 8250_dw: Add ACPI support for uart on Broadcom SoC
  serial: 8250_dw: always set baud rate in dw8250_set_termios
  dt-bindings: serial: Add binding for uartlite
  tty: serial: uartlite: Add support for suspend and resume
  tty: serial: uartlite: Add clock adaptation
  tty: serial: uartlite: Add structure for private data
  serial: sh-sci: Improve support for separate TEI and DRI interrupts
  serial: sh-sci: Remove SCIx_RZ_SCIFA_REGTYPE
  serial: sh-sci: Allow for compressed SCIF address
  serial: sh-sci: Improve interrupts description
  serial: 8250: Use cached port name directly in messages
  serial: 8250_exar: Drop unused variable in pci_xr17v35x_setup()
  vt: drop unused struct vt_struct
  vt: avoid a VLA in the unicode screen scroll function
  vt: add /dev/vcsu* to devices.txt
  vt: coherence validation code for the unicode screen buffer
  vt: selection: take screen contents from uniscr if available
  ...
This commit is contained in:
Linus Torvalds 2018-08-18 10:50:41 -07:00
commit 336722eb9d
44 changed files with 1382 additions and 339 deletions

View File

@ -173,14 +173,18 @@
they are redirected through the parport multiplex layer.
7 char Virtual console capture devices
0 = /dev/vcs Current vc text contents
1 = /dev/vcs1 tty1 text contents
0 = /dev/vcs Current vc text (glyph) contents
1 = /dev/vcs1 tty1 text (glyph) contents
...
63 = /dev/vcs63 tty63 text contents
128 = /dev/vcsa Current vc text/attribute contents
129 = /dev/vcsa1 tty1 text/attribute contents
63 = /dev/vcs63 tty63 text (glyph) contents
64 = /dev/vcsu Current vc text (unicode) contents
65 = /dev/vcsu1 tty1 text (unicode) contents
...
191 = /dev/vcsa63 tty63 text/attribute contents
127 = /dev/vcsu63 tty63 text (unicode) contents
128 = /dev/vcsa Current vc text/attribute (glyph) contents
129 = /dev/vcsa1 tty1 text/attribute (glyph) contents
...
191 = /dev/vcsa63 tty63 text/attribute (glyph) contents
NOTE: These devices permit both read and write access.

View File

@ -11,6 +11,7 @@ compatible: Must contain one of
"mediatek,mt6589"
"mediatek,mt6592"
"mediatek,mt6755"
"mediatek,mt6765"
"mediatek,mt6795"
"mediatek,mt6797"
"mediatek,mt7622"
@ -41,6 +42,9 @@ Supported boards:
- Evaluation phone for MT6755(Helio P10):
Required root node properties:
- compatible = "mediatek,mt6755-evb", "mediatek,mt6755";
- Evaluation board for MT6765(Helio P22):
Required root node properties:
- compatible = "mediatek,mt6765-evb", "mediatek,mt6765";
- Evaluation board for MT6795(Helio X10):
Required root node properties:
- compatible = "mediatek,mt6795-evb", "mediatek,mt6795";

View File

@ -11,6 +11,7 @@ Required properties:
"mediatek,mt7622-sysirq", "mediatek,mt6577-sysirq": for MT7622
"mediatek,mt6795-sysirq", "mediatek,mt6577-sysirq": for MT6795
"mediatek,mt6797-sysirq", "mediatek,mt6577-sysirq": for MT6797
"mediatek,mt6765-sysirq", "mediatek,mt6577-sysirq": for MT6765
"mediatek,mt6755-sysirq", "mediatek,mt6577-sysirq": for MT6755
"mediatek,mt6592-sysirq", "mediatek,mt6577-sysirq": for MT6592
"mediatek,mt6589-sysirq", "mediatek,mt6577-sysirq": for MT6589

View File

@ -9,7 +9,11 @@ Optional properties:
- fsl,dte-mode : Indicate the uart works in DTE mode. The uart works
in DCE mode by default.
- rs485-rts-delay, rs485-rts-active-low, rs485-rx-during-tx,
linux,rs485-enabled-at-boot-time: see rs485.txt
linux,rs485-enabled-at-boot-time: see rs485.txt. Note that for RS485
you must enable either the "uart-has-rtscts" or the "rts-gpios"
properties. In case you use "uart-has-rtscts" the signal that controls
the transceiver is actually CTS_B, not RTS_B. CTS_B is always output,
and RTS_B is input, regardless of dte-mode.
Please check Documentation/devicetree/bindings/serial/serial.txt
for the complete list of generic properties.

View File

@ -8,6 +8,7 @@ Required properties:
* "mediatek,mt6582-uart" for MT6582 compatible UARTS
* "mediatek,mt6589-uart" for MT6589 compatible UARTS
* "mediatek,mt6755-uart" for MT6755 compatible UARTS
* "mediatek,mt6765-uart" for MT6765 compatible UARTS
* "mediatek,mt6795-uart" for MT6795 compatible UARTS
* "mediatek,mt6797-uart" for MT6797 compatible UARTS
* "mediatek,mt7622-uart" for MT7622 compatible UARTS

View File

@ -1,6 +1,7 @@
OMAP UART controller
Required properties:
- compatible : should be "ti,am654-uart" for AM654 controllers
- compatible : should be "ti,omap2-uart" for OMAP2 controllers
- compatible : should be "ti,omap3-uart" for OMAP3 controllers
- compatible : should be "ti,omap4-uart" for OMAP4 controllers

View File

@ -0,0 +1,10 @@
Renesas RZ/N1 UART
This controller is based on the Synopsys DesignWare ABP UART and inherits all
properties defined in snps-dw-apb-uart.txt except for the compatible property.
Required properties:
- compatible : The device specific string followed by the generic RZ/N1 string.
Therefore it must be one of:
"renesas,r9a06g032-uart", "renesas,rzn1-uart"
"renesas,r9a06g033-uart", "renesas,rzn1-uart"

View File

@ -5,6 +5,7 @@ Required properties:
- compatible: Must contain one or more of the following:
- "renesas,scif-r7s72100" for R7S72100 (RZ/A1H) SCIF compatible UART.
- "renesas,scif-r7s9210" for R7S9210 (RZ/A2) SCIF compatible UART.
- "renesas,scifa-r8a73a4" for R8A73A4 (R-Mobile APE6) SCIFA compatible UART.
- "renesas,scifb-r8a73a4" for R8A73A4 (R-Mobile APE6) SCIFB compatible UART.
- "renesas,scifa-r8a7740" for R8A7740 (R-Mobile A1) SCIFA compatible UART.
@ -72,7 +73,21 @@ Required properties:
family-specific and/or generic versions.
- reg: Base address and length of the I/O registers used by the UART.
- interrupts: Must contain an interrupt-specifier for the SCIx interrupt.
- interrupts: Must contain one or more interrupt-specifiers for the SCIx.
If a single interrupt is expressed, then all events are
multiplexed into this single interrupt.
If multiple interrupts are provided by the hardware, the order
in which the interrupts are listed must match order below. Note
that some HW interrupt events may be muxed together resulting
in duplicate entries.
The interrupt order is as follows:
1. Error (ERI)
2. Receive buffer full (RXI)
3. Transmit buffer empty (TXI)
4. Break (BRI)
5. Data Ready (DRI)
6. Transmit End (TEI)
- clocks: Must contain a phandle and clock-specifier pair for each entry
in clock-names.
@ -89,7 +104,7 @@ Required properties:
- "scif_clk" for the optional external clock source for the frequency
divider (SCIF_CLK).
Note: Each enabled SCIx UART should have an alias correctly numbered in the
Note: Each enabled SCIx UART may have an optional "serialN" alias in the
"aliases" node.
Optional properties:

View File

@ -0,0 +1,23 @@
Xilinx Axi Uartlite controller Device Tree Bindings
---------------------------------------------------------
Required properties:
- compatible : Can be either of
"xlnx,xps-uartlite-1.00.a"
"xlnx,opb-uartlite-1.00.b"
- reg : Physical base address and size of the Axi Uartlite
registers map.
- interrupts : Should contain the UART controller interrupt.
Optional properties:
- port-number : Set Uart port number
- clock-names : Should be "s_axi_aclk"
- clocks : Input clock specifier. Refer to common clock bindings.
Example:
serial@800c0000 {
compatible = "xlnx,xps-uartlite-1.00.a";
reg = <0x0 0x800c0000 0x10000>;
interrupts = <0x0 0x6e 0x1>;
port-number = <0>;
};

View File

@ -46,7 +46,7 @@ Child nodes should conform to I2C bus binding as described in i2c.txt.
Qualcomm Technologies Inc. GENI Serial Engine based UART Controller
Required properties:
- compatible: Must be "qcom,geni-debug-uart".
- compatible: Must be "qcom,geni-debug-uart" or "qcom,geni-uart".
- reg: Must contain UART register location and length.
- interrupts: Must contain UART core interrupts.
- clock-names: Must contain "se".

View File

@ -58,6 +58,7 @@ enum parport_pc_pci_cards {
timedia_9079c,
wch_ch353_1s1p,
wch_ch353_2s1p,
wch_ch382_0s1p,
wch_ch382_2s1p,
brainboxes_5s1p,
sunix_2s1p,
@ -147,6 +148,7 @@ static struct parport_pc_pci cards[] = {
/* timedia_9079c */ { 1, { { 2, 3 }, } },
/* wch_ch353_1s1p*/ { 1, { { 1, -1}, } },
/* wch_ch353_2s1p*/ { 1, { { 2, -1}, } },
/* wch_ch382_0s1p*/ { 1, { { 2, -1}, } },
/* wch_ch382_2s1p*/ { 1, { { 2, -1}, } },
/* brainboxes_5s1p */ { 1, { { 3, -1 }, } },
/* sunix_2s1p */ { 1, { { 3, -1 }, } },
@ -252,6 +254,7 @@ static struct pci_device_id parport_serial_pci_tbl[] = {
/* WCH CARDS */
{ 0x4348, 0x5053, PCI_ANY_ID, PCI_ANY_ID, 0, 0, wch_ch353_1s1p},
{ 0x4348, 0x7053, 0x4348, 0x3253, 0, 0, wch_ch353_2s1p},
{ 0x1c00, 0x3050, 0x1c00, 0x3050, 0, 0, wch_ch382_0s1p},
{ 0x1c00, 0x3250, 0x1c00, 0x3250, 0, 0, wch_ch382_2s1p},
/* BrainBoxes PX272/PX306 MIO card */
@ -494,6 +497,12 @@ static struct pciserial_board pci_parport_serial_boards[] = {
.base_baud = 115200,
.uart_offset = 8,
},
[wch_ch382_0s1p] = {
.flags = FL_BASE0,
.num_ports = 0,
.base_baud = 115200,
.uart_offset = 8,
},
[wch_ch382_2s1p] = {
.flags = FL_BASE0,
.num_ports = 2,

View File

@ -39,8 +39,34 @@ static const int kbd_max_vals[] = {
};
static const int KBD_NR_TYPES = ARRAY_SIZE(kbd_max_vals);
static unsigned char ret_diacr[NR_DEAD] = {
'`', '\'', '^', '~', '"', ','
static const unsigned char ret_diacr[NR_DEAD] = {
'`', /* dead_grave */
'\'', /* dead_acute */
'^', /* dead_circumflex */
'~', /* dead_tilda */
'"', /* dead_diaeresis */
',', /* dead_cedilla */
'_', /* dead_macron */
'U', /* dead_breve */
'.', /* dead_abovedot */
'*', /* dead_abovering */
'=', /* dead_doubleacute */
'c', /* dead_caron */
'k', /* dead_ogonek */
'i', /* dead_iota */
'#', /* dead_voiced_sound */
'o', /* dead_semivoiced_sound */
'!', /* dead_belowdot */
'?', /* dead_hook */
'+', /* dead_horn */
'-', /* dead_stroke */
')', /* dead_abovecomma */
'(', /* dead_abovereversedcomma */
':', /* dead_doublegrave */
'n', /* dead_invertedbreve */
';', /* dead_belowcomma */
'$', /* dead_currency */
'@', /* dead_greek */
};
/*

View File

@ -625,7 +625,7 @@ int ptm_open_peer(struct file *master, struct tty_struct *tty, int flags)
if (tty->driver != ptm_driver)
return -EIO;
fd = get_unused_fd_flags(0);
fd = get_unused_fd_flags(flags);
if (fd < 0) {
retval = fd;
goto err;

View File

@ -1881,7 +1881,7 @@ static __init int register_PCI(int i, struct pci_dev *dev)
ByteIO_t UPCIRingInd = 0;
if (!dev || !pci_match_id(rocket_pci_ids, dev) ||
pci_enable_device(dev))
pci_enable_device(dev) || i >= NUM_BOARDS)
return 0;
rcktpt_io_addr[i] = pci_resource_start(dev, 0);

View File

@ -13,6 +13,8 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/pm_domain.h>
#include <linux/pm_runtime.h>
#include <linux/serdev.h>
#include <linux/slab.h>
@ -143,11 +145,28 @@ EXPORT_SYMBOL_GPL(serdev_device_remove);
int serdev_device_open(struct serdev_device *serdev)
{
struct serdev_controller *ctrl = serdev->ctrl;
int ret;
if (!ctrl || !ctrl->ops->open)
return -EINVAL;
return ctrl->ops->open(ctrl);
ret = ctrl->ops->open(ctrl);
if (ret)
return ret;
ret = pm_runtime_get_sync(&ctrl->dev);
if (ret < 0) {
pm_runtime_put_noidle(&ctrl->dev);
goto err_close;
}
return 0;
err_close:
if (ctrl->ops->close)
ctrl->ops->close(ctrl);
return ret;
}
EXPORT_SYMBOL_GPL(serdev_device_open);
@ -158,6 +177,8 @@ void serdev_device_close(struct serdev_device *serdev)
if (!ctrl || !ctrl->ops->close)
return;
pm_runtime_put(&ctrl->dev);
ctrl->ops->close(ctrl);
}
EXPORT_SYMBOL_GPL(serdev_device_close);
@ -330,8 +351,17 @@ EXPORT_SYMBOL_GPL(serdev_device_set_tiocm);
static int serdev_drv_probe(struct device *dev)
{
const struct serdev_device_driver *sdrv = to_serdev_device_driver(dev->driver);
int ret;
return sdrv->probe(to_serdev_device(dev));
ret = dev_pm_domain_attach(dev, true);
if (ret)
return ret;
ret = sdrv->probe(to_serdev_device(dev));
if (ret)
dev_pm_domain_detach(dev, true);
return ret;
}
static int serdev_drv_remove(struct device *dev)
@ -339,6 +369,9 @@ static int serdev_drv_remove(struct device *dev)
const struct serdev_device_driver *sdrv = to_serdev_device_driver(dev->driver);
if (sdrv->remove)
sdrv->remove(to_serdev_device(dev));
dev_pm_domain_detach(dev, true);
return 0;
}
@ -416,6 +449,9 @@ struct serdev_controller *serdev_controller_alloc(struct device *parent,
dev_set_name(&ctrl->dev, "serial%d", id);
pm_runtime_no_callbacks(&ctrl->dev);
pm_suspend_ignore_children(&ctrl->dev, true);
dev_dbg(&ctrl->dev, "allocated controller 0x%p id %d\n", ctrl, id);
return ctrl;
@ -547,20 +583,23 @@ int serdev_controller_add(struct serdev_controller *ctrl)
if (ret)
return ret;
pm_runtime_enable(&ctrl->dev);
ret_of = of_serdev_register_devices(ctrl);
ret_acpi = acpi_serdev_register_devices(ctrl);
if (ret_of && ret_acpi) {
dev_dbg(&ctrl->dev, "no devices registered: of:%d acpi:%d\n",
ret_of, ret_acpi);
ret = -ENODEV;
goto out_dev_del;
goto err_rpm_disable;
}
dev_dbg(&ctrl->dev, "serdev%d registered: dev:%p\n",
ctrl->nr, &ctrl->dev);
return 0;
out_dev_del:
err_rpm_disable:
pm_runtime_disable(&ctrl->dev);
device_del(&ctrl->dev);
return ret;
};
@ -591,6 +630,7 @@ void serdev_controller_remove(struct serdev_controller *ctrl)
dummy = device_for_each_child(&ctrl->dev, NULL,
serdev_remove_device);
pm_runtime_disable(&ctrl->dev);
device_del(&ctrl->dev);
}
EXPORT_SYMBOL_GPL(serdev_controller_remove);

View File

@ -323,7 +323,7 @@ static int univ8250_setup_irq(struct uart_8250_port *up)
* the port is opened so this value needs to be preserved.
*/
if (up->bugs & UART_BUG_THRE) {
pr_debug("ttyS%d - using backup timer\n", serial_index(port));
pr_debug("%s - using backup timer\n", port->name);
up->timer.function = serial8250_backup_timeout;
mod_timer(&up->timer, jiffies +
@ -1023,6 +1023,10 @@ int serial8250_register_8250_port(struct uart_8250_port *up)
uart->port.get_mctrl = up->port.get_mctrl;
if (up->port.set_mctrl)
uart->port.set_mctrl = up->port.set_mctrl;
if (up->port.get_divisor)
uart->port.get_divisor = up->port.get_divisor;
if (up->port.set_divisor)
uart->port.set_divisor = up->port.set_divisor;
if (up->port.startup)
uart->port.startup = up->port.startup;
if (up->port.shutdown)

View File

@ -31,6 +31,7 @@
/* Offsets for the DesignWare specific registers */
#define DW_UART_USR 0x1f /* UART Status Register */
#define DW_UART_DLF 0xc0 /* Divisor Latch Fraction Register */
#define DW_UART_CPR 0xf4 /* Component Parameter Register */
#define DW_UART_UCV 0xf8 /* UART Component Version */
@ -55,6 +56,7 @@
struct dw8250_data {
u8 usr_reg;
u8 dlf_size;
int line;
int msr_mask_on;
int msr_mask_off;
@ -67,6 +69,21 @@ struct dw8250_data {
unsigned int uart_16550_compatible:1;
};
static inline u32 dw8250_readl_ext(struct uart_port *p, int offset)
{
if (p->iotype == UPIO_MEM32BE)
return ioread32be(p->membase + offset);
return readl(p->membase + offset);
}
static inline void dw8250_writel_ext(struct uart_port *p, int offset, u32 reg)
{
if (p->iotype == UPIO_MEM32BE)
iowrite32be(reg, p->membase + offset);
else
writel(reg, p->membase + offset);
}
static inline int dw8250_modify_msr(struct uart_port *p, int offset, int value)
{
struct dw8250_data *d = p->private_data;
@ -293,7 +310,7 @@ static void dw8250_set_termios(struct uart_port *p, struct ktermios *termios,
long rate;
int ret;
if (IS_ERR(d->clk) || !old)
if (IS_ERR(d->clk))
goto out;
clk_disable_unprepare(d->clk);
@ -351,6 +368,37 @@ static bool dw8250_idma_filter(struct dma_chan *chan, void *param)
return param == chan->device->dev->parent;
}
/*
* divisor = div(I) + div(F)
* "I" means integer, "F" means fractional
* quot = div(I) = clk / (16 * baud)
* frac = div(F) * 2^dlf_size
*
* let rem = clk % (16 * baud)
* we have: div(F) * (16 * baud) = rem
* so frac = 2^dlf_size * rem / (16 * baud) = (rem << dlf_size) / (16 * baud)
*/
static unsigned int dw8250_get_divisor(struct uart_port *p,
unsigned int baud,
unsigned int *frac)
{
unsigned int quot, rem, base_baud = baud * 16;
struct dw8250_data *d = p->private_data;
quot = p->uartclk / base_baud;
rem = p->uartclk % base_baud;
*frac = DIV_ROUND_CLOSEST(rem << d->dlf_size, base_baud);
return quot;
}
static void dw8250_set_divisor(struct uart_port *p, unsigned int baud,
unsigned int quot, unsigned int quot_frac)
{
dw8250_writel_ext(p, DW_UART_DLF, quot_frac);
serial8250_do_set_divisor(p, baud, quot, quot_frac);
}
static void dw8250_quirks(struct uart_port *p, struct dw8250_data *data)
{
if (p->dev->of_node) {
@ -404,20 +452,26 @@ static void dw8250_setup_port(struct uart_port *p)
* If the Component Version Register returns zero, we know that
* ADDITIONAL_FEATURES are not enabled. No need to go any further.
*/
if (p->iotype == UPIO_MEM32BE)
reg = ioread32be(p->membase + DW_UART_UCV);
else
reg = readl(p->membase + DW_UART_UCV);
reg = dw8250_readl_ext(p, DW_UART_UCV);
if (!reg)
return;
dev_dbg(p->dev, "Designware UART version %c.%c%c\n",
(reg >> 24) & 0xff, (reg >> 16) & 0xff, (reg >> 8) & 0xff);
if (p->iotype == UPIO_MEM32BE)
reg = ioread32be(p->membase + DW_UART_CPR);
else
reg = readl(p->membase + DW_UART_CPR);
dw8250_writel_ext(p, DW_UART_DLF, ~0U);
reg = dw8250_readl_ext(p, DW_UART_DLF);
dw8250_writel_ext(p, DW_UART_DLF, 0);
if (reg) {
struct dw8250_data *d = p->private_data;
d->dlf_size = fls(reg);
p->get_divisor = dw8250_get_divisor;
p->set_divisor = dw8250_set_divisor;
}
reg = dw8250_readl_ext(p, DW_UART_CPR);
if (!reg)
return;
@ -693,6 +747,7 @@ static const struct of_device_id dw8250_of_match[] = {
{ .compatible = "snps,dw-apb-uart" },
{ .compatible = "cavium,octeon-3860-uart" },
{ .compatible = "marvell,armada-38x-uart" },
{ .compatible = "renesas,rzn1-uart" },
{ /* Sentinel */ }
};
MODULE_DEVICE_TABLE(of, dw8250_of_match);
@ -707,6 +762,7 @@ static const struct acpi_device_id dw8250_acpi_match[] = {
{ "APMC0D08", 0},
{ "AMD0020", 0 },
{ "AMDI0020", 0 },
{ "BRCM2032", 0 },
{ "HISI0031", 0 },
{ },
};

View File

@ -109,11 +109,12 @@ struct exar8250_platform {
* struct exar8250_board - board information
* @num_ports: number of serial ports
* @reg_shift: describes UART register mapping in PCI memory
* @setup: quirk run at ->probe() stage
* @exit: quirk run at ->remove() stage
*/
struct exar8250_board {
unsigned int num_ports;
unsigned int reg_shift;
bool has_slave;
int (*setup)(struct exar8250 *, struct pci_dev *,
struct uart_8250_port *, int);
void (*exit)(struct pci_dev *pcidev);
@ -272,8 +273,32 @@ static int xr17v35x_register_gpio(struct pci_dev *pcidev,
return 0;
}
static int generic_rs485_config(struct uart_port *port,
struct serial_rs485 *rs485)
{
bool is_rs485 = !!(rs485->flags & SER_RS485_ENABLED);
u8 __iomem *p = port->membase;
u8 value;
value = readb(p + UART_EXAR_FCTR);
if (is_rs485)
value |= UART_FCTR_EXAR_485;
else
value &= ~UART_FCTR_EXAR_485;
writeb(value, p + UART_EXAR_FCTR);
if (is_rs485)
writeb(UART_EXAR_RS485_DLY(4), p + UART_MSR);
port->rs485 = *rs485;
return 0;
}
static const struct exar8250_platform exar8250_default_platform = {
.register_gpio = xr17v35x_register_gpio,
.rs485_config = generic_rs485_config,
};
static int iot2040_rs485_config(struct uart_port *port,
@ -306,19 +331,7 @@ static int iot2040_rs485_config(struct uart_port *port,
value |= mode;
writeb(value, p + UART_EXAR_MPIOLVL_7_0);
value = readb(p + UART_EXAR_FCTR);
if (is_rs485)
value |= UART_FCTR_EXAR_485;
else
value &= ~UART_FCTR_EXAR_485;
writeb(value, p + UART_EXAR_FCTR);
if (is_rs485)
writeb(UART_EXAR_RS485_DLY(4), p + UART_MSR);
port->rs485 = *rs485;
return 0;
return generic_rs485_config(port, rs485);
}
static const struct property_entry iot2040_gpio_properties[] = {
@ -364,7 +377,6 @@ static int
pci_xr17v35x_setup(struct exar8250 *priv, struct pci_dev *pcidev,
struct uart_8250_port *port, int idx)
{
const struct exar8250_board *board = priv->board;
const struct exar8250_platform *platform;
const struct dmi_system_id *dmi_match;
unsigned int offset = idx * 0x400;
@ -382,10 +394,10 @@ pci_xr17v35x_setup(struct exar8250 *priv, struct pci_dev *pcidev,
port->port.rs485_config = platform->rs485_config;
/*
* Setup the uart clock for the devices on expansion slot to
* Setup the UART clock for the devices on expansion slot to
* half the clock speed of the main chip (which is 125MHz)
*/
if (board->has_slave && idx >= 8)
if (idx >= 8)
port->port.uartclk /= 2;
ret = default_setup(priv, pcidev, idx, offset, port);
@ -433,7 +445,11 @@ static irqreturn_t exar_misc_handler(int irq, void *data)
struct exar8250 *priv = data;
/* Clear all PCI interrupts by reading INT0. No effect on IIR */
ioread8(priv->virt + UART_EXAR_INT0);
readb(priv->virt + UART_EXAR_INT0);
/* Clear INT0 for Expansion Interface slave ports, too */
if (priv->board->num_ports > 8)
readb(priv->virt + 0x2000 + UART_EXAR_INT0);
return IRQ_HANDLED;
}
@ -590,14 +606,12 @@ static const struct exar8250_board pbn_exar_XR17V35x = {
static const struct exar8250_board pbn_exar_XR17V4358 = {
.num_ports = 12,
.has_slave = true,
.setup = pci_xr17v35x_setup,
.exit = pci_xr17v35x_exit,
};
static const struct exar8250_board pbn_exar_XR17V8358 = {
.num_ports = 16,
.has_slave = true,
.setup = pci_xr17v35x_setup,
.exit = pci_xr17v35x_exit,
};

View File

@ -124,7 +124,7 @@ static int of_platform_serial_setup(struct platform_device *ofdev,
dev_warn(&ofdev->dev, "unsupported reg-io-width (%d)\n",
prop);
ret = -EINVAL;
goto err_dispose;
goto err_unprepare;
}
}
port->flags |= UPF_IOREMAP;
@ -144,6 +144,10 @@ static int of_platform_serial_setup(struct platform_device *ofdev,
port->line = ret;
port->irq = irq_of_parse_and_map(np, 0);
if (!port->irq) {
ret = -EPROBE_DEFER;
goto err_unprepare;
}
info->rst = devm_reset_control_get_optional_shared(&ofdev->dev, NULL);
if (IS_ERR(info->rst)) {

View File

@ -1115,6 +1115,7 @@ static const u8 am3352_habit = OMAP_DMA_TX_KICK | UART_ERRATA_CLOCK_DISABLE;
static const u8 dra742_habit = UART_ERRATA_CLOCK_DISABLE;
static const struct of_device_id omap8250_dt_ids[] = {
{ .compatible = "ti,am654-uart" },
{ .compatible = "ti,omap2-uart" },
{ .compatible = "ti,omap3-uart" },
{ .compatible = "ti,omap4-uart", .data = &omap4_habit, },

View File

@ -90,8 +90,7 @@ static const struct serial8250_config uart_config[] = {
.name = "16550A",
.fifo_size = 16,
.tx_loadsz = 16,
.fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10 |
UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT,
.fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
.rxtrig_bytes = {1, 4, 8, 14},
.flags = UART_CAP_FIFO,
},
@ -1211,8 +1210,8 @@ static void autoconfig(struct uart_8250_port *up)
if (!port->iobase && !port->mapbase && !port->membase)
return;
DEBUG_AUTOCONF("ttyS%d: autoconf (0x%04lx, 0x%p): ",
serial_index(port), port->iobase, port->membase);
DEBUG_AUTOCONF("%s: autoconf (0x%04lx, 0x%p): ",
port->name, port->iobase, port->membase);
/*
* We really do need global IRQs disabled here - we're going to
@ -1363,9 +1362,8 @@ out_lock:
fintek_8250_probe(up);
if (up->capabilities != old_capabilities) {
pr_warn("ttyS%d: detected caps %08x should be %08x\n",
serial_index(port), old_capabilities,
up->capabilities);
pr_warn("%s: detected caps %08x should be %08x\n",
port->name, old_capabilities, up->capabilities);
}
out:
DEBUG_AUTOCONF("iir=%d ", scratch);
@ -2212,8 +2210,7 @@ int serial8250_do_startup(struct uart_port *port)
*/
if (!(port->flags & UPF_BUGGY_UART) &&
(serial_port_in(port, UART_LSR) == 0xff)) {
printk_ratelimited(KERN_INFO "ttyS%d: LSR safety check engaged!\n",
serial_index(port));
pr_info_ratelimited("%s: LSR safety check engaged!\n", port->name);
retval = -ENODEV;
goto out;
}
@ -2245,8 +2242,8 @@ int serial8250_do_startup(struct uart_port *port)
(port->type == PORT_ALTR_16550_F128)) && (port->fifosize > 1)) {
/* Bounds checking of TX threshold (valid 0 to fifosize-2) */
if ((up->tx_loadsz < 2) || (up->tx_loadsz > port->fifosize)) {
pr_err("ttyS%d TX FIFO Threshold errors, skipping\n",
serial_index(port));
pr_err("%s TX FIFO Threshold errors, skipping\n",
port->name);
} else {
serial_port_out(port, UART_ALTR_AFR,
UART_ALTR_EN_TXFIFO_LW);
@ -2343,8 +2340,8 @@ int serial8250_do_startup(struct uart_port *port)
if (lsr & UART_LSR_TEMT && iir & UART_IIR_NO_INT) {
if (!(up->bugs & UART_BUG_TXEN)) {
up->bugs |= UART_BUG_TXEN;
pr_debug("ttyS%d - enabling bad tx status workarounds\n",
serial_index(port));
pr_debug("%s - enabling bad tx status workarounds\n",
port->name);
}
} else {
up->bugs &= ~UART_BUG_TXEN;
@ -2373,8 +2370,8 @@ dont_test_tx_en:
if (up->dma) {
retval = serial8250_request_dma(up);
if (retval) {
pr_warn_ratelimited("ttyS%d - failed to request DMA\n",
serial_index(port));
pr_warn_ratelimited("%s - failed to request DMA\n",
port->name);
up->dma = NULL;
}
}
@ -2498,11 +2495,11 @@ static unsigned int npcm_get_divisor(struct uart_8250_port *up,
return DIV_ROUND_CLOSEST(port->uartclk, 16 * baud + 2) - 2;
}
static unsigned int serial8250_get_divisor(struct uart_8250_port *up,
unsigned int baud,
unsigned int *frac)
static unsigned int serial8250_do_get_divisor(struct uart_port *port,
unsigned int baud,
unsigned int *frac)
{
struct uart_port *port = &up->port;
struct uart_8250_port *up = up_to_u8250p(port);
unsigned int quot;
/*
@ -2532,6 +2529,16 @@ static unsigned int serial8250_get_divisor(struct uart_8250_port *up,
return quot;
}
static unsigned int serial8250_get_divisor(struct uart_port *port,
unsigned int baud,
unsigned int *frac)
{
if (port->get_divisor)
return port->get_divisor(port, baud, frac);
return serial8250_do_get_divisor(port, baud, frac);
}
static unsigned char serial8250_compute_lcr(struct uart_8250_port *up,
tcflag_t c_cflag)
{
@ -2570,8 +2577,8 @@ static unsigned char serial8250_compute_lcr(struct uart_8250_port *up,
return cval;
}
static void serial8250_set_divisor(struct uart_port *port, unsigned int baud,
unsigned int quot, unsigned int quot_frac)
void serial8250_do_set_divisor(struct uart_port *port, unsigned int baud,
unsigned int quot, unsigned int quot_frac)
{
struct uart_8250_port *up = up_to_u8250p(port);
@ -2602,6 +2609,16 @@ static void serial8250_set_divisor(struct uart_port *port, unsigned int baud,
serial_port_out(port, 0x2, quot_frac);
}
}
EXPORT_SYMBOL_GPL(serial8250_do_set_divisor);
static void serial8250_set_divisor(struct uart_port *port, unsigned int baud,
unsigned int quot, unsigned int quot_frac)
{
if (port->set_divisor)
port->set_divisor(port, baud, quot, quot_frac);
else
serial8250_do_set_divisor(port, baud, quot, quot_frac);
}
static unsigned int serial8250_get_baud_rate(struct uart_port *port,
struct ktermios *termios,
@ -2636,7 +2653,7 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios,
cval = serial8250_compute_lcr(up, termios->c_cflag);
baud = serial8250_get_baud_rate(port, termios, old);
quot = serial8250_get_divisor(up, baud, &frac);
quot = serial8250_get_divisor(port, baud, &frac);
/*
* Ok, we're now changing the port state. Do it with
@ -3197,7 +3214,7 @@ static void serial8250_console_restore(struct uart_8250_port *up)
termios.c_cflag = port->state->port.tty->termios.c_cflag;
baud = serial8250_get_baud_rate(port, &termios, NULL);
quot = serial8250_get_divisor(up, baud, &frac);
quot = serial8250_get_divisor(port, baud, &frac);
serial8250_set_divisor(port, baud, quot, frac);
serial_port_out(port, UART_LCR, up->lcr);

View File

@ -638,8 +638,10 @@ static int serial_config(struct pcmcia_device *link)
(link->has_func_id) &&
(link->socket->pcmcia_pfc == 0) &&
((link->func_id == CISTPL_FUNCID_MULTI) ||
(link->func_id == CISTPL_FUNCID_SERIAL)))
pcmcia_loop_config(link, serial_check_for_multi, info);
(link->func_id == CISTPL_FUNCID_SERIAL))) {
if (pcmcia_loop_config(link, serial_check_for_multi, info))
goto failed;
}
/*
* Apply any multi-port quirk.

View File

@ -314,7 +314,8 @@ static u32 imx_uart_readl(struct imx_port *sport, u32 offset)
/*
* UCR2_SRST is the only bit in the cached registers that might
* differ from the value that was last written. As it only
* clears after being set, reread conditionally.
* automatically becomes one after being cleared, reread
* conditionally.
*/
if (!(sport->ucr2 & UCR2_SRST))
sport->ucr2 = readl(sport->port.membase + offset);
@ -1051,7 +1052,7 @@ static void imx_uart_dma_rx_callback(void *data)
unsigned int r_bytes;
unsigned int bd_size;
status = dmaengine_tx_status(chan, (dma_cookie_t)0, &state);
status = dmaengine_tx_status(chan, sport->rx_cookie, &state);
if (status == DMA_ERROR) {
imx_uart_clear_rx_errors(sport);

View File

@ -430,7 +430,6 @@ int jsm_uart_port_init(struct jsm_board *brd)
{
int i, rc;
unsigned int line;
struct jsm_channel *ch;
if (!brd)
return -ENXIO;
@ -444,7 +443,7 @@ int jsm_uart_port_init(struct jsm_board *brd)
brd->nasync = brd->maxports;
/* Set up channel variables */
for (i = 0; i < brd->nasync; i++, ch = brd->channels[i]) {
for (i = 0; i < brd->nasync; i++) {
if (!brd->channels[i])
continue;

View File

@ -531,8 +531,8 @@ static int max310x_update_best_err(unsigned long f, long *besterr)
return 1;
}
static int max310x_set_ref_clk(struct max310x_port *s, unsigned long freq,
bool xtal)
static int max310x_set_ref_clk(struct device *dev, struct max310x_port *s,
unsigned long freq, bool xtal)
{
unsigned int div, clksrc, pllcfg = 0;
long besterr = -1;
@ -588,8 +588,14 @@ static int max310x_set_ref_clk(struct max310x_port *s, unsigned long freq,
regmap_write(s->regmap, MAX310X_CLKSRC_REG, clksrc);
/* Wait for crystal */
if (pllcfg && xtal)
if (xtal) {
unsigned int val;
msleep(10);
regmap_read(s->regmap, MAX310X_STS_IRQSTS_REG, &val);
if (!(val & MAX310X_STS_CLKREADY_BIT)) {
dev_warn(dev, "clock is not stable yet\n");
}
}
return (int)bestfreq;
}
@ -1260,7 +1266,7 @@ static int max310x_probe(struct device *dev, struct max310x_devtype *devtype,
MAX310X_MODE1_AUTOSLEEP_BIT);
}
uartclk = max310x_set_ref_clk(s, freq, xtal);
uartclk = max310x_set_ref_clk(dev, s, freq, xtal);
dev_dbg(dev, "Reference clock set to %i Hz\n", uartclk);
mutex_init(&s->mutex);

View File

@ -887,7 +887,8 @@ static int serial_pxa_probe(struct platform_device *dev)
goto err_clk;
if (sport->port.line >= ARRAY_SIZE(serial_pxa_ports)) {
dev_err(&dev->dev, "serial%d out of range\n", sport->port.line);
return -EINVAL;
ret = -EINVAL;
goto err_clk;
}
snprintf(sport->name, PXA_NAME_LEN - 1, "UART%d", sport->port.line + 1);

View File

@ -17,6 +17,7 @@
#include <linux/tty_flip.h>
/* UART specific GENI registers */
#define SE_UART_LOOPBACK_CFG 0x22c
#define SE_UART_TX_TRANS_CFG 0x25c
#define SE_UART_TX_WORD_LEN 0x268
#define SE_UART_TX_STOP_BIT_LEN 0x26c
@ -26,6 +27,7 @@
#define SE_UART_RX_STALE_CNT 0x294
#define SE_UART_TX_PARITY_CFG 0x2a4
#define SE_UART_RX_PARITY_CFG 0x2a8
#define SE_UART_MANUAL_RFR 0x2ac
/* SE_UART_TRANS_CFG */
#define UART_TX_PAR_EN BIT(0)
@ -62,6 +64,11 @@
#define PAR_SPACE 0x10
#define PAR_MARK 0x11
/* SE_UART_MANUAL_RFR register fields */
#define UART_MANUAL_RFR_EN BIT(31)
#define UART_RFR_NOT_READY BIT(1)
#define UART_RFR_READY BIT(0)
/* UART M_CMD OP codes */
#define UART_START_TX 0x1
#define UART_START_BREAK 0x4
@ -74,10 +81,12 @@
#define STALE_TIMEOUT 16
#define DEFAULT_BITS_PER_CHAR 10
#define GENI_UART_CONS_PORTS 1
#define GENI_UART_PORTS 3
#define DEF_FIFO_DEPTH_WORDS 16
#define DEF_TX_WM 2
#define DEF_FIFO_WIDTH_BITS 32
#define UART_CONSOLE_RX_WM 2
#define MAX_LOOPBACK_CFG 3
#ifdef CONFIG_CONSOLE_POLL
#define RX_BYTES_PW 1
@ -101,22 +110,81 @@ struct qcom_geni_serial_port {
unsigned int baud;
unsigned int tx_bytes_pw;
unsigned int rx_bytes_pw;
u32 *rx_fifo;
u32 loopback;
bool brk;
};
static const struct uart_ops qcom_geni_console_pops;
static const struct uart_ops qcom_geni_uart_pops;
static struct uart_driver qcom_geni_console_driver;
static struct uart_driver qcom_geni_uart_driver;
static int handle_rx_console(struct uart_port *uport, u32 bytes, bool drop);
static int handle_rx_uart(struct uart_port *uport, u32 bytes, bool drop);
static unsigned int qcom_geni_serial_tx_empty(struct uart_port *port);
static void qcom_geni_serial_stop_rx(struct uart_port *uport);
static const unsigned long root_freq[] = {7372800, 14745600, 19200000, 29491200,
32000000, 48000000, 64000000, 80000000,
96000000, 100000000};
96000000, 100000000, 102400000,
112000000, 120000000, 128000000};
#define to_dev_port(ptr, member) \
container_of(ptr, struct qcom_geni_serial_port, member)
static struct qcom_geni_serial_port qcom_geni_uart_ports[GENI_UART_PORTS] = {
[0] = {
.uport = {
.iotype = UPIO_MEM,
.ops = &qcom_geni_uart_pops,
.flags = UPF_BOOT_AUTOCONF,
.line = 0,
},
},
[1] = {
.uport = {
.iotype = UPIO_MEM,
.ops = &qcom_geni_uart_pops,
.flags = UPF_BOOT_AUTOCONF,
.line = 1,
},
},
[2] = {
.uport = {
.iotype = UPIO_MEM,
.ops = &qcom_geni_uart_pops,
.flags = UPF_BOOT_AUTOCONF,
.line = 2,
},
},
};
static ssize_t loopback_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct platform_device *pdev = to_platform_device(dev);
struct qcom_geni_serial_port *port = platform_get_drvdata(pdev);
return snprintf(buf, sizeof(u32), "%d\n", port->loopback);
}
static ssize_t loopback_store(struct device *dev,
struct device_attribute *attr, const char *buf,
size_t size)
{
struct platform_device *pdev = to_platform_device(dev);
struct qcom_geni_serial_port *port = platform_get_drvdata(pdev);
u32 loopback;
if (kstrtoint(buf, 0, &loopback) || loopback > MAX_LOOPBACK_CFG) {
dev_err(dev, "Invalid input\n");
return -EINVAL;
}
port->loopback = loopback;
return size;
}
static DEVICE_ATTR_RW(loopback);
static struct qcom_geni_serial_port qcom_geni_console_port = {
.uport = {
.iotype = UPIO_MEM,
@ -148,14 +216,33 @@ static void qcom_geni_serial_config_port(struct uart_port *uport, int cfg_flags)
}
}
static unsigned int qcom_geni_cons_get_mctrl(struct uart_port *uport)
static unsigned int qcom_geni_serial_get_mctrl(struct uart_port *uport)
{
return TIOCM_DSR | TIOCM_CAR | TIOCM_CTS;
unsigned int mctrl = TIOCM_DSR | TIOCM_CAR;
u32 geni_ios;
if (uart_console(uport) || !uart_cts_enabled(uport)) {
mctrl |= TIOCM_CTS;
} else {
geni_ios = readl_relaxed(uport->membase + SE_GENI_IOS);
if (!(geni_ios & IO2_DATA_IN))
mctrl |= TIOCM_CTS;
}
return mctrl;
}
static void qcom_geni_cons_set_mctrl(struct uart_port *uport,
static void qcom_geni_serial_set_mctrl(struct uart_port *uport,
unsigned int mctrl)
{
u32 uart_manual_rfr = 0;
if (uart_console(uport) || !uart_cts_enabled(uport))
return;
if (!(mctrl & TIOCM_RTS))
uart_manual_rfr = UART_MANUAL_RFR_EN | UART_RFR_NOT_READY;
writel_relaxed(uart_manual_rfr, uport->membase + SE_UART_MANUAL_RFR);
}
static const char *qcom_geni_serial_get_type(struct uart_port *uport)
@ -163,11 +250,16 @@ static const char *qcom_geni_serial_get_type(struct uart_port *uport)
return "MSM";
}
static struct qcom_geni_serial_port *get_port_from_line(int line)
static struct qcom_geni_serial_port *get_port_from_line(int line, bool console)
{
if (line < 0 || line >= GENI_UART_CONS_PORTS)
struct qcom_geni_serial_port *port;
int nr_ports = console ? GENI_UART_CONS_PORTS : GENI_UART_PORTS;
if (line < 0 || line >= nr_ports)
return ERR_PTR(-ENXIO);
return &qcom_geni_console_port;
port = console ? &qcom_geni_console_port : &qcom_geni_uart_ports[line];
return port;
}
static bool qcom_geni_serial_poll_bit(struct uart_port *uport,
@ -346,7 +438,7 @@ static void qcom_geni_serial_console_write(struct console *co, const char *s,
WARN_ON(co->index < 0 || co->index >= GENI_UART_CONS_PORTS);
port = get_port_from_line(co->index);
port = get_port_from_line(co->index, true);
if (IS_ERR(port))
return;
@ -420,6 +512,32 @@ static int handle_rx_console(struct uart_port *uport, u32 bytes, bool drop)
#endif /* CONFIG_SERIAL_QCOM_GENI_CONSOLE */
static int handle_rx_uart(struct uart_port *uport, u32 bytes, bool drop)
{
unsigned char *buf;
struct tty_port *tport;
struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
u32 num_bytes_pw = port->tx_fifo_width / BITS_PER_BYTE;
u32 words = ALIGN(bytes, num_bytes_pw) / num_bytes_pw;
int ret;
tport = &uport->state->port;
ioread32_rep(uport->membase + SE_GENI_RX_FIFOn, port->rx_fifo, words);
if (drop)
return 0;
buf = (unsigned char *)port->rx_fifo;
ret = tty_insert_flip_string(tport, buf, bytes);
if (ret != bytes) {
dev_err(uport->dev, "%s:Unable to push data ret %d_bytes %d\n",
__func__, ret, bytes);
WARN_ON_ONCE(1);
}
uport->icount.rx += ret;
tty_flip_buffer_push(tport);
return ret;
}
static void qcom_geni_serial_start_tx(struct uart_port *uport)
{
u32 irq_en;
@ -586,6 +704,7 @@ static void qcom_geni_serial_handle_tx(struct uart_port *uport)
u32 status;
unsigned int chunk;
int tail;
u32 irq_en;
chunk = uart_circ_chars_pending(xmit);
status = readl_relaxed(uport->membase + SE_GENI_TX_FIFO_STATUS);
@ -595,6 +714,13 @@ static void qcom_geni_serial_handle_tx(struct uart_port *uport)
goto out_write_wakeup;
}
if (!uart_console(uport)) {
irq_en = readl_relaxed(uport->membase + SE_GENI_M_IRQ_EN);
irq_en &= ~(M_TX_FIFO_WATERMARK_EN);
writel_relaxed(0, uport->membase + SE_GENI_TX_WATERMARK_REG);
writel_relaxed(irq_en, uport->membase + SE_GENI_M_IRQ_EN);
}
avail = (port->tx_fifo_depth - port->tx_wm) * port->tx_bytes_pw;
tail = xmit->tail;
chunk = min3((size_t)chunk, (size_t)(UART_XMIT_SIZE - tail), avail);
@ -623,7 +749,8 @@ static void qcom_geni_serial_handle_tx(struct uart_port *uport)
}
xmit->tail = tail & (UART_XMIT_SIZE - 1);
qcom_geni_serial_poll_tx_done(uport);
if (uart_console(uport))
qcom_geni_serial_poll_tx_done(uport);
out_write_wakeup:
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(uport);
@ -710,7 +837,8 @@ static void qcom_geni_serial_shutdown(struct uart_port *uport)
unsigned long flags;
/* Stop the console before stopping the current tx */
console_stop(uport->cons);
if (uart_console(uport))
console_stop(uport->cons);
free_irq(uport->irq, uport);
spin_lock_irqsave(&uport->lock, flags);
@ -731,13 +859,20 @@ static int qcom_geni_serial_port_setup(struct uart_port *uport)
* it else we could end up in data loss scenarios.
*/
port->xfer_mode = GENI_SE_FIFO;
qcom_geni_serial_poll_tx_done(uport);
if (uart_console(uport))
qcom_geni_serial_poll_tx_done(uport);
geni_se_config_packing(&port->se, BITS_PER_BYTE, port->tx_bytes_pw,
false, true, false);
geni_se_config_packing(&port->se, BITS_PER_BYTE, port->rx_bytes_pw,
false, false, true);
geni_se_init(&port->se, port->rx_wm, port->rx_rfr);
geni_se_select_mode(&port->se, port->xfer_mode);
if (!uart_console(uport)) {
port->rx_fifo = devm_kzalloc(uport->dev,
port->rx_fifo_depth * sizeof(u32), GFP_KERNEL);
if (!port->rx_fifo)
return -ENOMEM;
}
port->setup = true;
return 0;
}
@ -749,8 +884,13 @@ static int qcom_geni_serial_startup(struct uart_port *uport)
struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
scnprintf(port->name, sizeof(port->name),
"qcom_serial_geni%d", uport->line);
"qcom_serial_%s%d",
(uart_console(uport) ? "console" : "uart"), uport->line);
if (!uart_console(uport)) {
port->tx_bytes_pw = 4;
port->rx_bytes_pw = RX_BYTES_PW;
}
proto = geni_se_read_proto(&port->se);
if (proto != GENI_SE_UART) {
dev_err(uport->dev, "Invalid FW loaded, proto: %d\n", proto);
@ -886,6 +1026,9 @@ static void qcom_geni_serial_set_termios(struct uart_port *uport,
if (baud)
uart_update_timeout(uport, termios->c_cflag, baud);
if (!uart_console(uport))
writel_relaxed(port->loopback,
uport->membase + SE_UART_LOOPBACK_CFG);
writel_relaxed(tx_trans_cfg, uport->membase + SE_UART_TX_TRANS_CFG);
writel_relaxed(tx_parity_cfg, uport->membase + SE_UART_TX_PARITY_CFG);
writel_relaxed(rx_trans_cfg, uport->membase + SE_UART_RX_TRANS_CFG);
@ -917,7 +1060,7 @@ static int __init qcom_geni_console_setup(struct console *co, char *options)
if (co->index >= GENI_UART_CONS_PORTS || co->index < 0)
return -ENXIO;
port = get_port_from_line(co->index);
port = get_port_from_line(co->index, true);
if (IS_ERR(port)) {
pr_err("Invalid line %d\n", co->index);
return PTR_ERR(port);
@ -1048,16 +1191,23 @@ static void console_unregister(struct uart_driver *drv)
}
#endif /* CONFIG_SERIAL_QCOM_GENI_CONSOLE */
static void qcom_geni_serial_cons_pm(struct uart_port *uport,
static struct uart_driver qcom_geni_uart_driver = {
.owner = THIS_MODULE,
.driver_name = "qcom_geni_uart",
.dev_name = "ttyHS",
.nr = GENI_UART_PORTS,
};
static void qcom_geni_serial_pm(struct uart_port *uport,
unsigned int new_state, unsigned int old_state)
{
struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
if (unlikely(!uart_console(uport)))
return;
if (new_state == UART_PM_STATE_ON && old_state == UART_PM_STATE_OFF)
geni_se_resources_on(&port->se);
else if (!uart_console(uport) && (new_state == UART_PM_STATE_ON &&
old_state == UART_PM_STATE_UNDEFINED))
geni_se_resources_on(&port->se);
else if (new_state == UART_PM_STATE_OFF &&
old_state == UART_PM_STATE_ON)
geni_se_resources_off(&port->se);
@ -1074,13 +1224,29 @@ static const struct uart_ops qcom_geni_console_pops = {
.config_port = qcom_geni_serial_config_port,
.shutdown = qcom_geni_serial_shutdown,
.type = qcom_geni_serial_get_type,
.set_mctrl = qcom_geni_cons_set_mctrl,
.get_mctrl = qcom_geni_cons_get_mctrl,
.set_mctrl = qcom_geni_serial_set_mctrl,
.get_mctrl = qcom_geni_serial_get_mctrl,
#ifdef CONFIG_CONSOLE_POLL
.poll_get_char = qcom_geni_serial_get_char,
.poll_put_char = qcom_geni_serial_poll_put_char,
#endif
.pm = qcom_geni_serial_cons_pm,
.pm = qcom_geni_serial_pm,
};
static const struct uart_ops qcom_geni_uart_pops = {
.tx_empty = qcom_geni_serial_tx_empty,
.stop_tx = qcom_geni_serial_stop_tx,
.start_tx = qcom_geni_serial_start_tx,
.stop_rx = qcom_geni_serial_stop_rx,
.set_termios = qcom_geni_serial_set_termios,
.startup = qcom_geni_serial_startup,
.request_port = qcom_geni_serial_request_port,
.config_port = qcom_geni_serial_config_port,
.shutdown = qcom_geni_serial_shutdown,
.type = qcom_geni_serial_get_type,
.set_mctrl = qcom_geni_serial_set_mctrl,
.get_mctrl = qcom_geni_serial_get_mctrl,
.pm = qcom_geni_serial_pm,
};
static int qcom_geni_serial_probe(struct platform_device *pdev)
@ -1091,13 +1257,23 @@ static int qcom_geni_serial_probe(struct platform_device *pdev)
struct uart_port *uport;
struct resource *res;
int irq;
bool console = false;
struct uart_driver *drv;
if (pdev->dev.of_node)
line = of_alias_get_id(pdev->dev.of_node, "serial");
if (of_device_is_compatible(pdev->dev.of_node, "qcom,geni-debug-uart"))
console = true;
if (line < 0 || line >= GENI_UART_CONS_PORTS)
return -ENXIO;
port = get_port_from_line(line);
if (pdev->dev.of_node) {
if (console) {
drv = &qcom_geni_console_driver;
line = of_alias_get_id(pdev->dev.of_node, "serial");
} else {
drv = &qcom_geni_uart_driver;
line = of_alias_get_id(pdev->dev.of_node, "hsuart");
}
}
port = get_port_from_line(line, console);
if (IS_ERR(port)) {
dev_err(&pdev->dev, "Invalid line %d\n", line);
return PTR_ERR(port);
@ -1134,10 +1310,12 @@ static int qcom_geni_serial_probe(struct platform_device *pdev)
}
uport->irq = irq;
uport->private_data = &qcom_geni_console_driver;
uport->private_data = drv;
platform_set_drvdata(pdev, port);
port->handle_rx = handle_rx_console;
return uart_add_one_port(&qcom_geni_console_driver, uport);
port->handle_rx = console ? handle_rx_console : handle_rx_uart;
if (!console)
device_create_file(uport->dev, &dev_attr_loopback);
return uart_add_one_port(drv, uport);
}
static int qcom_geni_serial_remove(struct platform_device *pdev)
@ -1154,7 +1332,17 @@ static int __maybe_unused qcom_geni_serial_sys_suspend_noirq(struct device *dev)
struct qcom_geni_serial_port *port = dev_get_drvdata(dev);
struct uart_port *uport = &port->uport;
uart_suspend_port(uport->private_data, uport);
if (uart_console(uport)) {
uart_suspend_port(uport->private_data, uport);
} else {
struct uart_state *state = uport->state;
/*
* If the port is open, deny system suspend.
*/
if (state->pm_state == UART_PM_STATE_ON)
return -EBUSY;
}
return 0;
}
@ -1163,7 +1351,8 @@ static int __maybe_unused qcom_geni_serial_sys_resume_noirq(struct device *dev)
struct qcom_geni_serial_port *port = dev_get_drvdata(dev);
struct uart_port *uport = &port->uport;
if (console_suspend_enabled && uport->suspended) {
if (uart_console(uport) &&
console_suspend_enabled && uport->suspended) {
uart_resume_port(uport->private_data, uport);
/*
* uart_suspend_port() invokes port shutdown which in turn
@ -1185,6 +1374,7 @@ static const struct dev_pm_ops qcom_geni_serial_pm_ops = {
static const struct of_device_id qcom_geni_serial_match_table[] = {
{ .compatible = "qcom,geni-debug-uart", },
{ .compatible = "qcom,geni-uart", },
{}
};
MODULE_DEVICE_TABLE(of, qcom_geni_serial_match_table);
@ -1207,9 +1397,17 @@ static int __init qcom_geni_serial_init(void)
if (ret)
return ret;
ret = platform_driver_register(&qcom_geni_serial_platform_driver);
if (ret)
ret = uart_register_driver(&qcom_geni_uart_driver);
if (ret) {
console_unregister(&qcom_geni_console_driver);
return ret;
}
ret = platform_driver_register(&qcom_geni_serial_platform_driver);
if (ret) {
console_unregister(&qcom_geni_console_driver);
uart_unregister_driver(&qcom_geni_uart_driver);
}
return ret;
}
module_init(qcom_geni_serial_init);
@ -1218,6 +1416,7 @@ static void __exit qcom_geni_serial_exit(void)
{
platform_driver_unregister(&qcom_geni_serial_platform_driver);
console_unregister(&qcom_geni_console_driver);
uart_unregister_driver(&qcom_geni_uart_driver);
}
module_exit(qcom_geni_serial_exit);

View File

@ -182,6 +182,7 @@ static int uart_port_startup(struct tty_struct *tty, struct uart_state *state,
{
struct uart_port *uport = uart_port_check(state);
unsigned long page;
unsigned long flags = 0;
int retval = 0;
if (uport->type == PORT_UNKNOWN)
@ -196,15 +197,18 @@ static int uart_port_startup(struct tty_struct *tty, struct uart_state *state,
* Initialise and allocate the transmit and temporary
* buffer.
*/
if (!state->xmit.buf) {
/* This is protected by the per port mutex */
page = get_zeroed_page(GFP_KERNEL);
if (!page)
return -ENOMEM;
page = get_zeroed_page(GFP_KERNEL);
if (!page)
return -ENOMEM;
uart_port_lock(state, flags);
if (!state->xmit.buf) {
state->xmit.buf = (unsigned char *) page;
uart_circ_clear(&state->xmit);
} else {
free_page(page);
}
uart_port_unlock(uport, flags);
retval = uport->ops->startup(uport);
if (retval == 0) {
@ -263,6 +267,7 @@ static void uart_shutdown(struct tty_struct *tty, struct uart_state *state)
{
struct uart_port *uport = uart_port_check(state);
struct tty_port *port = &state->port;
unsigned long flags = 0;
/*
* Set the TTY IO error marker
@ -295,10 +300,12 @@ static void uart_shutdown(struct tty_struct *tty, struct uart_state *state)
/*
* Free the transmit buffer page.
*/
uart_port_lock(state, flags);
if (state->xmit.buf) {
free_page((unsigned long)state->xmit.buf);
state->xmit.buf = NULL;
}
uart_port_unlock(uport, flags);
}
/**

View File

@ -65,6 +65,8 @@ enum {
SCIx_RXI_IRQ,
SCIx_TXI_IRQ,
SCIx_BRI_IRQ,
SCIx_DRI_IRQ,
SCIx_TEI_IRQ,
SCIx_NR_IRQS,
SCIx_MUX_IRQ = SCIx_NR_IRQS, /* special case */
@ -135,6 +137,8 @@ struct sci_port {
struct dma_chan *chan_rx;
#ifdef CONFIG_SERIAL_SH_SCI_DMA
struct dma_chan *chan_tx_saved;
struct dma_chan *chan_rx_saved;
dma_cookie_t cookie_tx;
dma_cookie_t cookie_rx[2];
dma_cookie_t active_rx;
@ -315,15 +319,15 @@ static const struct sci_port_params sci_port_params[SCIx_NR_REGTYPES] = {
[SCIx_SH4_SCIF_REGTYPE] = {
.regs = {
[SCSMR] = { 0x00, 16 },
[SCBRR] = { 0x04, 8 },
[SCSCR] = { 0x08, 16 },
[SCxTDR] = { 0x0c, 8 },
[SCxSR] = { 0x10, 16 },
[SCxRDR] = { 0x14, 8 },
[SCFCR] = { 0x18, 16 },
[SCFDR] = { 0x1c, 16 },
[SCSPTR] = { 0x20, 16 },
[SCLSR] = { 0x24, 16 },
[SCBRR] = { 0x02, 8 },
[SCSCR] = { 0x04, 16 },
[SCxTDR] = { 0x06, 8 },
[SCxSR] = { 0x08, 16 },
[SCxRDR] = { 0x0a, 8 },
[SCFCR] = { 0x0c, 16 },
[SCFDR] = { 0x0e, 16 },
[SCSPTR] = { 0x10, 16 },
[SCLSR] = { 0x12, 16 },
},
.fifosize = 16,
.overrun_reg = SCLSR,
@ -1212,25 +1216,16 @@ static int sci_dma_rx_find_active(struct sci_port *s)
return -1;
}
static void sci_rx_dma_release(struct sci_port *s, bool enable_pio)
static void sci_rx_dma_release(struct sci_port *s)
{
struct dma_chan *chan = s->chan_rx;
struct uart_port *port = &s->port;
unsigned long flags;
struct dma_chan *chan = s->chan_rx_saved;
spin_lock_irqsave(&port->lock, flags);
s->chan_rx = NULL;
s->chan_rx_saved = s->chan_rx = NULL;
s->cookie_rx[0] = s->cookie_rx[1] = -EINVAL;
spin_unlock_irqrestore(&port->lock, flags);
dmaengine_terminate_all(chan);
dmaengine_terminate_sync(chan);
dma_free_coherent(chan->device->dev, s->buf_len_rx * 2, s->rx_buf[0],
sg_dma_address(&s->sg_rx[0]));
dma_release_channel(chan);
if (enable_pio) {
spin_lock_irqsave(&port->lock, flags);
sci_start_rx(port);
spin_unlock_irqrestore(&port->lock, flags);
}
}
static void start_hrtimer_us(struct hrtimer *hrt, unsigned long usec)
@ -1289,33 +1284,31 @@ static void sci_dma_rx_complete(void *arg)
fail:
spin_unlock_irqrestore(&port->lock, flags);
dev_warn(port->dev, "Failed submitting Rx DMA descriptor\n");
sci_rx_dma_release(s, true);
/* Switch to PIO */
spin_lock_irqsave(&port->lock, flags);
s->chan_rx = NULL;
sci_start_rx(port);
spin_unlock_irqrestore(&port->lock, flags);
}
static void sci_tx_dma_release(struct sci_port *s, bool enable_pio)
static void sci_tx_dma_release(struct sci_port *s)
{
struct dma_chan *chan = s->chan_tx;
struct uart_port *port = &s->port;
unsigned long flags;
struct dma_chan *chan = s->chan_tx_saved;
spin_lock_irqsave(&port->lock, flags);
s->chan_tx = NULL;
cancel_work_sync(&s->work_tx);
s->chan_tx_saved = s->chan_tx = NULL;
s->cookie_tx = -EINVAL;
spin_unlock_irqrestore(&port->lock, flags);
dmaengine_terminate_all(chan);
dmaengine_terminate_sync(chan);
dma_unmap_single(chan->device->dev, s->tx_dma_addr, UART_XMIT_SIZE,
DMA_TO_DEVICE);
dma_release_channel(chan);
if (enable_pio) {
spin_lock_irqsave(&port->lock, flags);
sci_start_tx(port);
spin_unlock_irqrestore(&port->lock, flags);
}
}
static void sci_submit_rx(struct sci_port *s)
{
struct dma_chan *chan = s->chan_rx;
struct uart_port *port = &s->port;
unsigned long flags;
int i;
for (i = 0; i < 2; i++) {
@ -1343,11 +1336,15 @@ static void sci_submit_rx(struct sci_port *s)
fail:
if (i)
dmaengine_terminate_all(chan);
dmaengine_terminate_async(chan);
for (i = 0; i < 2; i++)
s->cookie_rx[i] = -EINVAL;
s->active_rx = -EINVAL;
sci_rx_dma_release(s, true);
/* Switch to PIO */
spin_lock_irqsave(&port->lock, flags);
s->chan_rx = NULL;
sci_start_rx(port);
spin_unlock_irqrestore(&port->lock, flags);
}
static void work_fn_tx(struct work_struct *work)
@ -1357,6 +1354,7 @@ static void work_fn_tx(struct work_struct *work)
struct dma_chan *chan = s->chan_tx;
struct uart_port *port = &s->port;
struct circ_buf *xmit = &port->state->xmit;
unsigned long flags;
dma_addr_t buf;
/*
@ -1378,9 +1376,7 @@ static void work_fn_tx(struct work_struct *work)
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
if (!desc) {
dev_warn(port->dev, "Failed preparing Tx DMA descriptor\n");
/* switch to PIO */
sci_tx_dma_release(s, true);
return;
goto switch_to_pio;
}
dma_sync_single_for_device(chan->device->dev, buf, s->tx_dma_len,
@ -1393,15 +1389,21 @@ static void work_fn_tx(struct work_struct *work)
s->cookie_tx = dmaengine_submit(desc);
if (dma_submit_error(s->cookie_tx)) {
dev_warn(port->dev, "Failed submitting Tx DMA descriptor\n");
/* switch to PIO */
sci_tx_dma_release(s, true);
return;
goto switch_to_pio;
}
dev_dbg(port->dev, "%s: %p: %d...%d, cookie %d\n",
__func__, xmit->buf, xmit->tail, xmit->head, s->cookie_tx);
dma_async_issue_pending(chan);
return;
switch_to_pio:
spin_lock_irqsave(&port->lock, flags);
s->chan_tx = NULL;
sci_start_tx(port);
spin_unlock_irqrestore(&port->lock, flags);
return;
}
static enum hrtimer_restart rx_timer_fn(struct hrtimer *t)
@ -1452,7 +1454,7 @@ static enum hrtimer_restart rx_timer_fn(struct hrtimer *t)
}
/* Handle incomplete DMA receive */
dmaengine_terminate_all(s->chan_rx);
dmaengine_terminate_async(s->chan_rx);
read = sg_dma_len(&s->sg_rx[active]) - state.residue;
if (read) {
@ -1535,7 +1537,6 @@ static void sci_request_dma(struct uart_port *port)
chan = sci_request_dma_chan(port, DMA_MEM_TO_DEV);
dev_dbg(port->dev, "%s: TX: got channel %p\n", __func__, chan);
if (chan) {
s->chan_tx = chan;
/* UART circular tx buffer is an aligned page. */
s->tx_dma_addr = dma_map_single(chan->device->dev,
port->state->xmit.buf,
@ -1544,14 +1545,14 @@ static void sci_request_dma(struct uart_port *port)
if (dma_mapping_error(chan->device->dev, s->tx_dma_addr)) {
dev_warn(port->dev, "Failed mapping Tx DMA descriptor\n");
dma_release_channel(chan);
s->chan_tx = NULL;
} else {
dev_dbg(port->dev, "%s: mapped %lu@%p to %pad\n",
__func__, UART_XMIT_SIZE,
port->state->xmit.buf, &s->tx_dma_addr);
}
INIT_WORK(&s->work_tx, work_fn_tx);
INIT_WORK(&s->work_tx, work_fn_tx);
s->chan_tx_saved = s->chan_tx = chan;
}
}
chan = sci_request_dma_chan(port, DMA_DEV_TO_MEM);
@ -1561,8 +1562,6 @@ static void sci_request_dma(struct uart_port *port)
dma_addr_t dma;
void *buf;
s->chan_rx = chan;
s->buf_len_rx = 2 * max_t(size_t, 16, port->fifosize);
buf = dma_alloc_coherent(chan->device->dev, s->buf_len_rx * 2,
&dma, GFP_KERNEL);
@ -1570,7 +1569,6 @@ static void sci_request_dma(struct uart_port *port)
dev_warn(port->dev,
"Failed to allocate Rx dma buffer, using PIO\n");
dma_release_channel(chan);
s->chan_rx = NULL;
return;
}
@ -1591,6 +1589,8 @@ static void sci_request_dma(struct uart_port *port)
if (port->type == PORT_SCIFA || port->type == PORT_SCIFB)
sci_submit_rx(s);
s->chan_rx_saved = s->chan_rx = chan;
}
}
@ -1598,10 +1598,10 @@ static void sci_free_dma(struct uart_port *port)
{
struct sci_port *s = to_sci_port(port);
if (s->chan_tx)
sci_tx_dma_release(s, false);
if (s->chan_rx)
sci_rx_dma_release(s, false);
if (s->chan_tx_saved)
sci_tx_dma_release(s);
if (s->chan_rx_saved)
sci_rx_dma_release(s);
}
static void sci_flush_buffer(struct uart_port *port)
@ -1683,11 +1683,35 @@ static irqreturn_t sci_tx_interrupt(int irq, void *ptr)
return IRQ_HANDLED;
}
static irqreturn_t sci_br_interrupt(int irq, void *ptr)
{
struct uart_port *port = ptr;
/* Handle BREAKs */
sci_handle_breaks(port);
sci_clear_SCxSR(port, SCxSR_BREAK_CLEAR(port));
return IRQ_HANDLED;
}
static irqreturn_t sci_er_interrupt(int irq, void *ptr)
{
struct uart_port *port = ptr;
struct sci_port *s = to_sci_port(port);
if (s->irqs[SCIx_ERI_IRQ] == s->irqs[SCIx_BRI_IRQ]) {
/* Break and Error interrupts are muxed */
unsigned short ssr_status = serial_port_in(port, SCxSR);
/* Break Interrupt */
if (ssr_status & SCxSR_BRK(port))
sci_br_interrupt(irq, ptr);
/* Break only? */
if (!(ssr_status & SCxSR_ERRORS(port)))
return IRQ_HANDLED;
}
/* Handle errors */
if (port->type == PORT_SCI) {
if (sci_handle_errors(port)) {
@ -1710,17 +1734,6 @@ static irqreturn_t sci_er_interrupt(int irq, void *ptr)
return IRQ_HANDLED;
}
static irqreturn_t sci_br_interrupt(int irq, void *ptr)
{
struct uart_port *port = ptr;
/* Handle BREAKs */
sci_handle_breaks(port);
sci_clear_SCxSR(port, SCxSR_BREAK_CLEAR(port));
return IRQ_HANDLED;
}
static irqreturn_t sci_mpxed_interrupt(int irq, void *ptr)
{
unsigned short ssr_status, scr_status, err_enabled, orer_status = 0;
@ -1794,6 +1807,16 @@ static const struct sci_irq_desc {
.handler = sci_br_interrupt,
},
[SCIx_DRI_IRQ] = {
.desc = "rx ready",
.handler = sci_rx_interrupt,
},
[SCIx_TEI_IRQ] = {
.desc = "tx end",
.handler = sci_tx_interrupt,
},
/*
* Special muxed handler.
*/
@ -1806,12 +1829,19 @@ static const struct sci_irq_desc {
static int sci_request_irq(struct sci_port *port)
{
struct uart_port *up = &port->port;
int i, j, ret = 0;
int i, j, w, ret = 0;
for (i = j = 0; i < SCIx_NR_IRQS; i++, j++) {
const struct sci_irq_desc *desc;
int irq;
/* Check if already registered (muxed) */
for (w = 0; w < i; w++)
if (port->irqs[w] == port->irqs[i])
w = i + 1;
if (w > i)
continue;
if (SCIx_IRQ_IS_MUXED(port)) {
i = SCIx_MUX_IRQ;
irq = up->irq;
@ -2092,13 +2122,15 @@ static void sci_shutdown(struct uart_port *port)
spin_unlock_irqrestore(&port->lock, flags);
#ifdef CONFIG_SERIAL_SH_SCI_DMA
if (s->chan_rx) {
if (s->chan_rx_saved) {
dev_dbg(port->dev, "%s(%d) deleting rx_timer\n", __func__,
port->line);
hrtimer_cancel(&s->rx_timer);
}
#endif
if (s->rx_trigger > 1 && s->rx_fifo_timeout > 0)
del_timer_sync(&s->rx_fifo_timer);
sci_free_irq(s);
sci_free_dma(port);
}
@ -2778,7 +2810,7 @@ static int sci_init_single(struct platform_device *dev,
{
struct uart_port *port = &sci_port->port;
const struct resource *res;
unsigned int i;
unsigned int i, regtype;
int ret;
sci_port->cfg = p;
@ -2799,22 +2831,23 @@ static int sci_init_single(struct platform_device *dev,
/* The SCI generates several interrupts. They can be muxed together or
* connected to different interrupt lines. In the muxed case only one
* interrupt resource is specified. In the non-muxed case three or four
* interrupt resources are specified, as the BRI interrupt is optional.
* interrupt resource is specified as there is only one interrupt ID.
* In the non-muxed case, up to 6 interrupt signals might be generated
* from the SCI, however those signals might have their own individual
* interrupt ID numbers, or muxed together with another interrupt.
*/
if (sci_port->irqs[0] < 0)
return -ENXIO;
if (sci_port->irqs[1] < 0) {
sci_port->irqs[1] = sci_port->irqs[0];
sci_port->irqs[2] = sci_port->irqs[0];
sci_port->irqs[3] = sci_port->irqs[0];
}
if (sci_port->irqs[1] < 0)
for (i = 1; i < ARRAY_SIZE(sci_port->irqs); i++)
sci_port->irqs[i] = sci_port->irqs[0];
sci_port->params = sci_probe_regmap(p);
if (unlikely(sci_port->params == NULL))
return -EINVAL;
regtype = sci_port->params - sci_port_params;
switch (p->type) {
case PORT_SCIFB:
sci_port->rx_trigger = 48;
@ -2869,6 +2902,10 @@ static int sci_init_single(struct platform_device *dev,
port->regshift = 1;
}
if (regtype == SCIx_SH4_SCIF_REGTYPE)
if (sci_port->reg_size >= 0x20)
port->regshift = 1;
/*
* The UART port needs an IRQ value, so we peg this to the RX IRQ
* for the multi-IRQ ports, which is where we are primarily

View File

@ -21,6 +21,7 @@
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/of_platform.h>
#include <linux/clk.h>
#define ULITE_NAME "ttyUL"
#define ULITE_MAJOR 204
@ -54,6 +55,11 @@
#define ULITE_CONTROL_RST_RX 0x02
#define ULITE_CONTROL_IE 0x10
struct uartlite_data {
const struct uartlite_reg_ops *reg_ops;
struct clk *clk;
};
struct uartlite_reg_ops {
u32 (*in)(void __iomem *addr);
void (*out)(u32 val, void __iomem *addr);
@ -91,16 +97,16 @@ static const struct uartlite_reg_ops uartlite_le = {
static inline u32 uart_in32(u32 offset, struct uart_port *port)
{
const struct uartlite_reg_ops *reg_ops = port->private_data;
struct uartlite_data *pdata = port->private_data;
return reg_ops->in(port->membase + offset);
return pdata->reg_ops->in(port->membase + offset);
}
static inline void uart_out32(u32 val, u32 offset, struct uart_port *port)
{
const struct uartlite_reg_ops *reg_ops = port->private_data;
struct uartlite_data *pdata = port->private_data;
reg_ops->out(val, port->membase + offset);
pdata->reg_ops->out(val, port->membase + offset);
}
static struct uart_port ulite_ports[ULITE_NR_UARTS];
@ -257,8 +263,15 @@ static void ulite_break_ctl(struct uart_port *port, int ctl)
static int ulite_startup(struct uart_port *port)
{
struct uartlite_data *pdata = port->private_data;
int ret;
ret = clk_enable(pdata->clk);
if (ret) {
dev_err(port->dev, "Failed to enable clock\n");
return ret;
}
ret = request_irq(port->irq, ulite_isr, IRQF_SHARED | IRQF_TRIGGER_RISING,
"uartlite", port);
if (ret)
@ -273,9 +286,12 @@ static int ulite_startup(struct uart_port *port)
static void ulite_shutdown(struct uart_port *port)
{
struct uartlite_data *pdata = port->private_data;
uart_out32(0, ULITE_CONTROL, port);
uart_in32(ULITE_CONTROL, port); /* dummy */
free_irq(port->irq, port);
clk_disable(pdata->clk);
}
static void ulite_set_termios(struct uart_port *port, struct ktermios *termios,
@ -325,6 +341,7 @@ static void ulite_release_port(struct uart_port *port)
static int ulite_request_port(struct uart_port *port)
{
struct uartlite_data *pdata = port->private_data;
int ret;
pr_debug("ulite console: port=%p; port->mapbase=%llx\n",
@ -342,13 +359,13 @@ static int ulite_request_port(struct uart_port *port)
return -EBUSY;
}
port->private_data = (void *)&uartlite_be;
pdata->reg_ops = &uartlite_be;
ret = uart_in32(ULITE_CONTROL, port);
uart_out32(ULITE_CONTROL_RST_TX, ULITE_CONTROL, port);
ret = uart_in32(ULITE_STATUS, port);
/* Endianess detection */
if ((ret & ULITE_STATUS_TXEMPTY) != ULITE_STATUS_TXEMPTY)
port->private_data = (void *)&uartlite_le;
pdata->reg_ops = &uartlite_le;
return 0;
}
@ -365,6 +382,17 @@ static int ulite_verify_port(struct uart_port *port, struct serial_struct *ser)
return -EINVAL;
}
static void ulite_pm(struct uart_port *port, unsigned int state,
unsigned int oldstate)
{
struct uartlite_data *pdata = port->private_data;
if (!state)
clk_enable(pdata->clk);
else
clk_disable(pdata->clk);
}
#ifdef CONFIG_CONSOLE_POLL
static int ulite_get_poll_char(struct uart_port *port)
{
@ -400,6 +428,7 @@ static const struct uart_ops ulite_ops = {
.request_port = ulite_request_port,
.config_port = ulite_config_port,
.verify_port = ulite_verify_port,
.pm = ulite_pm,
#ifdef CONFIG_CONSOLE_POLL
.poll_get_char = ulite_get_poll_char,
.poll_put_char = ulite_put_poll_char,
@ -585,10 +614,12 @@ static struct uart_driver ulite_uart_driver = {
* @id: requested id number. Pass -1 for automatic port assignment
* @base: base address of uartlite registers
* @irq: irq number for uartlite
* @pdata: private data for uartlite
*
* Returns: 0 on success, <0 otherwise
*/
static int ulite_assign(struct device *dev, int id, u32 base, int irq)
static int ulite_assign(struct device *dev, int id, u32 base, int irq,
struct uartlite_data *pdata)
{
struct uart_port *port;
int rc;
@ -625,6 +656,7 @@ static int ulite_assign(struct device *dev, int id, u32 base, int irq)
port->dev = dev;
port->type = PORT_UNKNOWN;
port->line = id;
port->private_data = pdata;
dev_set_drvdata(dev, port);
@ -658,10 +690,44 @@ static int ulite_release(struct device *dev)
return rc;
}
/**
* ulite_suspend - Stop the device.
*
* @dev: handle to the device structure.
* Return: 0 always.
*/
static int __maybe_unused ulite_suspend(struct device *dev)
{
struct uart_port *port = dev_get_drvdata(dev);
if (port)
uart_suspend_port(&ulite_uart_driver, port);
return 0;
}
/**
* ulite_resume - Resume the device.
*
* @dev: handle to the device structure.
* Return: 0 on success, errno otherwise.
*/
static int __maybe_unused ulite_resume(struct device *dev)
{
struct uart_port *port = dev_get_drvdata(dev);
if (port)
uart_resume_port(&ulite_uart_driver, port);
return 0;
}
/* ---------------------------------------------------------------------
* Platform bus binding
*/
static SIMPLE_DEV_PM_OPS(ulite_pm_ops, ulite_suspend, ulite_resume);
#if defined(CONFIG_OF)
/* Match table for of_platform binding */
static const struct of_device_id ulite_of_match[] = {
@ -675,7 +741,8 @@ MODULE_DEVICE_TABLE(of, ulite_of_match);
static int ulite_probe(struct platform_device *pdev)
{
struct resource *res;
int irq;
struct uartlite_data *pdata;
int irq, ret;
int id = pdev->id;
#ifdef CONFIG_OF
const __be32 *prop;
@ -684,6 +751,10 @@ static int ulite_probe(struct platform_device *pdev)
if (prop)
id = be32_to_cpup(prop);
#endif
pdata = devm_kzalloc(&pdev->dev, sizeof(struct uartlite_data),
GFP_KERNEL);
if (!pdata)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
@ -693,11 +764,33 @@ static int ulite_probe(struct platform_device *pdev)
if (irq <= 0)
return -ENXIO;
return ulite_assign(&pdev->dev, id, res->start, irq);
pdata->clk = devm_clk_get(&pdev->dev, "s_axi_aclk");
if (IS_ERR(pdata->clk)) {
if (PTR_ERR(pdata->clk) != -ENOENT)
return PTR_ERR(pdata->clk);
/*
* Clock framework support is optional, continue on
* anyways if we don't find a matching clock.
*/
pdata->clk = NULL;
}
ret = clk_prepare(pdata->clk);
if (ret) {
dev_err(&pdev->dev, "Failed to prepare clock\n");
return ret;
}
return ulite_assign(&pdev->dev, id, res->start, irq, pdata);
}
static int ulite_remove(struct platform_device *pdev)
{
struct uart_port *port = dev_get_drvdata(&pdev->dev);
struct uartlite_data *pdata = port->private_data;
clk_disable_unprepare(pdata->clk);
return ulite_release(&pdev->dev);
}
@ -710,6 +803,7 @@ static struct platform_driver ulite_platform_driver = {
.driver = {
.name = "uartlite",
.of_match_table = of_match_ptr(ulite_of_match),
.pm = &ulite_pm_ops,
},
};

View File

@ -167,6 +167,7 @@ MODULE_PARM_DESC(rx_timeout, "Rx timeout, 1-255");
#define CDNS_UART_SR_TXEMPTY 0x00000008 /* TX FIFO empty */
#define CDNS_UART_SR_TXFULL 0x00000010 /* TX FIFO full */
#define CDNS_UART_SR_RXTRIG 0x00000001 /* Rx Trigger */
#define CDNS_UART_SR_TACTIVE 0x00000800 /* TX state machine active */
/* baud dividers min/max values */
#define CDNS_UART_BDIV_MIN 4
@ -829,7 +830,7 @@ static int cdns_uart_startup(struct uart_port *port)
* the receiver.
*/
status = readl(port->membase + CDNS_UART_CR);
status &= CDNS_UART_CR_RX_DIS;
status &= ~CDNS_UART_CR_RX_DIS;
status |= CDNS_UART_CR_RX_EN;
writel(status, port->membase + CDNS_UART_CR);
@ -1098,16 +1099,6 @@ static const struct uart_ops cdns_uart_ops = {
};
#ifdef CONFIG_SERIAL_XILINX_PS_UART_CONSOLE
/**
* cdns_uart_console_wait_tx - Wait for the TX to be full
* @port: Handle to the uart port structure
*/
static void cdns_uart_console_wait_tx(struct uart_port *port)
{
while (!(readl(port->membase + CDNS_UART_SR) & CDNS_UART_SR_TXEMPTY))
barrier();
}
/**
* cdns_uart_console_putchar - write the character to the FIFO buffer
* @port: Handle to the uart port structure
@ -1115,7 +1106,8 @@ static void cdns_uart_console_wait_tx(struct uart_port *port)
*/
static void cdns_uart_console_putchar(struct uart_port *port, int ch)
{
cdns_uart_console_wait_tx(port);
while (readl(port->membase + CDNS_UART_SR) & CDNS_UART_SR_TXFULL)
cpu_relax();
writel(ch, port->membase + CDNS_UART_FIFO);
}
@ -1206,9 +1198,10 @@ static void cdns_uart_console_write(struct console *co, const char *s,
writel(ctrl, port->membase + CDNS_UART_CR);
uart_console_write(port, s, count, cdns_uart_console_putchar);
cdns_uart_console_wait_tx(port);
writel(ctrl, port->membase + CDNS_UART_CR);
while ((readl(port->membase + CDNS_UART_SR) &
(CDNS_UART_SR_TXEMPTY | CDNS_UART_SR_TACTIVE)) !=
CDNS_UART_SR_TXEMPTY)
cpu_relax();
/* restore interrupt state */
writel(imr, port->membase + CDNS_UART_IER);

View File

@ -100,11 +100,11 @@ speed_t tty_termios_input_baud_rate(struct ktermios *termios)
if (cbaud == B0)
return tty_termios_baud_rate(termios);
#ifdef BOTHER
/* Magic token for arbitrary speed via c_ispeed*/
if (cbaud == BOTHER)
return termios->c_ispeed;
#endif
if (cbaud & CBAUDEX) {
cbaud &= ~CBAUDEX;
@ -114,9 +114,9 @@ speed_t tty_termios_input_baud_rate(struct ktermios *termios)
cbaud += 15;
}
return baud_table[cbaud];
#else
#else /* IBSHIFT */
return tty_termios_baud_rate(termios);
#endif
#endif /* IBSHIFT */
}
EXPORT_SYMBOL(tty_termios_input_baud_rate);
@ -156,19 +156,27 @@ void tty_termios_encode_baud_rate(struct ktermios *termios,
termios->c_ispeed = ibaud;
termios->c_ospeed = obaud;
#ifdef IBSHIFT
if ((termios->c_cflag >> IBSHIFT) & CBAUD)
ibinput = 1; /* An input speed was specified */
#endif
#ifdef BOTHER
/* If the user asked for a precise weird speed give a precise weird
answer. If they asked for a Bfoo speed they may have problems
digesting non-exact replies so fuzz a bit */
if ((termios->c_cflag & CBAUD) == BOTHER)
if ((termios->c_cflag & CBAUD) == BOTHER) {
oclose = 0;
if (!ibinput)
iclose = 0;
}
if (((termios->c_cflag >> IBSHIFT) & CBAUD) == BOTHER)
iclose = 0;
if ((termios->c_cflag >> IBSHIFT) & CBAUD)
ibinput = 1; /* An input speed was specified */
#endif
termios->c_cflag &= ~CBAUD;
#ifdef IBSHIFT
termios->c_cflag &= ~(CBAUD << IBSHIFT);
#endif
/*
* Our goal is to find a close match to the standard baud rate

View File

@ -814,9 +814,9 @@ void start_tty(struct tty_struct *tty)
}
EXPORT_SYMBOL(start_tty);
static void tty_update_time(struct timespec *time)
static void tty_update_time(struct timespec64 *time)
{
unsigned long sec = get_seconds();
time64_t sec = ktime_get_real_seconds();
/*
* We only care if the two values differ in anything other than the
@ -867,13 +867,8 @@ static ssize_t tty_read(struct file *file, char __user *buf, size_t count,
i = -EIO;
tty_ldisc_deref(ld);
if (i > 0) {
struct timespec ts;
ts = timespec64_to_timespec(inode->i_atime);
tty_update_time(&ts);
inode->i_atime = timespec_to_timespec64(ts);
}
if (i > 0)
tty_update_time(&inode->i_atime);
return i;
}
@ -974,11 +969,7 @@ static inline ssize_t do_tty_write(
cond_resched();
}
if (written) {
struct timespec ts;
ts = timespec64_to_timespec(file_inode(file)->i_mtime);
tty_update_time(&ts);
file_inode(file)->i_mtime = timespec_to_timespec64(ts);
tty_update_time(&file_inode(file)->i_mtime);
ret = written;
}
out:

View File

@ -74,28 +74,6 @@ struct ldsem_waiter {
struct task_struct *task;
};
static inline long ldsem_atomic_update(long delta, struct ld_semaphore *sem)
{
return atomic_long_add_return(delta, (atomic_long_t *)&sem->count);
}
/*
* ldsem_cmpxchg() updates @*old with the last-known sem->count value.
* Returns 1 if count was successfully changed; @*old will have @new value.
* Returns 0 if count was not changed; @*old will have most recent sem->count
*/
static inline int ldsem_cmpxchg(long *old, long new, struct ld_semaphore *sem)
{
long tmp = atomic_long_cmpxchg(&sem->count, *old, new);
if (tmp == *old) {
*old = new;
return 1;
} else {
*old = tmp;
return 0;
}
}
/*
* Initialize an ldsem:
*/
@ -109,7 +87,7 @@ void __init_ldsem(struct ld_semaphore *sem, const char *name,
debug_check_no_locks_freed((void *)sem, sizeof(*sem));
lockdep_init_map(&sem->dep_map, name, key, 0);
#endif
sem->count = LDSEM_UNLOCKED;
atomic_long_set(&sem->count, LDSEM_UNLOCKED);
sem->wait_readers = 0;
raw_spin_lock_init(&sem->wait_lock);
INIT_LIST_HEAD(&sem->read_wait);
@ -122,16 +100,17 @@ static void __ldsem_wake_readers(struct ld_semaphore *sem)
struct task_struct *tsk;
long adjust, count;
/* Try to grant read locks to all readers on the read wait list.
/*
* Try to grant read locks to all readers on the read wait list.
* Note the 'active part' of the count is incremented by
* the number of readers before waking any processes up.
*/
adjust = sem->wait_readers * (LDSEM_ACTIVE_BIAS - LDSEM_WAIT_BIAS);
count = ldsem_atomic_update(adjust, sem);
count = atomic_long_add_return(adjust, &sem->count);
do {
if (count > 0)
break;
if (ldsem_cmpxchg(&count, count - adjust, sem))
if (atomic_long_try_cmpxchg(&sem->count, &count, count - adjust))
return;
} while (1);
@ -148,14 +127,15 @@ static void __ldsem_wake_readers(struct ld_semaphore *sem)
static inline int writer_trylock(struct ld_semaphore *sem)
{
/* only wake this writer if the active part of the count can be
/*
* Only wake this writer if the active part of the count can be
* transitioned from 0 -> 1
*/
long count = ldsem_atomic_update(LDSEM_ACTIVE_BIAS, sem);
long count = atomic_long_add_return(LDSEM_ACTIVE_BIAS, &sem->count);
do {
if ((count & LDSEM_ACTIVE_MASK) == LDSEM_ACTIVE_BIAS)
return 1;
if (ldsem_cmpxchg(&count, count - LDSEM_ACTIVE_BIAS, sem))
if (atomic_long_try_cmpxchg(&sem->count, &count, count - LDSEM_ACTIVE_BIAS))
return 0;
} while (1);
}
@ -205,12 +185,16 @@ down_read_failed(struct ld_semaphore *sem, long count, long timeout)
/* set up my own style of waitqueue */
raw_spin_lock_irq(&sem->wait_lock);
/* Try to reverse the lock attempt but if the count has changed
/*
* Try to reverse the lock attempt but if the count has changed
* so that reversing fails, check if there are are no waiters,
* and early-out if not */
* and early-out if not
*/
do {
if (ldsem_cmpxchg(&count, count + adjust, sem))
if (atomic_long_try_cmpxchg(&sem->count, &count, count + adjust)) {
count += adjust;
break;
}
if (count > 0) {
raw_spin_unlock_irq(&sem->wait_lock);
return sem;
@ -243,12 +227,14 @@ down_read_failed(struct ld_semaphore *sem, long count, long timeout)
__set_current_state(TASK_RUNNING);
if (!timeout) {
/* lock timed out but check if this task was just
/*
* Lock timed out but check if this task was just
* granted lock ownership - if so, pretend there
* was no timeout; otherwise, cleanup lock wait */
* was no timeout; otherwise, cleanup lock wait.
*/
raw_spin_lock_irq(&sem->wait_lock);
if (waiter.task) {
ldsem_atomic_update(-LDSEM_WAIT_BIAS, sem);
atomic_long_add_return(-LDSEM_WAIT_BIAS, &sem->count);
list_del(&waiter.list);
raw_spin_unlock_irq(&sem->wait_lock);
put_task_struct(waiter.task);
@ -273,11 +259,13 @@ down_write_failed(struct ld_semaphore *sem, long count, long timeout)
/* set up my own style of waitqueue */
raw_spin_lock_irq(&sem->wait_lock);
/* Try to reverse the lock attempt but if the count has changed
/*
* Try to reverse the lock attempt but if the count has changed
* so that reversing fails, check if the lock is now owned,
* and early-out if so */
* and early-out if so.
*/
do {
if (ldsem_cmpxchg(&count, count + adjust, sem))
if (atomic_long_try_cmpxchg(&sem->count, &count, count + adjust))
break;
if ((count & LDSEM_ACTIVE_MASK) == LDSEM_ACTIVE_BIAS) {
raw_spin_unlock_irq(&sem->wait_lock);
@ -303,7 +291,7 @@ down_write_failed(struct ld_semaphore *sem, long count, long timeout)
}
if (!locked)
ldsem_atomic_update(-LDSEM_WAIT_BIAS, sem);
atomic_long_add_return(-LDSEM_WAIT_BIAS, &sem->count);
list_del(&waiter.list);
raw_spin_unlock_irq(&sem->wait_lock);
@ -324,7 +312,7 @@ static int __ldsem_down_read_nested(struct ld_semaphore *sem,
lockdep_acquire_read(sem, subclass, 0, _RET_IP_);
count = ldsem_atomic_update(LDSEM_READ_BIAS, sem);
count = atomic_long_add_return(LDSEM_READ_BIAS, &sem->count);
if (count <= 0) {
lock_stat(sem, contended);
if (!down_read_failed(sem, count, timeout)) {
@ -343,7 +331,7 @@ static int __ldsem_down_write_nested(struct ld_semaphore *sem,
lockdep_acquire(sem, subclass, 0, _RET_IP_);
count = ldsem_atomic_update(LDSEM_WRITE_BIAS, sem);
count = atomic_long_add_return(LDSEM_WRITE_BIAS, &sem->count);
if ((count & LDSEM_ACTIVE_MASK) != LDSEM_ACTIVE_BIAS) {
lock_stat(sem, contended);
if (!down_write_failed(sem, count, timeout)) {
@ -370,10 +358,10 @@ int __sched ldsem_down_read(struct ld_semaphore *sem, long timeout)
*/
int ldsem_down_read_trylock(struct ld_semaphore *sem)
{
long count = sem->count;
long count = atomic_long_read(&sem->count);
while (count >= 0) {
if (ldsem_cmpxchg(&count, count + LDSEM_READ_BIAS, sem)) {
if (atomic_long_try_cmpxchg(&sem->count, &count, count + LDSEM_READ_BIAS)) {
lockdep_acquire_read(sem, 0, 1, _RET_IP_);
lock_stat(sem, acquired);
return 1;
@ -396,10 +384,10 @@ int __sched ldsem_down_write(struct ld_semaphore *sem, long timeout)
*/
int ldsem_down_write_trylock(struct ld_semaphore *sem)
{
long count = sem->count;
long count = atomic_long_read(&sem->count);
while ((count & LDSEM_ACTIVE_MASK) == 0) {
if (ldsem_cmpxchg(&count, count + LDSEM_WRITE_BIAS, sem)) {
if (atomic_long_try_cmpxchg(&sem->count, &count, count + LDSEM_WRITE_BIAS)) {
lockdep_acquire(sem, 0, 1, _RET_IP_);
lock_stat(sem, acquired);
return 1;
@ -417,7 +405,7 @@ void ldsem_up_read(struct ld_semaphore *sem)
lockdep_release(sem, 1, _RET_IP_);
count = ldsem_atomic_update(-LDSEM_READ_BIAS, sem);
count = atomic_long_add_return(-LDSEM_READ_BIAS, &sem->count);
if (count < 0 && (count & LDSEM_ACTIVE_MASK) == 0)
ldsem_wake(sem);
}
@ -431,7 +419,7 @@ void ldsem_up_write(struct ld_semaphore *sem)
lockdep_release(sem, 1, _RET_IP_);
count = ldsem_atomic_update(-LDSEM_WRITE_BIAS, sem);
count = atomic_long_add_return(-LDSEM_WRITE_BIAS, &sem->count);
if (count < 0)
ldsem_wake(sem);
}

View File

@ -690,7 +690,35 @@ static void k_dead2(struct vc_data *vc, unsigned char value, char up_flag)
*/
static void k_dead(struct vc_data *vc, unsigned char value, char up_flag)
{
static const unsigned char ret_diacr[NR_DEAD] = {'`', '\'', '^', '~', '"', ',' };
static const unsigned char ret_diacr[NR_DEAD] = {
'`', /* dead_grave */
'\'', /* dead_acute */
'^', /* dead_circumflex */
'~', /* dead_tilda */
'"', /* dead_diaeresis */
',', /* dead_cedilla */
'_', /* dead_macron */
'U', /* dead_breve */
'.', /* dead_abovedot */
'*', /* dead_abovering */
'=', /* dead_doubleacute */
'c', /* dead_caron */
'k', /* dead_ogonek */
'i', /* dead_iota */
'#', /* dead_voiced_sound */
'o', /* dead_semivoiced_sound */
'!', /* dead_belowdot */
'?', /* dead_hook */
'+', /* dead_horn */
'-', /* dead_stroke */
')', /* dead_abovecomma */
'(', /* dead_abovereversedcomma */
':', /* dead_doublegrave */
'n', /* dead_invertedbreve */
';', /* dead_belowcomma */
'$', /* dead_currency */
'@', /* dead_greek */
};
k_deadunicode(vc, ret_diacr[value], up_flag);
}

View File

@ -57,11 +57,13 @@ static inline void highlight_pointer(const int where)
complement_pos(sel_cons, where);
}
static u16
static u32
sel_pos(int n)
{
if (use_unicode)
return screen_glyph_unicode(sel_cons, n / 2);
return inverse_translate(sel_cons, screen_glyph(sel_cons, n),
use_unicode);
0);
}
/**
@ -90,7 +92,8 @@ static u32 inwordLut[]={
0x07FFFFFE, /* lowercase */
};
static inline int inword(const u16 c) {
static inline int inword(const u32 c)
{
return c > 0x7f || (( inwordLut[c>>5] >> (c & 0x1F) ) & 1);
}
@ -116,14 +119,8 @@ static inline int atedge(const int p, int size_row)
return (!(p % size_row) || !((p + 2) % size_row));
}
/* constrain v such that v <= u */
static inline unsigned short limit(const unsigned short v, const unsigned short u)
{
return (v > u) ? u : v;
}
/* stores the char in UTF8 and returns the number of bytes used (1-3) */
static int store_utf8(u16 c, char *p)
/* stores the char in UTF8 and returns the number of bytes used (1-4) */
static int store_utf8(u32 c, char *p)
{
if (c < 0x80) {
/* 0******* */
@ -134,13 +131,26 @@ static int store_utf8(u16 c, char *p)
p[0] = 0xc0 | (c >> 6);
p[1] = 0x80 | (c & 0x3f);
return 2;
} else {
} else if (c < 0x10000) {
/* 1110**** 10****** 10****** */
p[0] = 0xe0 | (c >> 12);
p[1] = 0x80 | ((c >> 6) & 0x3f);
p[2] = 0x80 | (c & 0x3f);
return 3;
}
} else if (c < 0x110000) {
/* 11110*** 10****** 10****** 10****** */
p[0] = 0xf0 | (c >> 18);
p[1] = 0x80 | ((c >> 12) & 0x3f);
p[2] = 0x80 | ((c >> 6) & 0x3f);
p[3] = 0x80 | (c & 0x3f);
return 4;
} else {
/* outside Unicode, replace with U+FFFD */
p[0] = 0xef;
p[1] = 0xbf;
p[2] = 0xbd;
return 3;
}
}
/**
@ -160,17 +170,17 @@ int set_selection(const struct tiocl_selection __user *sel, struct tty_struct *t
struct tiocl_selection v;
char *bp, *obp;
int i, ps, pe, multiplier;
u16 c;
u32 c;
int mode;
poke_blanked_console();
if (copy_from_user(&v, sel, sizeof(*sel)))
return -EFAULT;
v.xs = limit(v.xs - 1, vc->vc_cols - 1);
v.ys = limit(v.ys - 1, vc->vc_rows - 1);
v.xe = limit(v.xe - 1, vc->vc_cols - 1);
v.ye = limit(v.ye - 1, vc->vc_rows - 1);
v.xs = min_t(u16, v.xs - 1, vc->vc_cols - 1);
v.ys = min_t(u16, v.ys - 1, vc->vc_rows - 1);
v.xe = min_t(u16, v.xe - 1, vc->vc_cols - 1);
v.ye = min_t(u16, v.ye - 1, vc->vc_rows - 1);
ps = v.ys * vc->vc_size_row + (v.xs << 1);
pe = v.ye * vc->vc_size_row + (v.xe << 1);
@ -279,7 +289,7 @@ int set_selection(const struct tiocl_selection __user *sel, struct tty_struct *t
sel_end = new_sel_end;
/* Allocate a new buffer before freeing the old one ... */
multiplier = use_unicode ? 3 : 1; /* chars can take up to 3 bytes */
multiplier = use_unicode ? 4 : 1; /* chars can take up to 4 bytes */
bp = kmalloc_array((sel_end - sel_start) / 2 + 1, multiplier,
GFP_KERNEL);
if (!bp) {

View File

@ -10,6 +10,12 @@
* Attribute/character pair is in native endianity.
* [minor: N+128]
*
* /dev/vcsuN: similar to /dev/vcsaN but using 4-byte unicode values
* instead of 1-byte screen glyph values.
* [minor: N+64]
*
* /dev/vcsuaN: same idea as /dev/vcsaN for unicode (not yet implemented).
*
* This replaces screendump and part of selection, so that the system
* administrator can control access using file system permissions.
*
@ -51,6 +57,26 @@
#define CON_BUF_SIZE (CONFIG_BASE_SMALL ? 256 : PAGE_SIZE)
/*
* Our minor space:
*
* 0 ... 63 glyph mode without attributes
* 64 ... 127 unicode mode without attributes
* 128 ... 191 glyph mode with attributes
* 192 ... 255 unused (reserved for unicode with attributes)
*
* This relies on MAX_NR_CONSOLES being <= 63, meaning 63 actual consoles
* with minors 0, 64, 128 and 192 being proxies for the foreground console.
*/
#if MAX_NR_CONSOLES > 63
#warning "/dev/vcs* devices may not accommodate more than 63 consoles"
#endif
#define console(inode) (iminor(inode) & 63)
#define use_unicode(inode) (iminor(inode) & 64)
#define use_attributes(inode) (iminor(inode) & 128)
struct vcs_poll_data {
struct notifier_block notifier;
unsigned int cons_num;
@ -102,7 +128,7 @@ vcs_poll_data_get(struct file *file)
poll = kzalloc(sizeof(*poll), GFP_KERNEL);
if (!poll)
return NULL;
poll->cons_num = iminor(file_inode(file)) & 127;
poll->cons_num = console(file_inode(file));
init_waitqueue_head(&poll->waitq);
poll->notifier.notifier_call = vcs_notifier;
if (register_vt_notifier(&poll->notifier) != 0) {
@ -140,7 +166,7 @@ vcs_poll_data_get(struct file *file)
static struct vc_data*
vcs_vc(struct inode *inode, int *viewed)
{
unsigned int currcons = iminor(inode) & 127;
unsigned int currcons = console(inode);
WARN_CONSOLE_UNLOCKED();
@ -164,7 +190,6 @@ static int
vcs_size(struct inode *inode)
{
int size;
int minor = iminor(inode);
struct vc_data *vc;
WARN_CONSOLE_UNLOCKED();
@ -175,8 +200,12 @@ vcs_size(struct inode *inode)
size = vc->vc_rows * vc->vc_cols;
if (minor & 128)
if (use_attributes(inode)) {
if (use_unicode(inode))
return -EOPNOTSUPP;
size = 2*size + HEADER_SIZE;
} else if (use_unicode(inode))
size *= 4;
return size;
}
@ -197,12 +226,10 @@ static ssize_t
vcs_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
struct inode *inode = file_inode(file);
unsigned int currcons = iminor(inode);
struct vc_data *vc;
struct vcs_poll_data *poll;
long pos;
long attr, read;
int col, maxcol, viewed;
long pos, read;
int attr, uni_mode, row, col, maxcol, viewed;
unsigned short *org = NULL;
ssize_t ret;
char *con_buf;
@ -218,7 +245,8 @@ vcs_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
*/
console_lock();
attr = (currcons & 128);
uni_mode = use_unicode(inode);
attr = use_attributes(inode);
ret = -ENXIO;
vc = vcs_vc(inode, &viewed);
if (!vc)
@ -227,6 +255,10 @@ vcs_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
ret = -EINVAL;
if (pos < 0)
goto unlock_out;
/* we enforce 32-bit alignment for pos and count in unicode mode */
if (uni_mode && (pos | count) & 3)
goto unlock_out;
poll = file->private_data;
if (count && poll)
poll->seen_last_update = true;
@ -266,7 +298,28 @@ vcs_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
con_buf_start = con_buf0 = con_buf;
orig_count = this_round;
maxcol = vc->vc_cols;
if (!attr) {
if (uni_mode) {
unsigned int nr;
ret = vc_uniscr_check(vc);
if (ret)
break;
p /= 4;
row = p / vc->vc_cols;
col = p % maxcol;
nr = maxcol - col;
do {
if (nr > this_round/4)
nr = this_round/4;
vc_uniscr_copy_line(vc, con_buf0, viewed,
row, col, nr);
con_buf0 += nr * 4;
this_round -= nr * 4;
row++;
col = 0;
nr = maxcol;
} while (this_round);
} else if (!attr) {
org = screen_pos(vc, p, viewed);
col = p % maxcol;
p += maxcol - col;
@ -375,7 +428,6 @@ static ssize_t
vcs_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
struct inode *inode = file_inode(file);
unsigned int currcons = iminor(inode);
struct vc_data *vc;
long pos;
long attr, size, written;
@ -396,7 +448,7 @@ vcs_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
*/
console_lock();
attr = (currcons & 128);
attr = use_attributes(inode);
ret = -ENXIO;
vc = vcs_vc(inode, &viewed);
if (!vc)
@ -593,9 +645,15 @@ vcs_fasync(int fd, struct file *file, int on)
static int
vcs_open(struct inode *inode, struct file *filp)
{
unsigned int currcons = iminor(inode) & 127;
unsigned int currcons = console(inode);
bool attr = use_attributes(inode);
bool uni_mode = use_unicode(inode);
int ret = 0;
/* we currently don't support attributes in unicode mode */
if (attr && uni_mode)
return -EOPNOTSUPP;
console_lock();
if(currcons && !vc_cons_allocated(currcons-1))
ret = -ENXIO;
@ -628,6 +686,8 @@ void vcs_make_sysfs(int index)
{
device_create(vc_class, NULL, MKDEV(VCS_MAJOR, index + 1), NULL,
"vcs%u", index + 1);
device_create(vc_class, NULL, MKDEV(VCS_MAJOR, index + 65), NULL,
"vcsu%u", index + 1);
device_create(vc_class, NULL, MKDEV(VCS_MAJOR, index + 129), NULL,
"vcsa%u", index + 1);
}
@ -635,6 +695,7 @@ void vcs_make_sysfs(int index)
void vcs_remove_sysfs(int index)
{
device_destroy(vc_class, MKDEV(VCS_MAJOR, index + 1));
device_destroy(vc_class, MKDEV(VCS_MAJOR, index + 65));
device_destroy(vc_class, MKDEV(VCS_MAJOR, index + 129));
}
@ -647,6 +708,7 @@ int __init vcs_init(void)
vc_class = class_create(THIS_MODULE, "vc");
device_create(vc_class, NULL, MKDEV(VCS_MAJOR, 0), NULL, "vcs");
device_create(vc_class, NULL, MKDEV(VCS_MAJOR, 64), NULL, "vcsu");
device_create(vc_class, NULL, MKDEV(VCS_MAJOR, 128), NULL, "vcsa");
for (i = 0; i < MIN_NR_CONSOLES; i++)
vcs_make_sysfs(i);

View File

@ -104,6 +104,7 @@
#include <linux/kdb.h>
#include <linux/ctype.h>
#include <linux/bsearch.h>
#include <linux/gcd.h>
#define MAX_NR_CON_DRIVER 16
@ -317,6 +318,306 @@ void schedule_console_callback(void)
schedule_work(&console_work);
}
/*
* Code to manage unicode-based screen buffers
*/
#ifdef NO_VC_UNI_SCREEN
/* this disables and optimizes related code away at compile time */
#define get_vc_uniscr(vc) NULL
#else
#define get_vc_uniscr(vc) vc->vc_uni_screen
#endif
#define VC_UNI_SCREEN_DEBUG 0
typedef uint32_t char32_t;
/*
* Our screen buffer is preceded by an array of line pointers so that
* scrolling only implies some pointer shuffling.
*/
struct uni_screen {
char32_t *lines[0];
};
static struct uni_screen *vc_uniscr_alloc(unsigned int cols, unsigned int rows)
{
struct uni_screen *uniscr;
void *p;
unsigned int memsize, i;
/* allocate everything in one go */
memsize = cols * rows * sizeof(char32_t);
memsize += rows * sizeof(char32_t *);
p = kmalloc(memsize, GFP_KERNEL);
if (!p)
return NULL;
/* initial line pointers */
uniscr = p;
p = uniscr->lines + rows;
for (i = 0; i < rows; i++) {
uniscr->lines[i] = p;
p += cols * sizeof(char32_t);
}
return uniscr;
}
static void vc_uniscr_set(struct vc_data *vc, struct uni_screen *new_uniscr)
{
kfree(vc->vc_uni_screen);
vc->vc_uni_screen = new_uniscr;
}
static void vc_uniscr_putc(struct vc_data *vc, char32_t uc)
{
struct uni_screen *uniscr = get_vc_uniscr(vc);
if (uniscr)
uniscr->lines[vc->vc_y][vc->vc_x] = uc;
}
static void vc_uniscr_insert(struct vc_data *vc, unsigned int nr)
{
struct uni_screen *uniscr = get_vc_uniscr(vc);
if (uniscr) {
char32_t *ln = uniscr->lines[vc->vc_y];
unsigned int x = vc->vc_x, cols = vc->vc_cols;
memmove(&ln[x + nr], &ln[x], (cols - x - nr) * sizeof(*ln));
memset32(&ln[x], ' ', nr);
}
}
static void vc_uniscr_delete(struct vc_data *vc, unsigned int nr)
{
struct uni_screen *uniscr = get_vc_uniscr(vc);
if (uniscr) {
char32_t *ln = uniscr->lines[vc->vc_y];
unsigned int x = vc->vc_x, cols = vc->vc_cols;
memcpy(&ln[x], &ln[x + nr], (cols - x - nr) * sizeof(*ln));
memset32(&ln[cols - nr], ' ', nr);
}
}
static void vc_uniscr_clear_line(struct vc_data *vc, unsigned int x,
unsigned int nr)
{
struct uni_screen *uniscr = get_vc_uniscr(vc);
if (uniscr) {
char32_t *ln = uniscr->lines[vc->vc_y];
memset32(&ln[x], ' ', nr);
}
}
static void vc_uniscr_clear_lines(struct vc_data *vc, unsigned int y,
unsigned int nr)
{
struct uni_screen *uniscr = get_vc_uniscr(vc);
if (uniscr) {
unsigned int cols = vc->vc_cols;
while (nr--)
memset32(uniscr->lines[y++], ' ', cols);
}
}
static void vc_uniscr_scroll(struct vc_data *vc, unsigned int t, unsigned int b,
enum con_scroll dir, unsigned int nr)
{
struct uni_screen *uniscr = get_vc_uniscr(vc);
if (uniscr) {
unsigned int i, j, k, sz, d, clear;
sz = b - t;
clear = b - nr;
d = nr;
if (dir == SM_DOWN) {
clear = t;
d = sz - nr;
}
for (i = 0; i < gcd(d, sz); i++) {
char32_t *tmp = uniscr->lines[t + i];
j = i;
while (1) {
k = j + d;
if (k >= sz)
k -= sz;
if (k == i)
break;
uniscr->lines[t + j] = uniscr->lines[t + k];
j = k;
}
uniscr->lines[t + j] = tmp;
}
vc_uniscr_clear_lines(vc, clear, nr);
}
}
static void vc_uniscr_copy_area(struct uni_screen *dst,
unsigned int dst_cols,
unsigned int dst_rows,
struct uni_screen *src,
unsigned int src_cols,
unsigned int src_top_row,
unsigned int src_bot_row)
{
unsigned int dst_row = 0;
if (!dst)
return;
while (src_top_row < src_bot_row) {
char32_t *src_line = src->lines[src_top_row];
char32_t *dst_line = dst->lines[dst_row];
memcpy(dst_line, src_line, src_cols * sizeof(char32_t));
if (dst_cols - src_cols)
memset32(dst_line + src_cols, ' ', dst_cols - src_cols);
src_top_row++;
dst_row++;
}
while (dst_row < dst_rows) {
char32_t *dst_line = dst->lines[dst_row];
memset32(dst_line, ' ', dst_cols);
dst_row++;
}
}
/*
* Called from vcs_read() to make sure unicode screen retrieval is possible.
* This will initialize the unicode screen buffer if not already done.
* This returns 0 if OK, or a negative error code otherwise.
* In particular, -ENODATA is returned if the console is not in UTF-8 mode.
*/
int vc_uniscr_check(struct vc_data *vc)
{
struct uni_screen *uniscr;
unsigned short *p;
int x, y, mask;
if (__is_defined(NO_VC_UNI_SCREEN))
return -EOPNOTSUPP;
WARN_CONSOLE_UNLOCKED();
if (!vc->vc_utf)
return -ENODATA;
if (vc->vc_uni_screen)
return 0;
uniscr = vc_uniscr_alloc(vc->vc_cols, vc->vc_rows);
if (!uniscr)
return -ENOMEM;
/*
* Let's populate it initially with (imperfect) reverse translation.
* This is the next best thing we can do short of having it enabled
* from the start even when no users rely on this functionality. True
* unicode content will be available after a complete screen refresh.
*/
p = (unsigned short *)vc->vc_origin;
mask = vc->vc_hi_font_mask | 0xff;
for (y = 0; y < vc->vc_rows; y++) {
char32_t *line = uniscr->lines[y];
for (x = 0; x < vc->vc_cols; x++) {
u16 glyph = scr_readw(p++) & mask;
line[x] = inverse_translate(vc, glyph, true);
}
}
vc->vc_uni_screen = uniscr;
return 0;
}
/*
* Called from vcs_read() to get the unicode data from the screen.
* This must be preceded by a successful call to vc_uniscr_check() once
* the console lock has been taken.
*/
void vc_uniscr_copy_line(struct vc_data *vc, void *dest, int viewed,
unsigned int row, unsigned int col, unsigned int nr)
{
struct uni_screen *uniscr = get_vc_uniscr(vc);
int offset = row * vc->vc_size_row + col * 2;
unsigned long pos;
BUG_ON(!uniscr);
pos = (unsigned long)screenpos(vc, offset, viewed);
if (pos >= vc->vc_origin && pos < vc->vc_scr_end) {
/*
* Desired position falls in the main screen buffer.
* However the actual row/col might be different if
* scrollback is active.
*/
row = (pos - vc->vc_origin) / vc->vc_size_row;
col = ((pos - vc->vc_origin) % vc->vc_size_row) / 2;
memcpy(dest, &uniscr->lines[row][col], nr * sizeof(char32_t));
} else {
/*
* Scrollback is active. For now let's simply backtranslate
* the screen glyphs until the unicode screen buffer does
* synchronize with console display drivers for a scrollback
* buffer of its own.
*/
u16 *p = (u16 *)pos;
int mask = vc->vc_hi_font_mask | 0xff;
char32_t *uni_buf = dest;
while (nr--) {
u16 glyph = scr_readw(p++) & mask;
*uni_buf++ = inverse_translate(vc, glyph, true);
}
}
}
/* this is for validation and debugging only */
static void vc_uniscr_debug_check(struct vc_data *vc)
{
struct uni_screen *uniscr = get_vc_uniscr(vc);
unsigned short *p;
int x, y, mask;
if (!VC_UNI_SCREEN_DEBUG || !uniscr)
return;
WARN_CONSOLE_UNLOCKED();
/*
* Make sure our unicode screen translates into the same glyphs
* as the actual screen. This is brutal indeed.
*/
p = (unsigned short *)vc->vc_origin;
mask = vc->vc_hi_font_mask | 0xff;
for (y = 0; y < vc->vc_rows; y++) {
char32_t *line = uniscr->lines[y];
for (x = 0; x < vc->vc_cols; x++) {
u16 glyph = scr_readw(p++) & mask;
char32_t uc = line[x];
int tc = conv_uni_to_pc(vc, uc);
if (tc == -4)
tc = conv_uni_to_pc(vc, 0xfffd);
if (tc == -4)
tc = conv_uni_to_pc(vc, '?');
if (tc != glyph)
pr_err_ratelimited(
"%s: mismatch at %d,%d: glyph=%#x tc=%#x\n",
__func__, x, y, glyph, tc);
}
}
}
static void con_scroll(struct vc_data *vc, unsigned int t, unsigned int b,
enum con_scroll dir, unsigned int nr)
{
@ -326,6 +627,7 @@ static void con_scroll(struct vc_data *vc, unsigned int t, unsigned int b,
nr = b - t - 1;
if (b > vc->vc_rows || t >= b || nr < 1)
return;
vc_uniscr_scroll(vc, t, b, dir, nr);
if (con_is_visible(vc) && vc->vc_sw->con_scroll(vc, t, b, dir, nr))
return;
@ -533,6 +835,7 @@ static void insert_char(struct vc_data *vc, unsigned int nr)
{
unsigned short *p = (unsigned short *) vc->vc_pos;
vc_uniscr_insert(vc, nr);
scr_memmovew(p + nr, p, (vc->vc_cols - vc->vc_x - nr) * 2);
scr_memsetw(p, vc->vc_video_erase_char, nr * 2);
vc->vc_need_wrap = 0;
@ -545,6 +848,7 @@ static void delete_char(struct vc_data *vc, unsigned int nr)
{
unsigned short *p = (unsigned short *) vc->vc_pos;
vc_uniscr_delete(vc, nr);
scr_memcpyw(p, p + nr, (vc->vc_cols - vc->vc_x - nr) * 2);
scr_memsetw(p + vc->vc_cols - vc->vc_x - nr, vc->vc_video_erase_char,
nr * 2);
@ -845,10 +1149,11 @@ static int vc_do_resize(struct tty_struct *tty, struct vc_data *vc,
{
unsigned long old_origin, new_origin, new_scr_end, rlth, rrem, err = 0;
unsigned long end;
unsigned int old_rows, old_row_size;
unsigned int old_rows, old_row_size, first_copied_row;
unsigned int new_cols, new_rows, new_row_size, new_screen_size;
unsigned int user;
unsigned short *newscreen;
struct uni_screen *new_uniscr = NULL;
WARN_CONSOLE_UNLOCKED();
@ -875,6 +1180,14 @@ static int vc_do_resize(struct tty_struct *tty, struct vc_data *vc,
if (!newscreen)
return -ENOMEM;
if (get_vc_uniscr(vc)) {
new_uniscr = vc_uniscr_alloc(new_cols, new_rows);
if (!new_uniscr) {
kfree(newscreen);
return -ENOMEM;
}
}
if (vc == sel_cons)
clear_selection();
@ -884,6 +1197,7 @@ static int vc_do_resize(struct tty_struct *tty, struct vc_data *vc,
err = resize_screen(vc, new_cols, new_rows, user);
if (err) {
kfree(newscreen);
kfree(new_uniscr);
return err;
}
@ -904,18 +1218,24 @@ static int vc_do_resize(struct tty_struct *tty, struct vc_data *vc,
* Cursor near the bottom, copy contents from the
* bottom of buffer
*/
old_origin += (old_rows - new_rows) * old_row_size;
first_copied_row = (old_rows - new_rows);
} else {
/*
* Cursor is in no man's land, copy 1/2 screenful
* from the top and bottom of cursor position
*/
old_origin += (vc->vc_y - new_rows/2) * old_row_size;
first_copied_row = (vc->vc_y - new_rows/2);
}
}
old_origin += first_copied_row * old_row_size;
} else
first_copied_row = 0;
end = old_origin + old_row_size * min(old_rows, new_rows);
vc_uniscr_copy_area(new_uniscr, new_cols, new_rows,
get_vc_uniscr(vc), rlth/2, first_copied_row,
min(old_rows, new_rows));
vc_uniscr_set(vc, new_uniscr);
update_attr(vc);
while (old_origin < end) {
@ -1013,6 +1333,7 @@ struct vc_data *vc_deallocate(unsigned int currcons)
vc->vc_sw->con_deinit(vc);
put_pid(vc->vt_pid);
module_put(vc->vc_sw->owner);
vc_uniscr_set(vc, NULL);
kfree(vc->vc_screenbuf);
vc_cons[currcons].d = NULL;
}
@ -1171,15 +1492,22 @@ static void csi_J(struct vc_data *vc, int vpar)
switch (vpar) {
case 0: /* erase from cursor to end of display */
vc_uniscr_clear_line(vc, vc->vc_x,
vc->vc_cols - vc->vc_x);
vc_uniscr_clear_lines(vc, vc->vc_y + 1,
vc->vc_rows - vc->vc_y - 1);
count = (vc->vc_scr_end - vc->vc_pos) >> 1;
start = (unsigned short *)vc->vc_pos;
break;
case 1: /* erase from start to cursor */
vc_uniscr_clear_line(vc, 0, vc->vc_x + 1);
vc_uniscr_clear_lines(vc, 0, vc->vc_y);
count = ((vc->vc_pos - vc->vc_origin) >> 1) + 1;
start = (unsigned short *)vc->vc_origin;
break;
case 2: /* erase whole display */
case 3: /* (and scrollback buffer later) */
vc_uniscr_clear_lines(vc, 0, vc->vc_rows);
count = vc->vc_cols * vc->vc_rows;
start = (unsigned short *)vc->vc_origin;
break;
@ -1200,25 +1528,27 @@ static void csi_J(struct vc_data *vc, int vpar)
static void csi_K(struct vc_data *vc, int vpar)
{
unsigned int count;
unsigned short * start;
unsigned short *start = (unsigned short *)vc->vc_pos;
int offset;
switch (vpar) {
case 0: /* erase from cursor to end of line */
offset = 0;
count = vc->vc_cols - vc->vc_x;
start = (unsigned short *)vc->vc_pos;
break;
case 1: /* erase from start of line to cursor */
start = (unsigned short *)(vc->vc_pos - (vc->vc_x << 1));
offset = -vc->vc_x;
count = vc->vc_x + 1;
break;
case 2: /* erase whole line */
start = (unsigned short *)(vc->vc_pos - (vc->vc_x << 1));
offset = -vc->vc_x;
count = vc->vc_cols;
break;
default:
return;
}
scr_memsetw(start, vc->vc_video_erase_char, 2 * count);
vc_uniscr_clear_line(vc, vc->vc_x + offset, count);
scr_memsetw(start + offset, vc->vc_video_erase_char, 2 * count);
vc->vc_need_wrap = 0;
if (con_should_update(vc))
do_update_region(vc, (unsigned long) start, count);
@ -1232,6 +1562,7 @@ static void csi_X(struct vc_data *vc, int vpar) /* erase the following vpar posi
vpar++;
count = (vpar > vc->vc_cols - vc->vc_x) ? (vc->vc_cols - vc->vc_x) : vpar;
vc_uniscr_clear_line(vc, vc->vc_x, count);
scr_memsetw((unsigned short *)vc->vc_pos, vc->vc_video_erase_char, 2 * count);
if (con_should_update(vc))
vc->vc_sw->con_clear(vc, vc->vc_y, vc->vc_x, 1, count);
@ -2188,7 +2519,7 @@ static void con_flush(struct vc_data *vc, unsigned long draw_from,
/* acquires console_lock */
static int do_con_write(struct tty_struct *tty, const unsigned char *buf, int count)
{
int c, tc, ok, n = 0, draw_x = -1;
int c, next_c, tc, ok, n = 0, draw_x = -1;
unsigned int currcons;
unsigned long draw_from = 0, draw_to = 0;
struct vc_data *vc;
@ -2382,6 +2713,7 @@ rescan_last_byte:
con_flush(vc, draw_from, draw_to, &draw_x);
}
next_c = c;
while (1) {
if (vc->vc_need_wrap || vc->vc_decim)
con_flush(vc, draw_from, draw_to,
@ -2392,6 +2724,7 @@ rescan_last_byte:
}
if (vc->vc_decim)
insert_char(vc, 1);
vc_uniscr_putc(vc, next_c);
scr_writew(himask ?
((vc_attr << 8) & ~himask) + ((tc & 0x100) ? himask : 0) + (tc & 0xff) :
(vc_attr << 8) + tc,
@ -2412,6 +2745,7 @@ rescan_last_byte:
tc = conv_uni_to_pc(vc, ' '); /* A space is printed in the second column */
if (tc < 0) tc = ' ';
next_c = ' ';
}
notify_write(vc, c);
@ -2431,6 +2765,7 @@ rescan_last_byte:
do_con_trol(tty, vc, orig);
}
con_flush(vc, draw_from, draw_to, &draw_x);
vc_uniscr_debug_check(vc);
console_conditional_schedule();
console_unlock();
notify_update(vc);
@ -4257,6 +4592,16 @@ u16 screen_glyph(struct vc_data *vc, int offset)
}
EXPORT_SYMBOL_GPL(screen_glyph);
u32 screen_glyph_unicode(struct vc_data *vc, int n)
{
struct uni_screen *uniscr = get_vc_uniscr(vc);
if (uniscr)
return uniscr->lines[n / vc->vc_cols][n % vc->vc_cols];
return inverse_translate(vc, screen_glyph(vc, n * 2), 1);
}
EXPORT_SYMBOL_GPL(screen_glyph_unicode);
/* used by vcs - note the word offset */
unsigned short *screen_pos(struct vc_data *vc, int w_offset, int viewed)
{

View File

@ -17,8 +17,8 @@
#include <linux/vt.h>
#include <linux/workqueue.h>
struct vt_struct;
struct uni_pagedir;
struct uni_screen;
#define NPAR 16
@ -140,6 +140,7 @@ struct vc_data {
struct vc_data **vc_display_fg; /* [!] Ptr to var holding fg console for this display */
struct uni_pagedir *vc_uni_pagedir;
struct uni_pagedir **vc_uni_pagedir_loc; /* [!] Location of uni_pagedir variable for this console */
struct uni_screen *vc_uni_screen; /* unicode screen content */
bool vc_panic_force_write; /* when oops/panic this VC can accept forced output/blanking */
/* additional information is in vt_kern.h */
};
@ -148,7 +149,7 @@ struct vc {
struct vc_data *d;
struct work_struct SAK_work;
/* might add scrmem, vt_struct, kbd at some time,
/* might add scrmem, kbd at some time,
to have everything in one place - the disadvantage
would be that vc_cons etc can no longer be static */
};

View File

@ -32,6 +32,7 @@ extern unsigned char default_blu[];
extern unsigned short *screen_pos(struct vc_data *vc, int w_offset, int viewed);
extern u16 screen_glyph(struct vc_data *vc, int offset);
extern u32 screen_glyph_unicode(struct vc_data *vc, int offset);
extern void complement_pos(struct vc_data *vc, int offset);
extern void invert_screen(struct vc_data *vc, int offset, int count, int shift);
@ -42,4 +43,9 @@ extern u16 vcs_scr_readw(struct vc_data *vc, const u16 *org);
extern void vcs_scr_writew(struct vc_data *vc, u16 val, u16 *org);
extern void vcs_scr_updated(struct vc_data *vc);
extern int vc_uniscr_check(struct vc_data *vc);
extern void vc_uniscr_copy_line(struct vc_data *vc, void *dest, int viewed,
unsigned int row, unsigned int col,
unsigned int nr);
#endif

View File

@ -160,6 +160,9 @@ extern void serial8250_do_shutdown(struct uart_port *port);
extern void serial8250_do_pm(struct uart_port *port, unsigned int state,
unsigned int oldstate);
extern void serial8250_do_set_mctrl(struct uart_port *port, unsigned int mctrl);
extern void serial8250_do_set_divisor(struct uart_port *port, unsigned int baud,
unsigned int quot,
unsigned int quot_frac);
extern int fsl8250_handle_irq(struct uart_port *port);
int serial8250_handle_irq(struct uart_port *port, unsigned int iir);
unsigned char serial8250_rx_chars(struct uart_8250_port *up, unsigned char lsr);

View File

@ -127,6 +127,13 @@ struct uart_port {
struct ktermios *);
unsigned int (*get_mctrl)(struct uart_port *);
void (*set_mctrl)(struct uart_port *, unsigned int);
unsigned int (*get_divisor)(struct uart_port *,
unsigned int baud,
unsigned int *frac);
void (*set_divisor)(struct uart_port *,
unsigned int baud,
unsigned int quot,
unsigned int quot_frac);
int (*startup)(struct uart_port *port);
void (*shutdown)(struct uart_port *port);
void (*throttle)(struct uart_port *port);

View File

@ -119,13 +119,13 @@
#include <linux/fs.h>
#include <linux/wait.h>
#include <linux/atomic.h>
/*
* the semaphore definition
*/
struct ld_semaphore {
long count;
atomic_long_t count;
raw_spinlock_t wait_lock;
unsigned int wait_readers;
struct list_head read_wait;

View File

@ -357,8 +357,29 @@
#define K_DTILDE K(KT_DEAD,3)
#define K_DDIERE K(KT_DEAD,4)
#define K_DCEDIL K(KT_DEAD,5)
#define K_DMACRON K(KT_DEAD,6)
#define K_DBREVE K(KT_DEAD,7)
#define K_DABDOT K(KT_DEAD,8)
#define K_DABRING K(KT_DEAD,9)
#define K_DDBACUTE K(KT_DEAD,10)
#define K_DCARON K(KT_DEAD,11)
#define K_DOGONEK K(KT_DEAD,12)
#define K_DIOTA K(KT_DEAD,13)
#define K_DVOICED K(KT_DEAD,14)
#define K_DSEMVOICED K(KT_DEAD,15)
#define K_DBEDOT K(KT_DEAD,16)
#define K_DHOOK K(KT_DEAD,17)
#define K_DHORN K(KT_DEAD,18)
#define K_DSTROKE K(KT_DEAD,19)
#define K_DABCOMMA K(KT_DEAD,20)
#define K_DABREVCOMMA K(KT_DEAD,21)
#define K_DDBGRAVE K(KT_DEAD,22)
#define K_DINVBREVE K(KT_DEAD,23)
#define K_DBECOMMA K(KT_DEAD,24)
#define K_DCURRENCY K(KT_DEAD,25)
#define K_DGREEK K(KT_DEAD,26)
#define NR_DEAD 6
#define NR_DEAD 27
#define K_DOWN K(KT_CUR,0)
#define K_LEFT K(KT_CUR,1)