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:
@@ -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.
|
||||
|
||||
|
@@ -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";
|
||||
|
@@ -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
|
||||
|
@@ -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.
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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"
|
@@ -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:
|
||||
|
@@ -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>;
|
||||
};
|
@@ -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".
|
||||
|
@@ -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,
|
||||
|
@@ -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 */
|
||||
};
|
||||
|
||||
/*
|
||||
|
@@ -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;
|
||||
|
@@ -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);
|
||||
|
@@ -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);
|
||||
|
@@ -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)
|
||||
|
@@ -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 },
|
||||
{ },
|
||||
};
|
||||
|
@@ -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,
|
||||
};
|
||||
|
@@ -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)) {
|
||||
|
@@ -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, },
|
||||
|
@@ -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);
|
||||
|
@@ -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.
|
||||
|
@@ -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);
|
||||
|
@@ -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;
|
||||
|
@@ -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);
|
||||
|
@@ -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);
|
||||
|
||||
|
@@ -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);
|
||||
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -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
|
||||
|
@@ -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,
|
||||
},
|
||||
};
|
||||
|
||||
|
@@ -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);
|
||||
|
@@ -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
|
||||
|
@@ -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:
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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) {
|
||||
|
@@ -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);
|
||||
|
@@ -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)
|
||||
{
|
||||
|
@@ -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 */
|
||||
};
|
||||
|
@@ -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
|
||||
|
@@ -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);
|
||||
|
@@ -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);
|
||||
|
@@ -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;
|
||||
|
@@ -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)
|
||||
|
Reference in New Issue
Block a user