forked from Minki/linux
TTY/Serial driver patches for 3.20-rc1
Here's the big tty/serial driver update for 3.20-rc1. Nothing huge here, just lots of driver updates and some core tty layer fixes as well. All have been in linux-next with no reported issues. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iEYEABECAAYFAlTgtgkACgkQMUfUDdst+ykXbACg14oFAmeYjO9RsdIHPXBvKseO 47QAn0foy91bpNQ5UFOxWS5L6Fzj2ZND =syx2 -----END PGP SIGNATURE----- Merge tag 'tty-3.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty Pull tty/serial driver patches from Greg KH: "Here's the big tty/serial driver update for 3.20-rc1. Nothing huge here, just lots of driver updates and some core tty layer fixes as well. All have been in linux-next with no reported issues" * tag 'tty-3.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty: (119 commits) serial: 8250: Fix UART_BUG_TXEN workaround serial: driver for ETRAX FS UART tty: remove unused variable sprop serial: of-serial: fetch line number from DT serial: samsung: earlycon support depends on CONFIG_SERIAL_SAMSUNG_CONSOLE tty/serial: serial8250_set_divisor() can be static tty/serial: Add Spreadtrum sc9836-uart driver support Documentation: DT: Add bindings for Spreadtrum SoC Platform serial: samsung: remove redundant interrupt enabling tty: Remove external interface for tty_set_termios() serial: omap: Fix RTS handling serial: 8250_omap: Use UPSTAT_AUTORTS for RTS handling serial: core: Rework hw-assisted flow control support tty/serial: 8250_early: Add support for PXA UARTs tty/serial: of_serial: add support for PXA/MMP uarts tty/serial: of_serial: add DT alias ID handling serial: 8250: Prevent concurrent updates to shadow registers serial: 8250: Use canary to restart console after suspend serial: 8250: Refactor XR17V35X divisor calculation serial: 8250: Refactor divisor programming ...
This commit is contained in:
commit
a9724125ad
11
Documentation/devicetree/bindings/arm/sprd.txt
Normal file
11
Documentation/devicetree/bindings/arm/sprd.txt
Normal file
@ -0,0 +1,11 @@
|
||||
Spreadtrum SoC Platforms Device Tree Bindings
|
||||
----------------------------------------------------
|
||||
|
||||
Sharkl64 is a Spreadtrum's SoC Platform which is based
|
||||
on ARM 64-bit processor.
|
||||
|
||||
SC9836 openphone board with SC9836 SoC based on the
|
||||
Sharkl64 Platform shall have the following properties.
|
||||
|
||||
Required root node properties:
|
||||
- compatible = "sprd,sc9836-openphone", "sprd,sc9836";
|
27
Documentation/devicetree/bindings/serial/digicolor-usart.txt
Normal file
27
Documentation/devicetree/bindings/serial/digicolor-usart.txt
Normal file
@ -0,0 +1,27 @@
|
||||
Binding for Conexant Digicolor USART
|
||||
|
||||
Note: this binding is only applicable for using the USART peripheral as
|
||||
UART. USART also support synchronous serial protocols like SPI and I2S. Use
|
||||
the binding that matches the wiring of your system.
|
||||
|
||||
Required properties:
|
||||
- compatible : should be "cnxt,cx92755-usart".
|
||||
- reg: Should contain USART controller registers location and length.
|
||||
- interrupts: Should contain a single USART controller interrupt.
|
||||
- clocks: Must contain phandles to the USART clock
|
||||
See ../clocks/clock-bindings.txt for details.
|
||||
|
||||
Note: Each UART port should have an alias correctly numbered
|
||||
in "aliases" node.
|
||||
|
||||
Example:
|
||||
aliases {
|
||||
serial0 = &uart0;
|
||||
};
|
||||
|
||||
uart0: uart@f0000740 {
|
||||
compatible = "cnxt,cx92755-usart";
|
||||
reg = <0xf0000740 0x20>;
|
||||
clocks = <&main_clk>;
|
||||
interrupts = <44>;
|
||||
};
|
@ -2,7 +2,7 @@
|
||||
|
||||
Required properties:
|
||||
- compatible : Should be "sirf,prima2-uart", "sirf, prima2-usp-uart",
|
||||
"sirf,marco-uart" or "sirf,marco-bt-uart" which means
|
||||
"sirf,atlas7-uart" or "sirf,atlas7-bt-uart" which means
|
||||
uart located in BT module and used for BT.
|
||||
- reg : Offset and length of the register set for the device
|
||||
- interrupts : Should contain uart interrupt
|
||||
@ -37,7 +37,7 @@ usp@b0090000 {
|
||||
for uart use in BT module,
|
||||
uart6: uart@11000000 {
|
||||
cell-index = <6>;
|
||||
compatible = "sirf,marco-bt-uart", "sirf,marco-uart";
|
||||
compatible = "sirf,atlas7-bt-uart", "sirf,atlas7-uart";
|
||||
reg = <0x11000000 0x1000>;
|
||||
interrupts = <0 100 0>;
|
||||
clocks = <&clks 138>, <&clks 140>, <&clks 141>;
|
||||
|
7
Documentation/devicetree/bindings/serial/sprd-uart.txt
Normal file
7
Documentation/devicetree/bindings/serial/sprd-uart.txt
Normal file
@ -0,0 +1,7 @@
|
||||
* Spreadtrum serial UART
|
||||
|
||||
Required properties:
|
||||
- compatible: must be "sprd,sc9836-uart"
|
||||
- reg: offset and length of the register set for the device
|
||||
- interrupts: exactly one interrupt specifier
|
||||
- clocks: phandles to input clocks.
|
@ -37,6 +37,7 @@ chrp Common Hardware Reference Platform
|
||||
chunghwa Chunghwa Picture Tubes Ltd.
|
||||
cirrus Cirrus Logic, Inc.
|
||||
cnm Chips&Media, Inc.
|
||||
cnxt Conexant Systems, Inc.
|
||||
cortina Cortina Systems, Inc.
|
||||
cosmic Cosmic Circuits
|
||||
crystalfontz Crystalfontz America, Inc.
|
||||
@ -162,6 +163,7 @@ snps Synopsys, Inc.
|
||||
solidrun SolidRun
|
||||
sony Sony Corporation
|
||||
spansion Spansion Inc.
|
||||
sprd Spreadtrum Communications Inc.
|
||||
st STMicroelectronics
|
||||
ste ST-Ericsson
|
||||
stericsson ST-Ericsson
|
||||
|
@ -970,6 +970,18 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
|
||||
|
||||
smh Use ARM semihosting calls for early console.
|
||||
|
||||
s3c2410,<addr>
|
||||
s3c2412,<addr>
|
||||
s3c2440,<addr>
|
||||
s3c6400,<addr>
|
||||
s5pv210,<addr>
|
||||
exynos4210,<addr>
|
||||
Use early console provided by serial driver available
|
||||
on Samsung SoCs, requires selecting proper type and
|
||||
a correct base address of the selected UART port. The
|
||||
serial port must already be setup and configured.
|
||||
Options are not yet supported.
|
||||
|
||||
earlyprintk= [X86,SH,BLACKFIN,ARM,M68k]
|
||||
earlyprintk=vga
|
||||
earlyprintk=efi
|
||||
|
@ -31,6 +31,7 @@
|
||||
|
||||
chosen {
|
||||
bootargs ="root=/dev/ram0 rw ramdisk=8192 initrd=0x41000000,8M console=ttySAC2,115200 init=/linuxrc";
|
||||
stdout-path = &serial_2;
|
||||
};
|
||||
|
||||
regulators {
|
||||
|
@ -27,6 +27,7 @@
|
||||
|
||||
chosen {
|
||||
bootargs = "root=/dev/ram0 rw ramdisk=8192 initrd=0x41000000,8M console=ttySAC1,115200 init=/linuxrc";
|
||||
stdout-path = &serial_1;
|
||||
};
|
||||
|
||||
sdhci@12530000 {
|
||||
|
@ -28,6 +28,7 @@
|
||||
|
||||
chosen {
|
||||
bootargs = "console=ttySAC2,115200N8 root=/dev/mmcblk0p5 rootwait earlyprintk panic=5";
|
||||
stdout-path = &serial_2;
|
||||
};
|
||||
|
||||
regulators {
|
||||
|
@ -26,6 +26,7 @@
|
||||
|
||||
chosen {
|
||||
bootargs = "console=ttySAC2,115200N8 root=/dev/mmcblk0p5 rw rootwait earlyprintk panic=5 maxcpus=1";
|
||||
stdout-path = &serial_2;
|
||||
};
|
||||
|
||||
sysram@02020000 {
|
||||
|
@ -12,6 +12,10 @@
|
||||
#include "exynos4412.dtsi"
|
||||
|
||||
/ {
|
||||
chosen {
|
||||
stdout-path = &serial_1;
|
||||
};
|
||||
|
||||
firmware@0204F000 {
|
||||
compatible = "samsung,secure-firmware";
|
||||
reg = <0x0204F000 0x1000>;
|
||||
|
@ -26,6 +26,7 @@
|
||||
|
||||
chosen {
|
||||
bootargs ="console=ttySAC2,115200";
|
||||
stdout-path = &serial_2;
|
||||
};
|
||||
|
||||
firmware@0203F000 {
|
||||
|
@ -25,6 +25,7 @@
|
||||
|
||||
chosen {
|
||||
bootargs ="root=/dev/ram0 rw ramdisk=8192 initrd=0x41000000,8M console=ttySAC1,115200 init=/linuxrc";
|
||||
stdout-path = &serial_1;
|
||||
};
|
||||
|
||||
g2d@10800000 {
|
||||
|
@ -18,6 +18,10 @@
|
||||
model = "FriendlyARM TINY4412 board based on Exynos4412";
|
||||
compatible = "friendlyarm,tiny4412", "samsung,exynos4412", "samsung,exynos4";
|
||||
|
||||
chosen {
|
||||
stdout-path = &serial_0;
|
||||
};
|
||||
|
||||
memory {
|
||||
reg = <0x40000000 0x40000000>;
|
||||
};
|
||||
|
@ -32,6 +32,7 @@
|
||||
|
||||
chosen {
|
||||
bootargs = "console=ttySAC2,115200N8 root=/dev/mmcblk0p5 rootwait earlyprintk panic=5";
|
||||
stdout-path = &serial_2;
|
||||
};
|
||||
|
||||
firmware@0204F000 {
|
||||
|
@ -136,9 +136,6 @@ extern enum intel_mid_timer_options intel_mid_timer_options;
|
||||
#define SFI_MTMR_MAX_NUM 8
|
||||
#define SFI_MRTC_MAX 8
|
||||
|
||||
extern struct console early_mrst_console;
|
||||
extern void mrst_early_console_init(void);
|
||||
|
||||
extern struct console early_hsu_console;
|
||||
extern void hsu_early_console_init(const char *);
|
||||
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include <linux/usb/ehci_def.h>
|
||||
#include <linux/efi.h>
|
||||
#include <asm/efi.h>
|
||||
#include <asm/pci_x86.h>
|
||||
|
||||
/* Simple VGA output */
|
||||
#define VGABASE (__ISA_IO_base + 0xb8000)
|
||||
@ -76,7 +77,7 @@ static struct console early_vga_console = {
|
||||
|
||||
/* Serial functions loosely based on a similar package from Klaus P. Gerlicher */
|
||||
|
||||
static int early_serial_base = 0x3f8; /* ttyS0 */
|
||||
static unsigned long early_serial_base = 0x3f8; /* ttyS0 */
|
||||
|
||||
#define XMTRDY 0x20
|
||||
|
||||
@ -94,13 +95,40 @@ static int early_serial_base = 0x3f8; /* ttyS0 */
|
||||
#define DLL 0 /* Divisor Latch Low */
|
||||
#define DLH 1 /* Divisor latch High */
|
||||
|
||||
static void mem32_serial_out(unsigned long addr, int offset, int value)
|
||||
{
|
||||
uint32_t *vaddr = (uint32_t *)addr;
|
||||
/* shift implied by pointer type */
|
||||
writel(value, vaddr + offset);
|
||||
}
|
||||
|
||||
static unsigned int mem32_serial_in(unsigned long addr, int offset)
|
||||
{
|
||||
uint32_t *vaddr = (uint32_t *)addr;
|
||||
/* shift implied by pointer type */
|
||||
return readl(vaddr + offset);
|
||||
}
|
||||
|
||||
static unsigned int io_serial_in(unsigned long addr, int offset)
|
||||
{
|
||||
return inb(addr + offset);
|
||||
}
|
||||
|
||||
static void io_serial_out(unsigned long addr, int offset, int value)
|
||||
{
|
||||
outb(value, addr + offset);
|
||||
}
|
||||
|
||||
static unsigned int (*serial_in)(unsigned long addr, int offset) = io_serial_in;
|
||||
static void (*serial_out)(unsigned long addr, int offset, int value) = io_serial_out;
|
||||
|
||||
static int early_serial_putc(unsigned char ch)
|
||||
{
|
||||
unsigned timeout = 0xffff;
|
||||
|
||||
while ((inb(early_serial_base + LSR) & XMTRDY) == 0 && --timeout)
|
||||
while ((serial_in(early_serial_base, LSR) & XMTRDY) == 0 && --timeout)
|
||||
cpu_relax();
|
||||
outb(ch, early_serial_base + TXR);
|
||||
serial_out(early_serial_base, TXR, ch);
|
||||
return timeout ? 0 : -1;
|
||||
}
|
||||
|
||||
@ -114,13 +142,28 @@ static void early_serial_write(struct console *con, const char *s, unsigned n)
|
||||
}
|
||||
}
|
||||
|
||||
static __init void early_serial_hw_init(unsigned divisor)
|
||||
{
|
||||
unsigned char c;
|
||||
|
||||
serial_out(early_serial_base, LCR, 0x3); /* 8n1 */
|
||||
serial_out(early_serial_base, IER, 0); /* no interrupt */
|
||||
serial_out(early_serial_base, FCR, 0); /* no fifo */
|
||||
serial_out(early_serial_base, MCR, 0x3); /* DTR + RTS */
|
||||
|
||||
c = serial_in(early_serial_base, LCR);
|
||||
serial_out(early_serial_base, LCR, c | DLAB);
|
||||
serial_out(early_serial_base, DLL, divisor & 0xff);
|
||||
serial_out(early_serial_base, DLH, (divisor >> 8) & 0xff);
|
||||
serial_out(early_serial_base, LCR, c & ~DLAB);
|
||||
}
|
||||
|
||||
#define DEFAULT_BAUD 9600
|
||||
|
||||
static __init void early_serial_init(char *s)
|
||||
{
|
||||
unsigned char c;
|
||||
unsigned divisor;
|
||||
unsigned baud = DEFAULT_BAUD;
|
||||
unsigned long baud = DEFAULT_BAUD;
|
||||
char *e;
|
||||
|
||||
if (*s == ',')
|
||||
@ -145,25 +188,125 @@ static __init void early_serial_init(char *s)
|
||||
s++;
|
||||
}
|
||||
|
||||
outb(0x3, early_serial_base + LCR); /* 8n1 */
|
||||
outb(0, early_serial_base + IER); /* no interrupt */
|
||||
outb(0, early_serial_base + FCR); /* no fifo */
|
||||
outb(0x3, early_serial_base + MCR); /* DTR + RTS */
|
||||
|
||||
if (*s) {
|
||||
baud = simple_strtoul(s, &e, 0);
|
||||
if (baud == 0 || s == e)
|
||||
if (kstrtoul(s, 0, &baud) < 0 || baud == 0)
|
||||
baud = DEFAULT_BAUD;
|
||||
}
|
||||
|
||||
/* Convert from baud to divisor value */
|
||||
divisor = 115200 / baud;
|
||||
c = inb(early_serial_base + LCR);
|
||||
outb(c | DLAB, early_serial_base + LCR);
|
||||
outb(divisor & 0xff, early_serial_base + DLL);
|
||||
outb((divisor >> 8) & 0xff, early_serial_base + DLH);
|
||||
outb(c & ~DLAB, early_serial_base + LCR);
|
||||
|
||||
/* These will always be IO based ports */
|
||||
serial_in = io_serial_in;
|
||||
serial_out = io_serial_out;
|
||||
|
||||
/* Set up the HW */
|
||||
early_serial_hw_init(divisor);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PCI
|
||||
/*
|
||||
* early_pci_serial_init()
|
||||
*
|
||||
* This function is invoked when the early_printk param starts with "pciserial"
|
||||
* The rest of the param should be ",B:D.F,baud" where B, D & F describe the
|
||||
* location of a PCI device that must be a UART device.
|
||||
*/
|
||||
static __init void early_pci_serial_init(char *s)
|
||||
{
|
||||
unsigned divisor;
|
||||
unsigned long baud = DEFAULT_BAUD;
|
||||
u8 bus, slot, func;
|
||||
uint32_t classcode, bar0;
|
||||
uint16_t cmdreg;
|
||||
char *e;
|
||||
|
||||
|
||||
/*
|
||||
* First, part the param to get the BDF values
|
||||
*/
|
||||
if (*s == ',')
|
||||
++s;
|
||||
|
||||
if (*s == 0)
|
||||
return;
|
||||
|
||||
bus = (u8)simple_strtoul(s, &e, 16);
|
||||
s = e;
|
||||
if (*s != ':')
|
||||
return;
|
||||
++s;
|
||||
slot = (u8)simple_strtoul(s, &e, 16);
|
||||
s = e;
|
||||
if (*s != '.')
|
||||
return;
|
||||
++s;
|
||||
func = (u8)simple_strtoul(s, &e, 16);
|
||||
s = e;
|
||||
|
||||
/* A baud might be following */
|
||||
if (*s == ',')
|
||||
s++;
|
||||
|
||||
/*
|
||||
* Second, find the device from the BDF
|
||||
*/
|
||||
cmdreg = read_pci_config(bus, slot, func, PCI_COMMAND);
|
||||
classcode = read_pci_config(bus, slot, func, PCI_CLASS_REVISION);
|
||||
bar0 = read_pci_config(bus, slot, func, PCI_BASE_ADDRESS_0);
|
||||
|
||||
/*
|
||||
* Verify it is a UART type device
|
||||
*/
|
||||
if (((classcode >> 16 != PCI_CLASS_COMMUNICATION_MODEM) &&
|
||||
(classcode >> 16 != PCI_CLASS_COMMUNICATION_SERIAL)) ||
|
||||
(((classcode >> 8) & 0xff) != 0x02)) /* 16550 I/F at BAR0 */
|
||||
return;
|
||||
|
||||
/*
|
||||
* Determine if it is IO or memory mapped
|
||||
*/
|
||||
if (bar0 & 0x01) {
|
||||
/* it is IO mapped */
|
||||
serial_in = io_serial_in;
|
||||
serial_out = io_serial_out;
|
||||
early_serial_base = bar0&0xfffffffc;
|
||||
write_pci_config(bus, slot, func, PCI_COMMAND,
|
||||
cmdreg|PCI_COMMAND_IO);
|
||||
} else {
|
||||
/* It is memory mapped - assume 32-bit alignment */
|
||||
serial_in = mem32_serial_in;
|
||||
serial_out = mem32_serial_out;
|
||||
/* WARNING! assuming the address is always in the first 4G */
|
||||
early_serial_base =
|
||||
(unsigned long)early_ioremap(bar0 & 0xfffffff0, 0x10);
|
||||
write_pci_config(bus, slot, func, PCI_COMMAND,
|
||||
cmdreg|PCI_COMMAND_MEMORY);
|
||||
}
|
||||
|
||||
/*
|
||||
* Lastly, initalize the hardware
|
||||
*/
|
||||
if (*s) {
|
||||
if (strcmp(s, "nocfg") == 0)
|
||||
/* Sometimes, we want to leave the UART alone
|
||||
* and assume the BIOS has set it up correctly.
|
||||
* "nocfg" tells us this is the case, and we
|
||||
* should do no more setup.
|
||||
*/
|
||||
return;
|
||||
if (kstrtoul(s, 0, &baud) < 0 || baud == 0)
|
||||
baud = DEFAULT_BAUD;
|
||||
}
|
||||
|
||||
/* Convert from baud to divisor value */
|
||||
divisor = 115200 / baud;
|
||||
|
||||
/* Set up the HW */
|
||||
early_serial_hw_init(divisor);
|
||||
}
|
||||
#endif
|
||||
|
||||
static struct console early_serial_console = {
|
||||
.name = "earlyser",
|
||||
.write = early_serial_write,
|
||||
@ -210,6 +353,13 @@ static int __init setup_early_printk(char *buf)
|
||||
early_serial_init(buf + 4);
|
||||
early_console_register(&early_serial_console, keep);
|
||||
}
|
||||
#ifdef CONFIG_PCI
|
||||
if (!strncmp(buf, "pciserial", 9)) {
|
||||
early_pci_serial_init(buf + 9);
|
||||
early_console_register(&early_serial_console, keep);
|
||||
buf += 9; /* Keep from match the above "serial" */
|
||||
}
|
||||
#endif
|
||||
if (!strncmp(buf, "vga", 3) &&
|
||||
boot_params.screen_info.orig_video_isVGA == 1) {
|
||||
max_xpos = boot_params.screen_info.orig_video_cols;
|
||||
@ -226,11 +376,6 @@ static int __init setup_early_printk(char *buf)
|
||||
early_console_register(&xenboot_console, keep);
|
||||
#endif
|
||||
#ifdef CONFIG_EARLY_PRINTK_INTEL_MID
|
||||
if (!strncmp(buf, "mrst", 4)) {
|
||||
mrst_early_console_init();
|
||||
early_console_register(&early_mrst_console, keep);
|
||||
}
|
||||
|
||||
if (!strncmp(buf, "hsu", 3)) {
|
||||
hsu_early_console_init(buf + 3);
|
||||
early_console_register(&early_hsu_console, keep);
|
||||
|
@ -16,8 +16,6 @@ obj-$(subst m,y,$(CONFIG_INPUT_MPU3050)) += platform_mpu3050.o
|
||||
obj-$(subst m,y,$(CONFIG_INPUT_BMA150)) += platform_bma023.o
|
||||
obj-$(subst m,y,$(CONFIG_GPIO_PCA953X)) += platform_tca6416.o
|
||||
obj-$(subst m,y,$(CONFIG_DRM_MEDFIELD)) += platform_tc35876x.o
|
||||
# SPI Devices
|
||||
obj-$(subst m,y,$(CONFIG_SERIAL_MRST_MAX3110)) += platform_max3111.o
|
||||
# MISC Devices
|
||||
obj-$(subst m,y,$(CONFIG_KEYBOARD_GPIO)) += platform_gpio_keys.o
|
||||
obj-$(subst m,y,$(CONFIG_INTEL_MID_WATCHDOG)) += platform_wdt.o
|
||||
|
@ -1,35 +0,0 @@
|
||||
/*
|
||||
* platform_max3111.c: max3111 platform data initilization file
|
||||
*
|
||||
* (C) Copyright 2013 Intel Corporation
|
||||
* Author: Sathyanarayanan Kuppuswamy <sathyanarayanan.kuppuswamy@intel.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; version 2
|
||||
* of the License.
|
||||
*/
|
||||
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <asm/intel-mid.h>
|
||||
|
||||
static void __init *max3111_platform_data(void *info)
|
||||
{
|
||||
struct spi_board_info *spi_info = info;
|
||||
int intr = get_gpio_by_name("max3111_int");
|
||||
|
||||
spi_info->mode = SPI_MODE_0;
|
||||
if (intr == -1)
|
||||
return NULL;
|
||||
spi_info->irq = intr + INTEL_MID_IRQ_OFFSET;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const struct devs_id max3111_dev_id __initconst = {
|
||||
.name = "spi_max3111",
|
||||
.type = SFI_DEV_TYPE_SPI,
|
||||
.get_platform_data = &max3111_platform_data,
|
||||
};
|
||||
|
||||
sfi_device(max3111_dev_id);
|
@ -10,15 +10,13 @@
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file implements two early consoles named mrst and hsu.
|
||||
* mrst is based on Maxim3110 spi-uart device, it exists in both
|
||||
* Moorestown and Medfield platforms, while hsu is based on a High
|
||||
* Speed UART device which only exists in the Medfield platform
|
||||
* This file implements early console named hsu.
|
||||
* hsu is based on a High Speed UART device which only exists in the Medfield
|
||||
* platform
|
||||
*/
|
||||
|
||||
#include <linux/serial_reg.h>
|
||||
#include <linux/serial_mfd.h>
|
||||
#include <linux/kmsg_dump.h>
|
||||
#include <linux/console.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/delay.h>
|
||||
@ -28,216 +26,6 @@
|
||||
#include <asm/pgtable.h>
|
||||
#include <asm/intel-mid.h>
|
||||
|
||||
#define MRST_SPI_TIMEOUT 0x200000
|
||||
#define MRST_REGBASE_SPI0 0xff128000
|
||||
#define MRST_REGBASE_SPI1 0xff128400
|
||||
#define MRST_CLK_SPI0_REG 0xff11d86c
|
||||
|
||||
/* Bit fields in CTRLR0 */
|
||||
#define SPI_DFS_OFFSET 0
|
||||
|
||||
#define SPI_FRF_OFFSET 4
|
||||
#define SPI_FRF_SPI 0x0
|
||||
#define SPI_FRF_SSP 0x1
|
||||
#define SPI_FRF_MICROWIRE 0x2
|
||||
#define SPI_FRF_RESV 0x3
|
||||
|
||||
#define SPI_MODE_OFFSET 6
|
||||
#define SPI_SCPH_OFFSET 6
|
||||
#define SPI_SCOL_OFFSET 7
|
||||
#define SPI_TMOD_OFFSET 8
|
||||
#define SPI_TMOD_TR 0x0 /* xmit & recv */
|
||||
#define SPI_TMOD_TO 0x1 /* xmit only */
|
||||
#define SPI_TMOD_RO 0x2 /* recv only */
|
||||
#define SPI_TMOD_EPROMREAD 0x3 /* eeprom read mode */
|
||||
|
||||
#define SPI_SLVOE_OFFSET 10
|
||||
#define SPI_SRL_OFFSET 11
|
||||
#define SPI_CFS_OFFSET 12
|
||||
|
||||
/* Bit fields in SR, 7 bits */
|
||||
#define SR_MASK 0x7f /* cover 7 bits */
|
||||
#define SR_BUSY (1 << 0)
|
||||
#define SR_TF_NOT_FULL (1 << 1)
|
||||
#define SR_TF_EMPT (1 << 2)
|
||||
#define SR_RF_NOT_EMPT (1 << 3)
|
||||
#define SR_RF_FULL (1 << 4)
|
||||
#define SR_TX_ERR (1 << 5)
|
||||
#define SR_DCOL (1 << 6)
|
||||
|
||||
struct dw_spi_reg {
|
||||
u32 ctrl0;
|
||||
u32 ctrl1;
|
||||
u32 ssienr;
|
||||
u32 mwcr;
|
||||
u32 ser;
|
||||
u32 baudr;
|
||||
u32 txfltr;
|
||||
u32 rxfltr;
|
||||
u32 txflr;
|
||||
u32 rxflr;
|
||||
u32 sr;
|
||||
u32 imr;
|
||||
u32 isr;
|
||||
u32 risr;
|
||||
u32 txoicr;
|
||||
u32 rxoicr;
|
||||
u32 rxuicr;
|
||||
u32 msticr;
|
||||
u32 icr;
|
||||
u32 dmacr;
|
||||
u32 dmatdlr;
|
||||
u32 dmardlr;
|
||||
u32 idr;
|
||||
u32 version;
|
||||
|
||||
/* Currently operates as 32 bits, though only the low 16 bits matter */
|
||||
u32 dr;
|
||||
} __packed;
|
||||
|
||||
#define dw_readl(dw, name) __raw_readl(&(dw)->name)
|
||||
#define dw_writel(dw, name, val) __raw_writel((val), &(dw)->name)
|
||||
|
||||
/* Default use SPI0 register for mrst, we will detect Penwell and use SPI1 */
|
||||
static unsigned long mrst_spi_paddr = MRST_REGBASE_SPI0;
|
||||
|
||||
static u32 *pclk_spi0;
|
||||
/* Always contains an accessible address, start with 0 */
|
||||
static struct dw_spi_reg *pspi;
|
||||
|
||||
static struct kmsg_dumper dw_dumper;
|
||||
static int dumper_registered;
|
||||
|
||||
static void dw_kmsg_dump(struct kmsg_dumper *dumper,
|
||||
enum kmsg_dump_reason reason)
|
||||
{
|
||||
static char line[1024];
|
||||
size_t len;
|
||||
|
||||
/* When run to this, we'd better re-init the HW */
|
||||
mrst_early_console_init();
|
||||
|
||||
while (kmsg_dump_get_line(dumper, true, line, sizeof(line), &len))
|
||||
early_mrst_console.write(&early_mrst_console, line, len);
|
||||
}
|
||||
|
||||
/* Set the ratio rate to 115200, 8n1, IRQ disabled */
|
||||
static void max3110_write_config(void)
|
||||
{
|
||||
u16 config;
|
||||
|
||||
config = 0xc001;
|
||||
dw_writel(pspi, dr, config);
|
||||
}
|
||||
|
||||
/* Translate char to a eligible word and send to max3110 */
|
||||
static void max3110_write_data(char c)
|
||||
{
|
||||
u16 data;
|
||||
|
||||
data = 0x8000 | c;
|
||||
dw_writel(pspi, dr, data);
|
||||
}
|
||||
|
||||
void mrst_early_console_init(void)
|
||||
{
|
||||
u32 ctrlr0 = 0;
|
||||
u32 spi0_cdiv;
|
||||
u32 freq; /* Freqency info only need be searched once */
|
||||
|
||||
/* Base clk is 100 MHz, the actual clk = 100M / (clk_divider + 1) */
|
||||
pclk_spi0 = (void *)set_fixmap_offset_nocache(FIX_EARLYCON_MEM_BASE,
|
||||
MRST_CLK_SPI0_REG);
|
||||
spi0_cdiv = ((*pclk_spi0) & 0xe00) >> 9;
|
||||
freq = 100000000 / (spi0_cdiv + 1);
|
||||
|
||||
if (intel_mid_identify_cpu() == INTEL_MID_CPU_CHIP_PENWELL)
|
||||
mrst_spi_paddr = MRST_REGBASE_SPI1;
|
||||
|
||||
pspi = (void *)set_fixmap_offset_nocache(FIX_EARLYCON_MEM_BASE,
|
||||
mrst_spi_paddr);
|
||||
|
||||
/* Disable SPI controller */
|
||||
dw_writel(pspi, ssienr, 0);
|
||||
|
||||
/* Set control param, 8 bits, transmit only mode */
|
||||
ctrlr0 = dw_readl(pspi, ctrl0);
|
||||
|
||||
ctrlr0 &= 0xfcc0;
|
||||
ctrlr0 |= 0xf | (SPI_FRF_SPI << SPI_FRF_OFFSET)
|
||||
| (SPI_TMOD_TO << SPI_TMOD_OFFSET);
|
||||
dw_writel(pspi, ctrl0, ctrlr0);
|
||||
|
||||
/*
|
||||
* Change the spi0 clk to comply with 115200 bps, use 100000 to
|
||||
* calculate the clk dividor to make the clock a little slower
|
||||
* than real baud rate.
|
||||
*/
|
||||
dw_writel(pspi, baudr, freq/100000);
|
||||
|
||||
/* Disable all INT for early phase */
|
||||
dw_writel(pspi, imr, 0x0);
|
||||
|
||||
/* Set the cs to spi-uart */
|
||||
dw_writel(pspi, ser, 0x2);
|
||||
|
||||
/* Enable the HW, the last step for HW init */
|
||||
dw_writel(pspi, ssienr, 0x1);
|
||||
|
||||
/* Set the default configuration */
|
||||
max3110_write_config();
|
||||
|
||||
/* Register the kmsg dumper */
|
||||
if (!dumper_registered) {
|
||||
dw_dumper.dump = dw_kmsg_dump;
|
||||
kmsg_dump_register(&dw_dumper);
|
||||
dumper_registered = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Slave select should be called in the read/write function */
|
||||
static void early_mrst_spi_putc(char c)
|
||||
{
|
||||
unsigned int timeout;
|
||||
u32 sr;
|
||||
|
||||
timeout = MRST_SPI_TIMEOUT;
|
||||
/* Early putc needs to make sure the TX FIFO is not full */
|
||||
while (--timeout) {
|
||||
sr = dw_readl(pspi, sr);
|
||||
if (!(sr & SR_TF_NOT_FULL))
|
||||
cpu_relax();
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
if (!timeout)
|
||||
pr_warn("MRST earlycon: timed out\n");
|
||||
else
|
||||
max3110_write_data(c);
|
||||
}
|
||||
|
||||
/* Early SPI only uses polling mode */
|
||||
static void early_mrst_spi_write(struct console *con, const char *str,
|
||||
unsigned n)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < n && *str; i++) {
|
||||
if (*str == '\n')
|
||||
early_mrst_spi_putc('\r');
|
||||
early_mrst_spi_putc(*str);
|
||||
str++;
|
||||
}
|
||||
}
|
||||
|
||||
struct console early_mrst_console = {
|
||||
.name = "earlymrst",
|
||||
.write = early_mrst_spi_write,
|
||||
.flags = CON_PRINTBUFFER,
|
||||
.index = -1,
|
||||
};
|
||||
|
||||
/*
|
||||
* Following is the early console based on Medfield HSU (High
|
||||
* Speed UART) device.
|
||||
@ -259,7 +47,7 @@ void hsu_early_console_init(const char *s)
|
||||
port = clamp_val(port, 0, 2);
|
||||
|
||||
paddr = HSU_PORT_BASE + port * 0x80;
|
||||
phsu = (void *)set_fixmap_offset_nocache(FIX_EARLYCON_MEM_BASE, paddr);
|
||||
phsu = (void __iomem *)set_fixmap_offset_nocache(FIX_EARLYCON_MEM_BASE, paddr);
|
||||
|
||||
/* Disable FIFO */
|
||||
writeb(0x0, phsu + UART_FCR);
|
||||
|
@ -51,33 +51,22 @@ struct ath_struct {
|
||||
|
||||
static int ath_wakeup_ar3k(struct tty_struct *tty)
|
||||
{
|
||||
struct ktermios ktermios;
|
||||
int status = tty->driver->ops->tiocmget(tty);
|
||||
|
||||
if (status & TIOCM_CTS)
|
||||
return status;
|
||||
|
||||
/* Disable Automatic RTSCTS */
|
||||
ktermios = tty->termios;
|
||||
ktermios.c_cflag &= ~CRTSCTS;
|
||||
tty_set_termios(tty, &ktermios);
|
||||
|
||||
/* Clear RTS first */
|
||||
status = tty->driver->ops->tiocmget(tty);
|
||||
tty->driver->ops->tiocmget(tty);
|
||||
tty->driver->ops->tiocmset(tty, 0x00, TIOCM_RTS);
|
||||
mdelay(20);
|
||||
|
||||
/* Set RTS, wake up board */
|
||||
status = tty->driver->ops->tiocmget(tty);
|
||||
tty->driver->ops->tiocmget(tty);
|
||||
tty->driver->ops->tiocmset(tty, TIOCM_RTS, 0x00);
|
||||
mdelay(20);
|
||||
|
||||
status = tty->driver->ops->tiocmget(tty);
|
||||
|
||||
/* Enable Automatic RTSCTS */
|
||||
ktermios.c_cflag |= CRTSCTS;
|
||||
status = tty_set_termios(tty, &ktermios);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
@ -182,7 +182,7 @@ static int __pnp_bus_suspend(struct device *dev, pm_message_t state)
|
||||
return error;
|
||||
}
|
||||
|
||||
if (pnp_dev->protocol->suspend)
|
||||
if (pnp_can_suspend(pnp_dev))
|
||||
pnp_dev->protocol->suspend(pnp_dev, state);
|
||||
return 0;
|
||||
}
|
||||
|
@ -931,7 +931,7 @@ static void rs_send_xchar(struct tty_struct *tty, char ch)
|
||||
struct serial_state *info = tty->driver_data;
|
||||
unsigned long flags;
|
||||
|
||||
if (serial_paranoia_check(info, tty->name, "rs_send_char"))
|
||||
if (serial_paranoia_check(info, tty->name, "rs_send_xchar"))
|
||||
return;
|
||||
|
||||
info->x_char = ch;
|
||||
|
@ -112,7 +112,6 @@ static void disable_tx_interrupt(struct ehv_bc_data *bc)
|
||||
static int find_console_handle(void)
|
||||
{
|
||||
struct device_node *np = of_stdout;
|
||||
const char *sprop = NULL;
|
||||
const uint32_t *iprop;
|
||||
|
||||
/* We don't care what the aliased node is actually called. We only
|
||||
|
@ -1055,7 +1055,7 @@ static int isicom_send_break(struct tty_struct *tty, int length)
|
||||
|
||||
outw(0x8000 | ((port->channel) << (card->shift_count)) | 0x3, base);
|
||||
outw((length & 0xff) << 8 | 0x00, base);
|
||||
outw((length & 0xff00), base);
|
||||
outw((length & 0xff00u), base);
|
||||
InterruptTheCard(base);
|
||||
|
||||
unlock_card(card);
|
||||
|
@ -90,6 +90,7 @@
|
||||
struct n_tty_data {
|
||||
/* producer-published */
|
||||
size_t read_head;
|
||||
size_t commit_head;
|
||||
size_t canon_head;
|
||||
size_t echo_head;
|
||||
size_t echo_commit;
|
||||
@ -161,36 +162,11 @@ static inline int tty_put_user(struct tty_struct *tty, unsigned char x,
|
||||
return put_user(x, ptr);
|
||||
}
|
||||
|
||||
static int receive_room(struct tty_struct *tty)
|
||||
{
|
||||
struct n_tty_data *ldata = tty->disc_data;
|
||||
int left;
|
||||
|
||||
if (I_PARMRK(tty)) {
|
||||
/* Multiply read_cnt by 3, since each byte might take up to
|
||||
* three times as many spaces when PARMRK is set (depending on
|
||||
* its flags, e.g. parity error). */
|
||||
left = N_TTY_BUF_SIZE - read_cnt(ldata) * 3 - 1;
|
||||
} else
|
||||
left = N_TTY_BUF_SIZE - read_cnt(ldata) - 1;
|
||||
|
||||
/*
|
||||
* If we are doing input canonicalization, and there are no
|
||||
* pending newlines, let characters through without limit, so
|
||||
* that erase characters will be handled. Other excess
|
||||
* characters will be beeped.
|
||||
*/
|
||||
if (left <= 0)
|
||||
left = ldata->icanon && ldata->canon_head == ldata->read_tail;
|
||||
|
||||
return left;
|
||||
}
|
||||
|
||||
/**
|
||||
* n_tty_set_room - receive space
|
||||
* n_tty_kick_worker - start input worker (if required)
|
||||
* @tty: terminal
|
||||
*
|
||||
* Re-schedules the flip buffer work if space just became available.
|
||||
* Re-schedules the flip buffer work if it may have stopped
|
||||
*
|
||||
* Caller holds exclusive termios_rwsem
|
||||
* or
|
||||
@ -198,12 +174,12 @@ static int receive_room(struct tty_struct *tty)
|
||||
* holds non-exclusive termios_rwsem
|
||||
*/
|
||||
|
||||
static void n_tty_set_room(struct tty_struct *tty)
|
||||
static void n_tty_kick_worker(struct tty_struct *tty)
|
||||
{
|
||||
struct n_tty_data *ldata = tty->disc_data;
|
||||
|
||||
/* Did this open up the receive buffer? We may need to flip */
|
||||
if (unlikely(ldata->no_room) && receive_room(tty)) {
|
||||
/* Did the input worker stop? Restart it */
|
||||
if (unlikely(ldata->no_room)) {
|
||||
ldata->no_room = 0;
|
||||
|
||||
WARN_RATELIMIT(tty->port->itty == NULL,
|
||||
@ -224,7 +200,7 @@ static ssize_t chars_in_buffer(struct tty_struct *tty)
|
||||
ssize_t n = 0;
|
||||
|
||||
if (!ldata->icanon)
|
||||
n = read_cnt(ldata);
|
||||
n = ldata->commit_head - ldata->read_tail;
|
||||
else
|
||||
n = ldata->canon_head - ldata->read_tail;
|
||||
return n;
|
||||
@ -247,17 +223,20 @@ static void n_tty_write_wakeup(struct tty_struct *tty)
|
||||
|
||||
static void n_tty_check_throttle(struct tty_struct *tty)
|
||||
{
|
||||
if (tty->driver->type == TTY_DRIVER_TYPE_PTY)
|
||||
return;
|
||||
struct n_tty_data *ldata = tty->disc_data;
|
||||
|
||||
/*
|
||||
* Check the remaining room for the input canonicalization
|
||||
* mode. We don't want to throttle the driver if we're in
|
||||
* canonical mode and don't have a newline yet!
|
||||
*/
|
||||
if (ldata->icanon && ldata->canon_head == ldata->read_tail)
|
||||
return;
|
||||
|
||||
while (1) {
|
||||
int throttled;
|
||||
tty_set_flow_change(tty, TTY_THROTTLE_SAFE);
|
||||
if (receive_room(tty) >= TTY_THRESHOLD_THROTTLE)
|
||||
if (N_TTY_BUF_SIZE - read_cnt(ldata) >= TTY_THRESHOLD_THROTTLE)
|
||||
break;
|
||||
throttled = tty_throttle_safe(tty);
|
||||
if (!throttled)
|
||||
@ -274,7 +253,7 @@ static void n_tty_check_unthrottle(struct tty_struct *tty)
|
||||
return;
|
||||
if (!tty->count)
|
||||
return;
|
||||
n_tty_set_room(tty);
|
||||
n_tty_kick_worker(tty);
|
||||
n_tty_write_wakeup(tty->link);
|
||||
if (waitqueue_active(&tty->link->write_wait))
|
||||
wake_up_interruptible_poll(&tty->link->write_wait, POLLOUT);
|
||||
@ -296,7 +275,7 @@ static void n_tty_check_unthrottle(struct tty_struct *tty)
|
||||
break;
|
||||
if (!tty->count)
|
||||
break;
|
||||
n_tty_set_room(tty);
|
||||
n_tty_kick_worker(tty);
|
||||
unthrottled = tty_unthrottle_safe(tty);
|
||||
if (!unthrottled)
|
||||
break;
|
||||
@ -313,10 +292,6 @@ static void n_tty_check_unthrottle(struct tty_struct *tty)
|
||||
*
|
||||
* n_tty_receive_buf()/producer path:
|
||||
* caller holds non-exclusive termios_rwsem
|
||||
* modifies read_head
|
||||
*
|
||||
* read_head is only considered 'published' if canonical mode is
|
||||
* not active.
|
||||
*/
|
||||
|
||||
static inline void put_tty_queue(unsigned char c, struct n_tty_data *ldata)
|
||||
@ -340,6 +315,7 @@ static void reset_buffer_flags(struct n_tty_data *ldata)
|
||||
{
|
||||
ldata->read_head = ldata->canon_head = ldata->read_tail = 0;
|
||||
ldata->echo_head = ldata->echo_tail = ldata->echo_commit = 0;
|
||||
ldata->commit_head = 0;
|
||||
ldata->echo_mark = 0;
|
||||
ldata->line_start = 0;
|
||||
|
||||
@ -379,7 +355,7 @@ static void n_tty_flush_buffer(struct tty_struct *tty)
|
||||
{
|
||||
down_write(&tty->termios_rwsem);
|
||||
reset_buffer_flags(tty->disc_data);
|
||||
n_tty_set_room(tty);
|
||||
n_tty_kick_worker(tty);
|
||||
|
||||
if (tty->link)
|
||||
n_tty_packet_mode_flush(tty);
|
||||
@ -987,10 +963,6 @@ static inline void finish_erasing(struct n_tty_data *ldata)
|
||||
*
|
||||
* n_tty_receive_buf()/producer path:
|
||||
* caller holds non-exclusive termios_rwsem
|
||||
* modifies read_head
|
||||
*
|
||||
* Modifying the read_head is not considered a publish in this context
|
||||
* because canonical mode is active -- only canon_head publishes
|
||||
*/
|
||||
|
||||
static void eraser(unsigned char c, struct tty_struct *tty)
|
||||
@ -1118,16 +1090,45 @@ static void eraser(unsigned char c, struct tty_struct *tty)
|
||||
* Called when a signal is being sent due to terminal input.
|
||||
* Called from the driver receive_buf path so serialized.
|
||||
*
|
||||
* Performs input and output flush if !NOFLSH. In this context, the echo
|
||||
* buffer is 'output'. The signal is processed first to alert any current
|
||||
* readers or writers to discontinue and exit their i/o loops.
|
||||
*
|
||||
* Locking: ctrl_lock
|
||||
*/
|
||||
|
||||
static void isig(int sig, struct tty_struct *tty)
|
||||
{
|
||||
struct n_tty_data *ldata = tty->disc_data;
|
||||
struct pid *tty_pgrp = tty_get_pgrp(tty);
|
||||
if (tty_pgrp) {
|
||||
kill_pgrp(tty_pgrp, sig, 1);
|
||||
put_pid(tty_pgrp);
|
||||
}
|
||||
|
||||
if (!L_NOFLSH(tty)) {
|
||||
up_read(&tty->termios_rwsem);
|
||||
down_write(&tty->termios_rwsem);
|
||||
|
||||
/* clear echo buffer */
|
||||
mutex_lock(&ldata->output_lock);
|
||||
ldata->echo_head = ldata->echo_tail = 0;
|
||||
ldata->echo_mark = ldata->echo_commit = 0;
|
||||
mutex_unlock(&ldata->output_lock);
|
||||
|
||||
/* clear output buffer */
|
||||
tty_driver_flush_buffer(tty);
|
||||
|
||||
/* clear input buffer */
|
||||
reset_buffer_flags(tty->disc_data);
|
||||
|
||||
/* notify pty master of flush */
|
||||
if (tty->link)
|
||||
n_tty_packet_mode_flush(tty);
|
||||
|
||||
up_write(&tty->termios_rwsem);
|
||||
down_read(&tty->termios_rwsem);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1139,7 +1140,6 @@ static void isig(int sig, struct tty_struct *tty)
|
||||
*
|
||||
* n_tty_receive_buf()/producer path:
|
||||
* caller holds non-exclusive termios_rwsem
|
||||
* publishes read_head via put_tty_queue()
|
||||
*
|
||||
* Note: may get exclusive termios_rwsem if flushing input buffer
|
||||
*/
|
||||
@ -1152,13 +1152,6 @@ static void n_tty_receive_break(struct tty_struct *tty)
|
||||
return;
|
||||
if (I_BRKINT(tty)) {
|
||||
isig(SIGINT, tty);
|
||||
if (!L_NOFLSH(tty)) {
|
||||
/* flushing needs exclusive termios_rwsem */
|
||||
up_read(&tty->termios_rwsem);
|
||||
n_tty_flush_buffer(tty);
|
||||
tty_driver_flush_buffer(tty);
|
||||
down_read(&tty->termios_rwsem);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (I_PARMRK(tty)) {
|
||||
@ -1209,7 +1202,6 @@ static void n_tty_receive_overrun(struct tty_struct *tty)
|
||||
*
|
||||
* n_tty_receive_buf()/producer path:
|
||||
* caller holds non-exclusive termios_rwsem
|
||||
* publishes read_head via put_tty_queue()
|
||||
*/
|
||||
static void n_tty_receive_parity_error(struct tty_struct *tty, unsigned char c)
|
||||
{
|
||||
@ -1233,13 +1225,7 @@ static void n_tty_receive_parity_error(struct tty_struct *tty, unsigned char c)
|
||||
static void
|
||||
n_tty_receive_signal_char(struct tty_struct *tty, int signal, unsigned char c)
|
||||
{
|
||||
if (!L_NOFLSH(tty)) {
|
||||
/* flushing needs exclusive termios_rwsem */
|
||||
up_read(&tty->termios_rwsem);
|
||||
n_tty_flush_buffer(tty);
|
||||
tty_driver_flush_buffer(tty);
|
||||
down_read(&tty->termios_rwsem);
|
||||
}
|
||||
isig(signal, tty);
|
||||
if (I_IXON(tty))
|
||||
start_tty(tty);
|
||||
if (L_ECHO(tty)) {
|
||||
@ -1247,7 +1233,6 @@ n_tty_receive_signal_char(struct tty_struct *tty, int signal, unsigned char c)
|
||||
commit_echoes(tty);
|
||||
} else
|
||||
process_echoes(tty);
|
||||
isig(signal, tty);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1263,7 +1248,6 @@ n_tty_receive_signal_char(struct tty_struct *tty, int signal, unsigned char c)
|
||||
* n_tty_receive_buf()/producer path:
|
||||
* caller holds non-exclusive termios_rwsem
|
||||
* publishes canon_head if canonical mode is active
|
||||
* otherwise, publishes read_head via put_tty_queue()
|
||||
*
|
||||
* Returns 1 if LNEXT was received, else returns 0
|
||||
*/
|
||||
@ -1376,7 +1360,7 @@ n_tty_receive_char_special(struct tty_struct *tty, unsigned char c)
|
||||
handle_newline:
|
||||
set_bit(ldata->read_head & (N_TTY_BUF_SIZE - 1), ldata->read_flags);
|
||||
put_tty_queue(c, ldata);
|
||||
ldata->canon_head = ldata->read_head;
|
||||
smp_store_release(&ldata->canon_head, ldata->read_head);
|
||||
kill_fasync(&tty->fasync, SIGIO, POLL_IN);
|
||||
if (waitqueue_active(&tty->read_wait))
|
||||
wake_up_interruptible_poll(&tty->read_wait, POLLIN);
|
||||
@ -1512,23 +1496,6 @@ n_tty_receive_char_lnext(struct tty_struct *tty, unsigned char c, char flag)
|
||||
n_tty_receive_char_flagged(tty, c, flag);
|
||||
}
|
||||
|
||||
/**
|
||||
* n_tty_receive_buf - data receive
|
||||
* @tty: terminal device
|
||||
* @cp: buffer
|
||||
* @fp: flag buffer
|
||||
* @count: characters
|
||||
*
|
||||
* Called by the terminal driver when a block of characters has
|
||||
* been received. This function must be called from soft contexts
|
||||
* not from interrupt context. The driver is responsible for making
|
||||
* calls one at a time and in order (or using flush_to_ldisc)
|
||||
*
|
||||
* n_tty_receive_buf()/producer path:
|
||||
* claims non-exclusive termios_rwsem
|
||||
* publishes read_head and canon_head
|
||||
*/
|
||||
|
||||
static void
|
||||
n_tty_receive_buf_real_raw(struct tty_struct *tty, const unsigned char *cp,
|
||||
char *fp, int count)
|
||||
@ -1537,16 +1504,14 @@ n_tty_receive_buf_real_raw(struct tty_struct *tty, const unsigned char *cp,
|
||||
size_t n, head;
|
||||
|
||||
head = ldata->read_head & (N_TTY_BUF_SIZE - 1);
|
||||
n = N_TTY_BUF_SIZE - max(read_cnt(ldata), head);
|
||||
n = min_t(size_t, count, n);
|
||||
n = min_t(size_t, count, N_TTY_BUF_SIZE - head);
|
||||
memcpy(read_buf_addr(ldata, head), cp, n);
|
||||
ldata->read_head += n;
|
||||
cp += n;
|
||||
count -= n;
|
||||
|
||||
head = ldata->read_head & (N_TTY_BUF_SIZE - 1);
|
||||
n = N_TTY_BUF_SIZE - max(read_cnt(ldata), head);
|
||||
n = min_t(size_t, count, n);
|
||||
n = min_t(size_t, count, N_TTY_BUF_SIZE - head);
|
||||
memcpy(read_buf_addr(ldata, head), cp, n);
|
||||
ldata->read_head += n;
|
||||
}
|
||||
@ -1676,32 +1641,98 @@ static void __receive_buf(struct tty_struct *tty, const unsigned char *cp,
|
||||
tty->ops->flush_chars(tty);
|
||||
}
|
||||
|
||||
if ((!ldata->icanon && (read_cnt(ldata) >= ldata->minimum_to_wake)) ||
|
||||
L_EXTPROC(tty)) {
|
||||
if (ldata->icanon && !L_EXTPROC(tty))
|
||||
return;
|
||||
|
||||
/* publish read_head to consumer */
|
||||
smp_store_release(&ldata->commit_head, ldata->read_head);
|
||||
|
||||
if ((read_cnt(ldata) >= ldata->minimum_to_wake) || L_EXTPROC(tty)) {
|
||||
kill_fasync(&tty->fasync, SIGIO, POLL_IN);
|
||||
if (waitqueue_active(&tty->read_wait))
|
||||
wake_up_interruptible_poll(&tty->read_wait, POLLIN);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* n_tty_receive_buf_common - process input
|
||||
* @tty: device to receive input
|
||||
* @cp: input chars
|
||||
* @fp: flags for each char (if NULL, all chars are TTY_NORMAL)
|
||||
* @count: number of input chars in @cp
|
||||
*
|
||||
* Called by the terminal driver when a block of characters has
|
||||
* been received. This function must be called from soft contexts
|
||||
* not from interrupt context. The driver is responsible for making
|
||||
* calls one at a time and in order (or using flush_to_ldisc)
|
||||
*
|
||||
* Returns the # of input chars from @cp which were processed.
|
||||
*
|
||||
* In canonical mode, the maximum line length is 4096 chars (including
|
||||
* the line termination char); lines longer than 4096 chars are
|
||||
* truncated. After 4095 chars, input data is still processed but
|
||||
* not stored. Overflow processing ensures the tty can always
|
||||
* receive more input until at least one line can be read.
|
||||
*
|
||||
* In non-canonical mode, the read buffer will only accept 4095 chars;
|
||||
* this provides the necessary space for a newline char if the input
|
||||
* mode is switched to canonical.
|
||||
*
|
||||
* Note it is possible for the read buffer to _contain_ 4096 chars
|
||||
* in non-canonical mode: the read buffer could already contain the
|
||||
* maximum canon line of 4096 chars when the mode is switched to
|
||||
* non-canonical.
|
||||
*
|
||||
* n_tty_receive_buf()/producer path:
|
||||
* claims non-exclusive termios_rwsem
|
||||
* publishes commit_head or canon_head
|
||||
*/
|
||||
static int
|
||||
n_tty_receive_buf_common(struct tty_struct *tty, const unsigned char *cp,
|
||||
char *fp, int count, int flow)
|
||||
{
|
||||
struct n_tty_data *ldata = tty->disc_data;
|
||||
int room, n, rcvd = 0;
|
||||
int room, n, rcvd = 0, overflow;
|
||||
|
||||
down_read(&tty->termios_rwsem);
|
||||
|
||||
while (1) {
|
||||
room = receive_room(tty);
|
||||
/*
|
||||
* When PARMRK is set, each input char may take up to 3 chars
|
||||
* in the read buf; reduce the buffer space avail by 3x
|
||||
*
|
||||
* If we are doing input canonicalization, and there are no
|
||||
* pending newlines, let characters through without limit, so
|
||||
* that erase characters will be handled. Other excess
|
||||
* characters will be beeped.
|
||||
*
|
||||
* paired with store in *_copy_from_read_buf() -- guarantees
|
||||
* the consumer has loaded the data in read_buf up to the new
|
||||
* read_tail (so this producer will not overwrite unread data)
|
||||
*/
|
||||
size_t tail = smp_load_acquire(&ldata->read_tail);
|
||||
|
||||
room = N_TTY_BUF_SIZE - (ldata->read_head - tail);
|
||||
if (I_PARMRK(tty))
|
||||
room = (room + 2) / 3;
|
||||
room--;
|
||||
if (room <= 0) {
|
||||
overflow = ldata->icanon && ldata->canon_head == tail;
|
||||
if (overflow && room < 0)
|
||||
ldata->read_head--;
|
||||
room = overflow;
|
||||
ldata->no_room = flow && !room;
|
||||
} else
|
||||
overflow = 0;
|
||||
|
||||
n = min(count, room);
|
||||
if (!n) {
|
||||
if (flow && !room)
|
||||
ldata->no_room = 1;
|
||||
if (!n)
|
||||
break;
|
||||
}
|
||||
__receive_buf(tty, cp, fp, n);
|
||||
|
||||
/* ignore parity errors if handling overflow */
|
||||
if (!overflow || !fp || *fp != TTY_PARITY)
|
||||
__receive_buf(tty, cp, fp, n);
|
||||
|
||||
cp += n;
|
||||
if (fp)
|
||||
fp += n;
|
||||
@ -1710,7 +1741,17 @@ n_tty_receive_buf_common(struct tty_struct *tty, const unsigned char *cp,
|
||||
}
|
||||
|
||||
tty->receive_room = room;
|
||||
n_tty_check_throttle(tty);
|
||||
|
||||
/* Unthrottle if handling overflow on pty */
|
||||
if (tty->driver->type == TTY_DRIVER_TYPE_PTY) {
|
||||
if (overflow) {
|
||||
tty_set_flow_change(tty, TTY_UNTHROTTLE_SAFE);
|
||||
tty_unthrottle_safe(tty);
|
||||
__tty_set_flow_change(tty, 0);
|
||||
}
|
||||
} else
|
||||
n_tty_check_throttle(tty);
|
||||
|
||||
up_read(&tty->termios_rwsem);
|
||||
|
||||
return rcvd;
|
||||
@ -1764,6 +1805,7 @@ static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old)
|
||||
ldata->canon_head = ldata->read_head;
|
||||
ldata->push = 1;
|
||||
}
|
||||
ldata->commit_head = ldata->read_head;
|
||||
ldata->erasing = 0;
|
||||
ldata->lnext = 0;
|
||||
}
|
||||
@ -1817,7 +1859,6 @@ static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old)
|
||||
else
|
||||
ldata->real_raw = 0;
|
||||
}
|
||||
n_tty_set_room(tty);
|
||||
/*
|
||||
* Fix tty hang when I_IXON(tty) is cleared, but the tty
|
||||
* been stopped by STOP_CHAR(tty) before it.
|
||||
@ -1905,7 +1946,7 @@ static inline int input_available_p(struct tty_struct *tty, int poll)
|
||||
if (ldata->icanon && !L_EXTPROC(tty))
|
||||
return ldata->canon_head != ldata->read_tail;
|
||||
else
|
||||
return read_cnt(ldata) >= amt;
|
||||
return ldata->commit_head - ldata->read_tail >= amt;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1937,10 +1978,11 @@ static int copy_from_read_buf(struct tty_struct *tty,
|
||||
int retval;
|
||||
size_t n;
|
||||
bool is_eof;
|
||||
size_t head = smp_load_acquire(&ldata->commit_head);
|
||||
size_t tail = ldata->read_tail & (N_TTY_BUF_SIZE - 1);
|
||||
|
||||
retval = 0;
|
||||
n = min(read_cnt(ldata), N_TTY_BUF_SIZE - tail);
|
||||
n = min(head - ldata->read_tail, N_TTY_BUF_SIZE - tail);
|
||||
n = min(*nr, n);
|
||||
if (n) {
|
||||
retval = copy_to_user(*b, read_buf_addr(ldata, tail), n);
|
||||
@ -1948,9 +1990,10 @@ static int copy_from_read_buf(struct tty_struct *tty,
|
||||
is_eof = n == 1 && read_buf(ldata, tail) == EOF_CHAR(tty);
|
||||
tty_audit_add_data(tty, read_buf_addr(ldata, tail), n,
|
||||
ldata->icanon);
|
||||
ldata->read_tail += n;
|
||||
smp_store_release(&ldata->read_tail, ldata->read_tail + n);
|
||||
/* Turn single EOF into zero-length read */
|
||||
if (L_EXTPROC(tty) && ldata->icanon && is_eof && !read_cnt(ldata))
|
||||
if (L_EXTPROC(tty) && ldata->icanon && is_eof &&
|
||||
(head == ldata->read_tail))
|
||||
n = 0;
|
||||
*b += n;
|
||||
*nr -= n;
|
||||
@ -1993,7 +2036,7 @@ static int canon_copy_from_read_buf(struct tty_struct *tty,
|
||||
bool eof_push = 0;
|
||||
|
||||
/* N.B. avoid overrun if nr == 0 */
|
||||
n = min(*nr, read_cnt(ldata));
|
||||
n = min(*nr, smp_load_acquire(&ldata->canon_head) - ldata->read_tail);
|
||||
if (!n)
|
||||
return 0;
|
||||
|
||||
@ -2043,8 +2086,7 @@ static int canon_copy_from_read_buf(struct tty_struct *tty,
|
||||
|
||||
if (found)
|
||||
clear_bit(eol, ldata->read_flags);
|
||||
smp_mb__after_atomic();
|
||||
ldata->read_tail += c;
|
||||
smp_store_release(&ldata->read_tail, ldata->read_tail + c);
|
||||
|
||||
if (found) {
|
||||
if (!ldata->push)
|
||||
@ -2130,6 +2172,7 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
|
||||
ssize_t retval = 0;
|
||||
long timeout;
|
||||
int packet;
|
||||
size_t tail;
|
||||
|
||||
c = job_control(tty, file);
|
||||
if (c < 0)
|
||||
@ -2166,6 +2209,7 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
|
||||
}
|
||||
|
||||
packet = tty->packet;
|
||||
tail = ldata->read_tail;
|
||||
|
||||
add_wait_queue(&tty->read_wait, &wait);
|
||||
while (nr) {
|
||||
@ -2208,7 +2252,6 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
|
||||
retval = -ERESTARTSYS;
|
||||
break;
|
||||
}
|
||||
n_tty_set_room(tty);
|
||||
up_read(&tty->termios_rwsem);
|
||||
|
||||
timeout = wait_woken(&wait, TASK_INTERRUPTIBLE,
|
||||
@ -2253,7 +2296,8 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
|
||||
if (time)
|
||||
timeout = time;
|
||||
}
|
||||
n_tty_set_room(tty);
|
||||
if (tail != ldata->read_tail)
|
||||
n_tty_kick_worker(tty);
|
||||
up_read(&tty->termios_rwsem);
|
||||
|
||||
remove_wait_queue(&tty->read_wait, &wait);
|
||||
|
@ -87,19 +87,6 @@ static void pty_unthrottle(struct tty_struct *tty)
|
||||
set_bit(TTY_THROTTLED, &tty->flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* pty_space - report space left for writing
|
||||
* @to: tty we are writing into
|
||||
*
|
||||
* Limit the buffer space used by ptys to 8k.
|
||||
*/
|
||||
|
||||
static int pty_space(struct tty_struct *to)
|
||||
{
|
||||
int n = tty_buffer_space_avail(to->port);
|
||||
return min(n, 8192);
|
||||
}
|
||||
|
||||
/**
|
||||
* pty_write - write to a pty
|
||||
* @tty: the tty we write from
|
||||
@ -141,7 +128,7 @@ static int pty_write_room(struct tty_struct *tty)
|
||||
{
|
||||
if (tty->stopped)
|
||||
return 0;
|
||||
return pty_space(tty->link);
|
||||
return tty_buffer_space_avail(tty->link->port);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -210,6 +197,9 @@ static int pty_signal(struct tty_struct *tty, int sig)
|
||||
{
|
||||
struct pid *pgrp;
|
||||
|
||||
if (sig != SIGINT && sig != SIGQUIT && sig != SIGTSTP)
|
||||
return -EINVAL;
|
||||
|
||||
if (tty->link) {
|
||||
pgrp = tty_get_pgrp(tty->link);
|
||||
if (pgrp)
|
||||
@ -222,10 +212,16 @@ static int pty_signal(struct tty_struct *tty, int sig)
|
||||
static void pty_flush_buffer(struct tty_struct *tty)
|
||||
{
|
||||
struct tty_struct *to = tty->link;
|
||||
struct tty_ldisc *ld;
|
||||
|
||||
if (!to)
|
||||
return;
|
||||
/* tty_buffer_flush(to); FIXME */
|
||||
|
||||
ld = tty_ldisc_ref(to);
|
||||
tty_buffer_flush(to, ld);
|
||||
if (ld)
|
||||
tty_ldisc_deref(ld);
|
||||
|
||||
if (to->packet) {
|
||||
spin_lock_irq(&tty->ctrl_lock);
|
||||
tty->ctrl_status |= TIOCPKT_FLUSHWRITE;
|
||||
@ -399,6 +395,7 @@ static int pty_common_install(struct tty_driver *driver, struct tty_struct *tty,
|
||||
goto err_put_module;
|
||||
|
||||
tty_set_lock_subclass(o_tty);
|
||||
lockdep_set_subclass(&o_tty->termios_rwsem, TTY_LOCK_SLAVE);
|
||||
|
||||
if (legacy) {
|
||||
/* We always use new tty termios data so we can do this
|
||||
@ -429,10 +426,14 @@ static int pty_common_install(struct tty_driver *driver, struct tty_struct *tty,
|
||||
o_tty->link = tty;
|
||||
tty_port_init(ports[0]);
|
||||
tty_port_init(ports[1]);
|
||||
tty_buffer_set_limit(ports[0], 8192);
|
||||
tty_buffer_set_limit(ports[1], 8192);
|
||||
o_tty->port = ports[0];
|
||||
tty->port = ports[1];
|
||||
o_tty->port->itty = o_tty;
|
||||
|
||||
tty_buffer_set_lock_subclass(o_tty->port);
|
||||
|
||||
tty_driver_kref_get(driver);
|
||||
tty->count++;
|
||||
o_tty->count++;
|
||||
|
@ -1390,7 +1390,7 @@ static void rp_unthrottle(struct tty_struct *tty)
|
||||
tty->ldisc.chars_in_buffer(tty));
|
||||
#endif
|
||||
|
||||
if (rocket_paranoia_check(info, "rp_throttle"))
|
||||
if (rocket_paranoia_check(info, "rp_unthrottle"))
|
||||
return;
|
||||
|
||||
if (I_IXOFF(tty))
|
||||
@ -1458,7 +1458,7 @@ static void rp_wait_until_sent(struct tty_struct *tty, int timeout)
|
||||
|
||||
orig_jiffies = jiffies;
|
||||
#ifdef ROCKET_DEBUG_WAIT_UNTIL_SENT
|
||||
printk(KERN_INFO "In RP_wait_until_sent(%d) (jiff=%lu)...\n", timeout,
|
||||
printk(KERN_INFO "In %s(%d) (jiff=%lu)...\n", __func__, timeout,
|
||||
jiffies);
|
||||
printk(KERN_INFO "cps=%d...\n", info->cps);
|
||||
#endif
|
||||
|
@ -329,6 +329,17 @@ static const struct serial8250_config uart_config[] = {
|
||||
.fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
|
||||
.flags = UART_CAP_FIFO | UART_CAP_AFE,
|
||||
},
|
||||
/* tx_loadsz is set to 63-bytes instead of 64-bytes to implement
|
||||
workaround of errata A-008006 which states that tx_loadsz should be
|
||||
configured less than Maximum supported fifo bytes */
|
||||
[PORT_16550A_FSL64] = {
|
||||
.name = "16550A_FSL64",
|
||||
.fifo_size = 64,
|
||||
.tx_loadsz = 63,
|
||||
.fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10 |
|
||||
UART_FCR7_64BYTE,
|
||||
.flags = UART_CAP_FIFO,
|
||||
},
|
||||
};
|
||||
|
||||
/* Uart divisor latch read */
|
||||
@ -956,7 +967,17 @@ static void autoconfig_16550a(struct uart_8250_port *up)
|
||||
up->port.type = PORT_16650;
|
||||
up->capabilities |= UART_CAP_EFR | UART_CAP_SLEEP;
|
||||
} else {
|
||||
DEBUG_AUTOCONF("Motorola 8xxx DUART ");
|
||||
serial_out(up, UART_LCR, 0);
|
||||
serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO |
|
||||
UART_FCR7_64BYTE);
|
||||
status1 = serial_in(up, UART_IIR) >> 5;
|
||||
serial_out(up, UART_FCR, 0);
|
||||
serial_out(up, UART_LCR, 0);
|
||||
|
||||
if (status1 == 7)
|
||||
up->port.type = PORT_16550A_FSL64;
|
||||
else
|
||||
DEBUG_AUTOCONF("Motorola 8xxx DUART ");
|
||||
}
|
||||
serial_out(up, UART_EFR, 0);
|
||||
return;
|
||||
@ -1355,9 +1376,11 @@ static void serial8250_start_tx(struct uart_port *port)
|
||||
struct uart_8250_port *up = up_to_u8250p(port);
|
||||
|
||||
serial8250_rpm_get_tx(up);
|
||||
if (up->dma && !up->dma->tx_dma(up)) {
|
||||
|
||||
if (up->dma && !up->dma->tx_dma(up))
|
||||
return;
|
||||
} else if (!(up->ier & UART_IER_THRI)) {
|
||||
|
||||
if (!(up->ier & UART_IER_THRI)) {
|
||||
up->ier |= UART_IER_THRI;
|
||||
serial_port_out(port, UART_IER, up->ier);
|
||||
|
||||
@ -1365,7 +1388,7 @@ static void serial8250_start_tx(struct uart_port *port)
|
||||
unsigned char lsr;
|
||||
lsr = serial_in(up, UART_LSR);
|
||||
up->lsr_saved_flags |= lsr & LSR_SAVE_FLAGS;
|
||||
if (lsr & UART_LSR_TEMT)
|
||||
if (lsr & UART_LSR_THRE)
|
||||
serial8250_tx_chars(up);
|
||||
}
|
||||
}
|
||||
@ -1924,7 +1947,7 @@ static unsigned int serial8250_get_mctrl(struct uart_port *port)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void serial8250_set_mctrl(struct uart_port *port, unsigned int mctrl)
|
||||
void serial8250_do_set_mctrl(struct uart_port *port, unsigned int mctrl)
|
||||
{
|
||||
struct uart_8250_port *up = up_to_u8250p(port);
|
||||
unsigned char mcr = 0;
|
||||
@ -1944,6 +1967,14 @@ static void serial8250_set_mctrl(struct uart_port *port, unsigned int mctrl)
|
||||
|
||||
serial_port_out(port, UART_MCR, mcr);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(serial8250_do_set_mctrl);
|
||||
|
||||
static void serial8250_set_mctrl(struct uart_port *port, unsigned int mctrl)
|
||||
{
|
||||
if (port->set_mctrl)
|
||||
return port->set_mctrl(port, mctrl);
|
||||
return serial8250_do_set_mctrl(port, mctrl);
|
||||
}
|
||||
|
||||
static void serial8250_break_ctl(struct uart_port *port, int break_state)
|
||||
{
|
||||
@ -2382,13 +2413,34 @@ static void serial8250_shutdown(struct uart_port *port)
|
||||
serial8250_do_shutdown(port);
|
||||
}
|
||||
|
||||
static unsigned int serial8250_get_divisor(struct uart_port *port, unsigned int baud)
|
||||
/*
|
||||
* XR17V35x UARTs have an extra fractional divisor register (DLD)
|
||||
* Calculate divisor with extra 4-bit fractional portion
|
||||
*/
|
||||
static unsigned int xr17v35x_get_divisor(struct uart_8250_port *up,
|
||||
unsigned int baud,
|
||||
unsigned int *frac)
|
||||
{
|
||||
struct uart_port *port = &up->port;
|
||||
unsigned int quot_16;
|
||||
|
||||
quot_16 = DIV_ROUND_CLOSEST(port->uartclk, baud);
|
||||
*frac = quot_16 & 0x0f;
|
||||
|
||||
return quot_16 >> 4;
|
||||
}
|
||||
|
||||
static unsigned int serial8250_get_divisor(struct uart_8250_port *up,
|
||||
unsigned int baud,
|
||||
unsigned int *frac)
|
||||
{
|
||||
struct uart_port *port = &up->port;
|
||||
unsigned int quot;
|
||||
|
||||
/*
|
||||
* Handle magic divisors for baud rates above baud_base on
|
||||
* SMSC SuperIO chips.
|
||||
*
|
||||
*/
|
||||
if ((port->flags & UPF_MAGIC_MULTIPLIER) &&
|
||||
baud == (port->uartclk/4))
|
||||
@ -2396,22 +2448,26 @@ static unsigned int serial8250_get_divisor(struct uart_port *port, unsigned int
|
||||
else if ((port->flags & UPF_MAGIC_MULTIPLIER) &&
|
||||
baud == (port->uartclk/8))
|
||||
quot = 0x8002;
|
||||
else if (up->port.type == PORT_XR17V35X)
|
||||
quot = xr17v35x_get_divisor(up, baud, frac);
|
||||
else
|
||||
quot = uart_get_divisor(port, baud);
|
||||
|
||||
/*
|
||||
* Oxford Semi 952 rev B workaround
|
||||
*/
|
||||
if (up->bugs & UART_BUG_QUOT && (quot & 0xff) == 0)
|
||||
quot++;
|
||||
|
||||
return quot;
|
||||
}
|
||||
|
||||
void
|
||||
serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios,
|
||||
struct ktermios *old)
|
||||
static unsigned char serial8250_compute_lcr(struct uart_8250_port *up,
|
||||
tcflag_t c_cflag)
|
||||
{
|
||||
struct uart_8250_port *up = up_to_u8250p(port);
|
||||
unsigned char cval;
|
||||
unsigned long flags;
|
||||
unsigned int baud, quot;
|
||||
|
||||
switch (termios->c_cflag & CSIZE) {
|
||||
switch (c_cflag & CSIZE) {
|
||||
case CS5:
|
||||
cval = UART_LCR_WLEN5;
|
||||
break;
|
||||
@ -2427,33 +2483,80 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios,
|
||||
break;
|
||||
}
|
||||
|
||||
if (termios->c_cflag & CSTOPB)
|
||||
if (c_cflag & CSTOPB)
|
||||
cval |= UART_LCR_STOP;
|
||||
if (termios->c_cflag & PARENB) {
|
||||
if (c_cflag & PARENB) {
|
||||
cval |= UART_LCR_PARITY;
|
||||
if (up->bugs & UART_BUG_PARITY)
|
||||
up->fifo_bug = true;
|
||||
}
|
||||
if (!(termios->c_cflag & PARODD))
|
||||
if (!(c_cflag & PARODD))
|
||||
cval |= UART_LCR_EPAR;
|
||||
#ifdef CMSPAR
|
||||
if (termios->c_cflag & CMSPAR)
|
||||
if (c_cflag & CMSPAR)
|
||||
cval |= UART_LCR_SPAR;
|
||||
#endif
|
||||
|
||||
return cval;
|
||||
}
|
||||
|
||||
static void serial8250_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);
|
||||
|
||||
/* Workaround to enable 115200 baud on OMAP1510 internal ports */
|
||||
if (is_omap1510_8250(up)) {
|
||||
if (baud == 115200) {
|
||||
quot = 1;
|
||||
serial_port_out(port, UART_OMAP_OSC_12M_SEL, 1);
|
||||
} else
|
||||
serial_port_out(port, UART_OMAP_OSC_12M_SEL, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* For NatSemi, switch to bank 2 not bank 1, to avoid resetting EXCR2,
|
||||
* otherwise just set DLAB
|
||||
*/
|
||||
if (up->capabilities & UART_NATSEMI)
|
||||
serial_port_out(port, UART_LCR, 0xe0);
|
||||
else
|
||||
serial_port_out(port, UART_LCR, up->lcr | UART_LCR_DLAB);
|
||||
|
||||
serial_dl_write(up, quot);
|
||||
|
||||
/* XR17V35x UARTs have an extra fractional divisor register (DLD) */
|
||||
if (up->port.type == PORT_XR17V35X)
|
||||
serial_port_out(port, 0x2, quot_frac);
|
||||
}
|
||||
|
||||
void
|
||||
serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios,
|
||||
struct ktermios *old)
|
||||
{
|
||||
struct uart_8250_port *up = up_to_u8250p(port);
|
||||
unsigned char cval;
|
||||
unsigned long flags;
|
||||
unsigned int baud, quot, frac = 0;
|
||||
|
||||
cval = serial8250_compute_lcr(up, termios->c_cflag);
|
||||
|
||||
/*
|
||||
* Ask the core to calculate the divisor for us.
|
||||
*/
|
||||
baud = uart_get_baud_rate(port, termios, old,
|
||||
port->uartclk / 16 / 0xffff,
|
||||
port->uartclk / 16);
|
||||
quot = serial8250_get_divisor(port, baud);
|
||||
quot = serial8250_get_divisor(up, baud, &frac);
|
||||
|
||||
/*
|
||||
* Oxford Semi 952 rev B workaround
|
||||
* Ok, we're now changing the port state. Do it with
|
||||
* interrupts disabled.
|
||||
*/
|
||||
if (up->bugs & UART_BUG_QUOT && (quot & 0xff) == 0)
|
||||
quot++;
|
||||
serial8250_rpm_get(up);
|
||||
spin_lock_irqsave(&port->lock, flags);
|
||||
|
||||
up->lcr = cval; /* Save computed LCR */
|
||||
|
||||
if (up->capabilities & UART_CAP_FIFO && port->fifosize > 1) {
|
||||
/* NOTE: If fifo_bug is not set, a user can set RX_trigger. */
|
||||
@ -2477,13 +2580,6 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios,
|
||||
up->mcr |= UART_MCR_AFE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ok, we're now changing the port state. Do it with
|
||||
* interrupts disabled.
|
||||
*/
|
||||
serial8250_rpm_get(up);
|
||||
spin_lock_irqsave(&port->lock, flags);
|
||||
|
||||
/*
|
||||
* Update the per-port timeout.
|
||||
*/
|
||||
@ -2548,43 +2644,7 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios,
|
||||
serial_port_out(port, UART_EFR, efr);
|
||||
}
|
||||
|
||||
/* Workaround to enable 115200 baud on OMAP1510 internal ports */
|
||||
if (is_omap1510_8250(up)) {
|
||||
if (baud == 115200) {
|
||||
quot = 1;
|
||||
serial_port_out(port, UART_OMAP_OSC_12M_SEL, 1);
|
||||
} else
|
||||
serial_port_out(port, UART_OMAP_OSC_12M_SEL, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* For NatSemi, switch to bank 2 not bank 1, to avoid resetting EXCR2,
|
||||
* otherwise just set DLAB
|
||||
*/
|
||||
if (up->capabilities & UART_NATSEMI)
|
||||
serial_port_out(port, UART_LCR, 0xe0);
|
||||
else
|
||||
serial_port_out(port, UART_LCR, cval | UART_LCR_DLAB);
|
||||
|
||||
serial_dl_write(up, quot);
|
||||
|
||||
/*
|
||||
* XR17V35x UARTs have an extra fractional divisor register (DLD)
|
||||
*
|
||||
* We need to recalculate all of the registers, because DLM and DLL
|
||||
* are already rounded to a whole integer.
|
||||
*
|
||||
* When recalculating we use a 32x clock instead of a 16x clock to
|
||||
* allow 1-bit for rounding in the fractional part.
|
||||
*/
|
||||
if (up->port.type == PORT_XR17V35X) {
|
||||
unsigned int baud_x32 = (port->uartclk * 2) / baud;
|
||||
u16 quot = baud_x32 / 32;
|
||||
u8 quot_frac = DIV_ROUND_CLOSEST(baud_x32 % 32, 2);
|
||||
|
||||
serial_dl_write(up, quot);
|
||||
serial_port_out(port, 0x2, quot_frac & 0xf);
|
||||
}
|
||||
serial8250_set_divisor(port, baud, quot, frac);
|
||||
|
||||
/*
|
||||
* LCR DLAB must be set to enable 64-byte FIFO mode. If the FCR
|
||||
@ -2593,8 +2653,7 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios,
|
||||
if (port->type == PORT_16750)
|
||||
serial_port_out(port, UART_FCR, up->fcr);
|
||||
|
||||
serial_port_out(port, UART_LCR, cval); /* reset DLAB */
|
||||
up->lcr = cval; /* Save LCR */
|
||||
serial_port_out(port, UART_LCR, up->lcr); /* reset DLAB */
|
||||
if (port->type != PORT_16750) {
|
||||
/* emulated UARTs (Lucent Venus 167x) need two steps */
|
||||
if (up->fcr & UART_FCR_ENABLE_FIFO)
|
||||
@ -3208,6 +3267,27 @@ serial8250_console_write(struct console *co, const char *s, unsigned int count)
|
||||
else
|
||||
serial_port_out(port, UART_IER, 0);
|
||||
|
||||
/* check scratch reg to see if port powered off during system sleep */
|
||||
if (up->canary && (up->canary != serial_port_in(port, UART_SCR))) {
|
||||
struct ktermios termios;
|
||||
unsigned int baud, quot, frac = 0;
|
||||
|
||||
termios.c_cflag = port->cons->cflag;
|
||||
if (port->state->port.tty && termios.c_cflag == 0)
|
||||
termios.c_cflag = port->state->port.tty->termios.c_cflag;
|
||||
|
||||
baud = uart_get_baud_rate(port, &termios, NULL,
|
||||
port->uartclk / 16 / 0xffff,
|
||||
port->uartclk / 16);
|
||||
quot = serial8250_get_divisor(up, baud, &frac);
|
||||
|
||||
serial8250_set_divisor(port, baud, quot, frac);
|
||||
serial_port_out(port, UART_LCR, up->lcr);
|
||||
serial_port_out(port, UART_MCR, UART_MCR_DTR | UART_MCR_RTS);
|
||||
|
||||
up->canary = 0;
|
||||
}
|
||||
|
||||
uart_console_write(port, s, count, serial8250_console_putchar);
|
||||
|
||||
/*
|
||||
@ -3358,7 +3438,17 @@ int __init early_serial_setup(struct uart_port *port)
|
||||
*/
|
||||
void serial8250_suspend_port(int line)
|
||||
{
|
||||
uart_suspend_port(&serial8250_reg, &serial8250_ports[line].port);
|
||||
struct uart_8250_port *up = &serial8250_ports[line];
|
||||
struct uart_port *port = &up->port;
|
||||
|
||||
if (!console_suspend_enabled && uart_console(port) &&
|
||||
port->type != PORT_8250) {
|
||||
unsigned char canary = 0xa5;
|
||||
serial_out(up, UART_SCR, canary);
|
||||
up->canary = canary;
|
||||
}
|
||||
|
||||
uart_suspend_port(&serial8250_reg, port);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -3372,6 +3462,8 @@ void serial8250_resume_port(int line)
|
||||
struct uart_8250_port *up = &serial8250_ports[line];
|
||||
struct uart_port *port = &up->port;
|
||||
|
||||
up->canary = 0;
|
||||
|
||||
if (up->capabilities & UART_NATSEMI) {
|
||||
/* Ensure it's still in high speed mode */
|
||||
serial_port_out(port, UART_LCR, 0xE0);
|
||||
@ -3605,6 +3697,8 @@ int serial8250_register_8250_port(struct uart_8250_port *up)
|
||||
/* Possibly override set_termios call */
|
||||
if (up->port.set_termios)
|
||||
uart->port.set_termios = up->port.set_termios;
|
||||
if (up->port.set_mctrl)
|
||||
uart->port.set_mctrl = up->port.set_mctrl;
|
||||
if (up->port.startup)
|
||||
uart->port.startup = up->port.startup;
|
||||
if (up->port.shutdown)
|
||||
|
@ -59,7 +59,6 @@ static void __dma_rx_complete(void *param)
|
||||
|
||||
dma->rx_running = 0;
|
||||
dmaengine_tx_status(dma->rxchan, dma->rx_cookie, &state);
|
||||
dmaengine_terminate_all(dma->rxchan);
|
||||
|
||||
count = dma->rx_size - state.residue;
|
||||
|
||||
@ -81,6 +80,10 @@ int serial8250_tx_dma(struct uart_8250_port *p)
|
||||
return 0;
|
||||
|
||||
dma->tx_size = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
|
||||
if (dma->tx_size < p->port.fifosize) {
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
desc = dmaengine_prep_slave_single(dma->txchan,
|
||||
dma->tx_addr + xmit->tail,
|
||||
@ -131,6 +134,7 @@ int serial8250_rx_dma(struct uart_8250_port *p, unsigned int iir)
|
||||
if (dma->rx_running) {
|
||||
dmaengine_pause(dma->rxchan);
|
||||
__dma_rx_complete(p);
|
||||
dmaengine_terminate_all(dma->rxchan);
|
||||
}
|
||||
return -ETIMEDOUT;
|
||||
default:
|
||||
|
@ -351,10 +351,20 @@ static int dw8250_probe_of(struct uart_port *p,
|
||||
static int dw8250_probe_acpi(struct uart_8250_port *up,
|
||||
struct dw8250_data *data)
|
||||
{
|
||||
const struct acpi_device_id *id;
|
||||
struct uart_port *p = &up->port;
|
||||
|
||||
dw8250_setup_port(up);
|
||||
|
||||
id = acpi_match_device(p->dev->driver->acpi_match_table, p->dev);
|
||||
if (!id)
|
||||
return -ENODEV;
|
||||
|
||||
if (!p->uartclk)
|
||||
if (device_property_read_u32(p->dev, "clock-frequency",
|
||||
&p->uartclk))
|
||||
return -EINVAL;
|
||||
|
||||
p->iotype = UPIO_MEM32;
|
||||
p->serial_in = dw8250_serial_in32;
|
||||
p->serial_out = dw8250_serial_out32;
|
||||
@ -577,6 +587,7 @@ static const struct acpi_device_id dw8250_acpi_match[] = {
|
||||
{ "INT3435", 0 },
|
||||
{ "80860F0A", 0 },
|
||||
{ "8086228A", 0 },
|
||||
{ "APMC0D08", 0},
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, dw8250_acpi_match);
|
||||
|
@ -93,15 +93,18 @@ static void __init early_serial8250_write(struct console *console,
|
||||
struct uart_port *port = &early_device->port;
|
||||
unsigned int ier;
|
||||
|
||||
/* Save the IER and disable interrupts */
|
||||
/* Save the IER and disable interrupts preserving the UUE bit */
|
||||
ier = serial8250_early_in(port, UART_IER);
|
||||
serial8250_early_out(port, UART_IER, 0);
|
||||
if (ier)
|
||||
serial8250_early_out(port, UART_IER, ier & UART_IER_UUE);
|
||||
|
||||
uart_console_write(port, s, count, serial_putc);
|
||||
|
||||
/* Wait for transmitter to become empty and restore the IER */
|
||||
wait_for_xmitr(port);
|
||||
serial8250_early_out(port, UART_IER, ier);
|
||||
|
||||
if (ier)
|
||||
serial8250_early_out(port, UART_IER, ier);
|
||||
}
|
||||
|
||||
static unsigned int __init probe_baud(struct uart_port *port)
|
||||
@ -124,9 +127,11 @@ static void __init init_port(struct earlycon_device *device)
|
||||
struct uart_port *port = &device->port;
|
||||
unsigned int divisor;
|
||||
unsigned char c;
|
||||
unsigned int ier;
|
||||
|
||||
serial8250_early_out(port, UART_LCR, 0x3); /* 8n1 */
|
||||
serial8250_early_out(port, UART_IER, 0); /* no interrupt */
|
||||
ier = serial8250_early_in(port, UART_IER);
|
||||
serial8250_early_out(port, UART_IER, ier & UART_IER_UUE); /* no interrupt */
|
||||
serial8250_early_out(port, UART_FCR, 0); /* no fifo */
|
||||
serial8250_early_out(port, UART_MCR, 0x3); /* DTR + RTS */
|
||||
|
||||
|
@ -106,6 +106,28 @@ static u32 uart_read(struct uart_8250_port *up, u32 reg)
|
||||
return readl(up->port.membase + (reg << up->port.regshift));
|
||||
}
|
||||
|
||||
static void omap8250_set_mctrl(struct uart_port *port, unsigned int mctrl)
|
||||
{
|
||||
struct uart_8250_port *up = up_to_u8250p(port);
|
||||
struct omap8250_priv *priv = up->port.private_data;
|
||||
u8 lcr;
|
||||
|
||||
serial8250_do_set_mctrl(port, mctrl);
|
||||
|
||||
/*
|
||||
* Turn off autoRTS if RTS is lowered and restore autoRTS setting
|
||||
* if RTS is raised
|
||||
*/
|
||||
lcr = serial_in(up, UART_LCR);
|
||||
serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
|
||||
if ((mctrl & TIOCM_RTS) && (port->status & UPSTAT_AUTORTS))
|
||||
priv->efr |= UART_EFR_RTS;
|
||||
else
|
||||
priv->efr &= ~UART_EFR_RTS;
|
||||
serial_out(up, UART_EFR, priv->efr);
|
||||
serial_out(up, UART_LCR, lcr);
|
||||
}
|
||||
|
||||
/*
|
||||
* Work Around for Errata i202 (2430, 3430, 3630, 4430 and 4460)
|
||||
* The access to uart register after MDR1 Access
|
||||
@ -397,12 +419,12 @@ static void omap_8250_set_termios(struct uart_port *port,
|
||||
|
||||
priv->efr = 0;
|
||||
up->mcr &= ~(UART_MCR_RTS | UART_MCR_XONANY);
|
||||
if (termios->c_cflag & CRTSCTS && up->port.flags & UPF_HARD_FLOW) {
|
||||
/* Enable AUTORTS and AUTOCTS */
|
||||
priv->efr |= UART_EFR_CTS | UART_EFR_RTS;
|
||||
up->port.status &= ~(UPSTAT_AUTOCTS | UPSTAT_AUTORTS | UPSTAT_AUTOXOFF);
|
||||
|
||||
/* Ensure MCR RTS is asserted */
|
||||
up->mcr |= UART_MCR_RTS;
|
||||
if (termios->c_cflag & CRTSCTS && up->port.flags & UPF_HARD_FLOW) {
|
||||
/* Enable AUTOCTS (autoRTS is enabled when RTS is raised) */
|
||||
up->port.status |= UPSTAT_AUTOCTS | UPSTAT_AUTORTS;
|
||||
priv->efr |= UART_EFR_CTS;
|
||||
} else if (up->port.flags & UPF_SOFT_FLOW) {
|
||||
/*
|
||||
* IXON Flag:
|
||||
@ -417,8 +439,10 @@ static void omap_8250_set_termios(struct uart_port *port,
|
||||
* Enable XON/XOFF flow control on output.
|
||||
* Transmit XON1, XOFF1
|
||||
*/
|
||||
if (termios->c_iflag & IXOFF)
|
||||
if (termios->c_iflag & IXOFF) {
|
||||
up->port.status |= UPSTAT_AUTOXOFF;
|
||||
priv->efr |= OMAP_UART_SW_TX;
|
||||
}
|
||||
|
||||
/*
|
||||
* IXANY Flag:
|
||||
@ -450,18 +474,18 @@ static void omap_8250_set_termios(struct uart_port *port,
|
||||
static void omap_8250_pm(struct uart_port *port, unsigned int state,
|
||||
unsigned int oldstate)
|
||||
{
|
||||
struct uart_8250_port *up =
|
||||
container_of(port, struct uart_8250_port, port);
|
||||
struct omap8250_priv *priv = up->port.private_data;
|
||||
struct uart_8250_port *up = up_to_u8250p(port);
|
||||
u8 efr;
|
||||
|
||||
pm_runtime_get_sync(port->dev);
|
||||
serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
|
||||
serial_out(up, UART_EFR, priv->efr | UART_EFR_ECB);
|
||||
efr = serial_in(up, UART_EFR);
|
||||
serial_out(up, UART_EFR, efr | UART_EFR_ECB);
|
||||
serial_out(up, UART_LCR, 0);
|
||||
|
||||
serial_out(up, UART_IER, (state != 0) ? UART_IERX_SLEEP : 0);
|
||||
serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
|
||||
serial_out(up, UART_EFR, priv->efr);
|
||||
serial_out(up, UART_EFR, efr);
|
||||
serial_out(up, UART_LCR, 0);
|
||||
|
||||
pm_runtime_mark_last_busy(port->dev);
|
||||
@ -1007,6 +1031,7 @@ static int omap8250_probe(struct platform_device *pdev)
|
||||
up.capabilities |= UART_CAP_RPM;
|
||||
#endif
|
||||
up.port.set_termios = omap_8250_set_termios;
|
||||
up.port.set_mctrl = omap8250_set_mctrl;
|
||||
up.port.pm = omap_8250_pm;
|
||||
up.port.startup = omap_8250_startup;
|
||||
up.port.shutdown = omap_8250_shutdown;
|
||||
@ -1248,6 +1273,46 @@ static int omap8250_runtime_resume(struct device *dev)
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SERIAL_8250_OMAP_TTYO_FIXUP
|
||||
static int __init omap8250_console_fixup(void)
|
||||
{
|
||||
char *omap_str;
|
||||
char *options;
|
||||
u8 idx;
|
||||
|
||||
if (strstr(boot_command_line, "console=ttyS"))
|
||||
/* user set a ttyS based name for the console */
|
||||
return 0;
|
||||
|
||||
omap_str = strstr(boot_command_line, "console=ttyO");
|
||||
if (!omap_str)
|
||||
/* user did not set ttyO based console, so we don't care */
|
||||
return 0;
|
||||
|
||||
omap_str += 12;
|
||||
if ('0' <= *omap_str && *omap_str <= '9')
|
||||
idx = *omap_str - '0';
|
||||
else
|
||||
return 0;
|
||||
|
||||
omap_str++;
|
||||
if (omap_str[0] == ',') {
|
||||
omap_str++;
|
||||
options = omap_str;
|
||||
} else {
|
||||
options = NULL;
|
||||
}
|
||||
|
||||
add_preferred_console("ttyS", idx, options);
|
||||
pr_err("WARNING: Your 'console=ttyO%d' has been replaced by 'ttyS%d'\n",
|
||||
idx, idx);
|
||||
pr_err("This ensures that you still see kernel messages. Please\n");
|
||||
pr_err("update your kernel commandline.\n");
|
||||
return 0;
|
||||
}
|
||||
console_initcall(omap8250_console_fixup);
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops omap8250_dev_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(omap8250_suspend, omap8250_resume)
|
||||
SET_RUNTIME_PM_OPS(omap8250_runtime_suspend,
|
||||
@ -1269,7 +1334,6 @@ static struct platform_driver omap8250_platform_driver = {
|
||||
.name = "omap8250",
|
||||
.pm = &omap8250_dev_pm_ops,
|
||||
.of_match_table = omap8250_dt_ids,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = omap8250_probe,
|
||||
.remove = omap8250_remove,
|
||||
|
@ -221,13 +221,13 @@ pci_hp_diva_setup(struct serial_private *priv,
|
||||
*/
|
||||
static int pci_inteli960ni_init(struct pci_dev *dev)
|
||||
{
|
||||
unsigned long oldval;
|
||||
u32 oldval;
|
||||
|
||||
if (!(dev->subsystem_device & 0x1000))
|
||||
return -ENODEV;
|
||||
|
||||
/* is firmware started? */
|
||||
pci_read_config_dword(dev, 0x44, (void *)&oldval);
|
||||
pci_read_config_dword(dev, 0x44, &oldval);
|
||||
if (oldval == 0x00001000L) { /* RESET value */
|
||||
dev_dbg(&dev->dev, "Local i960 firmware missing\n");
|
||||
return -ENODEV;
|
||||
|
@ -426,7 +426,7 @@ static int serial_pnp_guess_board(struct pnp_dev *dev)
|
||||
static int
|
||||
serial_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id)
|
||||
{
|
||||
struct uart_8250_port uart;
|
||||
struct uart_8250_port uart, *port;
|
||||
int ret, line, flags = dev_id->driver_data;
|
||||
|
||||
if (flags & UNKNOWN_DEV) {
|
||||
@ -471,6 +471,10 @@ serial_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id)
|
||||
if (line < 0 || (flags & CIR_PORT))
|
||||
return -ENODEV;
|
||||
|
||||
port = serial8250_get_port(line);
|
||||
if (uart_console(&port->port))
|
||||
dev->capabilities |= PNP_CONSOLE;
|
||||
|
||||
pnp_set_drvdata(dev, (void *)((long)line + 1));
|
||||
return 0;
|
||||
}
|
||||
@ -478,6 +482,8 @@ serial_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id)
|
||||
static void serial_pnp_remove(struct pnp_dev *dev)
|
||||
{
|
||||
long line = (long)pnp_get_drvdata(dev);
|
||||
|
||||
dev->capabilities &= ~PNP_CONSOLE;
|
||||
if (line)
|
||||
serial8250_unregister_port(line - 1);
|
||||
}
|
||||
|
@ -308,6 +308,25 @@ config SERIAL_8250_OMAP
|
||||
|
||||
This driver uses ttyS instead of ttyO.
|
||||
|
||||
config SERIAL_8250_OMAP_TTYO_FIXUP
|
||||
bool "Replace ttyO with ttyS"
|
||||
depends on SERIAL_8250_OMAP=y && SERIAL_8250_CONSOLE
|
||||
default y
|
||||
help
|
||||
This option replaces the "console=ttyO" argument with the matching
|
||||
ttyS argument if the user did not specified it on the command line.
|
||||
This ensures that the user can see the kernel output during boot
|
||||
which he wouldn't see otherwise. The getty has still to be configured
|
||||
for ttyS instead of ttyO regardless of this option.
|
||||
This option is intended for people who "automatically" enable this
|
||||
driver without knowing that this driver requires a different console=
|
||||
argument. If you read this, please keep this option disabled and
|
||||
instead update your kernel command line. If you prepare a kernel for a
|
||||
distribution or other kind of larger user base then you probably want
|
||||
to keep this option enabled. Otherwise people might complain about a
|
||||
not booting kernel because the serial console remains silent in case
|
||||
they forgot to update the command line.
|
||||
|
||||
config SERIAL_8250_FINTEK
|
||||
tristate "Support for Fintek F81216A LPC to 4 UART"
|
||||
depends on SERIAL_8250 && PNP
|
||||
|
@ -241,6 +241,7 @@ config SERIAL_SAMSUNG
|
||||
tristate "Samsung SoC serial support"
|
||||
depends on PLAT_SAMSUNG || ARCH_EXYNOS
|
||||
select SERIAL_CORE
|
||||
select SERIAL_EARLYCON
|
||||
help
|
||||
Support for the on-chip UARTs on the Samsung S3C24XX series CPUs,
|
||||
providing /dev/ttySAC0, 1 and 2 (note, some machines may not
|
||||
@ -482,16 +483,6 @@ config SERIAL_SA1100_CONSOLE
|
||||
your boot loader (lilo or loadlin) about how to pass options to the
|
||||
kernel at boot time.)
|
||||
|
||||
config SERIAL_MRST_MAX3110
|
||||
tristate "SPI UART driver for Max3110"
|
||||
depends on SPI_DW_PCI
|
||||
select SERIAL_CORE
|
||||
select SERIAL_CORE_CONSOLE
|
||||
help
|
||||
This is the UART protocol driver for the MAX3110 device on
|
||||
the Intel Moorestown platform. On other systems use the max3100
|
||||
driver.
|
||||
|
||||
config SERIAL_MFD_HSU
|
||||
tristate "Medfield High Speed UART support"
|
||||
depends on PCI
|
||||
@ -1094,6 +1085,16 @@ config SERIAL_VT8500_CONSOLE
|
||||
depends on SERIAL_VT8500=y
|
||||
select SERIAL_CORE_CONSOLE
|
||||
|
||||
config SERIAL_ETRAXFS
|
||||
bool "ETRAX FS serial port support"
|
||||
depends on ETRAX_ARCH_V32 && OF
|
||||
select SERIAL_CORE
|
||||
|
||||
config SERIAL_ETRAXFS_CONSOLE
|
||||
bool "ETRAX FS serial console support"
|
||||
depends on SERIAL_ETRAXFS
|
||||
select SERIAL_CORE_CONSOLE
|
||||
|
||||
config SERIAL_NETX
|
||||
tristate "NetX serial port support"
|
||||
depends on ARCH_NETX
|
||||
@ -1549,6 +1550,21 @@ config SERIAL_FSL_LPUART_CONSOLE
|
||||
If you have enabled the lpuart serial port on the Freescale SoCs,
|
||||
you can make it the console by answering Y to this option.
|
||||
|
||||
config SERIAL_CONEXANT_DIGICOLOR
|
||||
tristate "Conexant Digicolor CX92xxx USART serial port support"
|
||||
depends on OF
|
||||
select SERIAL_CORE
|
||||
help
|
||||
Support for the on-chip USART on Conexant Digicolor SoCs.
|
||||
|
||||
config SERIAL_CONEXANT_DIGICOLOR_CONSOLE
|
||||
bool "Console on Conexant Digicolor serial port"
|
||||
depends on SERIAL_CONEXANT_DIGICOLOR=y
|
||||
select SERIAL_CORE_CONSOLE
|
||||
help
|
||||
If you have enabled the USART serial port on Conexant Digicolor
|
||||
SoCs, you can make it the console by answering Y to this option.
|
||||
|
||||
config SERIAL_ST_ASC
|
||||
tristate "ST ASC serial port support"
|
||||
select SERIAL_CORE
|
||||
@ -1577,6 +1593,24 @@ config SERIAL_MEN_Z135
|
||||
This driver can also be build as a module. If so, the module will be called
|
||||
men_z135_uart.ko
|
||||
|
||||
config SERIAL_SPRD
|
||||
tristate "Support for Spreadtrum serial"
|
||||
depends on ARCH_SPRD
|
||||
select SERIAL_CORE
|
||||
help
|
||||
This enables the driver for the Spreadtrum's serial.
|
||||
|
||||
config SERIAL_SPRD_CONSOLE
|
||||
bool "Spreadtrum UART console support"
|
||||
depends on SERIAL_SPRD=y
|
||||
select SERIAL_CORE_CONSOLE
|
||||
select SERIAL_EARLYCON
|
||||
help
|
||||
Support for early debug console using Spreadtrum's serial. This enables
|
||||
the console before standard serial driver is probed. This is enabled
|
||||
with "earlycon" on the kernel command line. The console is
|
||||
enabled when early_param is processed.
|
||||
|
||||
endmenu
|
||||
|
||||
config SERIAL_MCTRL_GPIO
|
||||
|
@ -51,6 +51,7 @@ obj-$(CONFIG_SERIAL_MPSC) += mpsc.o
|
||||
obj-$(CONFIG_SERIAL_MESON) += meson_uart.o
|
||||
obj-$(CONFIG_SERIAL_SB1250_DUART) += sb1250-duart.o
|
||||
obj-$(CONFIG_ETRAX_SERIAL) += crisv10.o
|
||||
obj-$(CONFIG_SERIAL_ETRAXFS) += etraxfs-uart.o
|
||||
obj-$(CONFIG_SERIAL_SCCNXP) += sccnxp.o
|
||||
obj-$(CONFIG_SERIAL_SC16IS7XX) += sc16is7xx.o
|
||||
obj-$(CONFIG_SERIAL_JSM) += jsm/
|
||||
@ -77,7 +78,6 @@ obj-$(CONFIG_SERIAL_TIMBERDALE) += timbuart.o
|
||||
obj-$(CONFIG_SERIAL_GRLIB_GAISLER_APBUART) += apbuart.o
|
||||
obj-$(CONFIG_SERIAL_ALTERA_JTAGUART) += altera_jtaguart.o
|
||||
obj-$(CONFIG_SERIAL_VT8500) += vt8500_serial.o
|
||||
obj-$(CONFIG_SERIAL_MRST_MAX3110) += mrst_max3110.o
|
||||
obj-$(CONFIG_SERIAL_MFD_HSU) += mfd.o
|
||||
obj-$(CONFIG_SERIAL_IFX6X60) += ifx6x60.o
|
||||
obj-$(CONFIG_SERIAL_PCH_UART) += pch_uart.o
|
||||
@ -92,7 +92,9 @@ obj-$(CONFIG_SERIAL_EFM32_UART) += efm32-uart.o
|
||||
obj-$(CONFIG_SERIAL_ARC) += arc_uart.o
|
||||
obj-$(CONFIG_SERIAL_RP2) += rp2.o
|
||||
obj-$(CONFIG_SERIAL_FSL_LPUART) += fsl_lpuart.o
|
||||
obj-$(CONFIG_SERIAL_CONEXANT_DIGICOLOR) += digicolor-usart.o
|
||||
obj-$(CONFIG_SERIAL_MEN_Z135) += men_z135_uart.o
|
||||
obj-$(CONFIG_SERIAL_SPRD) += sprd_serial.o
|
||||
|
||||
# GPIOLIB helpers for modem control lines
|
||||
obj-$(CONFIG_SERIAL_MCTRL_GPIO) += serial_mctrl_gpio.o
|
||||
|
@ -441,6 +441,7 @@ static int altera_jtaguart_probe(struct platform_device *pdev)
|
||||
port->iotype = SERIAL_IO_MEM;
|
||||
port->ops = &altera_jtaguart_ops;
|
||||
port->flags = UPF_BOOT_AUTOCONF;
|
||||
port->dev = &pdev->dev;
|
||||
|
||||
uart_add_one_port(&altera_jtaguart_driver, port);
|
||||
|
||||
|
@ -589,6 +589,7 @@ static int altera_uart_probe(struct platform_device *pdev)
|
||||
port->iotype = SERIAL_IO_MEM;
|
||||
port->ops = &altera_uart_ops;
|
||||
port->flags = UPF_BOOT_AUTOCONF;
|
||||
port->dev = &pdev->dev;
|
||||
|
||||
platform_set_drvdata(pdev, port);
|
||||
|
||||
|
@ -341,13 +341,37 @@ static u_int atmel_tx_empty(struct uart_port *port)
|
||||
static void atmel_set_mctrl(struct uart_port *port, u_int mctrl)
|
||||
{
|
||||
unsigned int control = 0;
|
||||
unsigned int mode;
|
||||
unsigned int mode = UART_GET_MR(port);
|
||||
unsigned int rts_paused, rts_ready;
|
||||
struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
|
||||
|
||||
/* override mode to RS485 if needed, otherwise keep the current mode */
|
||||
if (port->rs485.flags & SER_RS485_ENABLED) {
|
||||
if ((port->rs485.delay_rts_after_send) > 0)
|
||||
UART_PUT_TTGR(port, port->rs485.delay_rts_after_send);
|
||||
mode &= ~ATMEL_US_USMODE;
|
||||
mode |= ATMEL_US_USMODE_RS485;
|
||||
}
|
||||
|
||||
/* set the RTS line state according to the mode */
|
||||
if ((mode & ATMEL_US_USMODE) == ATMEL_US_USMODE_HWHS) {
|
||||
/* force RTS line to high level */
|
||||
rts_paused = ATMEL_US_RTSEN;
|
||||
|
||||
/* give the control of the RTS line back to the hardware */
|
||||
rts_ready = ATMEL_US_RTSDIS;
|
||||
} else {
|
||||
/* force RTS line to high level */
|
||||
rts_paused = ATMEL_US_RTSDIS;
|
||||
|
||||
/* force RTS line to low level */
|
||||
rts_ready = ATMEL_US_RTSEN;
|
||||
}
|
||||
|
||||
if (mctrl & TIOCM_RTS)
|
||||
control |= ATMEL_US_RTSEN;
|
||||
control |= rts_ready;
|
||||
else
|
||||
control |= ATMEL_US_RTSDIS;
|
||||
control |= rts_paused;
|
||||
|
||||
if (mctrl & TIOCM_DTR)
|
||||
control |= ATMEL_US_DTREN;
|
||||
@ -359,23 +383,12 @@ static void atmel_set_mctrl(struct uart_port *port, u_int mctrl)
|
||||
mctrl_gpio_set(atmel_port->gpios, mctrl);
|
||||
|
||||
/* Local loopback mode? */
|
||||
mode = UART_GET_MR(port) & ~ATMEL_US_CHMODE;
|
||||
mode &= ~ATMEL_US_CHMODE;
|
||||
if (mctrl & TIOCM_LOOP)
|
||||
mode |= ATMEL_US_CHMODE_LOC_LOOP;
|
||||
else
|
||||
mode |= ATMEL_US_CHMODE_NORMAL;
|
||||
|
||||
/* Resetting serial mode to RS232 (0x0) */
|
||||
mode &= ~ATMEL_US_USMODE;
|
||||
|
||||
if (port->rs485.flags & SER_RS485_ENABLED) {
|
||||
dev_dbg(port->dev, "Setting UART to RS485\n");
|
||||
if ((port->rs485.delay_rts_after_send) > 0)
|
||||
UART_PUT_TTGR(port, port->rs485.delay_rts_after_send);
|
||||
mode |= ATMEL_US_USMODE_RS485;
|
||||
} else {
|
||||
dev_dbg(port->dev, "Setting UART to RS232\n");
|
||||
}
|
||||
UART_PUT_MR(port, mode);
|
||||
}
|
||||
|
||||
@ -725,7 +738,11 @@ static void atmel_complete_tx_dma(void *arg)
|
||||
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
|
||||
uart_write_wakeup(port);
|
||||
|
||||
/* Do we really need this? */
|
||||
/*
|
||||
* xmit is a circular buffer so, if we have just send data from
|
||||
* xmit->tail to the end of xmit->buf, now we have to transmit the
|
||||
* remaining data from the beginning of xmit->buf to xmit->head.
|
||||
*/
|
||||
if (!uart_circ_empty(xmit))
|
||||
tasklet_schedule(&atmel_port->tasklet);
|
||||
|
||||
@ -784,17 +801,17 @@ static void atmel_tx_dma(struct uart_port *port)
|
||||
BUG_ON(!sg_dma_len(sg));
|
||||
|
||||
desc = dmaengine_prep_slave_sg(chan,
|
||||
sg,
|
||||
1,
|
||||
DMA_MEM_TO_DEV,
|
||||
DMA_PREP_INTERRUPT |
|
||||
DMA_CTRL_ACK);
|
||||
sg,
|
||||
1,
|
||||
DMA_MEM_TO_DEV,
|
||||
DMA_PREP_INTERRUPT |
|
||||
DMA_CTRL_ACK);
|
||||
if (!desc) {
|
||||
dev_err(port->dev, "Failed to send via dma!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
dma_sync_sg_for_device(port->dev, sg, 1, DMA_MEM_TO_DEV);
|
||||
dma_sync_sg_for_device(port->dev, sg, 1, DMA_TO_DEVICE);
|
||||
|
||||
atmel_port->desc_tx = desc;
|
||||
desc->callback = atmel_complete_tx_dma;
|
||||
@ -927,7 +944,7 @@ static void atmel_rx_from_dma(struct uart_port *port)
|
||||
dma_sync_sg_for_cpu(port->dev,
|
||||
&atmel_port->sg_rx,
|
||||
1,
|
||||
DMA_DEV_TO_MEM);
|
||||
DMA_FROM_DEVICE);
|
||||
|
||||
/*
|
||||
* ring->head points to the end of data already written by the DMA.
|
||||
@ -974,7 +991,7 @@ static void atmel_rx_from_dma(struct uart_port *port)
|
||||
dma_sync_sg_for_device(port->dev,
|
||||
&atmel_port->sg_rx,
|
||||
1,
|
||||
DMA_DEV_TO_MEM);
|
||||
DMA_FROM_DEVICE);
|
||||
|
||||
/*
|
||||
* Drop the lock here since it might end up calling
|
||||
@ -1012,13 +1029,13 @@ static int atmel_prepare_rx_dma(struct uart_port *port)
|
||||
/* UART circular rx buffer is an aligned page. */
|
||||
BUG_ON((int)port->state->xmit.buf & ~PAGE_MASK);
|
||||
sg_set_page(&atmel_port->sg_rx,
|
||||
virt_to_page(ring->buf),
|
||||
ATMEL_SERIAL_RINGSIZE,
|
||||
(int)ring->buf & ~PAGE_MASK);
|
||||
nent = dma_map_sg(port->dev,
|
||||
&atmel_port->sg_rx,
|
||||
1,
|
||||
DMA_FROM_DEVICE);
|
||||
virt_to_page(ring->buf),
|
||||
ATMEL_SERIAL_RINGSIZE,
|
||||
(int)ring->buf & ~PAGE_MASK);
|
||||
nent = dma_map_sg(port->dev,
|
||||
&atmel_port->sg_rx,
|
||||
1,
|
||||
DMA_FROM_DEVICE);
|
||||
|
||||
if (!nent) {
|
||||
dev_dbg(port->dev, "need to release resource of dma\n");
|
||||
@ -1047,11 +1064,11 @@ static int atmel_prepare_rx_dma(struct uart_port *port)
|
||||
* each one is half ring buffer size
|
||||
*/
|
||||
desc = dmaengine_prep_dma_cyclic(atmel_port->chan_rx,
|
||||
sg_dma_address(&atmel_port->sg_rx),
|
||||
sg_dma_len(&atmel_port->sg_rx),
|
||||
sg_dma_len(&atmel_port->sg_rx)/2,
|
||||
DMA_DEV_TO_MEM,
|
||||
DMA_PREP_INTERRUPT);
|
||||
sg_dma_address(&atmel_port->sg_rx),
|
||||
sg_dma_len(&atmel_port->sg_rx),
|
||||
sg_dma_len(&atmel_port->sg_rx)/2,
|
||||
DMA_DEV_TO_MEM,
|
||||
DMA_PREP_INTERRUPT);
|
||||
desc->callback = atmel_complete_rx_dma;
|
||||
desc->callback_param = port;
|
||||
atmel_port->desc_rx = desc;
|
||||
@ -1921,12 +1938,14 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios,
|
||||
struct ktermios *old)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned int mode, imr, quot, baud;
|
||||
unsigned int old_mode, mode, imr, quot, baud;
|
||||
|
||||
/* Get current mode register */
|
||||
mode = UART_GET_MR(port) & ~(ATMEL_US_USCLKS | ATMEL_US_CHRL
|
||||
| ATMEL_US_NBSTOP | ATMEL_US_PAR
|
||||
| ATMEL_US_USMODE);
|
||||
/* save the current mode register */
|
||||
mode = old_mode = UART_GET_MR(port);
|
||||
|
||||
/* reset the mode, clock divisor, parity, stop bits and data size */
|
||||
mode &= ~(ATMEL_US_USCLKS | ATMEL_US_CHRL | ATMEL_US_NBSTOP |
|
||||
ATMEL_US_PAR | ATMEL_US_USMODE);
|
||||
|
||||
baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16);
|
||||
quot = uart_get_divisor(port, baud);
|
||||
@ -1971,12 +1990,6 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios,
|
||||
} else
|
||||
mode |= ATMEL_US_PAR_NONE;
|
||||
|
||||
/* hardware handshake (RTS/CTS) */
|
||||
if (termios->c_cflag & CRTSCTS)
|
||||
mode |= ATMEL_US_USMODE_HWHS;
|
||||
else
|
||||
mode |= ATMEL_US_USMODE_NORMAL;
|
||||
|
||||
spin_lock_irqsave(&port->lock, flags);
|
||||
|
||||
port->read_status_mask = ATMEL_US_OVRE;
|
||||
@ -2020,18 +2033,40 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios,
|
||||
/* disable receiver and transmitter */
|
||||
UART_PUT_CR(port, ATMEL_US_TXDIS | ATMEL_US_RXDIS);
|
||||
|
||||
/* Resetting serial mode to RS232 (0x0) */
|
||||
mode &= ~ATMEL_US_USMODE;
|
||||
|
||||
/* mode */
|
||||
if (port->rs485.flags & SER_RS485_ENABLED) {
|
||||
if ((port->rs485.delay_rts_after_send) > 0)
|
||||
UART_PUT_TTGR(port, port->rs485.delay_rts_after_send);
|
||||
mode |= ATMEL_US_USMODE_RS485;
|
||||
} else if (termios->c_cflag & CRTSCTS) {
|
||||
/* RS232 with hardware handshake (RTS/CTS) */
|
||||
mode |= ATMEL_US_USMODE_HWHS;
|
||||
} else {
|
||||
/* RS232 without hadware handshake */
|
||||
mode |= ATMEL_US_USMODE_NORMAL;
|
||||
}
|
||||
|
||||
/* set the parity, stop bits and data size */
|
||||
/* set the mode, clock divisor, parity, stop bits and data size */
|
||||
UART_PUT_MR(port, mode);
|
||||
|
||||
/*
|
||||
* when switching the mode, set the RTS line state according to the
|
||||
* new mode, otherwise keep the former state
|
||||
*/
|
||||
if ((old_mode & ATMEL_US_USMODE) != (mode & ATMEL_US_USMODE)) {
|
||||
unsigned int rts_state;
|
||||
|
||||
if ((mode & ATMEL_US_USMODE) == ATMEL_US_USMODE_HWHS) {
|
||||
/* let the hardware control the RTS line */
|
||||
rts_state = ATMEL_US_RTSDIS;
|
||||
} else {
|
||||
/* force RTS line to low level */
|
||||
rts_state = ATMEL_US_RTSEN;
|
||||
}
|
||||
|
||||
UART_PUT_CR(port, rts_state);
|
||||
}
|
||||
|
||||
/* set the baud rate */
|
||||
UART_PUT_BRGR(port, quot);
|
||||
UART_PUT_CR(port, ATMEL_US_RSTSTA | ATMEL_US_RSTRX);
|
||||
@ -2565,7 +2600,7 @@ static int atmel_serial_probe(struct platform_device *pdev)
|
||||
|
||||
ret = atmel_init_port(port, pdev);
|
||||
if (ret)
|
||||
goto err;
|
||||
goto err_clear_bit;
|
||||
|
||||
if (!atmel_use_pdc_rx(&port->uart)) {
|
||||
ret = -ENOMEM;
|
||||
@ -2596,6 +2631,12 @@ static int atmel_serial_probe(struct platform_device *pdev)
|
||||
device_init_wakeup(&pdev->dev, 1);
|
||||
platform_set_drvdata(pdev, port);
|
||||
|
||||
/*
|
||||
* The peripheral clock has been disabled by atmel_init_port():
|
||||
* enable it before accessing I/O registers
|
||||
*/
|
||||
clk_prepare_enable(port->clk);
|
||||
|
||||
if (rs485_enabled) {
|
||||
UART_PUT_MR(&port->uart, ATMEL_US_USMODE_NORMAL);
|
||||
UART_PUT_CR(&port->uart, ATMEL_US_RTSEN);
|
||||
@ -2606,6 +2647,12 @@ static int atmel_serial_probe(struct platform_device *pdev)
|
||||
*/
|
||||
atmel_get_ip_name(&port->uart);
|
||||
|
||||
/*
|
||||
* The peripheral clock can now safely be disabled till the port
|
||||
* is used
|
||||
*/
|
||||
clk_disable_unprepare(port->clk);
|
||||
|
||||
return 0;
|
||||
|
||||
err_add_port:
|
||||
@ -2616,6 +2663,8 @@ err_alloc_ring:
|
||||
clk_put(port->clk);
|
||||
port->clk = NULL;
|
||||
}
|
||||
err_clear_bit:
|
||||
clear_bit(port->uart.line, atmel_ports_in_use);
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
560
drivers/tty/serial/digicolor-usart.c
Normal file
560
drivers/tty/serial/digicolor-usart.c
Normal file
@ -0,0 +1,560 @@
|
||||
/*
|
||||
* Driver for Conexant Digicolor serial ports (USART)
|
||||
*
|
||||
* Author: Baruch Siach <baruch@tkos.co.il>
|
||||
*
|
||||
* Copyright (C) 2014 Paradox Innovation Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/console.h>
|
||||
#include <linux/serial_core.h>
|
||||
#include <linux/serial.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/tty.h>
|
||||
#include <linux/tty_flip.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#define UA_ENABLE 0x00
|
||||
#define UA_ENABLE_ENABLE BIT(0)
|
||||
|
||||
#define UA_CONTROL 0x01
|
||||
#define UA_CONTROL_RX_ENABLE BIT(0)
|
||||
#define UA_CONTROL_TX_ENABLE BIT(1)
|
||||
#define UA_CONTROL_SOFT_RESET BIT(2)
|
||||
|
||||
#define UA_STATUS 0x02
|
||||
#define UA_STATUS_PARITY_ERR BIT(0)
|
||||
#define UA_STATUS_FRAME_ERR BIT(1)
|
||||
#define UA_STATUS_OVERRUN_ERR BIT(2)
|
||||
#define UA_STATUS_TX_READY BIT(6)
|
||||
|
||||
#define UA_CONFIG 0x03
|
||||
#define UA_CONFIG_CHAR_LEN BIT(0)
|
||||
#define UA_CONFIG_STOP_BITS BIT(1)
|
||||
#define UA_CONFIG_PARITY BIT(2)
|
||||
#define UA_CONFIG_ODD_PARITY BIT(4)
|
||||
|
||||
#define UA_EMI_REC 0x04
|
||||
|
||||
#define UA_HBAUD_LO 0x08
|
||||
#define UA_HBAUD_HI 0x09
|
||||
|
||||
#define UA_STATUS_FIFO 0x0a
|
||||
#define UA_STATUS_FIFO_RX_EMPTY BIT(2)
|
||||
#define UA_STATUS_FIFO_RX_INT_ALMOST BIT(3)
|
||||
#define UA_STATUS_FIFO_TX_FULL BIT(4)
|
||||
#define UA_STATUS_FIFO_TX_INT_ALMOST BIT(7)
|
||||
|
||||
#define UA_CONFIG_FIFO 0x0b
|
||||
#define UA_CONFIG_FIFO_RX_THRESH 7
|
||||
#define UA_CONFIG_FIFO_RX_FIFO_MODE BIT(3)
|
||||
#define UA_CONFIG_FIFO_TX_FIFO_MODE BIT(7)
|
||||
|
||||
#define UA_INTFLAG_CLEAR 0x1c
|
||||
#define UA_INTFLAG_SET 0x1d
|
||||
#define UA_INT_ENABLE 0x1e
|
||||
#define UA_INT_STATUS 0x1f
|
||||
|
||||
#define UA_INT_TX BIT(0)
|
||||
#define UA_INT_RX BIT(1)
|
||||
|
||||
#define DIGICOLOR_USART_NR 3
|
||||
|
||||
/*
|
||||
* We use the 16 bytes hardware FIFO to buffer Rx traffic. Rx interrupt is
|
||||
* only produced when the FIFO is filled more than a certain configurable
|
||||
* threshold. Unfortunately, there is no way to set this threshold below half
|
||||
* FIFO. This means that we must periodically poll the FIFO status register to
|
||||
* see whether there are waiting Rx bytes.
|
||||
*/
|
||||
|
||||
struct digicolor_port {
|
||||
struct uart_port port;
|
||||
struct delayed_work rx_poll_work;
|
||||
};
|
||||
|
||||
static struct uart_port *digicolor_ports[DIGICOLOR_USART_NR];
|
||||
|
||||
static bool digicolor_uart_tx_full(struct uart_port *port)
|
||||
{
|
||||
return !!(readb_relaxed(port->membase + UA_STATUS_FIFO) &
|
||||
UA_STATUS_FIFO_TX_FULL);
|
||||
}
|
||||
|
||||
static bool digicolor_uart_rx_empty(struct uart_port *port)
|
||||
{
|
||||
return !!(readb_relaxed(port->membase + UA_STATUS_FIFO) &
|
||||
UA_STATUS_FIFO_RX_EMPTY);
|
||||
}
|
||||
|
||||
static void digicolor_uart_stop_tx(struct uart_port *port)
|
||||
{
|
||||
u8 int_enable = readb_relaxed(port->membase + UA_INT_ENABLE);
|
||||
|
||||
int_enable &= ~UA_INT_TX;
|
||||
writeb_relaxed(int_enable, port->membase + UA_INT_ENABLE);
|
||||
}
|
||||
|
||||
static void digicolor_uart_start_tx(struct uart_port *port)
|
||||
{
|
||||
u8 int_enable = readb_relaxed(port->membase + UA_INT_ENABLE);
|
||||
|
||||
int_enable |= UA_INT_TX;
|
||||
writeb_relaxed(int_enable, port->membase + UA_INT_ENABLE);
|
||||
}
|
||||
|
||||
static void digicolor_uart_stop_rx(struct uart_port *port)
|
||||
{
|
||||
u8 int_enable = readb_relaxed(port->membase + UA_INT_ENABLE);
|
||||
|
||||
int_enable &= ~UA_INT_RX;
|
||||
writeb_relaxed(int_enable, port->membase + UA_INT_ENABLE);
|
||||
}
|
||||
|
||||
static void digicolor_rx_poll(struct work_struct *work)
|
||||
{
|
||||
struct digicolor_port *dp =
|
||||
container_of(to_delayed_work(work),
|
||||
struct digicolor_port, rx_poll_work);
|
||||
|
||||
if (!digicolor_uart_rx_empty(&dp->port))
|
||||
/* force RX interrupt */
|
||||
writeb_relaxed(UA_INT_RX, dp->port.membase + UA_INTFLAG_SET);
|
||||
|
||||
schedule_delayed_work(&dp->rx_poll_work, msecs_to_jiffies(100));
|
||||
}
|
||||
|
||||
static void digicolor_uart_rx(struct uart_port *port)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&port->lock, flags);
|
||||
|
||||
while (1) {
|
||||
u8 status, ch;
|
||||
unsigned int ch_flag;
|
||||
|
||||
if (digicolor_uart_rx_empty(port))
|
||||
break;
|
||||
|
||||
ch = readb_relaxed(port->membase + UA_EMI_REC);
|
||||
status = readb_relaxed(port->membase + UA_STATUS);
|
||||
|
||||
port->icount.rx++;
|
||||
ch_flag = TTY_NORMAL;
|
||||
|
||||
if (status) {
|
||||
if (status & UA_STATUS_PARITY_ERR)
|
||||
port->icount.parity++;
|
||||
else if (status & UA_STATUS_FRAME_ERR)
|
||||
port->icount.frame++;
|
||||
else if (status & UA_STATUS_OVERRUN_ERR)
|
||||
port->icount.overrun++;
|
||||
|
||||
status &= port->read_status_mask;
|
||||
|
||||
if (status & UA_STATUS_PARITY_ERR)
|
||||
ch_flag = TTY_PARITY;
|
||||
else if (status & UA_STATUS_FRAME_ERR)
|
||||
ch_flag = TTY_FRAME;
|
||||
else if (status & UA_STATUS_OVERRUN_ERR)
|
||||
ch_flag = TTY_OVERRUN;
|
||||
}
|
||||
|
||||
if (status & port->ignore_status_mask)
|
||||
continue;
|
||||
|
||||
uart_insert_char(port, status, UA_STATUS_OVERRUN_ERR, ch,
|
||||
ch_flag);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&port->lock, flags);
|
||||
|
||||
tty_flip_buffer_push(&port->state->port);
|
||||
}
|
||||
|
||||
static void digicolor_uart_tx(struct uart_port *port)
|
||||
{
|
||||
struct circ_buf *xmit = &port->state->xmit;
|
||||
unsigned long flags;
|
||||
|
||||
if (digicolor_uart_tx_full(port))
|
||||
return;
|
||||
|
||||
spin_lock_irqsave(&port->lock, flags);
|
||||
|
||||
if (port->x_char) {
|
||||
writeb_relaxed(port->x_char, port->membase + UA_EMI_REC);
|
||||
port->icount.tx++;
|
||||
port->x_char = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
|
||||
digicolor_uart_stop_tx(port);
|
||||
goto out;
|
||||
}
|
||||
|
||||
while (!uart_circ_empty(xmit)) {
|
||||
writeb(xmit->buf[xmit->tail], port->membase + UA_EMI_REC);
|
||||
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
|
||||
port->icount.tx++;
|
||||
|
||||
if (digicolor_uart_tx_full(port))
|
||||
break;
|
||||
}
|
||||
|
||||
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
|
||||
uart_write_wakeup(port);
|
||||
|
||||
out:
|
||||
spin_unlock_irqrestore(&port->lock, flags);
|
||||
}
|
||||
|
||||
static irqreturn_t digicolor_uart_int(int irq, void *dev_id)
|
||||
{
|
||||
struct uart_port *port = dev_id;
|
||||
u8 int_status = readb_relaxed(port->membase + UA_INT_STATUS);
|
||||
|
||||
writeb_relaxed(UA_INT_RX | UA_INT_TX,
|
||||
port->membase + UA_INTFLAG_CLEAR);
|
||||
|
||||
if (int_status & UA_INT_RX)
|
||||
digicolor_uart_rx(port);
|
||||
if (int_status & UA_INT_TX)
|
||||
digicolor_uart_tx(port);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static unsigned int digicolor_uart_tx_empty(struct uart_port *port)
|
||||
{
|
||||
u8 status = readb_relaxed(port->membase + UA_STATUS);
|
||||
|
||||
return (status & UA_STATUS_TX_READY) ? TIOCSER_TEMT : 0;
|
||||
}
|
||||
|
||||
static unsigned int digicolor_uart_get_mctrl(struct uart_port *port)
|
||||
{
|
||||
return TIOCM_CTS;
|
||||
}
|
||||
|
||||
static void digicolor_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
|
||||
{
|
||||
}
|
||||
|
||||
static void digicolor_uart_break_ctl(struct uart_port *port, int state)
|
||||
{
|
||||
}
|
||||
|
||||
static int digicolor_uart_startup(struct uart_port *port)
|
||||
{
|
||||
struct digicolor_port *dp =
|
||||
container_of(port, struct digicolor_port, port);
|
||||
|
||||
writeb_relaxed(UA_ENABLE_ENABLE, port->membase + UA_ENABLE);
|
||||
writeb_relaxed(UA_CONTROL_SOFT_RESET, port->membase + UA_CONTROL);
|
||||
writeb_relaxed(0, port->membase + UA_CONTROL);
|
||||
|
||||
writeb_relaxed(UA_CONFIG_FIFO_RX_FIFO_MODE
|
||||
| UA_CONFIG_FIFO_TX_FIFO_MODE | UA_CONFIG_FIFO_RX_THRESH,
|
||||
port->membase + UA_CONFIG_FIFO);
|
||||
writeb_relaxed(UA_STATUS_FIFO_RX_INT_ALMOST,
|
||||
port->membase + UA_STATUS_FIFO);
|
||||
writeb_relaxed(UA_CONTROL_RX_ENABLE | UA_CONTROL_TX_ENABLE,
|
||||
port->membase + UA_CONTROL);
|
||||
writeb_relaxed(UA_INT_TX | UA_INT_RX,
|
||||
port->membase + UA_INT_ENABLE);
|
||||
|
||||
schedule_delayed_work(&dp->rx_poll_work, msecs_to_jiffies(100));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void digicolor_uart_shutdown(struct uart_port *port)
|
||||
{
|
||||
struct digicolor_port *dp =
|
||||
container_of(port, struct digicolor_port, port);
|
||||
|
||||
writeb_relaxed(0, port->membase + UA_ENABLE);
|
||||
cancel_delayed_work_sync(&dp->rx_poll_work);
|
||||
}
|
||||
|
||||
static void digicolor_uart_set_termios(struct uart_port *port,
|
||||
struct ktermios *termios,
|
||||
struct ktermios *old)
|
||||
{
|
||||
unsigned int baud, divisor;
|
||||
u8 config = 0;
|
||||
unsigned long flags;
|
||||
|
||||
/* Mask termios capabilities we don't support */
|
||||
termios->c_cflag &= ~CMSPAR;
|
||||
termios->c_iflag &= ~(BRKINT | IGNBRK);
|
||||
|
||||
/* Limit baud rates so that we don't need the fractional divider */
|
||||
baud = uart_get_baud_rate(port, termios, old,
|
||||
port->uartclk / (0x10000*16),
|
||||
port->uartclk / 256);
|
||||
divisor = uart_get_divisor(port, baud) - 1;
|
||||
|
||||
switch (termios->c_cflag & CSIZE) {
|
||||
case CS7:
|
||||
break;
|
||||
case CS8:
|
||||
default:
|
||||
config |= UA_CONFIG_CHAR_LEN;
|
||||
break;
|
||||
}
|
||||
|
||||
if (termios->c_cflag & CSTOPB)
|
||||
config |= UA_CONFIG_STOP_BITS;
|
||||
|
||||
if (termios->c_cflag & PARENB) {
|
||||
config |= UA_CONFIG_PARITY;
|
||||
if (termios->c_cflag & PARODD)
|
||||
config |= UA_CONFIG_ODD_PARITY;
|
||||
}
|
||||
|
||||
/* Set read status mask */
|
||||
port->read_status_mask = UA_STATUS_OVERRUN_ERR;
|
||||
if (termios->c_iflag & INPCK)
|
||||
port->read_status_mask |= UA_STATUS_PARITY_ERR
|
||||
| UA_STATUS_FRAME_ERR;
|
||||
|
||||
/* Set status ignore mask */
|
||||
port->ignore_status_mask = 0;
|
||||
if (!(termios->c_cflag & CREAD))
|
||||
port->ignore_status_mask |= UA_STATUS_OVERRUN_ERR
|
||||
| UA_STATUS_PARITY_ERR | UA_STATUS_FRAME_ERR;
|
||||
|
||||
spin_lock_irqsave(&port->lock, flags);
|
||||
|
||||
uart_update_timeout(port, termios->c_cflag, baud);
|
||||
|
||||
writeb_relaxed(config, port->membase + UA_CONFIG);
|
||||
writeb_relaxed(divisor & 0xff, port->membase + UA_HBAUD_LO);
|
||||
writeb_relaxed(divisor >> 8, port->membase + UA_HBAUD_HI);
|
||||
|
||||
spin_unlock_irqrestore(&port->lock, flags);
|
||||
}
|
||||
|
||||
static const char *digicolor_uart_type(struct uart_port *port)
|
||||
{
|
||||
return (port->type == PORT_DIGICOLOR) ? "DIGICOLOR USART" : NULL;
|
||||
}
|
||||
|
||||
static void digicolor_uart_config_port(struct uart_port *port, int flags)
|
||||
{
|
||||
if (flags & UART_CONFIG_TYPE)
|
||||
port->type = PORT_DIGICOLOR;
|
||||
}
|
||||
|
||||
static void digicolor_uart_release_port(struct uart_port *port)
|
||||
{
|
||||
}
|
||||
|
||||
static int digicolor_uart_request_port(struct uart_port *port)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct uart_ops digicolor_uart_ops = {
|
||||
.tx_empty = digicolor_uart_tx_empty,
|
||||
.set_mctrl = digicolor_uart_set_mctrl,
|
||||
.get_mctrl = digicolor_uart_get_mctrl,
|
||||
.stop_tx = digicolor_uart_stop_tx,
|
||||
.start_tx = digicolor_uart_start_tx,
|
||||
.stop_rx = digicolor_uart_stop_rx,
|
||||
.break_ctl = digicolor_uart_break_ctl,
|
||||
.startup = digicolor_uart_startup,
|
||||
.shutdown = digicolor_uart_shutdown,
|
||||
.set_termios = digicolor_uart_set_termios,
|
||||
.type = digicolor_uart_type,
|
||||
.config_port = digicolor_uart_config_port,
|
||||
.release_port = digicolor_uart_release_port,
|
||||
.request_port = digicolor_uart_request_port,
|
||||
};
|
||||
|
||||
static void digicolor_uart_console_putchar(struct uart_port *port, int ch)
|
||||
{
|
||||
while (digicolor_uart_tx_full(port))
|
||||
cpu_relax();
|
||||
|
||||
writeb_relaxed(ch, port->membase + UA_EMI_REC);
|
||||
}
|
||||
|
||||
static void digicolor_uart_console_write(struct console *co, const char *c,
|
||||
unsigned n)
|
||||
{
|
||||
struct uart_port *port = digicolor_ports[co->index];
|
||||
u8 status;
|
||||
unsigned long flags;
|
||||
int locked = 1;
|
||||
|
||||
if (oops_in_progress)
|
||||
locked = spin_trylock_irqsave(&port->lock, flags);
|
||||
else
|
||||
spin_lock_irqsave(&port->lock, flags);
|
||||
|
||||
uart_console_write(port, c, n, digicolor_uart_console_putchar);
|
||||
|
||||
if (locked)
|
||||
spin_unlock_irqrestore(&port->lock, flags);
|
||||
|
||||
/* Wait for transmitter to become empty */
|
||||
do {
|
||||
status = readb_relaxed(port->membase + UA_STATUS);
|
||||
} while ((status & UA_STATUS_TX_READY) == 0);
|
||||
}
|
||||
|
||||
static int digicolor_uart_console_setup(struct console *co, char *options)
|
||||
{
|
||||
int baud = 115200, bits = 8, parity = 'n', flow = 'n';
|
||||
struct uart_port *port;
|
||||
|
||||
if (co->index < 0 || co->index >= DIGICOLOR_USART_NR)
|
||||
return -EINVAL;
|
||||
|
||||
port = digicolor_ports[co->index];
|
||||
if (!port)
|
||||
return -ENODEV;
|
||||
|
||||
if (options)
|
||||
uart_parse_options(options, &baud, &parity, &bits, &flow);
|
||||
|
||||
return uart_set_options(port, co, baud, parity, bits, flow);
|
||||
}
|
||||
|
||||
static struct console digicolor_console = {
|
||||
.name = "ttyS",
|
||||
.device = uart_console_device,
|
||||
.write = digicolor_uart_console_write,
|
||||
.setup = digicolor_uart_console_setup,
|
||||
.flags = CON_PRINTBUFFER,
|
||||
.index = -1,
|
||||
};
|
||||
|
||||
static struct uart_driver digicolor_uart = {
|
||||
.driver_name = "digicolor-usart",
|
||||
.dev_name = "ttyS",
|
||||
.nr = DIGICOLOR_USART_NR,
|
||||
};
|
||||
|
||||
static int digicolor_uart_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
int ret, index;
|
||||
struct digicolor_port *dp;
|
||||
struct resource *res;
|
||||
struct clk *uart_clk;
|
||||
|
||||
if (!np) {
|
||||
dev_err(&pdev->dev, "Missing device tree node\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
index = of_alias_get_id(np, "serial");
|
||||
if (index < 0 || index >= DIGICOLOR_USART_NR)
|
||||
return -EINVAL;
|
||||
|
||||
dp = devm_kzalloc(&pdev->dev, sizeof(*dp), GFP_KERNEL);
|
||||
if (!dp)
|
||||
return -ENOMEM;
|
||||
|
||||
uart_clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(uart_clk))
|
||||
return PTR_ERR(uart_clk);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
dp->port.mapbase = res->start;
|
||||
dp->port.membase = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(dp->port.membase))
|
||||
return PTR_ERR(dp->port.membase);
|
||||
|
||||
dp->port.irq = platform_get_irq(pdev, 0);
|
||||
if (IS_ERR_VALUE(dp->port.irq))
|
||||
return dp->port.irq;
|
||||
|
||||
dp->port.iotype = UPIO_MEM;
|
||||
dp->port.uartclk = clk_get_rate(uart_clk);
|
||||
dp->port.fifosize = 16;
|
||||
dp->port.dev = &pdev->dev;
|
||||
dp->port.ops = &digicolor_uart_ops;
|
||||
dp->port.line = index;
|
||||
dp->port.type = PORT_DIGICOLOR;
|
||||
spin_lock_init(&dp->port.lock);
|
||||
|
||||
digicolor_ports[index] = &dp->port;
|
||||
platform_set_drvdata(pdev, &dp->port);
|
||||
|
||||
INIT_DELAYED_WORK(&dp->rx_poll_work, digicolor_rx_poll);
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, dp->port.irq, digicolor_uart_int, 0,
|
||||
dev_name(&pdev->dev), &dp->port);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return uart_add_one_port(&digicolor_uart, &dp->port);
|
||||
}
|
||||
|
||||
static int digicolor_uart_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct uart_port *port = platform_get_drvdata(pdev);
|
||||
|
||||
uart_remove_one_port(&digicolor_uart, port);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id digicolor_uart_dt_ids[] = {
|
||||
{ .compatible = "cnxt,cx92755-usart", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, digicolor_uart_dt_ids);
|
||||
|
||||
static struct platform_driver digicolor_uart_platform = {
|
||||
.driver = {
|
||||
.name = "digicolor-usart",
|
||||
.of_match_table = of_match_ptr(digicolor_uart_dt_ids),
|
||||
},
|
||||
.probe = digicolor_uart_probe,
|
||||
.remove = digicolor_uart_remove,
|
||||
};
|
||||
|
||||
static int __init digicolor_uart_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (IS_ENABLED(CONFIG_SERIAL_CONEXANT_DIGICOLOR_CONSOLE)) {
|
||||
digicolor_uart.cons = &digicolor_console;
|
||||
digicolor_console.data = &digicolor_uart;
|
||||
}
|
||||
|
||||
ret = uart_register_driver(&digicolor_uart);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return platform_driver_register(&digicolor_uart_platform);
|
||||
}
|
||||
module_init(digicolor_uart_init);
|
||||
|
||||
static void __exit digicolor_uart_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&digicolor_uart_platform);
|
||||
uart_unregister_driver(&digicolor_uart);
|
||||
}
|
||||
module_exit(digicolor_uart_exit);
|
||||
|
||||
MODULE_AUTHOR("Baruch Siach <baruch@tkos.co.il>");
|
||||
MODULE_DESCRIPTION("Conexant Digicolor USART serial driver");
|
||||
MODULE_LICENSE("GPL");
|
996
drivers/tty/serial/etraxfs-uart.c
Normal file
996
drivers/tty/serial/etraxfs-uart.c
Normal file
@ -0,0 +1,996 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/console.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/serial_core.h>
|
||||
#include <linux/tty_flip.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <hwregs/ser_defs.h>
|
||||
|
||||
#define DRV_NAME "etraxfs-uart"
|
||||
#define UART_NR CONFIG_ETRAX_SERIAL_PORTS
|
||||
|
||||
#define MODIFY_REG(instance, reg, var) \
|
||||
do { \
|
||||
if (REG_RD_INT(ser, instance, reg) != \
|
||||
REG_TYPE_CONV(int, reg_ser_##reg, var)) \
|
||||
REG_WR(ser, instance, reg, var); \
|
||||
} while (0)
|
||||
|
||||
struct uart_cris_port {
|
||||
struct uart_port port;
|
||||
|
||||
int initialized;
|
||||
int irq;
|
||||
|
||||
void __iomem *regi_ser;
|
||||
|
||||
struct gpio_desc *dtr_pin;
|
||||
struct gpio_desc *dsr_pin;
|
||||
struct gpio_desc *ri_pin;
|
||||
struct gpio_desc *cd_pin;
|
||||
|
||||
int write_ongoing;
|
||||
};
|
||||
|
||||
static struct uart_driver etraxfs_uart_driver;
|
||||
static struct uart_port *console_port;
|
||||
static int console_baud = 115200;
|
||||
static struct uart_cris_port *etraxfs_uart_ports[UART_NR];
|
||||
|
||||
static void cris_serial_port_init(struct uart_port *port, int line);
|
||||
static void etraxfs_uart_stop_rx(struct uart_port *port);
|
||||
static inline void etraxfs_uart_start_tx_bottom(struct uart_port *port);
|
||||
|
||||
#ifdef CONFIG_SERIAL_ETRAXFS_CONSOLE
|
||||
static void
|
||||
cris_console_write(struct console *co, const char *s, unsigned int count)
|
||||
{
|
||||
struct uart_cris_port *up;
|
||||
int i;
|
||||
reg_ser_r_stat_din stat;
|
||||
reg_ser_rw_tr_dma_en tr_dma_en, old;
|
||||
|
||||
up = etraxfs_uart_ports[co->index];
|
||||
|
||||
if (!up)
|
||||
return;
|
||||
|
||||
/* Switch to manual mode. */
|
||||
tr_dma_en = old = REG_RD(ser, up->regi_ser, rw_tr_dma_en);
|
||||
if (tr_dma_en.en == regk_ser_yes) {
|
||||
tr_dma_en.en = regk_ser_no;
|
||||
REG_WR(ser, up->regi_ser, rw_tr_dma_en, tr_dma_en);
|
||||
}
|
||||
|
||||
/* Send data. */
|
||||
for (i = 0; i < count; i++) {
|
||||
/* LF -> CRLF */
|
||||
if (s[i] == '\n') {
|
||||
do {
|
||||
stat = REG_RD(ser, up->regi_ser, r_stat_din);
|
||||
} while (!stat.tr_rdy);
|
||||
REG_WR_INT(ser, up->regi_ser, rw_dout, '\r');
|
||||
}
|
||||
/* Wait until transmitter is ready and send. */
|
||||
do {
|
||||
stat = REG_RD(ser, up->regi_ser, r_stat_din);
|
||||
} while (!stat.tr_rdy);
|
||||
REG_WR_INT(ser, up->regi_ser, rw_dout, s[i]);
|
||||
}
|
||||
|
||||
/* Restore mode. */
|
||||
if (tr_dma_en.en != old.en)
|
||||
REG_WR(ser, up->regi_ser, rw_tr_dma_en, old);
|
||||
}
|
||||
|
||||
static int __init
|
||||
cris_console_setup(struct console *co, char *options)
|
||||
{
|
||||
struct uart_port *port;
|
||||
int baud = 115200;
|
||||
int bits = 8;
|
||||
int parity = 'n';
|
||||
int flow = 'n';
|
||||
|
||||
if (co->index < 0 || co->index >= UART_NR)
|
||||
co->index = 0;
|
||||
port = &etraxfs_uart_ports[co->index]->port;
|
||||
console_port = port;
|
||||
|
||||
co->flags |= CON_CONSDEV;
|
||||
|
||||
if (options)
|
||||
uart_parse_options(options, &baud, &parity, &bits, &flow);
|
||||
console_baud = baud;
|
||||
cris_serial_port_init(port, co->index);
|
||||
uart_set_options(port, co, baud, parity, bits, flow);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct tty_driver *cris_console_device(struct console *co, int *index)
|
||||
{
|
||||
struct uart_driver *p = co->data;
|
||||
*index = co->index;
|
||||
return p->tty_driver;
|
||||
}
|
||||
|
||||
static struct console cris_console = {
|
||||
.name = "ttyS",
|
||||
.write = cris_console_write,
|
||||
.device = cris_console_device,
|
||||
.setup = cris_console_setup,
|
||||
.flags = CON_PRINTBUFFER,
|
||||
.index = -1,
|
||||
.data = &etraxfs_uart_driver,
|
||||
};
|
||||
#endif /* CONFIG_SERIAL_ETRAXFS_CONSOLE */
|
||||
|
||||
static struct uart_driver etraxfs_uart_driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.driver_name = "serial",
|
||||
.dev_name = "ttyS",
|
||||
.major = TTY_MAJOR,
|
||||
.minor = 64,
|
||||
.nr = UART_NR,
|
||||
#ifdef CONFIG_SERIAL_ETRAXFS_CONSOLE
|
||||
.cons = &cris_console,
|
||||
#endif /* CONFIG_SERIAL_ETRAXFS_CONSOLE */
|
||||
};
|
||||
|
||||
static inline int crisv32_serial_get_rts(struct uart_cris_port *up)
|
||||
{
|
||||
void __iomem *regi_ser = up->regi_ser;
|
||||
/*
|
||||
* Return what the user has controlled rts to or
|
||||
* what the pin is? (if auto_rts is used it differs during tx)
|
||||
*/
|
||||
reg_ser_r_stat_din rstat = REG_RD(ser, regi_ser, r_stat_din);
|
||||
|
||||
return !(rstat.rts_n == regk_ser_active);
|
||||
}
|
||||
|
||||
/*
|
||||
* A set = 0 means 3.3V on the pin, bitvalue: 0=active, 1=inactive
|
||||
* 0=0V , 1=3.3V
|
||||
*/
|
||||
static inline void crisv32_serial_set_rts(struct uart_cris_port *up,
|
||||
int set, int force)
|
||||
{
|
||||
void __iomem *regi_ser = up->regi_ser;
|
||||
|
||||
unsigned long flags;
|
||||
reg_ser_rw_rec_ctrl rec_ctrl;
|
||||
|
||||
local_irq_save(flags);
|
||||
rec_ctrl = REG_RD(ser, regi_ser, rw_rec_ctrl);
|
||||
|
||||
if (set)
|
||||
rec_ctrl.rts_n = regk_ser_active;
|
||||
else
|
||||
rec_ctrl.rts_n = regk_ser_inactive;
|
||||
REG_WR(ser, regi_ser, rw_rec_ctrl, rec_ctrl);
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
static inline int crisv32_serial_get_cts(struct uart_cris_port *up)
|
||||
{
|
||||
void __iomem *regi_ser = up->regi_ser;
|
||||
reg_ser_r_stat_din rstat = REG_RD(ser, regi_ser, r_stat_din);
|
||||
|
||||
return (rstat.cts_n == regk_ser_active);
|
||||
}
|
||||
|
||||
/*
|
||||
* Send a single character for XON/XOFF purposes. We do it in this separate
|
||||
* function instead of the alternative support port.x_char, in the ...start_tx
|
||||
* function, so we don't mix up this case with possibly enabling transmission
|
||||
* of queued-up data (in case that's disabled after *receiving* an XOFF or
|
||||
* negative CTS). This function is used for both DMA and non-DMA case; see HW
|
||||
* docs specifically blessing sending characters manually when DMA for
|
||||
* transmission is enabled and running. We may be asked to transmit despite
|
||||
* the transmitter being disabled by a ..._stop_tx call so we need to enable
|
||||
* it temporarily but restore the state afterwards.
|
||||
*/
|
||||
static void etraxfs_uart_send_xchar(struct uart_port *port, char ch)
|
||||
{
|
||||
struct uart_cris_port *up = (struct uart_cris_port *)port;
|
||||
reg_ser_rw_dout dout = { .data = ch };
|
||||
reg_ser_rw_ack_intr ack_intr = { .tr_rdy = regk_ser_yes };
|
||||
reg_ser_r_stat_din rstat;
|
||||
reg_ser_rw_tr_ctrl prev_tr_ctrl, tr_ctrl;
|
||||
void __iomem *regi_ser = up->regi_ser;
|
||||
unsigned long flags;
|
||||
|
||||
/*
|
||||
* Wait for tr_rdy in case a character is already being output. Make
|
||||
* sure we have integrity between the register reads and the writes
|
||||
* below, but don't busy-wait with interrupts off and the port lock
|
||||
* taken.
|
||||
*/
|
||||
spin_lock_irqsave(&port->lock, flags);
|
||||
do {
|
||||
spin_unlock_irqrestore(&port->lock, flags);
|
||||
spin_lock_irqsave(&port->lock, flags);
|
||||
prev_tr_ctrl = tr_ctrl = REG_RD(ser, regi_ser, rw_tr_ctrl);
|
||||
rstat = REG_RD(ser, regi_ser, r_stat_din);
|
||||
} while (!rstat.tr_rdy);
|
||||
|
||||
/*
|
||||
* Ack an interrupt if one was just issued for the previous character
|
||||
* that was output. This is required for non-DMA as the interrupt is
|
||||
* used as the only indicator that the transmitter is ready and it
|
||||
* isn't while this x_char is being transmitted.
|
||||
*/
|
||||
REG_WR(ser, regi_ser, rw_ack_intr, ack_intr);
|
||||
|
||||
/* Enable the transmitter in case it was disabled. */
|
||||
tr_ctrl.stop = 0;
|
||||
REG_WR(ser, regi_ser, rw_tr_ctrl, tr_ctrl);
|
||||
|
||||
/*
|
||||
* Finally, send the blessed character; nothing should stop it now,
|
||||
* except for an xoff-detected state, which we'll handle below.
|
||||
*/
|
||||
REG_WR(ser, regi_ser, rw_dout, dout);
|
||||
up->port.icount.tx++;
|
||||
|
||||
/* There might be an xoff state to clear. */
|
||||
rstat = REG_RD(ser, up->regi_ser, r_stat_din);
|
||||
|
||||
/*
|
||||
* Clear any xoff state that *may* have been there to
|
||||
* inhibit transmission of the character.
|
||||
*/
|
||||
if (rstat.xoff_detect) {
|
||||
reg_ser_rw_xoff_clr xoff_clr = { .clr = 1 };
|
||||
reg_ser_rw_tr_dma_en tr_dma_en;
|
||||
|
||||
REG_WR(ser, regi_ser, rw_xoff_clr, xoff_clr);
|
||||
tr_dma_en = REG_RD(ser, regi_ser, rw_tr_dma_en);
|
||||
|
||||
/*
|
||||
* If we had an xoff state but cleared it, instead sneak in a
|
||||
* disabled state for the transmitter, after the character we
|
||||
* sent. Thus we keep the port disabled, just as if the xoff
|
||||
* state was still in effect (or actually, as if stop_tx had
|
||||
* been called, as we stop DMA too).
|
||||
*/
|
||||
prev_tr_ctrl.stop = 1;
|
||||
|
||||
tr_dma_en.en = 0;
|
||||
REG_WR(ser, regi_ser, rw_tr_dma_en, tr_dma_en);
|
||||
}
|
||||
|
||||
/* Restore "previous" enabled/disabled state of the transmitter. */
|
||||
REG_WR(ser, regi_ser, rw_tr_ctrl, prev_tr_ctrl);
|
||||
|
||||
spin_unlock_irqrestore(&port->lock, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* Do not spin_lock_irqsave or disable interrupts by other means here; it's
|
||||
* already done by the caller.
|
||||
*/
|
||||
static void etraxfs_uart_start_tx(struct uart_port *port)
|
||||
{
|
||||
struct uart_cris_port *up = (struct uart_cris_port *)port;
|
||||
|
||||
/* we have already done below if a write is ongoing */
|
||||
if (up->write_ongoing)
|
||||
return;
|
||||
|
||||
/* Signal that write is ongoing */
|
||||
up->write_ongoing = 1;
|
||||
|
||||
etraxfs_uart_start_tx_bottom(port);
|
||||
}
|
||||
|
||||
static inline void etraxfs_uart_start_tx_bottom(struct uart_port *port)
|
||||
{
|
||||
struct uart_cris_port *up = (struct uart_cris_port *)port;
|
||||
void __iomem *regi_ser = up->regi_ser;
|
||||
reg_ser_rw_tr_ctrl tr_ctrl;
|
||||
reg_ser_rw_intr_mask intr_mask;
|
||||
|
||||
tr_ctrl = REG_RD(ser, regi_ser, rw_tr_ctrl);
|
||||
tr_ctrl.stop = regk_ser_no;
|
||||
REG_WR(ser, regi_ser, rw_tr_ctrl, tr_ctrl);
|
||||
intr_mask = REG_RD(ser, regi_ser, rw_intr_mask);
|
||||
intr_mask.tr_rdy = regk_ser_yes;
|
||||
REG_WR(ser, regi_ser, rw_intr_mask, intr_mask);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function handles both the DMA and non-DMA case by ordering the
|
||||
* transmitter to stop of after the current character. We don't need to wait
|
||||
* for any such character to be completely transmitted; we do that where it
|
||||
* matters, like in etraxfs_uart_set_termios. Don't busy-wait here; see
|
||||
* Documentation/serial/driver: this function is called within
|
||||
* spin_lock_irq{,save} and thus separate ones would be disastrous (when SMP).
|
||||
* There's no documented need to set the txd pin to any particular value;
|
||||
* break setting is controlled solely by etraxfs_uart_break_ctl.
|
||||
*/
|
||||
static void etraxfs_uart_stop_tx(struct uart_port *port)
|
||||
{
|
||||
struct uart_cris_port *up = (struct uart_cris_port *)port;
|
||||
void __iomem *regi_ser = up->regi_ser;
|
||||
reg_ser_rw_tr_ctrl tr_ctrl;
|
||||
reg_ser_rw_intr_mask intr_mask;
|
||||
reg_ser_rw_tr_dma_en tr_dma_en = {0};
|
||||
reg_ser_rw_xoff_clr xoff_clr = {0};
|
||||
|
||||
/*
|
||||
* For the non-DMA case, we'd get a tr_rdy interrupt that we're not
|
||||
* interested in as we're not transmitting any characters. For the
|
||||
* DMA case, that interrupt is already turned off, but no reason to
|
||||
* waste code on conditionals here.
|
||||
*/
|
||||
intr_mask = REG_RD(ser, regi_ser, rw_intr_mask);
|
||||
intr_mask.tr_rdy = regk_ser_no;
|
||||
REG_WR(ser, regi_ser, rw_intr_mask, intr_mask);
|
||||
|
||||
tr_ctrl = REG_RD(ser, regi_ser, rw_tr_ctrl);
|
||||
tr_ctrl.stop = 1;
|
||||
REG_WR(ser, regi_ser, rw_tr_ctrl, tr_ctrl);
|
||||
|
||||
/*
|
||||
* Always clear possible hardware xoff-detected state here, no need to
|
||||
* unnecessary consider mctrl settings and when they change. We clear
|
||||
* it here rather than in start_tx: both functions are called as the
|
||||
* effect of XOFF processing, but start_tx is also called when upper
|
||||
* levels tell the driver that there are more characters to send, so
|
||||
* avoid adding code there.
|
||||
*/
|
||||
xoff_clr.clr = 1;
|
||||
REG_WR(ser, regi_ser, rw_xoff_clr, xoff_clr);
|
||||
|
||||
/*
|
||||
* Disable transmitter DMA, so that if we're in XON/XOFF, we can send
|
||||
* those single characters without also giving go-ahead for queued up
|
||||
* DMA data.
|
||||
*/
|
||||
tr_dma_en.en = 0;
|
||||
REG_WR(ser, regi_ser, rw_tr_dma_en, tr_dma_en);
|
||||
|
||||
/*
|
||||
* Make sure that write_ongoing is reset when stopping tx.
|
||||
*/
|
||||
up->write_ongoing = 0;
|
||||
}
|
||||
|
||||
static void etraxfs_uart_stop_rx(struct uart_port *port)
|
||||
{
|
||||
struct uart_cris_port *up = (struct uart_cris_port *)port;
|
||||
void __iomem *regi_ser = up->regi_ser;
|
||||
reg_ser_rw_rec_ctrl rec_ctrl = REG_RD(ser, regi_ser, rw_rec_ctrl);
|
||||
|
||||
rec_ctrl.en = regk_ser_no;
|
||||
REG_WR(ser, regi_ser, rw_rec_ctrl, rec_ctrl);
|
||||
}
|
||||
|
||||
static void etraxfs_uart_enable_ms(struct uart_port *port)
|
||||
{
|
||||
}
|
||||
|
||||
static void check_modem_status(struct uart_cris_port *up)
|
||||
{
|
||||
}
|
||||
|
||||
static unsigned int etraxfs_uart_tx_empty(struct uart_port *port)
|
||||
{
|
||||
struct uart_cris_port *up = (struct uart_cris_port *)port;
|
||||
unsigned long flags;
|
||||
unsigned int ret;
|
||||
reg_ser_r_stat_din rstat = {0};
|
||||
|
||||
spin_lock_irqsave(&up->port.lock, flags);
|
||||
|
||||
rstat = REG_RD(ser, up->regi_ser, r_stat_din);
|
||||
ret = rstat.tr_empty ? TIOCSER_TEMT : 0;
|
||||
|
||||
spin_unlock_irqrestore(&up->port.lock, flags);
|
||||
return ret;
|
||||
}
|
||||
static unsigned int etraxfs_uart_get_mctrl(struct uart_port *port)
|
||||
{
|
||||
struct uart_cris_port *up = (struct uart_cris_port *)port;
|
||||
unsigned int ret;
|
||||
|
||||
ret = 0;
|
||||
if (crisv32_serial_get_rts(up))
|
||||
ret |= TIOCM_RTS;
|
||||
/* DTR is active low */
|
||||
if (up->dtr_pin && !gpiod_get_raw_value(up->dtr_pin))
|
||||
ret |= TIOCM_DTR;
|
||||
/* CD is active low */
|
||||
if (up->cd_pin && !gpiod_get_raw_value(up->cd_pin))
|
||||
ret |= TIOCM_CD;
|
||||
/* RI is active low */
|
||||
if (up->ri_pin && !gpiod_get_raw_value(up->ri_pin))
|
||||
ret |= TIOCM_RI;
|
||||
/* DSR is active low */
|
||||
if (up->dsr_pin && !gpiod_get_raw_value(up->dsr_pin))
|
||||
ret |= TIOCM_DSR;
|
||||
if (crisv32_serial_get_cts(up))
|
||||
ret |= TIOCM_CTS;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void etraxfs_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
|
||||
{
|
||||
struct uart_cris_port *up = (struct uart_cris_port *)port;
|
||||
|
||||
crisv32_serial_set_rts(up, mctrl & TIOCM_RTS ? 1 : 0, 0);
|
||||
/* DTR is active low */
|
||||
if (up->dtr_pin)
|
||||
gpiod_set_raw_value(up->dtr_pin, mctrl & TIOCM_DTR ? 0 : 1);
|
||||
/* RI is active low */
|
||||
if (up->ri_pin)
|
||||
gpiod_set_raw_value(up->ri_pin, mctrl & TIOCM_RNG ? 0 : 1);
|
||||
/* CD is active low */
|
||||
if (up->cd_pin)
|
||||
gpiod_set_raw_value(up->cd_pin, mctrl & TIOCM_CD ? 0 : 1);
|
||||
}
|
||||
|
||||
static void etraxfs_uart_break_ctl(struct uart_port *port, int break_state)
|
||||
{
|
||||
struct uart_cris_port *up = (struct uart_cris_port *)port;
|
||||
unsigned long flags;
|
||||
reg_ser_rw_tr_ctrl tr_ctrl;
|
||||
reg_ser_rw_tr_dma_en tr_dma_en;
|
||||
reg_ser_rw_intr_mask intr_mask;
|
||||
|
||||
spin_lock_irqsave(&up->port.lock, flags);
|
||||
tr_ctrl = REG_RD(ser, up->regi_ser, rw_tr_ctrl);
|
||||
tr_dma_en = REG_RD(ser, up->regi_ser, rw_tr_dma_en);
|
||||
intr_mask = REG_RD(ser, up->regi_ser, rw_intr_mask);
|
||||
|
||||
if (break_state != 0) { /* Send break */
|
||||
/*
|
||||
* We need to disable DMA (if used) or tr_rdy interrupts if no
|
||||
* DMA. No need to make this conditional on use of DMA;
|
||||
* disabling will be a no-op for the other mode.
|
||||
*/
|
||||
intr_mask.tr_rdy = regk_ser_no;
|
||||
tr_dma_en.en = 0;
|
||||
|
||||
/*
|
||||
* Stop transmission and set the txd pin to 0 after the
|
||||
* current character. The txd setting will take effect after
|
||||
* any current transmission has completed.
|
||||
*/
|
||||
tr_ctrl.stop = 1;
|
||||
tr_ctrl.txd = 0;
|
||||
} else {
|
||||
/* Re-enable the serial interrupt. */
|
||||
intr_mask.tr_rdy = regk_ser_yes;
|
||||
|
||||
tr_ctrl.stop = 0;
|
||||
tr_ctrl.txd = 1;
|
||||
}
|
||||
REG_WR(ser, up->regi_ser, rw_tr_ctrl, tr_ctrl);
|
||||
REG_WR(ser, up->regi_ser, rw_tr_dma_en, tr_dma_en);
|
||||
REG_WR(ser, up->regi_ser, rw_intr_mask, intr_mask);
|
||||
|
||||
spin_unlock_irqrestore(&up->port.lock, flags);
|
||||
}
|
||||
|
||||
static void
|
||||
transmit_chars_no_dma(struct uart_cris_port *up)
|
||||
{
|
||||
int max_count;
|
||||
struct circ_buf *xmit = &up->port.state->xmit;
|
||||
|
||||
void __iomem *regi_ser = up->regi_ser;
|
||||
reg_ser_r_stat_din rstat;
|
||||
reg_ser_rw_ack_intr ack_intr = { .tr_rdy = regk_ser_yes };
|
||||
|
||||
if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) {
|
||||
/* No more to send, so disable the interrupt. */
|
||||
reg_ser_rw_intr_mask intr_mask;
|
||||
|
||||
intr_mask = REG_RD(ser, regi_ser, rw_intr_mask);
|
||||
intr_mask.tr_rdy = 0;
|
||||
intr_mask.tr_empty = 0;
|
||||
REG_WR(ser, regi_ser, rw_intr_mask, intr_mask);
|
||||
up->write_ongoing = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
/* If the serport is fast, we send up to max_count bytes before
|
||||
exiting the loop. */
|
||||
max_count = 64;
|
||||
do {
|
||||
reg_ser_rw_dout dout = { .data = xmit->buf[xmit->tail] };
|
||||
|
||||
REG_WR(ser, regi_ser, rw_dout, dout);
|
||||
REG_WR(ser, regi_ser, rw_ack_intr, ack_intr);
|
||||
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE-1);
|
||||
up->port.icount.tx++;
|
||||
if (xmit->head == xmit->tail)
|
||||
break;
|
||||
rstat = REG_RD(ser, regi_ser, r_stat_din);
|
||||
} while ((--max_count > 0) && rstat.tr_rdy);
|
||||
|
||||
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
|
||||
uart_write_wakeup(&up->port);
|
||||
}
|
||||
|
||||
static void receive_chars_no_dma(struct uart_cris_port *up)
|
||||
{
|
||||
reg_ser_rs_stat_din stat_din;
|
||||
reg_ser_r_stat_din rstat;
|
||||
struct tty_port *port;
|
||||
struct uart_icount *icount;
|
||||
int max_count = 16;
|
||||
char flag;
|
||||
reg_ser_rw_ack_intr ack_intr = { 0 };
|
||||
|
||||
rstat = REG_RD(ser, up->regi_ser, r_stat_din);
|
||||
icount = &up->port.icount;
|
||||
port = &up->port.state->port;
|
||||
|
||||
do {
|
||||
stat_din = REG_RD(ser, up->regi_ser, rs_stat_din);
|
||||
|
||||
flag = TTY_NORMAL;
|
||||
ack_intr.dav = 1;
|
||||
REG_WR(ser, up->regi_ser, rw_ack_intr, ack_intr);
|
||||
icount->rx++;
|
||||
|
||||
if (stat_din.framing_err | stat_din.par_err | stat_din.orun) {
|
||||
if (stat_din.data == 0x00 &&
|
||||
stat_din.framing_err) {
|
||||
/* Most likely a break. */
|
||||
flag = TTY_BREAK;
|
||||
icount->brk++;
|
||||
} else if (stat_din.par_err) {
|
||||
flag = TTY_PARITY;
|
||||
icount->parity++;
|
||||
} else if (stat_din.orun) {
|
||||
flag = TTY_OVERRUN;
|
||||
icount->overrun++;
|
||||
} else if (stat_din.framing_err) {
|
||||
flag = TTY_FRAME;
|
||||
icount->frame++;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If this becomes important, we probably *could* handle this
|
||||
* gracefully by keeping track of the unhandled character.
|
||||
*/
|
||||
if (!tty_insert_flip_char(port, stat_din.data, flag))
|
||||
panic("%s: No tty buffer space", __func__);
|
||||
rstat = REG_RD(ser, up->regi_ser, r_stat_din);
|
||||
} while (rstat.dav && (max_count-- > 0));
|
||||
spin_unlock(&up->port.lock);
|
||||
tty_flip_buffer_push(port);
|
||||
spin_lock(&up->port.lock);
|
||||
}
|
||||
|
||||
static irqreturn_t
|
||||
ser_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct uart_cris_port *up = (struct uart_cris_port *)dev_id;
|
||||
void __iomem *regi_ser;
|
||||
int handled = 0;
|
||||
|
||||
spin_lock(&up->port.lock);
|
||||
|
||||
regi_ser = up->regi_ser;
|
||||
|
||||
if (regi_ser) {
|
||||
reg_ser_r_masked_intr masked_intr;
|
||||
|
||||
masked_intr = REG_RD(ser, regi_ser, r_masked_intr);
|
||||
/*
|
||||
* Check what interrupts are active before taking
|
||||
* actions. If DMA is used the interrupt shouldn't
|
||||
* be enabled.
|
||||
*/
|
||||
if (masked_intr.dav) {
|
||||
receive_chars_no_dma(up);
|
||||
handled = 1;
|
||||
}
|
||||
check_modem_status(up);
|
||||
|
||||
if (masked_intr.tr_rdy) {
|
||||
transmit_chars_no_dma(up);
|
||||
handled = 1;
|
||||
}
|
||||
}
|
||||
spin_unlock(&up->port.lock);
|
||||
return IRQ_RETVAL(handled);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_CONSOLE_POLL
|
||||
static int etraxfs_uart_get_poll_char(struct uart_port *port)
|
||||
{
|
||||
reg_ser_rs_stat_din stat;
|
||||
reg_ser_rw_ack_intr ack_intr = { 0 };
|
||||
struct uart_cris_port *up = (struct uart_cris_port *)port;
|
||||
|
||||
do {
|
||||
stat = REG_RD(ser, up->regi_ser, rs_stat_din);
|
||||
} while (!stat.dav);
|
||||
|
||||
/* Ack the data_avail interrupt. */
|
||||
ack_intr.dav = 1;
|
||||
REG_WR(ser, up->regi_ser, rw_ack_intr, ack_intr);
|
||||
|
||||
return stat.data;
|
||||
}
|
||||
|
||||
static void etraxfs_uart_put_poll_char(struct uart_port *port,
|
||||
unsigned char c)
|
||||
{
|
||||
reg_ser_r_stat_din stat;
|
||||
struct uart_cris_port *up = (struct uart_cris_port *)port;
|
||||
|
||||
do {
|
||||
stat = REG_RD(ser, up->regi_ser, r_stat_din);
|
||||
} while (!stat.tr_rdy);
|
||||
REG_WR_INT(ser, up->regi_ser, rw_dout, c);
|
||||
}
|
||||
#endif /* CONFIG_CONSOLE_POLL */
|
||||
|
||||
static int etraxfs_uart_startup(struct uart_port *port)
|
||||
{
|
||||
struct uart_cris_port *up = (struct uart_cris_port *)port;
|
||||
unsigned long flags;
|
||||
reg_ser_rw_intr_mask ser_intr_mask = {0};
|
||||
|
||||
ser_intr_mask.dav = regk_ser_yes;
|
||||
|
||||
if (request_irq(etraxfs_uart_ports[port->line]->irq, ser_interrupt,
|
||||
0, DRV_NAME, etraxfs_uart_ports[port->line]))
|
||||
panic("irq ser%d", port->line);
|
||||
|
||||
spin_lock_irqsave(&up->port.lock, flags);
|
||||
|
||||
REG_WR(ser, up->regi_ser, rw_intr_mask, ser_intr_mask);
|
||||
|
||||
etraxfs_uart_set_mctrl(&up->port, up->port.mctrl);
|
||||
|
||||
spin_unlock_irqrestore(&up->port.lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void etraxfs_uart_shutdown(struct uart_port *port)
|
||||
{
|
||||
struct uart_cris_port *up = (struct uart_cris_port *)port;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&up->port.lock, flags);
|
||||
|
||||
etraxfs_uart_stop_tx(port);
|
||||
etraxfs_uart_stop_rx(port);
|
||||
|
||||
free_irq(etraxfs_uart_ports[port->line]->irq,
|
||||
etraxfs_uart_ports[port->line]);
|
||||
|
||||
etraxfs_uart_set_mctrl(&up->port, up->port.mctrl);
|
||||
|
||||
spin_unlock_irqrestore(&up->port.lock, flags);
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
etraxfs_uart_set_termios(struct uart_port *port, struct ktermios *termios,
|
||||
struct ktermios *old)
|
||||
{
|
||||
struct uart_cris_port *up = (struct uart_cris_port *)port;
|
||||
unsigned long flags;
|
||||
reg_ser_rw_xoff xoff;
|
||||
reg_ser_rw_xoff_clr xoff_clr = {0};
|
||||
reg_ser_rw_tr_ctrl tx_ctrl = {0};
|
||||
reg_ser_rw_tr_dma_en tx_dma_en = {0};
|
||||
reg_ser_rw_rec_ctrl rx_ctrl = {0};
|
||||
reg_ser_rw_tr_baud_div tx_baud_div = {0};
|
||||
reg_ser_rw_rec_baud_div rx_baud_div = {0};
|
||||
int baud;
|
||||
|
||||
if (old &&
|
||||
termios->c_cflag == old->c_cflag &&
|
||||
termios->c_iflag == old->c_iflag)
|
||||
return;
|
||||
|
||||
/* Tx: 8 bit, no/even parity, 1 stop bit, no cts. */
|
||||
tx_ctrl.base_freq = regk_ser_f29_493;
|
||||
tx_ctrl.en = 0;
|
||||
tx_ctrl.stop = 0;
|
||||
tx_ctrl.auto_rts = regk_ser_no;
|
||||
tx_ctrl.txd = 1;
|
||||
tx_ctrl.auto_cts = 0;
|
||||
/* Rx: 8 bit, no/even parity. */
|
||||
rx_ctrl.dma_err = regk_ser_stop;
|
||||
rx_ctrl.sampling = regk_ser_majority;
|
||||
rx_ctrl.timeout = 1;
|
||||
|
||||
rx_ctrl.rts_n = regk_ser_inactive;
|
||||
|
||||
/* Common for tx and rx: 8N1. */
|
||||
tx_ctrl.data_bits = regk_ser_bits8;
|
||||
rx_ctrl.data_bits = regk_ser_bits8;
|
||||
tx_ctrl.par = regk_ser_even;
|
||||
rx_ctrl.par = regk_ser_even;
|
||||
tx_ctrl.par_en = regk_ser_no;
|
||||
rx_ctrl.par_en = regk_ser_no;
|
||||
|
||||
tx_ctrl.stop_bits = regk_ser_bits1;
|
||||
|
||||
/*
|
||||
* Change baud-rate and write it to the hardware.
|
||||
*
|
||||
* baud_clock = base_freq / (divisor*8)
|
||||
* divisor = base_freq / (baud_clock * 8)
|
||||
* base_freq is either:
|
||||
* off, ext, 29.493MHz, 32.000 MHz, 32.768 MHz or 100 MHz
|
||||
* 20.493MHz is used for standard baudrates
|
||||
*/
|
||||
|
||||
/*
|
||||
* For the console port we keep the original baudrate here. Not very
|
||||
* beautiful.
|
||||
*/
|
||||
if ((port != console_port) || old)
|
||||
baud = uart_get_baud_rate(port, termios, old, 0,
|
||||
port->uartclk / 8);
|
||||
else
|
||||
baud = console_baud;
|
||||
|
||||
tx_baud_div.div = 29493000 / (8 * baud);
|
||||
/* Rx uses same as tx. */
|
||||
rx_baud_div.div = tx_baud_div.div;
|
||||
rx_ctrl.base_freq = tx_ctrl.base_freq;
|
||||
|
||||
if ((termios->c_cflag & CSIZE) == CS7) {
|
||||
/* Set 7 bit mode. */
|
||||
tx_ctrl.data_bits = regk_ser_bits7;
|
||||
rx_ctrl.data_bits = regk_ser_bits7;
|
||||
}
|
||||
|
||||
if (termios->c_cflag & CSTOPB) {
|
||||
/* Set 2 stop bit mode. */
|
||||
tx_ctrl.stop_bits = regk_ser_bits2;
|
||||
}
|
||||
|
||||
if (termios->c_cflag & PARENB) {
|
||||
/* Enable parity. */
|
||||
tx_ctrl.par_en = regk_ser_yes;
|
||||
rx_ctrl.par_en = regk_ser_yes;
|
||||
}
|
||||
|
||||
if (termios->c_cflag & CMSPAR) {
|
||||
if (termios->c_cflag & PARODD) {
|
||||
/* Set mark parity if PARODD and CMSPAR. */
|
||||
tx_ctrl.par = regk_ser_mark;
|
||||
rx_ctrl.par = regk_ser_mark;
|
||||
} else {
|
||||
tx_ctrl.par = regk_ser_space;
|
||||
rx_ctrl.par = regk_ser_space;
|
||||
}
|
||||
} else {
|
||||
if (termios->c_cflag & PARODD) {
|
||||
/* Set odd parity. */
|
||||
tx_ctrl.par = regk_ser_odd;
|
||||
rx_ctrl.par = regk_ser_odd;
|
||||
}
|
||||
}
|
||||
|
||||
if (termios->c_cflag & CRTSCTS) {
|
||||
/* Enable automatic CTS handling. */
|
||||
tx_ctrl.auto_cts = regk_ser_yes;
|
||||
}
|
||||
|
||||
/* Make sure the tx and rx are enabled. */
|
||||
tx_ctrl.en = regk_ser_yes;
|
||||
rx_ctrl.en = regk_ser_yes;
|
||||
|
||||
spin_lock_irqsave(&port->lock, flags);
|
||||
|
||||
tx_dma_en.en = 0;
|
||||
REG_WR(ser, up->regi_ser, rw_tr_dma_en, tx_dma_en);
|
||||
|
||||
/* Actually write the control regs (if modified) to the hardware. */
|
||||
uart_update_timeout(port, termios->c_cflag, port->uartclk/8);
|
||||
MODIFY_REG(up->regi_ser, rw_rec_baud_div, rx_baud_div);
|
||||
MODIFY_REG(up->regi_ser, rw_rec_ctrl, rx_ctrl);
|
||||
|
||||
MODIFY_REG(up->regi_ser, rw_tr_baud_div, tx_baud_div);
|
||||
MODIFY_REG(up->regi_ser, rw_tr_ctrl, tx_ctrl);
|
||||
|
||||
tx_dma_en.en = 0;
|
||||
REG_WR(ser, up->regi_ser, rw_tr_dma_en, tx_dma_en);
|
||||
|
||||
xoff = REG_RD(ser, up->regi_ser, rw_xoff);
|
||||
|
||||
if (up->port.state && up->port.state->port.tty &&
|
||||
(up->port.state->port.tty->termios.c_iflag & IXON)) {
|
||||
xoff.chr = STOP_CHAR(up->port.state->port.tty);
|
||||
xoff.automatic = regk_ser_yes;
|
||||
} else
|
||||
xoff.automatic = regk_ser_no;
|
||||
|
||||
MODIFY_REG(up->regi_ser, rw_xoff, xoff);
|
||||
|
||||
/*
|
||||
* Make sure we don't start in an automatically shut-off state due to
|
||||
* a previous early exit.
|
||||
*/
|
||||
xoff_clr.clr = 1;
|
||||
REG_WR(ser, up->regi_ser, rw_xoff_clr, xoff_clr);
|
||||
|
||||
etraxfs_uart_set_mctrl(&up->port, up->port.mctrl);
|
||||
spin_unlock_irqrestore(&up->port.lock, flags);
|
||||
}
|
||||
|
||||
static const char *
|
||||
etraxfs_uart_type(struct uart_port *port)
|
||||
{
|
||||
return "CRISv32";
|
||||
}
|
||||
|
||||
static void etraxfs_uart_release_port(struct uart_port *port)
|
||||
{
|
||||
}
|
||||
|
||||
static int etraxfs_uart_request_port(struct uart_port *port)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void etraxfs_uart_config_port(struct uart_port *port, int flags)
|
||||
{
|
||||
struct uart_cris_port *up = (struct uart_cris_port *)port;
|
||||
|
||||
up->port.type = PORT_CRIS;
|
||||
}
|
||||
|
||||
static const struct uart_ops etraxfs_uart_pops = {
|
||||
.tx_empty = etraxfs_uart_tx_empty,
|
||||
.set_mctrl = etraxfs_uart_set_mctrl,
|
||||
.get_mctrl = etraxfs_uart_get_mctrl,
|
||||
.stop_tx = etraxfs_uart_stop_tx,
|
||||
.start_tx = etraxfs_uart_start_tx,
|
||||
.send_xchar = etraxfs_uart_send_xchar,
|
||||
.stop_rx = etraxfs_uart_stop_rx,
|
||||
.enable_ms = etraxfs_uart_enable_ms,
|
||||
.break_ctl = etraxfs_uart_break_ctl,
|
||||
.startup = etraxfs_uart_startup,
|
||||
.shutdown = etraxfs_uart_shutdown,
|
||||
.set_termios = etraxfs_uart_set_termios,
|
||||
.type = etraxfs_uart_type,
|
||||
.release_port = etraxfs_uart_release_port,
|
||||
.request_port = etraxfs_uart_request_port,
|
||||
.config_port = etraxfs_uart_config_port,
|
||||
#ifdef CONFIG_CONSOLE_POLL
|
||||
.poll_get_char = etraxfs_uart_get_poll_char,
|
||||
.poll_put_char = etraxfs_uart_put_poll_char,
|
||||
#endif
|
||||
};
|
||||
|
||||
static void cris_serial_port_init(struct uart_port *port, int line)
|
||||
{
|
||||
struct uart_cris_port *up = (struct uart_cris_port *)port;
|
||||
|
||||
if (up->initialized)
|
||||
return;
|
||||
up->initialized = 1;
|
||||
port->line = line;
|
||||
spin_lock_init(&port->lock);
|
||||
port->ops = &etraxfs_uart_pops;
|
||||
port->irq = up->irq;
|
||||
port->iobase = (unsigned long) up->regi_ser;
|
||||
port->uartclk = 29493000;
|
||||
|
||||
/*
|
||||
* We can't fit any more than 255 here (unsigned char), though
|
||||
* actually UART_XMIT_SIZE characters could be pending output.
|
||||
* At time of this writing, the definition of "fifosize" is here the
|
||||
* amount of characters that can be pending output after a start_tx call
|
||||
* until tx_empty returns 1: see serial_core.c:uart_wait_until_sent.
|
||||
* This matters for timeout calculations unfortunately, but keeping
|
||||
* larger amounts at the DMA wouldn't win much so let's just play nice.
|
||||
*/
|
||||
port->fifosize = 255;
|
||||
port->flags = UPF_BOOT_AUTOCONF;
|
||||
}
|
||||
|
||||
static int etraxfs_uart_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct uart_cris_port *up;
|
||||
int dev_id;
|
||||
|
||||
if (!np)
|
||||
return -ENODEV;
|
||||
|
||||
dev_id = of_alias_get_id(np, "serial");
|
||||
if (dev_id < 0)
|
||||
dev_id = 0;
|
||||
|
||||
if (dev_id >= UART_NR)
|
||||
return -EINVAL;
|
||||
|
||||
if (etraxfs_uart_ports[dev_id])
|
||||
return -EBUSY;
|
||||
|
||||
up = devm_kzalloc(&pdev->dev, sizeof(struct uart_cris_port),
|
||||
GFP_KERNEL);
|
||||
if (!up)
|
||||
return -ENOMEM;
|
||||
|
||||
up->irq = irq_of_parse_and_map(np, 0);
|
||||
up->regi_ser = of_iomap(np, 0);
|
||||
up->dtr_pin = devm_gpiod_get_optional(&pdev->dev, "dtr");
|
||||
up->dsr_pin = devm_gpiod_get_optional(&pdev->dev, "dsr");
|
||||
up->ri_pin = devm_gpiod_get_optional(&pdev->dev, "ri");
|
||||
up->cd_pin = devm_gpiod_get_optional(&pdev->dev, "cd");
|
||||
up->port.dev = &pdev->dev;
|
||||
cris_serial_port_init(&up->port, dev_id);
|
||||
|
||||
etraxfs_uart_ports[dev_id] = up;
|
||||
platform_set_drvdata(pdev, &up->port);
|
||||
uart_add_one_port(&etraxfs_uart_driver, &up->port);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int etraxfs_uart_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct uart_port *port;
|
||||
|
||||
port = platform_get_drvdata(pdev);
|
||||
uart_remove_one_port(&etraxfs_uart_driver, port);
|
||||
etraxfs_uart_ports[pdev->id] = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id etraxfs_uart_dt_ids[] = {
|
||||
{ .compatible = "axis,etraxfs-uart" },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, etraxfs_uart_dt_ids);
|
||||
|
||||
static struct platform_driver etraxfs_uart_platform_driver = {
|
||||
.driver = {
|
||||
.name = DRV_NAME,
|
||||
.of_match_table = of_match_ptr(etraxfs_uart_dt_ids),
|
||||
},
|
||||
.probe = etraxfs_uart_probe,
|
||||
.remove = etraxfs_uart_remove,
|
||||
};
|
||||
|
||||
static int __init etraxfs_uart_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = uart_register_driver(&etraxfs_uart_driver);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = platform_driver_register(&etraxfs_uart_platform_driver);
|
||||
if (ret)
|
||||
uart_unregister_driver(&etraxfs_uart_driver);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit etraxfs_uart_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&etraxfs_uart_platform_driver);
|
||||
uart_unregister_driver(&etraxfs_uart_driver);
|
||||
}
|
||||
|
||||
module_init(etraxfs_uart_init);
|
||||
module_exit(etraxfs_uart_exit);
|
@ -237,7 +237,8 @@ struct lpuart_port {
|
||||
unsigned int rxfifo_size;
|
||||
bool lpuart32;
|
||||
|
||||
bool lpuart_dma_use;
|
||||
bool lpuart_dma_tx_use;
|
||||
bool lpuart_dma_rx_use;
|
||||
struct dma_chan *dma_tx_chan;
|
||||
struct dma_chan *dma_rx_chan;
|
||||
struct dma_async_tx_descriptor *dma_tx_desc;
|
||||
@ -454,6 +455,15 @@ static int lpuart_dma_rx(struct lpuart_port *sport)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void lpuart_flush_buffer(struct uart_port *port)
|
||||
{
|
||||
struct lpuart_port *sport = container_of(port, struct lpuart_port, port);
|
||||
if (sport->lpuart_dma_tx_use) {
|
||||
dmaengine_terminate_all(sport->dma_tx_chan);
|
||||
sport->dma_tx_in_progress = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void lpuart_dma_rx_complete(void *arg)
|
||||
{
|
||||
struct lpuart_port *sport = arg;
|
||||
@ -461,6 +471,7 @@ static void lpuart_dma_rx_complete(void *arg)
|
||||
unsigned long flags;
|
||||
|
||||
async_tx_ack(sport->dma_rx_desc);
|
||||
mod_timer(&sport->lpuart_timer, jiffies + sport->dma_rx_timeout);
|
||||
|
||||
spin_lock_irqsave(&sport->port.lock, flags);
|
||||
|
||||
@ -506,9 +517,6 @@ static inline void lpuart_prepare_rx(struct lpuart_port *sport)
|
||||
|
||||
spin_lock_irqsave(&sport->port.lock, flags);
|
||||
|
||||
init_timer(&sport->lpuart_timer);
|
||||
sport->lpuart_timer.function = lpuart_timer_func;
|
||||
sport->lpuart_timer.data = (unsigned long)sport;
|
||||
sport->lpuart_timer.expires = jiffies + sport->dma_rx_timeout;
|
||||
add_timer(&sport->lpuart_timer);
|
||||
|
||||
@ -571,7 +579,7 @@ static void lpuart_start_tx(struct uart_port *port)
|
||||
temp = readb(port->membase + UARTCR2);
|
||||
writeb(temp | UARTCR2_TIE, port->membase + UARTCR2);
|
||||
|
||||
if (sport->lpuart_dma_use) {
|
||||
if (sport->lpuart_dma_tx_use) {
|
||||
if (!uart_circ_empty(xmit) && !sport->dma_tx_in_progress)
|
||||
lpuart_prepare_tx(sport);
|
||||
} else {
|
||||
@ -758,19 +766,19 @@ out:
|
||||
static irqreturn_t lpuart_int(int irq, void *dev_id)
|
||||
{
|
||||
struct lpuart_port *sport = dev_id;
|
||||
unsigned char sts;
|
||||
unsigned char sts, crdma;
|
||||
|
||||
sts = readb(sport->port.membase + UARTSR1);
|
||||
crdma = readb(sport->port.membase + UARTCR5);
|
||||
|
||||
if (sts & UARTSR1_RDRF) {
|
||||
if (sport->lpuart_dma_use)
|
||||
if (sts & UARTSR1_RDRF && !(crdma & UARTCR5_RDMAS)) {
|
||||
if (sport->lpuart_dma_rx_use)
|
||||
lpuart_prepare_rx(sport);
|
||||
else
|
||||
lpuart_rxint(irq, dev_id);
|
||||
}
|
||||
if (sts & UARTSR1_TDRE &&
|
||||
!(readb(sport->port.membase + UARTCR5) & UARTCR5_TDMAS)) {
|
||||
if (sport->lpuart_dma_use)
|
||||
if (sts & UARTSR1_TDRE && !(crdma & UARTCR5_TDMAS)) {
|
||||
if (sport->lpuart_dma_tx_use)
|
||||
lpuart_pio_tx(sport);
|
||||
else
|
||||
lpuart_txint(irq, dev_id);
|
||||
@ -953,26 +961,17 @@ static int lpuart_dma_tx_request(struct uart_port *port)
|
||||
{
|
||||
struct lpuart_port *sport = container_of(port,
|
||||
struct lpuart_port, port);
|
||||
struct dma_chan *tx_chan;
|
||||
struct dma_slave_config dma_tx_sconfig;
|
||||
dma_addr_t dma_bus;
|
||||
unsigned char *dma_buf;
|
||||
int ret;
|
||||
|
||||
tx_chan = dma_request_slave_channel(sport->port.dev, "tx");
|
||||
|
||||
if (!tx_chan) {
|
||||
dev_err(sport->port.dev, "Dma tx channel request failed!\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
dma_bus = dma_map_single(tx_chan->device->dev,
|
||||
dma_bus = dma_map_single(sport->dma_tx_chan->device->dev,
|
||||
sport->port.state->xmit.buf,
|
||||
UART_XMIT_SIZE, DMA_TO_DEVICE);
|
||||
|
||||
if (dma_mapping_error(tx_chan->device->dev, dma_bus)) {
|
||||
if (dma_mapping_error(sport->dma_tx_chan->device->dev, dma_bus)) {
|
||||
dev_err(sport->port.dev, "dma_map_single tx failed\n");
|
||||
dma_release_channel(tx_chan);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
@ -981,16 +980,14 @@ static int lpuart_dma_tx_request(struct uart_port *port)
|
||||
dma_tx_sconfig.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
|
||||
dma_tx_sconfig.dst_maxburst = sport->txfifo_size;
|
||||
dma_tx_sconfig.direction = DMA_MEM_TO_DEV;
|
||||
ret = dmaengine_slave_config(tx_chan, &dma_tx_sconfig);
|
||||
ret = dmaengine_slave_config(sport->dma_tx_chan, &dma_tx_sconfig);
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(sport->port.dev,
|
||||
"Dma slave config failed, err = %d\n", ret);
|
||||
dma_release_channel(tx_chan);
|
||||
return ret;
|
||||
}
|
||||
|
||||
sport->dma_tx_chan = tx_chan;
|
||||
sport->dma_tx_buf_virt = dma_buf;
|
||||
sport->dma_tx_buf_bus = dma_bus;
|
||||
sport->dma_tx_in_progress = 0;
|
||||
@ -1002,34 +999,24 @@ static int lpuart_dma_rx_request(struct uart_port *port)
|
||||
{
|
||||
struct lpuart_port *sport = container_of(port,
|
||||
struct lpuart_port, port);
|
||||
struct dma_chan *rx_chan;
|
||||
struct dma_slave_config dma_rx_sconfig;
|
||||
dma_addr_t dma_bus;
|
||||
unsigned char *dma_buf;
|
||||
int ret;
|
||||
|
||||
rx_chan = dma_request_slave_channel(sport->port.dev, "rx");
|
||||
|
||||
if (!rx_chan) {
|
||||
dev_err(sport->port.dev, "Dma rx channel request failed!\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
dma_buf = devm_kzalloc(sport->port.dev,
|
||||
FSL_UART_RX_DMA_BUFFER_SIZE, GFP_KERNEL);
|
||||
|
||||
if (!dma_buf) {
|
||||
dev_err(sport->port.dev, "Dma rx alloc failed\n");
|
||||
dma_release_channel(rx_chan);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
dma_bus = dma_map_single(rx_chan->device->dev, dma_buf,
|
||||
dma_bus = dma_map_single(sport->dma_rx_chan->device->dev, dma_buf,
|
||||
FSL_UART_RX_DMA_BUFFER_SIZE, DMA_FROM_DEVICE);
|
||||
|
||||
if (dma_mapping_error(rx_chan->device->dev, dma_bus)) {
|
||||
if (dma_mapping_error(sport->dma_rx_chan->device->dev, dma_bus)) {
|
||||
dev_err(sport->port.dev, "dma_map_single rx failed\n");
|
||||
dma_release_channel(rx_chan);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
@ -1037,16 +1024,14 @@ static int lpuart_dma_rx_request(struct uart_port *port)
|
||||
dma_rx_sconfig.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
|
||||
dma_rx_sconfig.src_maxburst = 1;
|
||||
dma_rx_sconfig.direction = DMA_DEV_TO_MEM;
|
||||
ret = dmaengine_slave_config(rx_chan, &dma_rx_sconfig);
|
||||
ret = dmaengine_slave_config(sport->dma_rx_chan, &dma_rx_sconfig);
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(sport->port.dev,
|
||||
"Dma slave config failed, err = %d\n", ret);
|
||||
dma_release_channel(rx_chan);
|
||||
return ret;
|
||||
}
|
||||
|
||||
sport->dma_rx_chan = rx_chan;
|
||||
sport->dma_rx_buf_virt = dma_buf;
|
||||
sport->dma_rx_buf_bus = dma_bus;
|
||||
sport->dma_rx_in_progress = 0;
|
||||
@ -1058,31 +1043,24 @@ static void lpuart_dma_tx_free(struct uart_port *port)
|
||||
{
|
||||
struct lpuart_port *sport = container_of(port,
|
||||
struct lpuart_port, port);
|
||||
struct dma_chan *dma_chan;
|
||||
|
||||
dma_unmap_single(sport->port.dev, sport->dma_tx_buf_bus,
|
||||
UART_XMIT_SIZE, DMA_TO_DEVICE);
|
||||
dma_chan = sport->dma_tx_chan;
|
||||
sport->dma_tx_chan = NULL;
|
||||
|
||||
sport->dma_tx_buf_bus = 0;
|
||||
sport->dma_tx_buf_virt = NULL;
|
||||
dma_release_channel(dma_chan);
|
||||
}
|
||||
|
||||
static void lpuart_dma_rx_free(struct uart_port *port)
|
||||
{
|
||||
struct lpuart_port *sport = container_of(port,
|
||||
struct lpuart_port, port);
|
||||
struct dma_chan *dma_chan;
|
||||
|
||||
dma_unmap_single(sport->port.dev, sport->dma_rx_buf_bus,
|
||||
FSL_UART_RX_DMA_BUFFER_SIZE, DMA_FROM_DEVICE);
|
||||
|
||||
dma_chan = sport->dma_rx_chan;
|
||||
sport->dma_rx_chan = NULL;
|
||||
sport->dma_rx_buf_bus = 0;
|
||||
sport->dma_rx_buf_virt = NULL;
|
||||
dma_release_channel(dma_chan);
|
||||
}
|
||||
|
||||
static int lpuart_startup(struct uart_port *port)
|
||||
@ -1101,14 +1079,21 @@ static int lpuart_startup(struct uart_port *port)
|
||||
sport->rxfifo_size = 0x1 << (((temp >> UARTPFIFO_RXSIZE_OFF) &
|
||||
UARTPFIFO_FIFOSIZE_MASK) + 1);
|
||||
|
||||
/* Whether use dma support by dma request results */
|
||||
if (lpuart_dma_tx_request(port) || lpuart_dma_rx_request(port)) {
|
||||
sport->lpuart_dma_use = false;
|
||||
} else {
|
||||
sport->lpuart_dma_use = true;
|
||||
if (sport->dma_rx_chan && !lpuart_dma_rx_request(port)) {
|
||||
sport->lpuart_dma_rx_use = true;
|
||||
setup_timer(&sport->lpuart_timer, lpuart_timer_func,
|
||||
(unsigned long)sport);
|
||||
} else
|
||||
sport->lpuart_dma_rx_use = false;
|
||||
|
||||
|
||||
if (sport->dma_tx_chan && !lpuart_dma_tx_request(port)) {
|
||||
sport->lpuart_dma_tx_use = true;
|
||||
temp = readb(port->membase + UARTCR5);
|
||||
temp &= ~UARTCR5_RDMAS;
|
||||
writeb(temp | UARTCR5_TDMAS, port->membase + UARTCR5);
|
||||
}
|
||||
} else
|
||||
sport->lpuart_dma_tx_use = false;
|
||||
|
||||
ret = devm_request_irq(port->dev, port->irq, lpuart_int, 0,
|
||||
DRIVER_NAME, sport);
|
||||
@ -1179,10 +1164,13 @@ static void lpuart_shutdown(struct uart_port *port)
|
||||
|
||||
devm_free_irq(port->dev, port->irq, sport);
|
||||
|
||||
if (sport->lpuart_dma_use) {
|
||||
lpuart_dma_tx_free(port);
|
||||
lpuart_dma_rx_free(port);
|
||||
if (sport->lpuart_dma_rx_use) {
|
||||
lpuart_dma_rx_free(&sport->port);
|
||||
del_timer_sync(&sport->lpuart_timer);
|
||||
}
|
||||
|
||||
if (sport->lpuart_dma_tx_use)
|
||||
lpuart_dma_tx_free(&sport->port);
|
||||
}
|
||||
|
||||
static void lpuart32_shutdown(struct uart_port *port)
|
||||
@ -1304,7 +1292,7 @@ lpuart_set_termios(struct uart_port *port, struct ktermios *termios,
|
||||
/* update the per-port timeout */
|
||||
uart_update_timeout(port, termios->c_cflag, baud);
|
||||
|
||||
if (sport->lpuart_dma_use) {
|
||||
if (sport->lpuart_dma_rx_use) {
|
||||
/* Calculate delay for 1.5 DMA buffers */
|
||||
sport->dma_rx_timeout = (sport->port.timeout - HZ / 50) *
|
||||
FSL_UART_RX_DMA_BUFFER_SIZE * 3 /
|
||||
@ -1517,6 +1505,7 @@ static struct uart_ops lpuart_pops = {
|
||||
.release_port = lpuart_release_port,
|
||||
.config_port = lpuart_config_port,
|
||||
.verify_port = lpuart_verify_port,
|
||||
.flush_buffer = lpuart_flush_buffer,
|
||||
};
|
||||
|
||||
static struct uart_ops lpuart32_pops = {
|
||||
@ -1535,6 +1524,7 @@ static struct uart_ops lpuart32_pops = {
|
||||
.release_port = lpuart_release_port,
|
||||
.config_port = lpuart_config_port,
|
||||
.verify_port = lpuart_verify_port,
|
||||
.flush_buffer = lpuart_flush_buffer,
|
||||
};
|
||||
|
||||
static struct lpuart_port *lpuart_ports[UART_NR];
|
||||
@ -1833,6 +1823,16 @@ static int lpuart_probe(struct platform_device *pdev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
sport->dma_tx_chan = dma_request_slave_channel(sport->port.dev, "tx");
|
||||
if (!sport->dma_tx_chan)
|
||||
dev_info(sport->port.dev, "DMA tx channel request failed, "
|
||||
"operating without tx DMA\n");
|
||||
|
||||
sport->dma_rx_chan = dma_request_slave_channel(sport->port.dev, "rx");
|
||||
if (!sport->dma_rx_chan)
|
||||
dev_info(sport->port.dev, "DMA rx channel request failed, "
|
||||
"operating without rx DMA\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1844,6 +1844,12 @@ static int lpuart_remove(struct platform_device *pdev)
|
||||
|
||||
clk_disable_unprepare(sport->clk);
|
||||
|
||||
if (sport->dma_tx_chan)
|
||||
dma_release_channel(sport->dma_tx_chan);
|
||||
|
||||
if (sport->dma_rx_chan)
|
||||
dma_release_channel(sport->dma_rx_chan);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1851,6 +1857,19 @@ static int lpuart_remove(struct platform_device *pdev)
|
||||
static int lpuart_suspend(struct device *dev)
|
||||
{
|
||||
struct lpuart_port *sport = dev_get_drvdata(dev);
|
||||
unsigned long temp;
|
||||
|
||||
if (sport->lpuart32) {
|
||||
/* disable Rx/Tx and interrupts */
|
||||
temp = lpuart32_read(sport->port.membase + UARTCTRL);
|
||||
temp &= ~(UARTCTRL_TE | UARTCTRL_TIE | UARTCTRL_TCIE);
|
||||
lpuart32_write(temp, sport->port.membase + UARTCTRL);
|
||||
} else {
|
||||
/* disable Rx/Tx and interrupts */
|
||||
temp = readb(sport->port.membase + UARTCR2);
|
||||
temp &= ~(UARTCR2_TE | UARTCR2_TIE | UARTCR2_TCIE);
|
||||
writeb(temp, sport->port.membase + UARTCR2);
|
||||
}
|
||||
|
||||
uart_suspend_port(&lpuart_reg, &sport->port);
|
||||
|
||||
|
@ -74,6 +74,7 @@
|
||||
#define IMX21_UTS 0xb4 /* UART Test Register on all other i.mx*/
|
||||
|
||||
/* UART Control Register Bit Fields.*/
|
||||
#define URXD_DUMMY_READ (1<<16)
|
||||
#define URXD_CHARRDY (1<<15)
|
||||
#define URXD_ERR (1<<14)
|
||||
#define URXD_OVRRUN (1<<13)
|
||||
@ -463,13 +464,17 @@ static void imx_enable_ms(struct uart_port *port)
|
||||
mod_timer(&sport->timer, jiffies);
|
||||
}
|
||||
|
||||
static void imx_dma_tx(struct imx_port *sport);
|
||||
static inline void imx_transmit_buffer(struct imx_port *sport)
|
||||
{
|
||||
struct circ_buf *xmit = &sport->port.state->xmit;
|
||||
unsigned long temp;
|
||||
|
||||
if (sport->port.x_char) {
|
||||
/* Send next char */
|
||||
writel(sport->port.x_char, sport->port.membase + URTX0);
|
||||
sport->port.icount.tx++;
|
||||
sport->port.x_char = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -478,6 +483,22 @@ static inline void imx_transmit_buffer(struct imx_port *sport)
|
||||
return;
|
||||
}
|
||||
|
||||
if (sport->dma_is_enabled) {
|
||||
/*
|
||||
* We've just sent a X-char Ensure the TX DMA is enabled
|
||||
* and the TX IRQ is disabled.
|
||||
**/
|
||||
temp = readl(sport->port.membase + UCR1);
|
||||
temp &= ~UCR1_TXMPTYEN;
|
||||
if (sport->dma_is_txing) {
|
||||
temp |= UCR1_TDMAEN;
|
||||
writel(temp, sport->port.membase + UCR1);
|
||||
} else {
|
||||
writel(temp, sport->port.membase + UCR1);
|
||||
imx_dma_tx(sport);
|
||||
}
|
||||
}
|
||||
|
||||
while (!uart_circ_empty(xmit) &&
|
||||
!(readl(sport->port.membase + uts_reg(sport)) & UTS_TXFULL)) {
|
||||
/* send xmit->buf[xmit->tail]
|
||||
@ -500,26 +521,39 @@ static void dma_tx_callback(void *data)
|
||||
struct scatterlist *sgl = &sport->tx_sgl[0];
|
||||
struct circ_buf *xmit = &sport->port.state->xmit;
|
||||
unsigned long flags;
|
||||
unsigned long temp;
|
||||
|
||||
spin_lock_irqsave(&sport->port.lock, flags);
|
||||
|
||||
dma_unmap_sg(sport->port.dev, sgl, sport->dma_tx_nents, DMA_TO_DEVICE);
|
||||
|
||||
sport->dma_is_txing = 0;
|
||||
temp = readl(sport->port.membase + UCR1);
|
||||
temp &= ~UCR1_TDMAEN;
|
||||
writel(temp, sport->port.membase + UCR1);
|
||||
|
||||
/* update the stat */
|
||||
spin_lock_irqsave(&sport->port.lock, flags);
|
||||
xmit->tail = (xmit->tail + sport->tx_bytes) & (UART_XMIT_SIZE - 1);
|
||||
sport->port.icount.tx += sport->tx_bytes;
|
||||
spin_unlock_irqrestore(&sport->port.lock, flags);
|
||||
|
||||
dev_dbg(sport->port.dev, "we finish the TX DMA.\n");
|
||||
|
||||
uart_write_wakeup(&sport->port);
|
||||
sport->dma_is_txing = 0;
|
||||
|
||||
spin_unlock_irqrestore(&sport->port.lock, flags);
|
||||
|
||||
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
|
||||
uart_write_wakeup(&sport->port);
|
||||
|
||||
if (waitqueue_active(&sport->dma_wait)) {
|
||||
wake_up(&sport->dma_wait);
|
||||
dev_dbg(sport->port.dev, "exit in %s.\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&sport->port.lock, flags);
|
||||
if (!uart_circ_empty(xmit) && !uart_tx_stopped(&sport->port))
|
||||
imx_dma_tx(sport);
|
||||
spin_unlock_irqrestore(&sport->port.lock, flags);
|
||||
}
|
||||
|
||||
static void imx_dma_tx(struct imx_port *sport)
|
||||
@ -529,24 +563,23 @@ static void imx_dma_tx(struct imx_port *sport)
|
||||
struct dma_async_tx_descriptor *desc;
|
||||
struct dma_chan *chan = sport->dma_chan_tx;
|
||||
struct device *dev = sport->port.dev;
|
||||
enum dma_status status;
|
||||
unsigned long temp;
|
||||
int ret;
|
||||
|
||||
status = dmaengine_tx_status(chan, (dma_cookie_t)0, NULL);
|
||||
if (DMA_IN_PROGRESS == status)
|
||||
if (sport->dma_is_txing)
|
||||
return;
|
||||
|
||||
sport->tx_bytes = uart_circ_chars_pending(xmit);
|
||||
|
||||
if (xmit->tail > xmit->head && xmit->head > 0) {
|
||||
if (xmit->tail < xmit->head) {
|
||||
sport->dma_tx_nents = 1;
|
||||
sg_init_one(sgl, xmit->buf + xmit->tail, sport->tx_bytes);
|
||||
} else {
|
||||
sport->dma_tx_nents = 2;
|
||||
sg_init_table(sgl, 2);
|
||||
sg_set_buf(sgl, xmit->buf + xmit->tail,
|
||||
UART_XMIT_SIZE - xmit->tail);
|
||||
sg_set_buf(sgl + 1, xmit->buf, xmit->head);
|
||||
} else {
|
||||
sport->dma_tx_nents = 1;
|
||||
sg_init_one(sgl, xmit->buf + xmit->tail, sport->tx_bytes);
|
||||
}
|
||||
|
||||
ret = dma_map_sg(dev, sgl, sport->dma_tx_nents, DMA_TO_DEVICE);
|
||||
@ -557,6 +590,8 @@ static void imx_dma_tx(struct imx_port *sport)
|
||||
desc = dmaengine_prep_slave_sg(chan, sgl, sport->dma_tx_nents,
|
||||
DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT);
|
||||
if (!desc) {
|
||||
dma_unmap_sg(dev, sgl, sport->dma_tx_nents,
|
||||
DMA_TO_DEVICE);
|
||||
dev_err(dev, "We cannot prepare for the TX slave dma!\n");
|
||||
return;
|
||||
}
|
||||
@ -565,6 +600,11 @@ static void imx_dma_tx(struct imx_port *sport)
|
||||
|
||||
dev_dbg(dev, "TX: prepare to send %lu bytes by DMA.\n",
|
||||
uart_circ_chars_pending(xmit));
|
||||
|
||||
temp = readl(sport->port.membase + UCR1);
|
||||
temp |= UCR1_TDMAEN;
|
||||
writel(temp, sport->port.membase + UCR1);
|
||||
|
||||
/* fire it */
|
||||
sport->dma_is_txing = 1;
|
||||
dmaengine_submit(desc);
|
||||
@ -590,13 +630,6 @@ static void imx_start_tx(struct uart_port *port)
|
||||
temp &= ~(UCR1_RRDYEN);
|
||||
writel(temp, sport->port.membase + UCR1);
|
||||
}
|
||||
/* Clear any pending ORE flag before enabling interrupt */
|
||||
temp = readl(sport->port.membase + USR2);
|
||||
writel(temp | USR2_ORE, sport->port.membase + USR2);
|
||||
|
||||
temp = readl(sport->port.membase + UCR4);
|
||||
temp |= UCR4_OREN;
|
||||
writel(temp, sport->port.membase + UCR4);
|
||||
|
||||
if (!sport->dma_is_enabled) {
|
||||
temp = readl(sport->port.membase + UCR1);
|
||||
@ -614,15 +647,21 @@ static void imx_start_tx(struct uart_port *port)
|
||||
}
|
||||
|
||||
if (sport->dma_is_enabled) {
|
||||
/* FIXME: port->x_char must be transmitted if != 0 */
|
||||
if (sport->port.x_char) {
|
||||
/* We have X-char to send, so enable TX IRQ and
|
||||
* disable TX DMA to let TX interrupt to send X-char */
|
||||
temp = readl(sport->port.membase + UCR1);
|
||||
temp &= ~UCR1_TDMAEN;
|
||||
temp |= UCR1_TXMPTYEN;
|
||||
writel(temp, sport->port.membase + UCR1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!uart_circ_empty(&port->state->xmit) &&
|
||||
!uart_tx_stopped(port))
|
||||
imx_dma_tx(sport);
|
||||
return;
|
||||
}
|
||||
|
||||
if (readl(sport->port.membase + uts_reg(sport)) & UTS_TXEMPTY)
|
||||
imx_transmit_buffer(sport);
|
||||
}
|
||||
|
||||
static irqreturn_t imx_rtsint(int irq, void *dev_id)
|
||||
@ -694,7 +733,7 @@ static irqreturn_t imx_rxint(int irq, void *dev_id)
|
||||
continue;
|
||||
}
|
||||
|
||||
rx &= sport->port.read_status_mask;
|
||||
rx &= (sport->port.read_status_mask | 0xFF);
|
||||
|
||||
if (rx & URXD_BRK)
|
||||
flg = TTY_BREAK;
|
||||
@ -710,6 +749,9 @@ static irqreturn_t imx_rxint(int irq, void *dev_id)
|
||||
#endif
|
||||
}
|
||||
|
||||
if (sport->port.ignore_status_mask & URXD_DUMMY_READ)
|
||||
goto out;
|
||||
|
||||
tty_insert_flip_char(port, rx, flg);
|
||||
}
|
||||
|
||||
@ -727,6 +769,9 @@ static int start_rx_dma(struct imx_port *sport);
|
||||
static void imx_dma_rxint(struct imx_port *sport)
|
||||
{
|
||||
unsigned long temp;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&sport->port.lock, flags);
|
||||
|
||||
temp = readl(sport->port.membase + USR2);
|
||||
if ((temp & USR2_RDR) && !sport->dma_is_rxing) {
|
||||
@ -740,6 +785,8 @@ static void imx_dma_rxint(struct imx_port *sport)
|
||||
/* tell the DMA to receive the data. */
|
||||
start_rx_dma(sport);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&sport->port.lock, flags);
|
||||
}
|
||||
|
||||
static irqreturn_t imx_int(int irq, void *dev_id)
|
||||
@ -869,6 +916,9 @@ static int imx_setup_ufcr(struct imx_port *sport, unsigned int mode)
|
||||
static void imx_rx_dma_done(struct imx_port *sport)
|
||||
{
|
||||
unsigned long temp;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&sport->port.lock, flags);
|
||||
|
||||
/* Enable this interrupt when the RXFIFO is empty. */
|
||||
temp = readl(sport->port.membase + UCR1);
|
||||
@ -880,6 +930,8 @@ static void imx_rx_dma_done(struct imx_port *sport)
|
||||
/* Is the shutdown waiting for us? */
|
||||
if (waitqueue_active(&sport->dma_wait))
|
||||
wake_up(&sport->dma_wait);
|
||||
|
||||
spin_unlock_irqrestore(&sport->port.lock, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -910,12 +962,26 @@ static void dma_rx_callback(void *data)
|
||||
dev_dbg(sport->port.dev, "We get %d bytes.\n", count);
|
||||
|
||||
if (count) {
|
||||
tty_insert_flip_string(port, sport->rx_buf, count);
|
||||
if (!(sport->port.ignore_status_mask & URXD_DUMMY_READ))
|
||||
tty_insert_flip_string(port, sport->rx_buf, count);
|
||||
tty_flip_buffer_push(port);
|
||||
|
||||
start_rx_dma(sport);
|
||||
} else
|
||||
} else if (readl(sport->port.membase + USR2) & USR2_RDR) {
|
||||
/*
|
||||
* start rx_dma directly once data in RXFIFO, more efficient
|
||||
* than before:
|
||||
* 1. call imx_rx_dma_done to stop dma if no data received
|
||||
* 2. wait next RDR interrupt to start dma transfer.
|
||||
*/
|
||||
start_rx_dma(sport);
|
||||
} else {
|
||||
/*
|
||||
* stop dma to prevent too many IDLE event trigged if no data
|
||||
* in RXFIFO
|
||||
*/
|
||||
imx_rx_dma_done(sport);
|
||||
}
|
||||
}
|
||||
|
||||
static int start_rx_dma(struct imx_port *sport)
|
||||
@ -935,6 +1001,7 @@ static int start_rx_dma(struct imx_port *sport)
|
||||
desc = dmaengine_prep_slave_sg(chan, sgl, 1, DMA_DEV_TO_MEM,
|
||||
DMA_PREP_INTERRUPT);
|
||||
if (!desc) {
|
||||
dma_unmap_sg(dev, sgl, 1, DMA_FROM_DEVICE);
|
||||
dev_err(dev, "We cannot prepare for the RX slave dma!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -1108,12 +1175,20 @@ static int imx_startup(struct uart_port *port)
|
||||
while (!(readl(sport->port.membase + UCR2) & UCR2_SRST) && (--i > 0))
|
||||
udelay(1);
|
||||
|
||||
/* Can we enable the DMA support? */
|
||||
if (is_imx6q_uart(sport) && !uart_console(port) &&
|
||||
!sport->dma_is_inited)
|
||||
imx_uart_dma_init(sport);
|
||||
|
||||
spin_lock_irqsave(&sport->port.lock, flags);
|
||||
/*
|
||||
* Finally, clear and enable interrupts
|
||||
*/
|
||||
writel(USR1_RTSD, sport->port.membase + USR1);
|
||||
|
||||
if (sport->dma_is_inited && !sport->dma_is_enabled)
|
||||
imx_enable_dma(sport);
|
||||
|
||||
temp = readl(sport->port.membase + UCR1);
|
||||
temp |= UCR1_RRDYEN | UCR1_RTSDEN | UCR1_UARTEN;
|
||||
|
||||
@ -1124,6 +1199,14 @@ static int imx_startup(struct uart_port *port)
|
||||
|
||||
writel(temp, sport->port.membase + UCR1);
|
||||
|
||||
/* Clear any pending ORE flag before enabling interrupt */
|
||||
temp = readl(sport->port.membase + USR2);
|
||||
writel(temp | USR2_ORE, sport->port.membase + USR2);
|
||||
|
||||
temp = readl(sport->port.membase + UCR4);
|
||||
temp |= UCR4_OREN;
|
||||
writel(temp, sport->port.membase + UCR4);
|
||||
|
||||
temp = readl(sport->port.membase + UCR2);
|
||||
temp |= (UCR2_RXEN | UCR2_TXEN);
|
||||
if (!sport->have_rtscts)
|
||||
@ -1189,9 +1272,11 @@ static void imx_shutdown(struct uart_port *port)
|
||||
dmaengine_terminate_all(sport->dma_chan_tx);
|
||||
dmaengine_terminate_all(sport->dma_chan_rx);
|
||||
}
|
||||
spin_lock_irqsave(&sport->port.lock, flags);
|
||||
imx_stop_tx(port);
|
||||
imx_stop_rx(port);
|
||||
imx_disable_dma(sport);
|
||||
spin_unlock_irqrestore(&sport->port.lock, flags);
|
||||
imx_uart_dma_exit(sport);
|
||||
}
|
||||
|
||||
@ -1233,11 +1318,48 @@ static void imx_shutdown(struct uart_port *port)
|
||||
static void imx_flush_buffer(struct uart_port *port)
|
||||
{
|
||||
struct imx_port *sport = (struct imx_port *)port;
|
||||
struct scatterlist *sgl = &sport->tx_sgl[0];
|
||||
unsigned long temp;
|
||||
int i = 100, ubir, ubmr, ubrc, uts;
|
||||
|
||||
if (sport->dma_is_enabled) {
|
||||
sport->tx_bytes = 0;
|
||||
dmaengine_terminate_all(sport->dma_chan_tx);
|
||||
if (!sport->dma_chan_tx)
|
||||
return;
|
||||
|
||||
sport->tx_bytes = 0;
|
||||
dmaengine_terminate_all(sport->dma_chan_tx);
|
||||
if (sport->dma_is_txing) {
|
||||
dma_unmap_sg(sport->port.dev, sgl, sport->dma_tx_nents,
|
||||
DMA_TO_DEVICE);
|
||||
temp = readl(sport->port.membase + UCR1);
|
||||
temp &= ~UCR1_TDMAEN;
|
||||
writel(temp, sport->port.membase + UCR1);
|
||||
sport->dma_is_txing = false;
|
||||
}
|
||||
|
||||
/*
|
||||
* According to the Reference Manual description of the UART SRST bit:
|
||||
* "Reset the transmit and receive state machines,
|
||||
* all FIFOs and register USR1, USR2, UBIR, UBMR, UBRC, URXD, UTXD
|
||||
* and UTS[6-3]". As we don't need to restore the old values from
|
||||
* USR1, USR2, URXD, UTXD, only save/restore the other four registers
|
||||
*/
|
||||
ubir = readl(sport->port.membase + UBIR);
|
||||
ubmr = readl(sport->port.membase + UBMR);
|
||||
ubrc = readl(sport->port.membase + UBRC);
|
||||
uts = readl(sport->port.membase + IMX21_UTS);
|
||||
|
||||
temp = readl(sport->port.membase + UCR2);
|
||||
temp &= ~UCR2_SRST;
|
||||
writel(temp, sport->port.membase + UCR2);
|
||||
|
||||
while (!(readl(sport->port.membase + UCR2) & UCR2_SRST) && (--i > 0))
|
||||
udelay(1);
|
||||
|
||||
/* Restore the registers */
|
||||
writel(ubir, sport->port.membase + UBIR);
|
||||
writel(ubmr, sport->port.membase + UBMR);
|
||||
writel(ubrc, sport->port.membase + UBRC);
|
||||
writel(uts, sport->port.membase + IMX21_UTS);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -1280,11 +1402,6 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
|
||||
if (sport->have_rtscts) {
|
||||
ucr2 &= ~UCR2_IRTS;
|
||||
ucr2 |= UCR2_CTSC;
|
||||
|
||||
/* Can we enable the DMA support? */
|
||||
if (is_imx6q_uart(sport) && !uart_console(port)
|
||||
&& !sport->dma_is_inited)
|
||||
imx_uart_dma_init(sport);
|
||||
} else {
|
||||
termios->c_cflag &= ~CRTSCTS;
|
||||
}
|
||||
@ -1319,7 +1436,7 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
|
||||
*/
|
||||
sport->port.ignore_status_mask = 0;
|
||||
if (termios->c_iflag & IGNPAR)
|
||||
sport->port.ignore_status_mask |= URXD_PRERR;
|
||||
sport->port.ignore_status_mask |= URXD_PRERR | URXD_FRMERR;
|
||||
if (termios->c_iflag & IGNBRK) {
|
||||
sport->port.ignore_status_mask |= URXD_BRK;
|
||||
/*
|
||||
@ -1330,6 +1447,9 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
|
||||
sport->port.ignore_status_mask |= URXD_OVRRUN;
|
||||
}
|
||||
|
||||
if ((termios->c_cflag & CREAD) == 0)
|
||||
sport->port.ignore_status_mask |= URXD_DUMMY_READ;
|
||||
|
||||
/*
|
||||
* Update the per-port timeout.
|
||||
*/
|
||||
@ -1403,8 +1523,6 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
|
||||
if (UART_ENABLE_MS(&sport->port, termios->c_cflag))
|
||||
imx_enable_ms(&sport->port);
|
||||
|
||||
if (sport->dma_is_inited && !sport->dma_is_enabled)
|
||||
imx_enable_dma(sport);
|
||||
spin_unlock_irqrestore(&sport->port.lock, flags);
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
/*
|
||||
* mcf.c -- Freescale ColdFire UART driver
|
||||
*
|
||||
* (C) Copyright 2003-2007, Greg Ungerer <gerg@snapgear.com>
|
||||
* (C) Copyright 2003-2007, Greg Ungerer <gerg@uclinux.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -198,7 +198,6 @@ static void mcf_shutdown(struct uart_port *port)
|
||||
static void mcf_set_termios(struct uart_port *port, struct ktermios *termios,
|
||||
struct ktermios *old)
|
||||
{
|
||||
struct mcf_uart *pp = container_of(port, struct mcf_uart, port);
|
||||
unsigned long flags;
|
||||
unsigned int baud, baudclk;
|
||||
#if defined(CONFIG_M5272)
|
||||
@ -441,7 +440,6 @@ static int mcf_verify_port(struct uart_port *port, struct serial_struct *ser)
|
||||
/* Enable or disable the RS485 support */
|
||||
static int mcf_config_rs485(struct uart_port *port, struct serial_rs485 *rs485)
|
||||
{
|
||||
struct mcf_uart *pp = container_of(port, struct mcf_uart, port);
|
||||
unsigned char mr1, mr2;
|
||||
|
||||
/* Get mode registers */
|
||||
@ -631,6 +629,7 @@ static int mcf_probe(struct platform_device *pdev)
|
||||
port->mapbase = platp[i].mapbase;
|
||||
port->membase = (platp[i].membase) ? platp[i].membase :
|
||||
(unsigned char __iomem *) platp[i].mapbase;
|
||||
port->dev = &pdev->dev;
|
||||
port->iotype = SERIAL_IO_MEM;
|
||||
port->irq = platp[i].irq;
|
||||
port->uartclk = MCF_BUSCLK;
|
||||
@ -702,7 +701,7 @@ static void __exit mcf_exit(void)
|
||||
module_init(mcf_init);
|
||||
module_exit(mcf_exit);
|
||||
|
||||
MODULE_AUTHOR("Greg Ungerer <gerg@snapgear.com>");
|
||||
MODULE_AUTHOR("Greg Ungerer <gerg@uclinux.org>");
|
||||
MODULE_DESCRIPTION("Freescale ColdFire UART driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:mcfuart");
|
||||
|
@ -23,7 +23,6 @@
|
||||
#define MEN_Z135_MAX_PORTS 12
|
||||
#define MEN_Z135_BASECLK 29491200
|
||||
#define MEN_Z135_FIFO_SIZE 1024
|
||||
#define MEN_Z135_NUM_MSI_VECTORS 2
|
||||
#define MEN_Z135_FIFO_WATERMARK 1020
|
||||
|
||||
#define MEN_Z135_STAT_REG 0x0
|
||||
@ -34,12 +33,11 @@
|
||||
#define MEN_Z135_CONF_REG 0x808
|
||||
#define MEN_Z135_UART_FREQ 0x80c
|
||||
#define MEN_Z135_BAUD_REG 0x810
|
||||
#define MENZ135_TIMEOUT 0x814
|
||||
#define MEN_Z135_TIMEOUT 0x814
|
||||
|
||||
#define MEN_Z135_MEM_SIZE 0x818
|
||||
|
||||
#define IS_IRQ(x) ((x) & 1)
|
||||
#define IRQ_ID(x) (((x) >> 1) & 7)
|
||||
#define IRQ_ID(x) ((x) & 0x1f)
|
||||
|
||||
#define MEN_Z135_IER_RXCIEN BIT(0) /* RX Space IRQ */
|
||||
#define MEN_Z135_IER_TXCIEN BIT(1) /* TX Space IRQ */
|
||||
@ -94,11 +92,11 @@
|
||||
#define MEN_Z135_LSR_TEXP BIT(6)
|
||||
#define MEN_Z135_LSR_RXFIFOERR BIT(7)
|
||||
|
||||
#define MEN_Z135_IRQ_ID_MST 0
|
||||
#define MEN_Z135_IRQ_ID_TSA 1
|
||||
#define MEN_Z135_IRQ_ID_RDA 2
|
||||
#define MEN_Z135_IRQ_ID_RLS 3
|
||||
#define MEN_Z135_IRQ_ID_CTI 6
|
||||
#define MEN_Z135_IRQ_ID_RLS BIT(0)
|
||||
#define MEN_Z135_IRQ_ID_RDA BIT(1)
|
||||
#define MEN_Z135_IRQ_ID_CTI BIT(2)
|
||||
#define MEN_Z135_IRQ_ID_TSA BIT(3)
|
||||
#define MEN_Z135_IRQ_ID_MST BIT(4)
|
||||
|
||||
#define LCR(x) (((x) >> MEN_Z135_LCR_SHIFT) & 0xff)
|
||||
|
||||
@ -118,12 +116,18 @@ static int align;
|
||||
module_param(align, int, S_IRUGO);
|
||||
MODULE_PARM_DESC(align, "Keep hardware FIFO write pointer aligned, default 0");
|
||||
|
||||
static uint rx_timeout;
|
||||
module_param(rx_timeout, uint, S_IRUGO);
|
||||
MODULE_PARM_DESC(rx_timeout, "RX timeout. "
|
||||
"Timeout in seconds = (timeout_reg * baud_reg * 4) / freq_reg");
|
||||
|
||||
struct men_z135_port {
|
||||
struct uart_port port;
|
||||
struct mcb_device *mdev;
|
||||
unsigned char *rxbuf;
|
||||
u32 stat_reg;
|
||||
spinlock_t lock;
|
||||
bool automode;
|
||||
};
|
||||
#define to_men_z135(port) container_of((port), struct men_z135_port, port)
|
||||
|
||||
@ -180,12 +184,16 @@ static inline void men_z135_reg_clr(struct men_z135_port *uart,
|
||||
*/
|
||||
static void men_z135_handle_modem_status(struct men_z135_port *uart)
|
||||
{
|
||||
if (uart->stat_reg & MEN_Z135_MSR_DDCD)
|
||||
u8 msr;
|
||||
|
||||
msr = (uart->stat_reg >> 8) & 0xff;
|
||||
|
||||
if (msr & MEN_Z135_MSR_DDCD)
|
||||
uart_handle_dcd_change(&uart->port,
|
||||
uart->stat_reg & ~MEN_Z135_MSR_DCD);
|
||||
if (uart->stat_reg & MEN_Z135_MSR_DCTS)
|
||||
msr & MEN_Z135_MSR_DCD);
|
||||
if (msr & MEN_Z135_MSR_DCTS)
|
||||
uart_handle_cts_change(&uart->port,
|
||||
uart->stat_reg & ~MEN_Z135_MSR_CTS);
|
||||
msr & MEN_Z135_MSR_CTS);
|
||||
}
|
||||
|
||||
static void men_z135_handle_lsr(struct men_z135_port *uart)
|
||||
@ -322,7 +330,8 @@ static void men_z135_handle_tx(struct men_z135_port *uart)
|
||||
|
||||
txfree = MEN_Z135_FIFO_WATERMARK - txc;
|
||||
if (txfree <= 0) {
|
||||
pr_err("Not enough room in TX FIFO have %d, need %d\n",
|
||||
dev_err(&uart->mdev->dev,
|
||||
"Not enough room in TX FIFO have %d, need %d\n",
|
||||
txfree, qlen);
|
||||
goto irq_en;
|
||||
}
|
||||
@ -373,43 +382,54 @@ out:
|
||||
* @irq: The IRQ number
|
||||
* @data: Pointer to UART port
|
||||
*
|
||||
* Check IIR register to see which tasklet to start.
|
||||
* Check IIR register to find the cause of the interrupt and handle it.
|
||||
* It is possible that multiple interrupts reason bits are set and reading
|
||||
* the IIR is a destructive read, so we always need to check for all possible
|
||||
* interrupts and handle them.
|
||||
*/
|
||||
static irqreturn_t men_z135_intr(int irq, void *data)
|
||||
{
|
||||
struct men_z135_port *uart = (struct men_z135_port *)data;
|
||||
struct uart_port *port = &uart->port;
|
||||
bool handled = false;
|
||||
unsigned long flags;
|
||||
int irq_id;
|
||||
|
||||
uart->stat_reg = ioread32(port->membase + MEN_Z135_STAT_REG);
|
||||
/* IRQ pending is low active */
|
||||
if (IS_IRQ(uart->stat_reg))
|
||||
return IRQ_NONE;
|
||||
|
||||
irq_id = IRQ_ID(uart->stat_reg);
|
||||
switch (irq_id) {
|
||||
case MEN_Z135_IRQ_ID_MST:
|
||||
men_z135_handle_modem_status(uart);
|
||||
break;
|
||||
case MEN_Z135_IRQ_ID_TSA:
|
||||
men_z135_handle_tx(uart);
|
||||
break;
|
||||
case MEN_Z135_IRQ_ID_CTI:
|
||||
dev_dbg(&uart->mdev->dev, "Character Timeout Indication\n");
|
||||
/* Fallthrough */
|
||||
case MEN_Z135_IRQ_ID_RDA:
|
||||
/* Reading data clears RX IRQ */
|
||||
men_z135_handle_rx(uart);
|
||||
break;
|
||||
case MEN_Z135_IRQ_ID_RLS:
|
||||
|
||||
if (!irq_id)
|
||||
goto out;
|
||||
|
||||
spin_lock_irqsave(&port->lock, flags);
|
||||
/* It's save to write to IIR[7:6] RXC[9:8] */
|
||||
iowrite8(irq_id, port->membase + MEN_Z135_STAT_REG);
|
||||
|
||||
if (irq_id & MEN_Z135_IRQ_ID_RLS) {
|
||||
men_z135_handle_lsr(uart);
|
||||
break;
|
||||
default:
|
||||
dev_warn(&uart->mdev->dev, "Unknown IRQ id %d\n", irq_id);
|
||||
return IRQ_NONE;
|
||||
handled = true;
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
if (irq_id & (MEN_Z135_IRQ_ID_RDA | MEN_Z135_IRQ_ID_CTI)) {
|
||||
if (irq_id & MEN_Z135_IRQ_ID_CTI)
|
||||
dev_dbg(&uart->mdev->dev, "Character Timeout Indication\n");
|
||||
men_z135_handle_rx(uart);
|
||||
handled = true;
|
||||
}
|
||||
|
||||
if (irq_id & MEN_Z135_IRQ_ID_TSA) {
|
||||
men_z135_handle_tx(uart);
|
||||
handled = true;
|
||||
}
|
||||
|
||||
if (irq_id & MEN_Z135_IRQ_ID_MST) {
|
||||
men_z135_handle_modem_status(uart);
|
||||
handled = true;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&port->lock, flags);
|
||||
out:
|
||||
return IRQ_RETVAL(handled);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -464,21 +484,37 @@ static unsigned int men_z135_tx_empty(struct uart_port *port)
|
||||
*/
|
||||
static void men_z135_set_mctrl(struct uart_port *port, unsigned int mctrl)
|
||||
{
|
||||
struct men_z135_port *uart = to_men_z135(port);
|
||||
u32 conf_reg = 0;
|
||||
u32 old;
|
||||
u32 conf_reg;
|
||||
|
||||
conf_reg = old = ioread32(port->membase + MEN_Z135_CONF_REG);
|
||||
if (mctrl & TIOCM_RTS)
|
||||
conf_reg |= MEN_Z135_MCR_RTS;
|
||||
else
|
||||
conf_reg &= ~MEN_Z135_MCR_RTS;
|
||||
|
||||
if (mctrl & TIOCM_DTR)
|
||||
conf_reg |= MEN_Z135_MCR_DTR;
|
||||
else
|
||||
conf_reg &= ~MEN_Z135_MCR_DTR;
|
||||
|
||||
if (mctrl & TIOCM_OUT1)
|
||||
conf_reg |= MEN_Z135_MCR_OUT1;
|
||||
else
|
||||
conf_reg &= ~MEN_Z135_MCR_OUT1;
|
||||
|
||||
if (mctrl & TIOCM_OUT2)
|
||||
conf_reg |= MEN_Z135_MCR_OUT2;
|
||||
else
|
||||
conf_reg &= ~MEN_Z135_MCR_OUT2;
|
||||
|
||||
if (mctrl & TIOCM_LOOP)
|
||||
conf_reg |= MEN_Z135_MCR_LOOP;
|
||||
else
|
||||
conf_reg &= ~MEN_Z135_MCR_LOOP;
|
||||
|
||||
men_z135_reg_set(uart, MEN_Z135_CONF_REG, conf_reg);
|
||||
if (conf_reg != old)
|
||||
iowrite32(conf_reg, port->membase + MEN_Z135_CONF_REG);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -490,12 +526,9 @@ static void men_z135_set_mctrl(struct uart_port *port, unsigned int mctrl)
|
||||
static unsigned int men_z135_get_mctrl(struct uart_port *port)
|
||||
{
|
||||
unsigned int mctrl = 0;
|
||||
u32 stat_reg;
|
||||
u8 msr;
|
||||
|
||||
stat_reg = ioread32(port->membase + MEN_Z135_STAT_REG);
|
||||
|
||||
msr = ~((stat_reg >> 8) & 0xff);
|
||||
msr = ioread8(port->membase + MEN_Z135_STAT_REG + 1);
|
||||
|
||||
if (msr & MEN_Z135_MSR_CTS)
|
||||
mctrl |= TIOCM_CTS;
|
||||
@ -524,6 +557,19 @@ static void men_z135_stop_tx(struct uart_port *port)
|
||||
men_z135_reg_clr(uart, MEN_Z135_CONF_REG, MEN_Z135_IER_TXCIEN);
|
||||
}
|
||||
|
||||
/*
|
||||
* men_z135_disable_ms() - Disable Modem Status
|
||||
* port: The UART port
|
||||
*
|
||||
* Enable Modem Status IRQ.
|
||||
*/
|
||||
static void men_z135_disable_ms(struct uart_port *port)
|
||||
{
|
||||
struct men_z135_port *uart = to_men_z135(port);
|
||||
|
||||
men_z135_reg_clr(uart, MEN_Z135_CONF_REG, MEN_Z135_IER_MSIEN);
|
||||
}
|
||||
|
||||
/**
|
||||
* men_z135_start_tx() - Start transmitting characters
|
||||
* @port: The UART port
|
||||
@ -535,6 +581,9 @@ static void men_z135_start_tx(struct uart_port *port)
|
||||
{
|
||||
struct men_z135_port *uart = to_men_z135(port);
|
||||
|
||||
if (uart->automode)
|
||||
men_z135_disable_ms(port);
|
||||
|
||||
men_z135_handle_tx(uart);
|
||||
}
|
||||
|
||||
@ -584,6 +633,9 @@ static int men_z135_startup(struct uart_port *port)
|
||||
|
||||
iowrite32(conf_reg, port->membase + MEN_Z135_CONF_REG);
|
||||
|
||||
if (rx_timeout)
|
||||
iowrite32(rx_timeout, port->membase + MEN_Z135_TIMEOUT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -603,6 +655,7 @@ static void men_z135_set_termios(struct uart_port *port,
|
||||
struct ktermios *termios,
|
||||
struct ktermios *old)
|
||||
{
|
||||
struct men_z135_port *uart = to_men_z135(port);
|
||||
unsigned int baud;
|
||||
u32 conf_reg;
|
||||
u32 bd_reg;
|
||||
@ -643,6 +696,16 @@ static void men_z135_set_termios(struct uart_port *port,
|
||||
} else
|
||||
lcr |= MEN_Z135_PAR_DIS << MEN_Z135_PEN_SHIFT;
|
||||
|
||||
conf_reg |= MEN_Z135_IER_MSIEN;
|
||||
if (termios->c_cflag & CRTSCTS) {
|
||||
conf_reg |= MEN_Z135_MCR_RCFC;
|
||||
uart->automode = true;
|
||||
termios->c_cflag &= ~CLOCAL;
|
||||
} else {
|
||||
conf_reg &= ~MEN_Z135_MCR_RCFC;
|
||||
uart->automode = false;
|
||||
}
|
||||
|
||||
termios->c_cflag &= ~CMSPAR; /* Mark/Space parity is not supported */
|
||||
|
||||
conf_reg |= lcr << MEN_Z135_LCR_SHIFT;
|
||||
|
@ -1,909 +0,0 @@
|
||||
/*
|
||||
* mrst_max3110.c - spi uart protocol driver for Maxim 3110
|
||||
*
|
||||
* Copyright (c) 2008-2010, Intel Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Note:
|
||||
* 1. From Max3110 spec, the Rx FIFO has 8 words, while the Tx FIFO only has
|
||||
* 1 word. If SPI master controller doesn't support sclk frequency change,
|
||||
* then the char need be sent out one by one with some delay
|
||||
*
|
||||
* 2. Currently only RX available interrupt is used, no need for waiting TXE
|
||||
* interrupt for a low speed UART device
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#ifdef CONFIG_MAGIC_SYSRQ
|
||||
#define SUPPORT_SYSRQ
|
||||
#endif
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/console.h>
|
||||
#include <linux/tty.h>
|
||||
#include <linux/tty_flip.h>
|
||||
#include <linux/serial_core.h>
|
||||
#include <linux/serial_reg.h>
|
||||
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/pm.h>
|
||||
|
||||
#include "mrst_max3110.h"
|
||||
|
||||
#define UART_TX_NEEDED 1
|
||||
#define CON_TX_NEEDED 2
|
||||
#define BIT_IRQ_PENDING 3
|
||||
|
||||
struct uart_max3110 {
|
||||
struct uart_port port;
|
||||
struct spi_device *spi;
|
||||
char name[SPI_NAME_SIZE];
|
||||
|
||||
wait_queue_head_t wq;
|
||||
struct task_struct *main_thread;
|
||||
struct task_struct *read_thread;
|
||||
struct mutex thread_mutex;
|
||||
struct mutex io_mutex;
|
||||
|
||||
u32 baud;
|
||||
u16 cur_conf;
|
||||
u8 clock;
|
||||
u8 parity, word_7bits;
|
||||
u16 irq;
|
||||
|
||||
unsigned long uart_flags;
|
||||
|
||||
/* console related */
|
||||
struct circ_buf con_xmit;
|
||||
};
|
||||
|
||||
/* global data structure, may need be removed */
|
||||
static struct uart_max3110 *pmax;
|
||||
|
||||
static int receive_chars(struct uart_max3110 *max,
|
||||
unsigned short *str, int len);
|
||||
static int max3110_read_multi(struct uart_max3110 *max);
|
||||
static void max3110_con_receive(struct uart_max3110 *max);
|
||||
|
||||
static int max3110_write_then_read(struct uart_max3110 *max,
|
||||
const void *txbuf, void *rxbuf, unsigned len, int always_fast)
|
||||
{
|
||||
struct spi_device *spi = max->spi;
|
||||
struct spi_message message;
|
||||
struct spi_transfer x;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&max->io_mutex);
|
||||
spi_message_init(&message);
|
||||
memset(&x, 0, sizeof x);
|
||||
x.len = len;
|
||||
x.tx_buf = txbuf;
|
||||
x.rx_buf = rxbuf;
|
||||
spi_message_add_tail(&x, &message);
|
||||
|
||||
if (always_fast)
|
||||
x.speed_hz = spi->max_speed_hz;
|
||||
else if (max->baud)
|
||||
x.speed_hz = max->baud;
|
||||
|
||||
/* Do the i/o */
|
||||
ret = spi_sync(spi, &message);
|
||||
mutex_unlock(&max->io_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Write a 16b word to the device */
|
||||
static int max3110_out(struct uart_max3110 *max, const u16 out)
|
||||
{
|
||||
void *buf;
|
||||
u16 *obuf, *ibuf;
|
||||
int ret;
|
||||
|
||||
buf = kzalloc(8, GFP_KERNEL | GFP_DMA);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
obuf = buf;
|
||||
ibuf = buf + 4;
|
||||
*obuf = out;
|
||||
ret = max3110_write_then_read(max, obuf, ibuf, 2, 1);
|
||||
if (ret) {
|
||||
pr_warn("%s: get err msg %d when sending 0x%x\n",
|
||||
__func__, ret, out);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
receive_chars(max, ibuf, 1);
|
||||
|
||||
exit:
|
||||
kfree(buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is usually used to read data from SPIC RX FIFO, which doesn't
|
||||
* need any delay like flushing character out.
|
||||
*
|
||||
* Return how many valide bytes are read back
|
||||
*/
|
||||
static int max3110_read_multi(struct uart_max3110 *max)
|
||||
{
|
||||
void *buf;
|
||||
u16 *obuf, *ibuf;
|
||||
int ret, blen;
|
||||
|
||||
blen = M3110_RX_FIFO_DEPTH * sizeof(u16);
|
||||
buf = kzalloc(blen * 2, GFP_KERNEL | GFP_DMA);
|
||||
if (!buf)
|
||||
return 0;
|
||||
|
||||
/* tx/rx always have the same length */
|
||||
obuf = buf;
|
||||
ibuf = buf + blen;
|
||||
|
||||
if (max3110_write_then_read(max, obuf, ibuf, blen, 1)) {
|
||||
kfree(buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = receive_chars(max, ibuf, M3110_RX_FIFO_DEPTH);
|
||||
|
||||
kfree(buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void serial_m3110_con_putchar(struct uart_port *port, int ch)
|
||||
{
|
||||
struct uart_max3110 *max =
|
||||
container_of(port, struct uart_max3110, port);
|
||||
struct circ_buf *xmit = &max->con_xmit;
|
||||
|
||||
if (uart_circ_chars_free(xmit)) {
|
||||
xmit->buf[xmit->head] = (char)ch;
|
||||
xmit->head = (xmit->head + 1) & (PAGE_SIZE - 1);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Print a string to the serial port trying not to disturb
|
||||
* any possible real use of the port...
|
||||
*
|
||||
* The console_lock must be held when we get here.
|
||||
*/
|
||||
static void serial_m3110_con_write(struct console *co,
|
||||
const char *s, unsigned int count)
|
||||
{
|
||||
if (!pmax)
|
||||
return;
|
||||
|
||||
uart_console_write(&pmax->port, s, count, serial_m3110_con_putchar);
|
||||
|
||||
if (!test_and_set_bit(CON_TX_NEEDED, &pmax->uart_flags))
|
||||
wake_up(&pmax->wq);
|
||||
}
|
||||
|
||||
static int __init
|
||||
serial_m3110_con_setup(struct console *co, char *options)
|
||||
{
|
||||
struct uart_max3110 *max = pmax;
|
||||
int baud = 115200;
|
||||
int bits = 8;
|
||||
int parity = 'n';
|
||||
int flow = 'n';
|
||||
|
||||
pr_info("setting up console\n");
|
||||
|
||||
if (co->index == -1)
|
||||
co->index = 0;
|
||||
|
||||
if (!max) {
|
||||
pr_err("pmax is NULL, return\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (options)
|
||||
uart_parse_options(options, &baud, &parity, &bits, &flow);
|
||||
|
||||
return uart_set_options(&max->port, co, baud, parity, bits, flow);
|
||||
}
|
||||
|
||||
static struct tty_driver *serial_m3110_con_device(struct console *co,
|
||||
int *index)
|
||||
{
|
||||
struct uart_driver *p = co->data;
|
||||
*index = co->index;
|
||||
return p->tty_driver;
|
||||
}
|
||||
|
||||
static struct uart_driver serial_m3110_reg;
|
||||
static struct console serial_m3110_console = {
|
||||
.name = "ttyS",
|
||||
.write = serial_m3110_con_write,
|
||||
.device = serial_m3110_con_device,
|
||||
.setup = serial_m3110_con_setup,
|
||||
.flags = CON_PRINTBUFFER,
|
||||
.index = -1,
|
||||
.data = &serial_m3110_reg,
|
||||
};
|
||||
|
||||
static unsigned int serial_m3110_tx_empty(struct uart_port *port)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void serial_m3110_stop_tx(struct uart_port *port)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* stop_rx will be called in spin_lock env */
|
||||
static void serial_m3110_stop_rx(struct uart_port *port)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
#define WORDS_PER_XFER 128
|
||||
static void send_circ_buf(struct uart_max3110 *max,
|
||||
struct circ_buf *xmit)
|
||||
{
|
||||
void *buf;
|
||||
u16 *obuf, *ibuf;
|
||||
int i, len, blen, dma_size, left, ret = 0;
|
||||
|
||||
|
||||
dma_size = WORDS_PER_XFER * sizeof(u16) * 2;
|
||||
buf = kzalloc(dma_size, GFP_KERNEL | GFP_DMA);
|
||||
if (!buf)
|
||||
return;
|
||||
obuf = buf;
|
||||
ibuf = buf + dma_size/2;
|
||||
|
||||
while (!uart_circ_empty(xmit)) {
|
||||
left = uart_circ_chars_pending(xmit);
|
||||
while (left) {
|
||||
len = min(left, WORDS_PER_XFER);
|
||||
blen = len * sizeof(u16);
|
||||
memset(ibuf, 0, blen);
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
obuf[i] = (u8)xmit->buf[xmit->tail] | WD_TAG;
|
||||
xmit->tail = (xmit->tail + 1) &
|
||||
(UART_XMIT_SIZE - 1);
|
||||
}
|
||||
|
||||
/* Fail to send msg to console is not very critical */
|
||||
|
||||
ret = max3110_write_then_read(max, obuf, ibuf, blen, 0);
|
||||
if (ret)
|
||||
pr_warn("%s: get err msg %d\n", __func__, ret);
|
||||
|
||||
receive_chars(max, ibuf, len);
|
||||
|
||||
max->port.icount.tx += len;
|
||||
left -= len;
|
||||
}
|
||||
}
|
||||
|
||||
kfree(buf);
|
||||
}
|
||||
|
||||
static void transmit_char(struct uart_max3110 *max)
|
||||
{
|
||||
struct uart_port *port = &max->port;
|
||||
struct circ_buf *xmit = &port->state->xmit;
|
||||
|
||||
if (uart_circ_empty(xmit) || uart_tx_stopped(port))
|
||||
return;
|
||||
|
||||
send_circ_buf(max, xmit);
|
||||
|
||||
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
|
||||
uart_write_wakeup(port);
|
||||
|
||||
if (uart_circ_empty(xmit))
|
||||
serial_m3110_stop_tx(port);
|
||||
}
|
||||
|
||||
/*
|
||||
* This will be called by uart_write() and tty_write, can't
|
||||
* go to sleep
|
||||
*/
|
||||
static void serial_m3110_start_tx(struct uart_port *port)
|
||||
{
|
||||
struct uart_max3110 *max =
|
||||
container_of(port, struct uart_max3110, port);
|
||||
|
||||
if (!test_and_set_bit(UART_TX_NEEDED, &max->uart_flags))
|
||||
wake_up(&max->wq);
|
||||
}
|
||||
|
||||
static int
|
||||
receive_chars(struct uart_max3110 *max, unsigned short *str, int len)
|
||||
{
|
||||
struct uart_port *port = &max->port;
|
||||
struct tty_port *tport;
|
||||
char buf[M3110_RX_FIFO_DEPTH];
|
||||
int r, w, usable;
|
||||
|
||||
/* If uart is not opened, just return */
|
||||
if (!port->state)
|
||||
return 0;
|
||||
|
||||
tport = &port->state->port;
|
||||
|
||||
for (r = 0, w = 0; r < len; r++) {
|
||||
if (str[r] & MAX3110_BREAK &&
|
||||
uart_handle_break(port))
|
||||
continue;
|
||||
|
||||
if (str[r] & MAX3110_READ_DATA_AVAILABLE) {
|
||||
if (uart_handle_sysrq_char(port, str[r] & 0xff))
|
||||
continue;
|
||||
|
||||
buf[w++] = str[r] & 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
if (!w)
|
||||
return 0;
|
||||
|
||||
for (r = 0; w; r += usable, w -= usable) {
|
||||
usable = tty_buffer_request_room(tport, w);
|
||||
if (usable) {
|
||||
tty_insert_flip_string(tport, buf + r, usable);
|
||||
port->icount.rx += usable;
|
||||
}
|
||||
}
|
||||
tty_flip_buffer_push(tport);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/*
|
||||
* This routine will be used in read_thread or RX IRQ handling,
|
||||
* it will first do one round buffer read(8 words), if there is some
|
||||
* valid RX data, will try to read 5 more rounds till all data
|
||||
* is read out.
|
||||
*
|
||||
* Use stack space as data buffer to save some system load, and chose
|
||||
* 504 Btyes as a threadhold to do a bulk push to upper tty layer when
|
||||
* receiving bulk data, a much bigger buffer may cause stack overflow
|
||||
*/
|
||||
static void max3110_con_receive(struct uart_max3110 *max)
|
||||
{
|
||||
int loop = 1, num;
|
||||
|
||||
do {
|
||||
num = max3110_read_multi(max);
|
||||
|
||||
if (num) {
|
||||
loop = 5;
|
||||
}
|
||||
} while (--loop);
|
||||
}
|
||||
|
||||
static int max3110_main_thread(void *_max)
|
||||
{
|
||||
struct uart_max3110 *max = _max;
|
||||
wait_queue_head_t *wq = &max->wq;
|
||||
int ret = 0;
|
||||
struct circ_buf *xmit = &max->con_xmit;
|
||||
|
||||
pr_info("start main thread\n");
|
||||
|
||||
do {
|
||||
wait_event_interruptible(*wq,
|
||||
max->uart_flags || kthread_should_stop());
|
||||
|
||||
mutex_lock(&max->thread_mutex);
|
||||
|
||||
if (test_and_clear_bit(BIT_IRQ_PENDING, &max->uart_flags))
|
||||
max3110_con_receive(max);
|
||||
|
||||
/* first handle console output */
|
||||
if (test_and_clear_bit(CON_TX_NEEDED, &max->uart_flags))
|
||||
send_circ_buf(max, xmit);
|
||||
|
||||
/* handle uart output */
|
||||
if (test_and_clear_bit(UART_TX_NEEDED, &max->uart_flags))
|
||||
transmit_char(max);
|
||||
|
||||
mutex_unlock(&max->thread_mutex);
|
||||
|
||||
} while (!kthread_should_stop());
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static irqreturn_t serial_m3110_irq(int irq, void *dev_id)
|
||||
{
|
||||
struct uart_max3110 *max = dev_id;
|
||||
|
||||
/* max3110's irq is a falling edge, not level triggered,
|
||||
* so no need to disable the irq */
|
||||
|
||||
if (!test_and_set_bit(BIT_IRQ_PENDING, &max->uart_flags))
|
||||
wake_up(&max->wq);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/* if don't use RX IRQ, then need a thread to polling read */
|
||||
static int max3110_read_thread(void *_max)
|
||||
{
|
||||
struct uart_max3110 *max = _max;
|
||||
|
||||
pr_info("start read thread\n");
|
||||
do {
|
||||
/*
|
||||
* If can't acquire the mutex, it means the main thread
|
||||
* is running which will also perform the rx job
|
||||
*/
|
||||
if (mutex_trylock(&max->thread_mutex)) {
|
||||
max3110_con_receive(max);
|
||||
mutex_unlock(&max->thread_mutex);
|
||||
}
|
||||
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
schedule_timeout(HZ / 20);
|
||||
} while (!kthread_should_stop());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int serial_m3110_startup(struct uart_port *port)
|
||||
{
|
||||
struct uart_max3110 *max =
|
||||
container_of(port, struct uart_max3110, port);
|
||||
u16 config = 0;
|
||||
int ret = 0;
|
||||
|
||||
if (port->line != 0) {
|
||||
pr_err("uart port startup failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Disable all IRQ and config it to 115200, 8n1 */
|
||||
config = WC_TAG | WC_FIFO_ENABLE
|
||||
| WC_1_STOPBITS
|
||||
| WC_8BIT_WORD
|
||||
| WC_BAUD_DR2;
|
||||
|
||||
/* as we use thread to handle tx/rx, need set low latency */
|
||||
port->state->port.low_latency = 1;
|
||||
|
||||
if (max->irq) {
|
||||
/* Enable RX IRQ only */
|
||||
config |= WC_RXA_IRQ_ENABLE;
|
||||
} else {
|
||||
/* If IRQ is disabled, start a read thread for input data */
|
||||
max->read_thread =
|
||||
kthread_run(max3110_read_thread, max, "max3110_read");
|
||||
if (IS_ERR(max->read_thread)) {
|
||||
ret = PTR_ERR(max->read_thread);
|
||||
max->read_thread = NULL;
|
||||
pr_err("Can't create read thread!\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = max3110_out(max, config);
|
||||
if (ret) {
|
||||
if (max->read_thread)
|
||||
kthread_stop(max->read_thread);
|
||||
max->read_thread = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
max->cur_conf = config;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void serial_m3110_shutdown(struct uart_port *port)
|
||||
{
|
||||
struct uart_max3110 *max =
|
||||
container_of(port, struct uart_max3110, port);
|
||||
u16 config;
|
||||
|
||||
if (max->read_thread) {
|
||||
kthread_stop(max->read_thread);
|
||||
max->read_thread = NULL;
|
||||
}
|
||||
|
||||
/* Disable interrupts from this port */
|
||||
config = WC_TAG | WC_SW_SHDI;
|
||||
max3110_out(max, config);
|
||||
}
|
||||
|
||||
static void serial_m3110_release_port(struct uart_port *port)
|
||||
{
|
||||
}
|
||||
|
||||
static int serial_m3110_request_port(struct uart_port *port)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void serial_m3110_config_port(struct uart_port *port, int flags)
|
||||
{
|
||||
port->type = PORT_MAX3100;
|
||||
}
|
||||
|
||||
static int
|
||||
serial_m3110_verify_port(struct uart_port *port, struct serial_struct *ser)
|
||||
{
|
||||
/* we don't want the core code to modify any port params */
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
||||
static const char *serial_m3110_type(struct uart_port *port)
|
||||
{
|
||||
struct uart_max3110 *max =
|
||||
container_of(port, struct uart_max3110, port);
|
||||
return max->name;
|
||||
}
|
||||
|
||||
static void
|
||||
serial_m3110_set_termios(struct uart_port *port, struct ktermios *termios,
|
||||
struct ktermios *old)
|
||||
{
|
||||
struct uart_max3110 *max =
|
||||
container_of(port, struct uart_max3110, port);
|
||||
unsigned char cval;
|
||||
unsigned int baud, parity = 0;
|
||||
int clk_div = -1;
|
||||
u16 new_conf = max->cur_conf;
|
||||
|
||||
switch (termios->c_cflag & CSIZE) {
|
||||
case CS7:
|
||||
cval = UART_LCR_WLEN7;
|
||||
new_conf |= WC_7BIT_WORD;
|
||||
break;
|
||||
default:
|
||||
/* We only support CS7 & CS8 */
|
||||
termios->c_cflag &= ~CSIZE;
|
||||
termios->c_cflag |= CS8;
|
||||
case CS8:
|
||||
cval = UART_LCR_WLEN8;
|
||||
new_conf |= WC_8BIT_WORD;
|
||||
break;
|
||||
}
|
||||
|
||||
baud = uart_get_baud_rate(port, termios, old, 0, 230400);
|
||||
|
||||
/* First calc the div for 1.8MHZ clock case */
|
||||
switch (baud) {
|
||||
case 300:
|
||||
clk_div = WC_BAUD_DR384;
|
||||
break;
|
||||
case 600:
|
||||
clk_div = WC_BAUD_DR192;
|
||||
break;
|
||||
case 1200:
|
||||
clk_div = WC_BAUD_DR96;
|
||||
break;
|
||||
case 2400:
|
||||
clk_div = WC_BAUD_DR48;
|
||||
break;
|
||||
case 4800:
|
||||
clk_div = WC_BAUD_DR24;
|
||||
break;
|
||||
case 9600:
|
||||
clk_div = WC_BAUD_DR12;
|
||||
break;
|
||||
case 19200:
|
||||
clk_div = WC_BAUD_DR6;
|
||||
break;
|
||||
case 38400:
|
||||
clk_div = WC_BAUD_DR3;
|
||||
break;
|
||||
case 57600:
|
||||
clk_div = WC_BAUD_DR2;
|
||||
break;
|
||||
case 115200:
|
||||
clk_div = WC_BAUD_DR1;
|
||||
break;
|
||||
case 230400:
|
||||
if (max->clock & MAX3110_HIGH_CLK)
|
||||
break;
|
||||
default:
|
||||
/* Pick the previous baud rate */
|
||||
baud = max->baud;
|
||||
clk_div = max->cur_conf & WC_BAUD_DIV_MASK;
|
||||
tty_termios_encode_baud_rate(termios, baud, baud);
|
||||
}
|
||||
|
||||
if (max->clock & MAX3110_HIGH_CLK) {
|
||||
clk_div += 1;
|
||||
/* High clk version max3110 doesn't support B300 */
|
||||
if (baud == 300) {
|
||||
baud = 600;
|
||||
clk_div = WC_BAUD_DR384;
|
||||
}
|
||||
if (baud == 230400)
|
||||
clk_div = WC_BAUD_DR1;
|
||||
tty_termios_encode_baud_rate(termios, baud, baud);
|
||||
}
|
||||
|
||||
new_conf = (new_conf & ~WC_BAUD_DIV_MASK) | clk_div;
|
||||
|
||||
if (unlikely(termios->c_cflag & CMSPAR))
|
||||
termios->c_cflag &= ~CMSPAR;
|
||||
|
||||
if (termios->c_cflag & CSTOPB)
|
||||
new_conf |= WC_2_STOPBITS;
|
||||
else
|
||||
new_conf &= ~WC_2_STOPBITS;
|
||||
|
||||
if (termios->c_cflag & PARENB) {
|
||||
new_conf |= WC_PARITY_ENABLE;
|
||||
parity |= UART_LCR_PARITY;
|
||||
} else
|
||||
new_conf &= ~WC_PARITY_ENABLE;
|
||||
|
||||
if (!(termios->c_cflag & PARODD))
|
||||
parity |= UART_LCR_EPAR;
|
||||
max->parity = parity;
|
||||
|
||||
uart_update_timeout(port, termios->c_cflag, baud);
|
||||
|
||||
new_conf |= WC_TAG;
|
||||
if (new_conf != max->cur_conf) {
|
||||
if (!max3110_out(max, new_conf)) {
|
||||
max->cur_conf = new_conf;
|
||||
max->baud = baud;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Don't handle hw handshaking */
|
||||
static unsigned int serial_m3110_get_mctrl(struct uart_port *port)
|
||||
{
|
||||
return TIOCM_DSR | TIOCM_CAR | TIOCM_DSR;
|
||||
}
|
||||
|
||||
static void serial_m3110_set_mctrl(struct uart_port *port, unsigned int mctrl)
|
||||
{
|
||||
}
|
||||
|
||||
static void serial_m3110_break_ctl(struct uart_port *port, int break_state)
|
||||
{
|
||||
}
|
||||
|
||||
static void serial_m3110_pm(struct uart_port *port, unsigned int state,
|
||||
unsigned int oldstate)
|
||||
{
|
||||
}
|
||||
|
||||
static struct uart_ops serial_m3110_ops = {
|
||||
.tx_empty = serial_m3110_tx_empty,
|
||||
.set_mctrl = serial_m3110_set_mctrl,
|
||||
.get_mctrl = serial_m3110_get_mctrl,
|
||||
.stop_tx = serial_m3110_stop_tx,
|
||||
.start_tx = serial_m3110_start_tx,
|
||||
.stop_rx = serial_m3110_stop_rx,
|
||||
.break_ctl = serial_m3110_break_ctl,
|
||||
.startup = serial_m3110_startup,
|
||||
.shutdown = serial_m3110_shutdown,
|
||||
.set_termios = serial_m3110_set_termios,
|
||||
.pm = serial_m3110_pm,
|
||||
.type = serial_m3110_type,
|
||||
.release_port = serial_m3110_release_port,
|
||||
.request_port = serial_m3110_request_port,
|
||||
.config_port = serial_m3110_config_port,
|
||||
.verify_port = serial_m3110_verify_port,
|
||||
};
|
||||
|
||||
static struct uart_driver serial_m3110_reg = {
|
||||
.owner = THIS_MODULE,
|
||||
.driver_name = "MRST serial",
|
||||
.dev_name = "ttyS",
|
||||
.major = TTY_MAJOR,
|
||||
.minor = 64,
|
||||
.nr = 1,
|
||||
.cons = &serial_m3110_console,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int serial_m3110_suspend(struct device *dev)
|
||||
{
|
||||
struct spi_device *spi = to_spi_device(dev);
|
||||
struct uart_max3110 *max = spi_get_drvdata(spi);
|
||||
|
||||
if (max->irq > 0)
|
||||
disable_irq(max->irq);
|
||||
uart_suspend_port(&serial_m3110_reg, &max->port);
|
||||
max3110_out(max, max->cur_conf | WC_SW_SHDI);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int serial_m3110_resume(struct device *dev)
|
||||
{
|
||||
struct spi_device *spi = to_spi_device(dev);
|
||||
struct uart_max3110 *max = spi_get_drvdata(spi);
|
||||
|
||||
max3110_out(max, max->cur_conf);
|
||||
uart_resume_port(&serial_m3110_reg, &max->port);
|
||||
if (max->irq > 0)
|
||||
enable_irq(max->irq);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(serial_m3110_pm_ops, serial_m3110_suspend,
|
||||
serial_m3110_resume);
|
||||
#define SERIAL_M3110_PM_OPS (&serial_m3110_pm_ops)
|
||||
|
||||
#else
|
||||
#define SERIAL_M3110_PM_OPS NULL
|
||||
#endif
|
||||
|
||||
static int serial_m3110_probe(struct spi_device *spi)
|
||||
{
|
||||
struct uart_max3110 *max;
|
||||
void *buffer;
|
||||
u16 res;
|
||||
int ret = 0;
|
||||
|
||||
max = kzalloc(sizeof(*max), GFP_KERNEL);
|
||||
if (!max)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Set spi info */
|
||||
spi->bits_per_word = 16;
|
||||
max->clock = MAX3110_HIGH_CLK;
|
||||
|
||||
spi_setup(spi);
|
||||
|
||||
max->port.type = PORT_MAX3100;
|
||||
max->port.fifosize = 2; /* Only have 16b buffer */
|
||||
max->port.ops = &serial_m3110_ops;
|
||||
max->port.line = 0;
|
||||
max->port.dev = &spi->dev;
|
||||
max->port.uartclk = 115200;
|
||||
|
||||
max->spi = spi;
|
||||
strcpy(max->name, spi->modalias);
|
||||
max->irq = (u16)spi->irq;
|
||||
|
||||
mutex_init(&max->thread_mutex);
|
||||
mutex_init(&max->io_mutex);
|
||||
|
||||
max->word_7bits = 0;
|
||||
max->parity = 0;
|
||||
max->baud = 0;
|
||||
|
||||
max->cur_conf = 0;
|
||||
max->uart_flags = 0;
|
||||
|
||||
/* Check if reading configuration register returns something sane */
|
||||
|
||||
res = RC_TAG;
|
||||
ret = max3110_write_then_read(max, (u8 *)&res, (u8 *)&res, 2, 0);
|
||||
if (ret < 0 || res == 0 || res == 0xffff) {
|
||||
dev_dbg(&spi->dev, "MAX3111 deemed not present (conf reg %04x)",
|
||||
res);
|
||||
ret = -ENODEV;
|
||||
goto err_get_page;
|
||||
}
|
||||
|
||||
buffer = (void *)__get_free_page(GFP_KERNEL);
|
||||
if (!buffer) {
|
||||
ret = -ENOMEM;
|
||||
goto err_get_page;
|
||||
}
|
||||
max->con_xmit.buf = buffer;
|
||||
max->con_xmit.head = 0;
|
||||
max->con_xmit.tail = 0;
|
||||
|
||||
init_waitqueue_head(&max->wq);
|
||||
|
||||
max->main_thread = kthread_run(max3110_main_thread,
|
||||
max, "max3110_main");
|
||||
if (IS_ERR(max->main_thread)) {
|
||||
ret = PTR_ERR(max->main_thread);
|
||||
goto err_kthread;
|
||||
}
|
||||
|
||||
if (max->irq) {
|
||||
ret = request_irq(max->irq, serial_m3110_irq,
|
||||
IRQ_TYPE_EDGE_FALLING, "max3110", max);
|
||||
if (ret) {
|
||||
max->irq = 0;
|
||||
dev_warn(&spi->dev,
|
||||
"unable to allocate IRQ, will use polling method\n");
|
||||
}
|
||||
}
|
||||
|
||||
spi_set_drvdata(spi, max);
|
||||
pmax = max;
|
||||
|
||||
/* Give membase a psudo value to pass serial_core's check */
|
||||
max->port.membase = (unsigned char __iomem *)0xff110000;
|
||||
uart_add_one_port(&serial_m3110_reg, &max->port);
|
||||
|
||||
return 0;
|
||||
|
||||
err_kthread:
|
||||
free_page((unsigned long)buffer);
|
||||
err_get_page:
|
||||
kfree(max);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int serial_m3110_remove(struct spi_device *dev)
|
||||
{
|
||||
struct uart_max3110 *max = spi_get_drvdata(dev);
|
||||
|
||||
if (!max)
|
||||
return 0;
|
||||
|
||||
uart_remove_one_port(&serial_m3110_reg, &max->port);
|
||||
|
||||
free_page((unsigned long)max->con_xmit.buf);
|
||||
|
||||
if (max->irq)
|
||||
free_irq(max->irq, max);
|
||||
|
||||
if (max->main_thread)
|
||||
kthread_stop(max->main_thread);
|
||||
|
||||
kfree(max);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct spi_driver uart_max3110_driver = {
|
||||
.driver = {
|
||||
.name = "spi_max3111",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = SERIAL_M3110_PM_OPS,
|
||||
},
|
||||
.probe = serial_m3110_probe,
|
||||
.remove = serial_m3110_remove,
|
||||
};
|
||||
|
||||
static int __init serial_m3110_init(void)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
ret = uart_register_driver(&serial_m3110_reg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = spi_register_driver(&uart_max3110_driver);
|
||||
if (ret)
|
||||
uart_unregister_driver(&serial_m3110_reg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit serial_m3110_exit(void)
|
||||
{
|
||||
spi_unregister_driver(&uart_max3110_driver);
|
||||
uart_unregister_driver(&serial_m3110_reg);
|
||||
}
|
||||
|
||||
module_init(serial_m3110_init);
|
||||
module_exit(serial_m3110_exit);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("spi:max3110-uart");
|
@ -1,61 +0,0 @@
|
||||
#ifndef _MRST_MAX3110_H
|
||||
#define _MRST_MAX3110_H
|
||||
|
||||
#define MAX3110_HIGH_CLK 0x1 /* 3.6864 MHZ */
|
||||
#define MAX3110_LOW_CLK 0x0 /* 1.8432 MHZ */
|
||||
|
||||
/* status bits for all 4 MAX3110 operate modes */
|
||||
#define MAX3110_READ_DATA_AVAILABLE (1 << 15)
|
||||
#define MAX3110_WRITE_BUF_EMPTY (1 << 14)
|
||||
#define MAX3110_BREAK (1 << 10)
|
||||
|
||||
#define WC_TAG (3 << 14)
|
||||
#define RC_TAG (1 << 14)
|
||||
#define WD_TAG (2 << 14)
|
||||
#define RD_TAG (0 << 14)
|
||||
|
||||
/* bits def for write configuration */
|
||||
#define WC_FIFO_ENABLE_MASK (1 << 13)
|
||||
#define WC_FIFO_ENABLE (0 << 13)
|
||||
|
||||
#define WC_SW_SHDI (1 << 12)
|
||||
|
||||
#define WC_IRQ_MASK (0xF << 8)
|
||||
#define WC_TXE_IRQ_ENABLE (1 << 11) /* TX empty irq */
|
||||
#define WC_RXA_IRQ_ENABLE (1 << 10) /* RX available irq */
|
||||
#define WC_PAR_HIGH_IRQ_ENABLE (1 << 9)
|
||||
#define WC_REC_ACT_IRQ_ENABLE (1 << 8)
|
||||
|
||||
#define WC_IRDA_ENABLE (1 << 7)
|
||||
|
||||
#define WC_STOPBITS_MASK (1 << 6)
|
||||
#define WC_2_STOPBITS (1 << 6)
|
||||
#define WC_1_STOPBITS (0 << 6)
|
||||
|
||||
#define WC_PARITY_ENABLE_MASK (1 << 5)
|
||||
#define WC_PARITY_ENABLE (1 << 5)
|
||||
|
||||
#define WC_WORDLEN_MASK (1 << 4)
|
||||
#define WC_7BIT_WORD (1 << 4)
|
||||
#define WC_8BIT_WORD (0 << 4)
|
||||
|
||||
#define WC_BAUD_DIV_MASK (0xF)
|
||||
#define WC_BAUD_DR1 (0x0)
|
||||
#define WC_BAUD_DR2 (0x1)
|
||||
#define WC_BAUD_DR4 (0x2)
|
||||
#define WC_BAUD_DR8 (0x3)
|
||||
#define WC_BAUD_DR16 (0x4)
|
||||
#define WC_BAUD_DR32 (0x5)
|
||||
#define WC_BAUD_DR64 (0x6)
|
||||
#define WC_BAUD_DR128 (0x7)
|
||||
#define WC_BAUD_DR3 (0x8)
|
||||
#define WC_BAUD_DR6 (0x9)
|
||||
#define WC_BAUD_DR12 (0xA)
|
||||
#define WC_BAUD_DR24 (0xB)
|
||||
#define WC_BAUD_DR48 (0xC)
|
||||
#define WC_BAUD_DR96 (0xD)
|
||||
#define WC_BAUD_DR192 (0xE)
|
||||
#define WC_BAUD_DR384 (0xF)
|
||||
|
||||
#define M3110_RX_FIFO_DEPTH 8
|
||||
#endif
|
@ -920,14 +920,15 @@ static void msm_console_write(struct console *co, const char *s,
|
||||
static int __init msm_console_setup(struct console *co, char *options)
|
||||
{
|
||||
struct uart_port *port;
|
||||
struct msm_port *msm_port;
|
||||
int baud = 0, flow, bits, parity;
|
||||
int baud = 115200;
|
||||
int bits = 8;
|
||||
int parity = 'n';
|
||||
int flow = 'n';
|
||||
|
||||
if (unlikely(co->index >= UART_NR || co->index < 0))
|
||||
return -ENXIO;
|
||||
|
||||
port = get_port_from_line(co->index);
|
||||
msm_port = UART_TO_MSM(port);
|
||||
|
||||
if (unlikely(!port->membase))
|
||||
return -ENXIO;
|
||||
@ -937,23 +938,6 @@ static int __init msm_console_setup(struct console *co, char *options)
|
||||
if (options)
|
||||
uart_parse_options(options, &baud, &parity, &bits, &flow);
|
||||
|
||||
bits = 8;
|
||||
parity = 'n';
|
||||
flow = 'n';
|
||||
msm_write(port, UART_MR2_BITS_PER_CHAR_8 | UART_MR2_STOP_BIT_LEN_ONE,
|
||||
UART_MR2); /* 8N1 */
|
||||
|
||||
if (baud < 300 || baud > 115200)
|
||||
baud = 115200;
|
||||
msm_set_baud_rate(port, baud);
|
||||
|
||||
msm_reset(port);
|
||||
|
||||
if (msm_port->is_uartdm) {
|
||||
msm_write(port, UART_CR_CMD_PROTECTION_EN, UART_CR);
|
||||
msm_write(port, UART_CR_TX_ENABLE, UART_CR);
|
||||
}
|
||||
|
||||
pr_info("msm_serial: console setup on port #%d\n", port->line);
|
||||
|
||||
return uart_set_options(port, co, baud, parity, bits, flow);
|
||||
@ -1142,9 +1126,6 @@ static int __init msm_serial_init(void)
|
||||
|
||||
static void __exit msm_serial_exit(void)
|
||||
{
|
||||
#ifdef CONFIG_SERIAL_MSM_CONSOLE
|
||||
unregister_console(&msm_console);
|
||||
#endif
|
||||
platform_driver_unregister(&msm_platform_driver);
|
||||
uart_unregister_driver(&msm_uart_driver);
|
||||
}
|
||||
|
@ -152,8 +152,6 @@ struct mxs_auart_port {
|
||||
unsigned int mctrl_prev;
|
||||
enum mxs_auart_type devtype;
|
||||
|
||||
unsigned int irq;
|
||||
|
||||
struct clk *clk;
|
||||
struct device *dev;
|
||||
|
||||
@ -1228,37 +1226,32 @@ static int mxs_auart_probe(struct platform_device *pdev)
|
||||
of_match_device(mxs_auart_dt_ids, &pdev->dev);
|
||||
struct mxs_auart_port *s;
|
||||
u32 version;
|
||||
int ret = 0;
|
||||
int ret, irq;
|
||||
struct resource *r;
|
||||
|
||||
s = kzalloc(sizeof(struct mxs_auart_port), GFP_KERNEL);
|
||||
if (!s) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
s = devm_kzalloc(&pdev->dev, sizeof(*s), GFP_KERNEL);
|
||||
if (!s)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = serial_mxs_probe_dt(s, pdev);
|
||||
if (ret > 0)
|
||||
s->port.line = pdev->id < 0 ? 0 : pdev->id;
|
||||
else if (ret < 0)
|
||||
goto out_free;
|
||||
return ret;
|
||||
|
||||
if (of_id) {
|
||||
pdev->id_entry = of_id->data;
|
||||
s->devtype = pdev->id_entry->driver_data;
|
||||
}
|
||||
|
||||
s->clk = clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(s->clk)) {
|
||||
ret = PTR_ERR(s->clk);
|
||||
goto out_free;
|
||||
}
|
||||
s->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(s->clk))
|
||||
return PTR_ERR(s->clk);
|
||||
|
||||
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!r) {
|
||||
ret = -ENXIO;
|
||||
goto out_free_clk;
|
||||
}
|
||||
if (!r)
|
||||
return -ENXIO;
|
||||
|
||||
|
||||
s->port.mapbase = r->start;
|
||||
s->port.membase = ioremap(r->start, resource_size(r));
|
||||
@ -1271,11 +1264,15 @@ static int mxs_auart_probe(struct platform_device *pdev)
|
||||
|
||||
s->mctrl_prev = 0;
|
||||
|
||||
s->irq = platform_get_irq(pdev, 0);
|
||||
s->port.irq = s->irq;
|
||||
ret = request_irq(s->irq, mxs_auart_irq_handle, 0, dev_name(&pdev->dev), s);
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
s->port.irq = irq;
|
||||
ret = devm_request_irq(&pdev->dev, irq, mxs_auart_irq_handle, 0,
|
||||
dev_name(&pdev->dev), s);
|
||||
if (ret)
|
||||
goto out_free_clk;
|
||||
return ret;
|
||||
|
||||
platform_set_drvdata(pdev, s);
|
||||
|
||||
@ -1288,7 +1285,7 @@ static int mxs_auart_probe(struct platform_device *pdev)
|
||||
*/
|
||||
ret = mxs_auart_request_gpio_irq(s);
|
||||
if (ret)
|
||||
goto out_free_irq;
|
||||
return ret;
|
||||
|
||||
auart_port[s->port.line] = s;
|
||||
|
||||
@ -1307,14 +1304,7 @@ static int mxs_auart_probe(struct platform_device *pdev)
|
||||
|
||||
out_free_gpio_irq:
|
||||
mxs_auart_free_gpio_irq(s);
|
||||
out_free_irq:
|
||||
auart_port[pdev->id] = NULL;
|
||||
free_irq(s->irq, s);
|
||||
out_free_clk:
|
||||
clk_put(s->clk);
|
||||
out_free:
|
||||
kfree(s);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -1323,13 +1313,8 @@ static int mxs_auart_remove(struct platform_device *pdev)
|
||||
struct mxs_auart_port *s = platform_get_drvdata(pdev);
|
||||
|
||||
uart_remove_one_port(&auart_driver, &s->port);
|
||||
|
||||
auart_port[pdev->id] = NULL;
|
||||
|
||||
mxs_auart_free_gpio_irq(s);
|
||||
clk_put(s->clk);
|
||||
free_irq(s->irq, s);
|
||||
kfree(s);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -102,6 +102,11 @@ static int of_platform_serial_setup(struct platform_device *ofdev,
|
||||
if (of_property_read_u32(np, "fifo-size", &prop) == 0)
|
||||
port->fifosize = prop;
|
||||
|
||||
/* Check for a fixed line number */
|
||||
ret = of_alias_get_id(np, "serial");
|
||||
if (ret >= 0)
|
||||
port->line = ret;
|
||||
|
||||
port->irq = irq_of_parse_and_map(np, 0);
|
||||
port->iotype = UPIO_MEM;
|
||||
if (of_property_read_u32(np, "reg-io-width", &prop) == 0) {
|
||||
@ -128,6 +133,10 @@ static int of_platform_serial_setup(struct platform_device *ofdev,
|
||||
if (of_find_property(np, "no-loopback-test", NULL))
|
||||
port->flags |= UPF_SKIP_TEST;
|
||||
|
||||
ret = of_alias_get_id(np, "serial");
|
||||
if (ret >= 0)
|
||||
port->line = ret;
|
||||
|
||||
port->dev = &ofdev->dev;
|
||||
|
||||
switch (type) {
|
||||
@ -331,6 +340,10 @@ static struct of_device_id of_platform_serial_table[] = {
|
||||
.data = (void *)PORT_ALTR_16550_F64, },
|
||||
{ .compatible = "altr,16550-FIFO128",
|
||||
.data = (void *)PORT_ALTR_16550_F128, },
|
||||
{ .compatible = "mrvl,mmp-uart",
|
||||
.data = (void *)PORT_XSCALE, },
|
||||
{ .compatible = "mrvl,pxa-uart",
|
||||
.data = (void *)PORT_XSCALE, },
|
||||
#ifdef CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL
|
||||
{ .compatible = "ibm,qpace-nwp-serial",
|
||||
.data = (void *)PORT_NWPSERIAL, },
|
||||
|
@ -63,7 +63,7 @@
|
||||
#define UART_ERRATA_i202_MDR1_ACCESS BIT(0)
|
||||
#define UART_ERRATA_i291_DMA_FORCEIDLE BIT(1)
|
||||
|
||||
#define DEFAULT_CLK_SPEED 48000000 /* 48Mhz*/
|
||||
#define DEFAULT_CLK_SPEED 48000000 /* 48Mhz */
|
||||
|
||||
/* SCR register bitmasks */
|
||||
#define OMAP_UART_SCR_RX_TRIG_GRANU1_MASK (1 << 7)
|
||||
@ -93,7 +93,7 @@
|
||||
/* WER = 0x7F
|
||||
* Enable module level wakeup in WER reg
|
||||
*/
|
||||
#define OMAP_UART_WER_MOD_WKUP 0X7F
|
||||
#define OMAP_UART_WER_MOD_WKUP 0x7F
|
||||
|
||||
/* Enable XON/XOFF flow control on output */
|
||||
#define OMAP_UART_SW_TX 0x08
|
||||
@ -114,7 +114,7 @@ struct uart_omap_dma {
|
||||
dma_addr_t tx_buf_dma_phys;
|
||||
unsigned int uart_base;
|
||||
/*
|
||||
* Buffer for rx dma.It is not required for tx because the buffer
|
||||
* Buffer for rx dma. It is not required for tx because the buffer
|
||||
* comes from port structure.
|
||||
*/
|
||||
unsigned char *rx_buf;
|
||||
@ -151,7 +151,7 @@ struct uart_omap_port {
|
||||
int use_dma;
|
||||
/*
|
||||
* Some bits in registers are cleared on a read, so they must
|
||||
* be saved whenever the register is read but the bits will not
|
||||
* be saved whenever the register is read, but the bits will not
|
||||
* be immediately processed.
|
||||
*/
|
||||
unsigned int lsr_break_flag;
|
||||
@ -681,7 +681,7 @@ static unsigned int serial_omap_get_mctrl(struct uart_port *port)
|
||||
static void serial_omap_set_mctrl(struct uart_port *port, unsigned int mctrl)
|
||||
{
|
||||
struct uart_omap_port *up = to_uart_omap_port(port);
|
||||
unsigned char mcr = 0, old_mcr;
|
||||
unsigned char mcr = 0, old_mcr, lcr;
|
||||
|
||||
dev_dbg(up->port.dev, "serial_omap_set_mctrl+%d\n", up->port.line);
|
||||
if (mctrl & TIOCM_RTS)
|
||||
@ -701,6 +701,17 @@ static void serial_omap_set_mctrl(struct uart_port *port, unsigned int mctrl)
|
||||
UART_MCR_DTR | UART_MCR_RTS);
|
||||
up->mcr = old_mcr | mcr;
|
||||
serial_out(up, UART_MCR, up->mcr);
|
||||
|
||||
/* Turn off autoRTS if RTS is lowered; restore autoRTS if RTS raised */
|
||||
lcr = serial_in(up, UART_LCR);
|
||||
serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
|
||||
if ((mctrl & TIOCM_RTS) && (port->status & UPSTAT_AUTORTS))
|
||||
up->efr |= UART_EFR_RTS;
|
||||
else
|
||||
up->efr &= UART_EFR_RTS;
|
||||
serial_out(up, UART_EFR, up->efr);
|
||||
serial_out(up, UART_LCR, lcr);
|
||||
|
||||
pm_runtime_mark_last_busy(up->dev);
|
||||
pm_runtime_put_autosuspend(up->dev);
|
||||
}
|
||||
@ -756,8 +767,6 @@ static int serial_omap_startup(struct uart_port *port)
|
||||
* (they will be reenabled in set_termios())
|
||||
*/
|
||||
serial_omap_clear_fifos(up);
|
||||
/* For Hardware flow control */
|
||||
serial_out(up, UART_MCR, UART_MCR_RTS);
|
||||
|
||||
/*
|
||||
* Clear the interrupt registers.
|
||||
@ -1053,12 +1062,12 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios,
|
||||
|
||||
serial_out(up, UART_TI752_TCR, OMAP_UART_TCR_TRIG);
|
||||
|
||||
if (termios->c_cflag & CRTSCTS && up->port.flags & UPF_HARD_FLOW) {
|
||||
/* Enable AUTORTS and AUTOCTS */
|
||||
up->efr |= UART_EFR_CTS | UART_EFR_RTS;
|
||||
up->port.status &= ~(UPSTAT_AUTOCTS | UPSTAT_AUTORTS | UPSTAT_AUTOXOFF);
|
||||
|
||||
/* Ensure MCR RTS is asserted */
|
||||
up->mcr |= UART_MCR_RTS;
|
||||
if (termios->c_cflag & CRTSCTS && up->port.flags & UPF_HARD_FLOW) {
|
||||
/* Enable AUTOCTS (autoRTS is enabled when RTS is raised) */
|
||||
up->port.status |= UPSTAT_AUTOCTS | UPSTAT_AUTORTS;
|
||||
up->efr |= UART_EFR_CTS;
|
||||
} else {
|
||||
/* Disable AUTORTS and AUTOCTS */
|
||||
up->efr &= ~(UART_EFR_CTS | UART_EFR_RTS);
|
||||
@ -1081,8 +1090,10 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios,
|
||||
* Enable XON/XOFF flow control on output.
|
||||
* Transmit XON1, XOFF1
|
||||
*/
|
||||
if (termios->c_iflag & IXOFF)
|
||||
if (termios->c_iflag & IXOFF) {
|
||||
up->port.status |= UPSTAT_AUTOXOFF;
|
||||
up->efr |= OMAP_UART_SW_TX;
|
||||
}
|
||||
|
||||
/*
|
||||
* IXANY Flag:
|
||||
|
@ -28,6 +28,9 @@
|
||||
#define SUPPORT_SYSRQ
|
||||
#endif
|
||||
|
||||
#include <linux/dmaengine.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/io.h>
|
||||
@ -78,6 +81,10 @@ static void dbg(const char *fmt, ...)
|
||||
#define S3C24XX_SERIAL_MAJOR 204
|
||||
#define S3C24XX_SERIAL_MINOR 64
|
||||
|
||||
#define S3C24XX_TX_PIO 1
|
||||
#define S3C24XX_TX_DMA 2
|
||||
#define S3C24XX_RX_PIO 1
|
||||
#define S3C24XX_RX_DMA 2
|
||||
/* macros to change one thing to another */
|
||||
|
||||
#define tx_enabled(port) ((port)->unused[0])
|
||||
@ -154,39 +161,272 @@ static void s3c24xx_serial_rx_disable(struct uart_port *port)
|
||||
static void s3c24xx_serial_stop_tx(struct uart_port *port)
|
||||
{
|
||||
struct s3c24xx_uart_port *ourport = to_ourport(port);
|
||||
struct s3c24xx_uart_dma *dma = ourport->dma;
|
||||
struct circ_buf *xmit = &port->state->xmit;
|
||||
struct dma_tx_state state;
|
||||
int count;
|
||||
|
||||
if (tx_enabled(port)) {
|
||||
if (s3c24xx_serial_has_interrupt_mask(port))
|
||||
__set_bit(S3C64XX_UINTM_TXD,
|
||||
portaddrl(port, S3C64XX_UINTM));
|
||||
else
|
||||
disable_irq_nosync(ourport->tx_irq);
|
||||
tx_enabled(port) = 0;
|
||||
if (port->flags & UPF_CONS_FLOW)
|
||||
s3c24xx_serial_rx_enable(port);
|
||||
if (!tx_enabled(port))
|
||||
return;
|
||||
|
||||
if (s3c24xx_serial_has_interrupt_mask(port))
|
||||
__set_bit(S3C64XX_UINTM_TXD,
|
||||
portaddrl(port, S3C64XX_UINTM));
|
||||
else
|
||||
disable_irq_nosync(ourport->tx_irq);
|
||||
|
||||
if (dma && dma->tx_chan && ourport->tx_in_progress == S3C24XX_TX_DMA) {
|
||||
dmaengine_pause(dma->tx_chan);
|
||||
dmaengine_tx_status(dma->tx_chan, dma->tx_cookie, &state);
|
||||
dmaengine_terminate_all(dma->tx_chan);
|
||||
dma_sync_single_for_cpu(ourport->port.dev,
|
||||
dma->tx_transfer_addr, dma->tx_size, DMA_TO_DEVICE);
|
||||
async_tx_ack(dma->tx_desc);
|
||||
count = dma->tx_bytes_requested - state.residue;
|
||||
xmit->tail = (xmit->tail + count) & (UART_XMIT_SIZE - 1);
|
||||
port->icount.tx += count;
|
||||
}
|
||||
|
||||
tx_enabled(port) = 0;
|
||||
ourport->tx_in_progress = 0;
|
||||
|
||||
if (port->flags & UPF_CONS_FLOW)
|
||||
s3c24xx_serial_rx_enable(port);
|
||||
|
||||
ourport->tx_mode = 0;
|
||||
}
|
||||
|
||||
static void s3c24xx_serial_start_tx(struct uart_port *port)
|
||||
static void s3c24xx_serial_start_next_tx(struct s3c24xx_uart_port *ourport);
|
||||
|
||||
static void s3c24xx_serial_tx_dma_complete(void *args)
|
||||
{
|
||||
struct s3c24xx_uart_port *ourport = args;
|
||||
struct uart_port *port = &ourport->port;
|
||||
struct circ_buf *xmit = &port->state->xmit;
|
||||
struct s3c24xx_uart_dma *dma = ourport->dma;
|
||||
struct dma_tx_state state;
|
||||
unsigned long flags;
|
||||
int count;
|
||||
|
||||
|
||||
dmaengine_tx_status(dma->tx_chan, dma->tx_cookie, &state);
|
||||
count = dma->tx_bytes_requested - state.residue;
|
||||
async_tx_ack(dma->tx_desc);
|
||||
|
||||
dma_sync_single_for_cpu(ourport->port.dev, dma->tx_transfer_addr,
|
||||
dma->tx_size, DMA_TO_DEVICE);
|
||||
|
||||
spin_lock_irqsave(&port->lock, flags);
|
||||
|
||||
xmit->tail = (xmit->tail + count) & (UART_XMIT_SIZE - 1);
|
||||
port->icount.tx += count;
|
||||
ourport->tx_in_progress = 0;
|
||||
|
||||
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
|
||||
uart_write_wakeup(port);
|
||||
|
||||
s3c24xx_serial_start_next_tx(ourport);
|
||||
spin_unlock_irqrestore(&port->lock, flags);
|
||||
}
|
||||
|
||||
static void enable_tx_dma(struct s3c24xx_uart_port *ourport)
|
||||
{
|
||||
struct uart_port *port = &ourport->port;
|
||||
u32 ucon;
|
||||
|
||||
/* Mask Tx interrupt */
|
||||
if (s3c24xx_serial_has_interrupt_mask(port))
|
||||
__set_bit(S3C64XX_UINTM_TXD,
|
||||
portaddrl(port, S3C64XX_UINTM));
|
||||
else
|
||||
disable_irq_nosync(ourport->tx_irq);
|
||||
|
||||
/* Enable tx dma mode */
|
||||
ucon = rd_regl(port, S3C2410_UCON);
|
||||
ucon &= ~(S3C64XX_UCON_TXBURST_MASK | S3C64XX_UCON_TXMODE_MASK);
|
||||
ucon |= (dma_get_cache_alignment() >= 16) ?
|
||||
S3C64XX_UCON_TXBURST_16 : S3C64XX_UCON_TXBURST_1;
|
||||
ucon |= S3C64XX_UCON_TXMODE_DMA;
|
||||
wr_regl(port, S3C2410_UCON, ucon);
|
||||
|
||||
ourport->tx_mode = S3C24XX_TX_DMA;
|
||||
}
|
||||
|
||||
static void enable_tx_pio(struct s3c24xx_uart_port *ourport)
|
||||
{
|
||||
struct uart_port *port = &ourport->port;
|
||||
u32 ucon, ufcon;
|
||||
|
||||
/* Set ufcon txtrig */
|
||||
ourport->tx_in_progress = S3C24XX_TX_PIO;
|
||||
ufcon = rd_regl(port, S3C2410_UFCON);
|
||||
wr_regl(port, S3C2410_UFCON, ufcon);
|
||||
|
||||
/* Enable tx pio mode */
|
||||
ucon = rd_regl(port, S3C2410_UCON);
|
||||
ucon &= ~(S3C64XX_UCON_TXMODE_MASK);
|
||||
ucon |= S3C64XX_UCON_TXMODE_CPU;
|
||||
wr_regl(port, S3C2410_UCON, ucon);
|
||||
|
||||
/* Unmask Tx interrupt */
|
||||
if (s3c24xx_serial_has_interrupt_mask(port))
|
||||
__clear_bit(S3C64XX_UINTM_TXD,
|
||||
portaddrl(port, S3C64XX_UINTM));
|
||||
else
|
||||
enable_irq(ourport->tx_irq);
|
||||
|
||||
ourport->tx_mode = S3C24XX_TX_PIO;
|
||||
}
|
||||
|
||||
static void s3c24xx_serial_start_tx_pio(struct s3c24xx_uart_port *ourport)
|
||||
{
|
||||
if (ourport->tx_mode != S3C24XX_TX_PIO)
|
||||
enable_tx_pio(ourport);
|
||||
}
|
||||
|
||||
static int s3c24xx_serial_start_tx_dma(struct s3c24xx_uart_port *ourport,
|
||||
unsigned int count)
|
||||
{
|
||||
struct uart_port *port = &ourport->port;
|
||||
struct circ_buf *xmit = &port->state->xmit;
|
||||
struct s3c24xx_uart_dma *dma = ourport->dma;
|
||||
|
||||
|
||||
if (ourport->tx_mode != S3C24XX_TX_DMA)
|
||||
enable_tx_dma(ourport);
|
||||
|
||||
while (xmit->tail & (dma_get_cache_alignment() - 1)) {
|
||||
if (rd_regl(port, S3C2410_UFSTAT) & ourport->info->tx_fifofull)
|
||||
return 0;
|
||||
wr_regb(port, S3C2410_UTXH, xmit->buf[xmit->tail]);
|
||||
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
|
||||
port->icount.tx++;
|
||||
count--;
|
||||
}
|
||||
|
||||
dma->tx_size = count & ~(dma_get_cache_alignment() - 1);
|
||||
dma->tx_transfer_addr = dma->tx_addr + xmit->tail;
|
||||
|
||||
dma_sync_single_for_device(ourport->port.dev, dma->tx_transfer_addr,
|
||||
dma->tx_size, DMA_TO_DEVICE);
|
||||
|
||||
dma->tx_desc = dmaengine_prep_slave_single(dma->tx_chan,
|
||||
dma->tx_transfer_addr, dma->tx_size,
|
||||
DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT);
|
||||
if (!dma->tx_desc) {
|
||||
dev_err(ourport->port.dev, "Unable to get desc for Tx\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
dma->tx_desc->callback = s3c24xx_serial_tx_dma_complete;
|
||||
dma->tx_desc->callback_param = ourport;
|
||||
dma->tx_bytes_requested = dma->tx_size;
|
||||
|
||||
ourport->tx_in_progress = S3C24XX_TX_DMA;
|
||||
dma->tx_cookie = dmaengine_submit(dma->tx_desc);
|
||||
dma_async_issue_pending(dma->tx_chan);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void s3c24xx_serial_start_next_tx(struct s3c24xx_uart_port *ourport)
|
||||
{
|
||||
struct uart_port *port = &ourport->port;
|
||||
struct circ_buf *xmit = &port->state->xmit;
|
||||
unsigned long count;
|
||||
|
||||
/* Get data size up to the end of buffer */
|
||||
count = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
|
||||
|
||||
if (!count) {
|
||||
s3c24xx_serial_stop_tx(port);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ourport->dma || !ourport->dma->tx_chan || count < port->fifosize)
|
||||
s3c24xx_serial_start_tx_pio(ourport);
|
||||
else
|
||||
s3c24xx_serial_start_tx_dma(ourport, count);
|
||||
}
|
||||
|
||||
void s3c24xx_serial_start_tx(struct uart_port *port)
|
||||
{
|
||||
struct s3c24xx_uart_port *ourport = to_ourport(port);
|
||||
struct circ_buf *xmit = &port->state->xmit;
|
||||
|
||||
if (!tx_enabled(port)) {
|
||||
if (port->flags & UPF_CONS_FLOW)
|
||||
s3c24xx_serial_rx_disable(port);
|
||||
|
||||
if (s3c24xx_serial_has_interrupt_mask(port))
|
||||
__clear_bit(S3C64XX_UINTM_TXD,
|
||||
portaddrl(port, S3C64XX_UINTM));
|
||||
else
|
||||
enable_irq(ourport->tx_irq);
|
||||
tx_enabled(port) = 1;
|
||||
if (!ourport->dma || !ourport->dma->tx_chan)
|
||||
s3c24xx_serial_start_tx_pio(ourport);
|
||||
}
|
||||
|
||||
if (ourport->dma && ourport->dma->tx_chan) {
|
||||
if (!uart_circ_empty(xmit) && !ourport->tx_in_progress)
|
||||
s3c24xx_serial_start_next_tx(ourport);
|
||||
}
|
||||
}
|
||||
|
||||
static void s3c24xx_uart_copy_rx_to_tty(struct s3c24xx_uart_port *ourport,
|
||||
struct tty_port *tty, int count)
|
||||
{
|
||||
struct s3c24xx_uart_dma *dma = ourport->dma;
|
||||
int copied;
|
||||
|
||||
if (!count)
|
||||
return;
|
||||
|
||||
dma_sync_single_for_cpu(ourport->port.dev, dma->rx_addr,
|
||||
dma->rx_size, DMA_FROM_DEVICE);
|
||||
|
||||
ourport->port.icount.rx += count;
|
||||
if (!tty) {
|
||||
dev_err(ourport->port.dev, "No tty port\n");
|
||||
return;
|
||||
}
|
||||
copied = tty_insert_flip_string(tty,
|
||||
((unsigned char *)(ourport->dma->rx_buf)), count);
|
||||
if (copied != count) {
|
||||
WARN_ON(1);
|
||||
dev_err(ourport->port.dev, "RxData copy to tty layer failed\n");
|
||||
}
|
||||
}
|
||||
|
||||
static int s3c24xx_serial_rx_fifocnt(struct s3c24xx_uart_port *ourport,
|
||||
unsigned long ufstat);
|
||||
|
||||
static void uart_rx_drain_fifo(struct s3c24xx_uart_port *ourport)
|
||||
{
|
||||
struct uart_port *port = &ourport->port;
|
||||
struct tty_port *tty = &port->state->port;
|
||||
unsigned int ch, ufstat;
|
||||
unsigned int count;
|
||||
|
||||
ufstat = rd_regl(port, S3C2410_UFSTAT);
|
||||
count = s3c24xx_serial_rx_fifocnt(ourport, ufstat);
|
||||
|
||||
if (!count)
|
||||
return;
|
||||
|
||||
while (count-- > 0) {
|
||||
ch = rd_regb(port, S3C2410_URXH);
|
||||
|
||||
ourport->port.icount.rx++;
|
||||
tty_insert_flip_char(tty, ch, TTY_NORMAL);
|
||||
}
|
||||
|
||||
tty_flip_buffer_push(tty);
|
||||
}
|
||||
|
||||
static void s3c24xx_serial_stop_rx(struct uart_port *port)
|
||||
{
|
||||
struct s3c24xx_uart_port *ourport = to_ourport(port);
|
||||
struct s3c24xx_uart_dma *dma = ourport->dma;
|
||||
struct tty_port *t = &port->state->port;
|
||||
struct dma_tx_state state;
|
||||
enum dma_status dma_status;
|
||||
unsigned int received;
|
||||
|
||||
if (rx_enabled(port)) {
|
||||
dbg("s3c24xx_serial_stop_rx: port=%p\n", port);
|
||||
@ -197,6 +437,17 @@ static void s3c24xx_serial_stop_rx(struct uart_port *port)
|
||||
disable_irq_nosync(ourport->rx_irq);
|
||||
rx_enabled(port) = 0;
|
||||
}
|
||||
if (dma && dma->rx_chan) {
|
||||
dmaengine_pause(dma->tx_chan);
|
||||
dma_status = dmaengine_tx_status(dma->rx_chan,
|
||||
dma->rx_cookie, &state);
|
||||
if (dma_status == DMA_IN_PROGRESS ||
|
||||
dma_status == DMA_PAUSED) {
|
||||
received = dma->rx_bytes_requested - state.residue;
|
||||
dmaengine_terminate_all(dma->rx_chan);
|
||||
s3c24xx_uart_copy_rx_to_tty(ourport, t, received);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline struct s3c24xx_uart_info
|
||||
@ -228,12 +479,157 @@ static int s3c24xx_serial_rx_fifocnt(struct s3c24xx_uart_port *ourport,
|
||||
return (ufstat & info->rx_fifomask) >> info->rx_fifoshift;
|
||||
}
|
||||
|
||||
static void s3c64xx_start_rx_dma(struct s3c24xx_uart_port *ourport);
|
||||
static void s3c24xx_serial_rx_dma_complete(void *args)
|
||||
{
|
||||
struct s3c24xx_uart_port *ourport = args;
|
||||
struct uart_port *port = &ourport->port;
|
||||
|
||||
struct s3c24xx_uart_dma *dma = ourport->dma;
|
||||
struct tty_port *t = &port->state->port;
|
||||
struct tty_struct *tty = tty_port_tty_get(&ourport->port.state->port);
|
||||
|
||||
struct dma_tx_state state;
|
||||
unsigned long flags;
|
||||
int received;
|
||||
|
||||
dmaengine_tx_status(dma->rx_chan, dma->rx_cookie, &state);
|
||||
received = dma->rx_bytes_requested - state.residue;
|
||||
async_tx_ack(dma->rx_desc);
|
||||
|
||||
spin_lock_irqsave(&port->lock, flags);
|
||||
|
||||
if (received)
|
||||
s3c24xx_uart_copy_rx_to_tty(ourport, t, received);
|
||||
|
||||
if (tty) {
|
||||
tty_flip_buffer_push(t);
|
||||
tty_kref_put(tty);
|
||||
}
|
||||
|
||||
s3c64xx_start_rx_dma(ourport);
|
||||
|
||||
spin_unlock_irqrestore(&port->lock, flags);
|
||||
}
|
||||
|
||||
static void s3c64xx_start_rx_dma(struct s3c24xx_uart_port *ourport)
|
||||
{
|
||||
struct s3c24xx_uart_dma *dma = ourport->dma;
|
||||
|
||||
dma_sync_single_for_device(ourport->port.dev, dma->rx_addr,
|
||||
dma->rx_size, DMA_FROM_DEVICE);
|
||||
|
||||
dma->rx_desc = dmaengine_prep_slave_single(dma->rx_chan,
|
||||
dma->rx_addr, dma->rx_size, DMA_DEV_TO_MEM,
|
||||
DMA_PREP_INTERRUPT);
|
||||
if (!dma->rx_desc) {
|
||||
dev_err(ourport->port.dev, "Unable to get desc for Rx\n");
|
||||
return;
|
||||
}
|
||||
|
||||
dma->rx_desc->callback = s3c24xx_serial_rx_dma_complete;
|
||||
dma->rx_desc->callback_param = ourport;
|
||||
dma->rx_bytes_requested = dma->rx_size;
|
||||
|
||||
dma->rx_cookie = dmaengine_submit(dma->rx_desc);
|
||||
dma_async_issue_pending(dma->rx_chan);
|
||||
}
|
||||
|
||||
/* ? - where has parity gone?? */
|
||||
#define S3C2410_UERSTAT_PARITY (0x1000)
|
||||
|
||||
static irqreturn_t
|
||||
s3c24xx_serial_rx_chars(int irq, void *dev_id)
|
||||
static void enable_rx_dma(struct s3c24xx_uart_port *ourport)
|
||||
{
|
||||
struct uart_port *port = &ourport->port;
|
||||
unsigned int ucon;
|
||||
|
||||
/* set Rx mode to DMA mode */
|
||||
ucon = rd_regl(port, S3C2410_UCON);
|
||||
ucon &= ~(S3C64XX_UCON_RXBURST_MASK |
|
||||
S3C64XX_UCON_TIMEOUT_MASK |
|
||||
S3C64XX_UCON_EMPTYINT_EN |
|
||||
S3C64XX_UCON_DMASUS_EN |
|
||||
S3C64XX_UCON_TIMEOUT_EN |
|
||||
S3C64XX_UCON_RXMODE_MASK);
|
||||
ucon |= S3C64XX_UCON_RXBURST_16 |
|
||||
0xf << S3C64XX_UCON_TIMEOUT_SHIFT |
|
||||
S3C64XX_UCON_EMPTYINT_EN |
|
||||
S3C64XX_UCON_TIMEOUT_EN |
|
||||
S3C64XX_UCON_RXMODE_DMA;
|
||||
wr_regl(port, S3C2410_UCON, ucon);
|
||||
|
||||
ourport->rx_mode = S3C24XX_RX_DMA;
|
||||
}
|
||||
|
||||
static void enable_rx_pio(struct s3c24xx_uart_port *ourport)
|
||||
{
|
||||
struct uart_port *port = &ourport->port;
|
||||
unsigned int ucon;
|
||||
|
||||
/* set Rx mode to DMA mode */
|
||||
ucon = rd_regl(port, S3C2410_UCON);
|
||||
ucon &= ~(S3C64XX_UCON_TIMEOUT_MASK |
|
||||
S3C64XX_UCON_EMPTYINT_EN |
|
||||
S3C64XX_UCON_DMASUS_EN |
|
||||
S3C64XX_UCON_TIMEOUT_EN |
|
||||
S3C64XX_UCON_RXMODE_MASK);
|
||||
ucon |= 0xf << S3C64XX_UCON_TIMEOUT_SHIFT |
|
||||
S3C64XX_UCON_TIMEOUT_EN |
|
||||
S3C64XX_UCON_RXMODE_CPU;
|
||||
wr_regl(port, S3C2410_UCON, ucon);
|
||||
|
||||
ourport->rx_mode = S3C24XX_RX_PIO;
|
||||
}
|
||||
|
||||
static irqreturn_t s3c24xx_serial_rx_chars_dma(int irq, void *dev_id)
|
||||
{
|
||||
unsigned int utrstat, ufstat, received;
|
||||
struct s3c24xx_uart_port *ourport = dev_id;
|
||||
struct uart_port *port = &ourport->port;
|
||||
struct s3c24xx_uart_dma *dma = ourport->dma;
|
||||
struct tty_struct *tty = tty_port_tty_get(&ourport->port.state->port);
|
||||
struct tty_port *t = &port->state->port;
|
||||
unsigned long flags;
|
||||
struct dma_tx_state state;
|
||||
|
||||
utrstat = rd_regl(port, S3C2410_UTRSTAT);
|
||||
ufstat = rd_regl(port, S3C2410_UFSTAT);
|
||||
|
||||
spin_lock_irqsave(&port->lock, flags);
|
||||
|
||||
if (!(utrstat & S3C2410_UTRSTAT_TIMEOUT)) {
|
||||
s3c64xx_start_rx_dma(ourport);
|
||||
if (ourport->rx_mode == S3C24XX_RX_PIO)
|
||||
enable_rx_dma(ourport);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if (ourport->rx_mode == S3C24XX_RX_DMA) {
|
||||
dmaengine_pause(dma->rx_chan);
|
||||
dmaengine_tx_status(dma->rx_chan, dma->rx_cookie, &state);
|
||||
dmaengine_terminate_all(dma->rx_chan);
|
||||
received = dma->rx_bytes_requested - state.residue;
|
||||
s3c24xx_uart_copy_rx_to_tty(ourport, t, received);
|
||||
|
||||
enable_rx_pio(ourport);
|
||||
}
|
||||
|
||||
uart_rx_drain_fifo(ourport);
|
||||
|
||||
if (tty) {
|
||||
tty_flip_buffer_push(t);
|
||||
tty_kref_put(tty);
|
||||
}
|
||||
|
||||
wr_regl(port, S3C2410_UTRSTAT, S3C2410_UTRSTAT_TIMEOUT);
|
||||
|
||||
finish:
|
||||
spin_unlock_irqrestore(&port->lock, flags);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t s3c24xx_serial_rx_chars_pio(int irq, void *dev_id)
|
||||
{
|
||||
struct s3c24xx_uart_port *ourport = dev_id;
|
||||
struct uart_port *port = &ourport->port;
|
||||
@ -324,16 +720,33 @@ out:
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
|
||||
static irqreturn_t s3c24xx_serial_rx_chars(int irq, void *dev_id)
|
||||
{
|
||||
struct s3c24xx_uart_port *ourport = dev_id;
|
||||
|
||||
if (ourport->dma && ourport->dma->rx_chan)
|
||||
return s3c24xx_serial_rx_chars_dma(irq, dev_id);
|
||||
return s3c24xx_serial_rx_chars_pio(irq, dev_id);
|
||||
}
|
||||
|
||||
static irqreturn_t s3c24xx_serial_tx_chars(int irq, void *id)
|
||||
{
|
||||
struct s3c24xx_uart_port *ourport = id;
|
||||
struct uart_port *port = &ourport->port;
|
||||
struct circ_buf *xmit = &port->state->xmit;
|
||||
unsigned long flags;
|
||||
int count = port->fifosize;
|
||||
int count;
|
||||
|
||||
spin_lock_irqsave(&port->lock, flags);
|
||||
|
||||
count = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
|
||||
|
||||
if (ourport->dma && ourport->dma->tx_chan && count >= port->fifosize) {
|
||||
s3c24xx_serial_start_tx_dma(ourport, count);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (port->x_char) {
|
||||
wr_regb(port, S3C2410_UTXH, port->x_char);
|
||||
port->icount.tx++;
|
||||
@ -352,6 +765,7 @@ static irqreturn_t s3c24xx_serial_tx_chars(int irq, void *id)
|
||||
|
||||
/* try and drain the buffer... */
|
||||
|
||||
count = port->fifosize;
|
||||
while (!uart_circ_empty(xmit) && count-- > 0) {
|
||||
if (rd_regl(port, S3C2410_UFSTAT) & ourport->info->tx_fifofull)
|
||||
break;
|
||||
@ -453,6 +867,93 @@ static void s3c24xx_serial_break_ctl(struct uart_port *port, int break_state)
|
||||
spin_unlock_irqrestore(&port->lock, flags);
|
||||
}
|
||||
|
||||
static int s3c24xx_serial_request_dma(struct s3c24xx_uart_port *p)
|
||||
{
|
||||
struct s3c24xx_uart_dma *dma = p->dma;
|
||||
dma_cap_mask_t mask;
|
||||
unsigned long flags;
|
||||
|
||||
/* Default slave configuration parameters */
|
||||
dma->rx_conf.direction = DMA_DEV_TO_MEM;
|
||||
dma->rx_conf.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
|
||||
dma->rx_conf.src_addr = p->port.mapbase + S3C2410_URXH;
|
||||
dma->rx_conf.src_maxburst = 16;
|
||||
|
||||
dma->tx_conf.direction = DMA_MEM_TO_DEV;
|
||||
dma->tx_conf.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
|
||||
dma->tx_conf.dst_addr = p->port.mapbase + S3C2410_UTXH;
|
||||
if (dma_get_cache_alignment() >= 16)
|
||||
dma->tx_conf.dst_maxburst = 16;
|
||||
else
|
||||
dma->tx_conf.dst_maxburst = 1;
|
||||
|
||||
dma_cap_zero(mask);
|
||||
dma_cap_set(DMA_SLAVE, mask);
|
||||
|
||||
dma->rx_chan = dma_request_slave_channel_compat(mask, dma->fn,
|
||||
dma->rx_param, p->port.dev, "rx");
|
||||
if (!dma->rx_chan)
|
||||
return -ENODEV;
|
||||
|
||||
dmaengine_slave_config(dma->rx_chan, &dma->rx_conf);
|
||||
|
||||
dma->tx_chan = dma_request_slave_channel_compat(mask, dma->fn,
|
||||
dma->tx_param, p->port.dev, "tx");
|
||||
if (!dma->tx_chan) {
|
||||
dma_release_channel(dma->rx_chan);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
dmaengine_slave_config(dma->tx_chan, &dma->tx_conf);
|
||||
|
||||
/* RX buffer */
|
||||
dma->rx_size = PAGE_SIZE;
|
||||
|
||||
dma->rx_buf = kmalloc(dma->rx_size, GFP_KERNEL);
|
||||
|
||||
if (!dma->rx_buf) {
|
||||
dma_release_channel(dma->rx_chan);
|
||||
dma_release_channel(dma->tx_chan);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
dma->rx_addr = dma_map_single(dma->rx_chan->device->dev, dma->rx_buf,
|
||||
dma->rx_size, DMA_FROM_DEVICE);
|
||||
|
||||
spin_lock_irqsave(&p->port.lock, flags);
|
||||
|
||||
/* TX buffer */
|
||||
dma->tx_addr = dma_map_single(dma->tx_chan->device->dev,
|
||||
p->port.state->xmit.buf,
|
||||
UART_XMIT_SIZE, DMA_TO_DEVICE);
|
||||
|
||||
spin_unlock_irqrestore(&p->port.lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void s3c24xx_serial_release_dma(struct s3c24xx_uart_port *p)
|
||||
{
|
||||
struct s3c24xx_uart_dma *dma = p->dma;
|
||||
|
||||
if (dma->rx_chan) {
|
||||
dmaengine_terminate_all(dma->rx_chan);
|
||||
dma_unmap_single(dma->rx_chan->device->dev, dma->rx_addr,
|
||||
dma->rx_size, DMA_FROM_DEVICE);
|
||||
kfree(dma->rx_buf);
|
||||
dma_release_channel(dma->rx_chan);
|
||||
dma->rx_chan = NULL;
|
||||
}
|
||||
|
||||
if (dma->tx_chan) {
|
||||
dmaengine_terminate_all(dma->tx_chan);
|
||||
dma_unmap_single(dma->tx_chan->device->dev, dma->tx_addr,
|
||||
UART_XMIT_SIZE, DMA_TO_DEVICE);
|
||||
dma_release_channel(dma->tx_chan);
|
||||
dma->tx_chan = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void s3c24xx_serial_shutdown(struct uart_port *port)
|
||||
{
|
||||
struct s3c24xx_uart_port *ourport = to_ourport(port);
|
||||
@ -478,6 +979,11 @@ static void s3c24xx_serial_shutdown(struct uart_port *port)
|
||||
wr_regl(port, S3C64XX_UINTP, 0xf);
|
||||
wr_regl(port, S3C64XX_UINTM, 0xf);
|
||||
}
|
||||
|
||||
if (ourport->dma)
|
||||
s3c24xx_serial_release_dma(ourport);
|
||||
|
||||
ourport->tx_in_progress = 0;
|
||||
}
|
||||
|
||||
static int s3c24xx_serial_startup(struct uart_port *port)
|
||||
@ -529,12 +1035,21 @@ err:
|
||||
static int s3c64xx_serial_startup(struct uart_port *port)
|
||||
{
|
||||
struct s3c24xx_uart_port *ourport = to_ourport(port);
|
||||
unsigned long flags;
|
||||
unsigned int ufcon;
|
||||
int ret;
|
||||
|
||||
dbg("s3c64xx_serial_startup: port=%p (%08llx,%p)\n",
|
||||
port, (unsigned long long)port->mapbase, port->membase);
|
||||
|
||||
wr_regl(port, S3C64XX_UINTM, 0xf);
|
||||
if (ourport->dma) {
|
||||
ret = s3c24xx_serial_request_dma(ourport);
|
||||
if (ret < 0) {
|
||||
dev_warn(port->dev, "DMA request failed\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = request_irq(port->irq, s3c64xx_serial_handle_irq, IRQF_SHARED,
|
||||
s3c24xx_serial_portname(port), ourport);
|
||||
@ -549,8 +1064,20 @@ static int s3c64xx_serial_startup(struct uart_port *port)
|
||||
tx_enabled(port) = 0;
|
||||
ourport->tx_claimed = 1;
|
||||
|
||||
spin_lock_irqsave(&port->lock, flags);
|
||||
|
||||
ufcon = rd_regl(port, S3C2410_UFCON);
|
||||
ufcon |= S3C2410_UFCON_RESETRX | S3C2410_UFCON_RESETTX |
|
||||
S5PV210_UFCON_RXTRIG8;
|
||||
wr_regl(port, S3C2410_UFCON, ufcon);
|
||||
|
||||
enable_rx_pio(ourport);
|
||||
|
||||
spin_unlock_irqrestore(&port->lock, flags);
|
||||
|
||||
/* Enable Rx Interrupt */
|
||||
__clear_bit(S3C64XX_UINTM_RXD, portaddrl(port, S3C64XX_UINTM));
|
||||
|
||||
dbg("s3c64xx_serial_startup ok\n");
|
||||
return ret;
|
||||
}
|
||||
@ -1209,6 +1736,18 @@ static int s3c24xx_serial_init_port(struct s3c24xx_uart_port *ourport,
|
||||
ret = platform_get_irq(platdev, 1);
|
||||
if (ret > 0)
|
||||
ourport->tx_irq = ret;
|
||||
/*
|
||||
* DMA is currently supported only on DT platforms, if DMA properties
|
||||
* are specified.
|
||||
*/
|
||||
if (platdev->dev.of_node && of_find_property(platdev->dev.of_node,
|
||||
"dmas", NULL)) {
|
||||
ourport->dma = devm_kzalloc(port->dev,
|
||||
sizeof(*ourport->dma),
|
||||
GFP_KERNEL);
|
||||
if (!ourport->dma)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ourport->clk = clk_get(&platdev->dev, "uart");
|
||||
if (IS_ERR(ourport->clk)) {
|
||||
@ -1857,6 +2396,111 @@ static struct platform_driver samsung_serial_driver = {
|
||||
|
||||
module_platform_driver(samsung_serial_driver);
|
||||
|
||||
#ifdef CONFIG_SERIAL_SAMSUNG_CONSOLE
|
||||
/*
|
||||
* Early console.
|
||||
*/
|
||||
|
||||
struct samsung_early_console_data {
|
||||
u32 txfull_mask;
|
||||
};
|
||||
|
||||
static void samsung_early_busyuart(struct uart_port *port)
|
||||
{
|
||||
while (!(readl(port->membase + S3C2410_UTRSTAT) & S3C2410_UTRSTAT_TXFE))
|
||||
;
|
||||
}
|
||||
|
||||
static void samsung_early_busyuart_fifo(struct uart_port *port)
|
||||
{
|
||||
struct samsung_early_console_data *data = port->private_data;
|
||||
|
||||
while (readl(port->membase + S3C2410_UFSTAT) & data->txfull_mask)
|
||||
;
|
||||
}
|
||||
|
||||
static void samsung_early_putc(struct uart_port *port, int c)
|
||||
{
|
||||
if (readl(port->membase + S3C2410_UFCON) & S3C2410_UFCON_FIFOMODE)
|
||||
samsung_early_busyuart_fifo(port);
|
||||
else
|
||||
samsung_early_busyuart(port);
|
||||
|
||||
writeb(c, port->membase + S3C2410_UTXH);
|
||||
}
|
||||
|
||||
static void samsung_early_write(struct console *con, const char *s, unsigned n)
|
||||
{
|
||||
struct earlycon_device *dev = con->data;
|
||||
|
||||
uart_console_write(&dev->port, s, n, samsung_early_putc);
|
||||
}
|
||||
|
||||
static int __init samsung_early_console_setup(struct earlycon_device *device,
|
||||
const char *opt)
|
||||
{
|
||||
if (!device->port.membase)
|
||||
return -ENODEV;
|
||||
|
||||
device->con->write = samsung_early_write;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* S3C2410 */
|
||||
static struct samsung_early_console_data s3c2410_early_console_data = {
|
||||
.txfull_mask = S3C2410_UFSTAT_TXFULL,
|
||||
};
|
||||
|
||||
static int __init s3c2410_early_console_setup(struct earlycon_device *device,
|
||||
const char *opt)
|
||||
{
|
||||
device->port.private_data = &s3c2410_early_console_data;
|
||||
return samsung_early_console_setup(device, opt);
|
||||
}
|
||||
OF_EARLYCON_DECLARE(s3c2410, "samsung,s3c2410-uart",
|
||||
s3c2410_early_console_setup);
|
||||
EARLYCON_DECLARE(s3c2410, s3c2410_early_console_setup);
|
||||
|
||||
/* S3C2412, S3C2440, S3C64xx */
|
||||
static struct samsung_early_console_data s3c2440_early_console_data = {
|
||||
.txfull_mask = S3C2440_UFSTAT_TXFULL,
|
||||
};
|
||||
|
||||
static int __init s3c2440_early_console_setup(struct earlycon_device *device,
|
||||
const char *opt)
|
||||
{
|
||||
device->port.private_data = &s3c2440_early_console_data;
|
||||
return samsung_early_console_setup(device, opt);
|
||||
}
|
||||
OF_EARLYCON_DECLARE(s3c2412, "samsung,s3c2412-uart",
|
||||
s3c2440_early_console_setup);
|
||||
OF_EARLYCON_DECLARE(s3c2440, "samsung,s3c2440-uart",
|
||||
s3c2440_early_console_setup);
|
||||
OF_EARLYCON_DECLARE(s3c6400, "samsung,s3c6400-uart",
|
||||
s3c2440_early_console_setup);
|
||||
EARLYCON_DECLARE(s3c2412, s3c2440_early_console_setup);
|
||||
EARLYCON_DECLARE(s3c2440, s3c2440_early_console_setup);
|
||||
EARLYCON_DECLARE(s3c6400, s3c2440_early_console_setup);
|
||||
|
||||
/* S5PV210, EXYNOS */
|
||||
static struct samsung_early_console_data s5pv210_early_console_data = {
|
||||
.txfull_mask = S5PV210_UFSTAT_TXFULL,
|
||||
};
|
||||
|
||||
static int __init s5pv210_early_console_setup(struct earlycon_device *device,
|
||||
const char *opt)
|
||||
{
|
||||
device->port.private_data = &s5pv210_early_console_data;
|
||||
return samsung_early_console_setup(device, opt);
|
||||
}
|
||||
OF_EARLYCON_DECLARE(s5pv210, "samsung,s5pv210-uart",
|
||||
s5pv210_early_console_setup);
|
||||
OF_EARLYCON_DECLARE(exynos4210, "samsung,exynos4210-uart",
|
||||
s5pv210_early_console_setup);
|
||||
EARLYCON_DECLARE(s5pv210, s5pv210_early_console_setup);
|
||||
EARLYCON_DECLARE(exynos4210, s5pv210_early_console_setup);
|
||||
#endif
|
||||
|
||||
MODULE_ALIAS("platform:samsung-uart");
|
||||
MODULE_DESCRIPTION("Samsung SoC Serial port driver");
|
||||
MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
|
||||
|
@ -12,6 +12,8 @@
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/dmaengine.h>
|
||||
|
||||
struct s3c24xx_uart_info {
|
||||
char *name;
|
||||
unsigned int type;
|
||||
@ -41,6 +43,40 @@ struct s3c24xx_serial_drv_data {
|
||||
unsigned int fifosize[CONFIG_SERIAL_SAMSUNG_UARTS];
|
||||
};
|
||||
|
||||
struct s3c24xx_uart_dma {
|
||||
dma_filter_fn fn;
|
||||
void *rx_param;
|
||||
void *tx_param;
|
||||
|
||||
unsigned int rx_chan_id;
|
||||
unsigned int tx_chan_id;
|
||||
|
||||
struct dma_slave_config rx_conf;
|
||||
struct dma_slave_config tx_conf;
|
||||
|
||||
struct dma_chan *rx_chan;
|
||||
struct dma_chan *tx_chan;
|
||||
|
||||
dma_addr_t rx_addr;
|
||||
dma_addr_t tx_addr;
|
||||
|
||||
dma_cookie_t rx_cookie;
|
||||
dma_cookie_t tx_cookie;
|
||||
|
||||
char *rx_buf;
|
||||
|
||||
dma_addr_t tx_transfer_addr;
|
||||
|
||||
size_t rx_size;
|
||||
size_t tx_size;
|
||||
|
||||
struct dma_async_tx_descriptor *tx_desc;
|
||||
struct dma_async_tx_descriptor *rx_desc;
|
||||
|
||||
int tx_bytes_requested;
|
||||
int rx_bytes_requested;
|
||||
};
|
||||
|
||||
struct s3c24xx_uart_port {
|
||||
unsigned char rx_claimed;
|
||||
unsigned char tx_claimed;
|
||||
@ -50,6 +86,10 @@ struct s3c24xx_uart_port {
|
||||
unsigned int rx_irq;
|
||||
unsigned int tx_irq;
|
||||
|
||||
unsigned int tx_in_progress;
|
||||
unsigned int tx_mode;
|
||||
unsigned int rx_mode;
|
||||
|
||||
struct s3c24xx_uart_info *info;
|
||||
struct clk *clk;
|
||||
struct clk *baudclk;
|
||||
@ -59,6 +99,8 @@ struct s3c24xx_uart_port {
|
||||
/* reference to platform data */
|
||||
struct s3c2410_uartcfg *cfg;
|
||||
|
||||
struct s3c24xx_uart_dma *dma;
|
||||
|
||||
#ifdef CONFIG_CPU_FREQ
|
||||
struct notifier_block freq_transition;
|
||||
#endif
|
||||
|
@ -179,14 +179,6 @@ static int uart_port_startup(struct tty_struct *tty, struct uart_state *state,
|
||||
if (tty->termios.c_cflag & CBAUD)
|
||||
uart_set_mctrl(uport, TIOCM_RTS | TIOCM_DTR);
|
||||
}
|
||||
|
||||
spin_lock_irq(&uport->lock);
|
||||
if (uart_cts_enabled(uport) &&
|
||||
!(uport->ops->get_mctrl(uport) & TIOCM_CTS))
|
||||
uport->hw_stopped = 1;
|
||||
else
|
||||
uport->hw_stopped = 0;
|
||||
spin_unlock_irq(&uport->lock);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -442,6 +434,7 @@ static void uart_change_speed(struct tty_struct *tty, struct uart_state *state,
|
||||
{
|
||||
struct uart_port *uport = state->uart_port;
|
||||
struct ktermios *termios;
|
||||
int hw_stopped;
|
||||
|
||||
/*
|
||||
* If we have no tty, termios, or the port does not exist,
|
||||
@ -466,6 +459,18 @@ static void uart_change_speed(struct tty_struct *tty, struct uart_state *state,
|
||||
uport->status &= ~UPSTAT_DCD_ENABLE;
|
||||
else
|
||||
uport->status |= UPSTAT_DCD_ENABLE;
|
||||
|
||||
/* reset sw-assisted CTS flow control based on (possibly) new mode */
|
||||
hw_stopped = uport->hw_stopped;
|
||||
uport->hw_stopped = uart_softcts_mode(uport) &&
|
||||
!(uport->ops->get_mctrl(uport) & TIOCM_CTS);
|
||||
if (uport->hw_stopped) {
|
||||
if (!hw_stopped)
|
||||
uport->ops->stop_tx(uport);
|
||||
} else {
|
||||
if (hw_stopped)
|
||||
__uart_start(tty);
|
||||
}
|
||||
spin_unlock_irq(&uport->lock);
|
||||
}
|
||||
|
||||
@ -619,22 +624,22 @@ static void uart_throttle(struct tty_struct *tty)
|
||||
{
|
||||
struct uart_state *state = tty->driver_data;
|
||||
struct uart_port *port = state->uart_port;
|
||||
upf_t mask = 0;
|
||||
upstat_t mask = 0;
|
||||
|
||||
if (I_IXOFF(tty))
|
||||
mask |= UPF_SOFT_FLOW;
|
||||
mask |= UPSTAT_AUTOXOFF;
|
||||
if (tty->termios.c_cflag & CRTSCTS)
|
||||
mask |= UPF_HARD_FLOW;
|
||||
mask |= UPSTAT_AUTORTS;
|
||||
|
||||
if (port->flags & mask) {
|
||||
if (port->status & mask) {
|
||||
port->ops->throttle(port);
|
||||
mask &= ~port->flags;
|
||||
mask &= ~port->status;
|
||||
}
|
||||
|
||||
if (mask & UPF_SOFT_FLOW)
|
||||
if (mask & UPSTAT_AUTOXOFF)
|
||||
uart_send_xchar(tty, STOP_CHAR(tty));
|
||||
|
||||
if (mask & UPF_HARD_FLOW)
|
||||
if (mask & UPSTAT_AUTORTS)
|
||||
uart_clear_mctrl(port, TIOCM_RTS);
|
||||
}
|
||||
|
||||
@ -642,22 +647,22 @@ static void uart_unthrottle(struct tty_struct *tty)
|
||||
{
|
||||
struct uart_state *state = tty->driver_data;
|
||||
struct uart_port *port = state->uart_port;
|
||||
upf_t mask = 0;
|
||||
upstat_t mask = 0;
|
||||
|
||||
if (I_IXOFF(tty))
|
||||
mask |= UPF_SOFT_FLOW;
|
||||
mask |= UPSTAT_AUTOXOFF;
|
||||
if (tty->termios.c_cflag & CRTSCTS)
|
||||
mask |= UPF_HARD_FLOW;
|
||||
mask |= UPSTAT_AUTORTS;
|
||||
|
||||
if (port->flags & mask) {
|
||||
if (port->status & mask) {
|
||||
port->ops->unthrottle(port);
|
||||
mask &= ~port->flags;
|
||||
mask &= ~port->status;
|
||||
}
|
||||
|
||||
if (mask & UPF_SOFT_FLOW)
|
||||
if (mask & UPSTAT_AUTOXOFF)
|
||||
uart_send_xchar(tty, START_CHAR(tty));
|
||||
|
||||
if (mask & UPF_HARD_FLOW)
|
||||
if (mask & UPSTAT_AUTORTS)
|
||||
uart_set_mctrl(port, TIOCM_RTS);
|
||||
}
|
||||
|
||||
@ -1351,30 +1356,6 @@ static void uart_set_termios(struct tty_struct *tty,
|
||||
mask |= TIOCM_RTS;
|
||||
uart_set_mctrl(uport, mask);
|
||||
}
|
||||
|
||||
/*
|
||||
* If the port is doing h/w assisted flow control, do nothing.
|
||||
* We assume that port->hw_stopped has never been set.
|
||||
*/
|
||||
if (uport->flags & UPF_HARD_FLOW)
|
||||
return;
|
||||
|
||||
/* Handle turning off CRTSCTS */
|
||||
if ((old_termios->c_cflag & CRTSCTS) && !(cflag & CRTSCTS)) {
|
||||
spin_lock_irq(&uport->lock);
|
||||
uport->hw_stopped = 0;
|
||||
__uart_start(tty);
|
||||
spin_unlock_irq(&uport->lock);
|
||||
}
|
||||
/* Handle turning on CRTSCTS */
|
||||
else if (!(old_termios->c_cflag & CRTSCTS) && (cflag & CRTSCTS)) {
|
||||
spin_lock_irq(&uport->lock);
|
||||
if (!(uport->ops->get_mctrl(uport) & TIOCM_CTS)) {
|
||||
uport->hw_stopped = 1;
|
||||
uport->ops->stop_tx(uport);
|
||||
}
|
||||
spin_unlock_irq(&uport->lock);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -2008,23 +1989,24 @@ int uart_suspend_port(struct uart_driver *drv, struct uart_port *uport)
|
||||
}
|
||||
put_device(tty_dev);
|
||||
|
||||
if (console_suspend_enabled || !uart_console(uport))
|
||||
uport->suspended = 1;
|
||||
/* Nothing to do if the console is not suspending */
|
||||
if (!console_suspend_enabled && uart_console(uport))
|
||||
goto unlock;
|
||||
|
||||
uport->suspended = 1;
|
||||
|
||||
if (port->flags & ASYNC_INITIALIZED) {
|
||||
const struct uart_ops *ops = uport->ops;
|
||||
int tries;
|
||||
|
||||
if (console_suspend_enabled || !uart_console(uport)) {
|
||||
set_bit(ASYNCB_SUSPENDED, &port->flags);
|
||||
clear_bit(ASYNCB_INITIALIZED, &port->flags);
|
||||
set_bit(ASYNCB_SUSPENDED, &port->flags);
|
||||
clear_bit(ASYNCB_INITIALIZED, &port->flags);
|
||||
|
||||
spin_lock_irq(&uport->lock);
|
||||
ops->stop_tx(uport);
|
||||
ops->set_mctrl(uport, 0);
|
||||
ops->stop_rx(uport);
|
||||
spin_unlock_irq(&uport->lock);
|
||||
}
|
||||
spin_lock_irq(&uport->lock);
|
||||
ops->stop_tx(uport);
|
||||
ops->set_mctrl(uport, 0);
|
||||
ops->stop_rx(uport);
|
||||
spin_unlock_irq(&uport->lock);
|
||||
|
||||
/*
|
||||
* Wait for the transmitter to empty.
|
||||
@ -2036,19 +2018,17 @@ int uart_suspend_port(struct uart_driver *drv, struct uart_port *uport)
|
||||
drv->dev_name,
|
||||
drv->tty_driver->name_base + uport->line);
|
||||
|
||||
if (console_suspend_enabled || !uart_console(uport))
|
||||
ops->shutdown(uport);
|
||||
ops->shutdown(uport);
|
||||
}
|
||||
|
||||
/*
|
||||
* Disable the console device before suspending.
|
||||
*/
|
||||
if (console_suspend_enabled && uart_console(uport))
|
||||
if (uart_console(uport))
|
||||
console_stop(uport->cons);
|
||||
|
||||
if (console_suspend_enabled || !uart_console(uport))
|
||||
uart_change_pm(state, UART_PM_STATE_OFF);
|
||||
|
||||
uart_change_pm(state, UART_PM_STATE_OFF);
|
||||
unlock:
|
||||
mutex_unlock(&port->mutex);
|
||||
|
||||
return 0;
|
||||
@ -2856,7 +2836,7 @@ void uart_handle_cts_change(struct uart_port *uport, unsigned int status)
|
||||
|
||||
uport->icount.cts++;
|
||||
|
||||
if (uart_cts_enabled(uport)) {
|
||||
if (uart_softcts_mode(uport)) {
|
||||
if (uport->hw_stopped) {
|
||||
if (status) {
|
||||
uport->hw_stopped = 0;
|
||||
@ -2869,6 +2849,7 @@ void uart_handle_cts_change(struct uart_port *uport, unsigned int status)
|
||||
uport->ops->stop_tx(uport);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(uart_handle_cts_change);
|
||||
|
@ -858,7 +858,7 @@ static int sci_handle_fifo_overrun(struct uart_port *port)
|
||||
tty_insert_flip_char(tport, 0, TTY_OVERRUN);
|
||||
tty_flip_buffer_push(tport);
|
||||
|
||||
dev_notice(port->dev, "overrun error\n");
|
||||
dev_dbg(port->dev, "overrun error\n");
|
||||
copied++;
|
||||
}
|
||||
|
||||
@ -997,12 +997,15 @@ static inline unsigned long port_rx_irq_mask(struct uart_port *port)
|
||||
static irqreturn_t sci_mpxed_interrupt(int irq, void *ptr)
|
||||
{
|
||||
unsigned short ssr_status, scr_status, err_enabled;
|
||||
unsigned short slr_status = 0;
|
||||
struct uart_port *port = ptr;
|
||||
struct sci_port *s = to_sci_port(port);
|
||||
irqreturn_t ret = IRQ_NONE;
|
||||
|
||||
ssr_status = serial_port_in(port, SCxSR);
|
||||
scr_status = serial_port_in(port, SCSCR);
|
||||
if (port->type == PORT_SCIF || port->type == PORT_HSCIF)
|
||||
slr_status = serial_port_in(port, SCLSR);
|
||||
err_enabled = scr_status & port_rx_irq_mask(port);
|
||||
|
||||
/* Tx Interrupt */
|
||||
@ -1015,8 +1018,11 @@ static irqreturn_t sci_mpxed_interrupt(int irq, void *ptr)
|
||||
* DR flags
|
||||
*/
|
||||
if (((ssr_status & SCxSR_RDxF(port)) || s->chan_rx) &&
|
||||
(scr_status & SCSCR_RIE))
|
||||
(scr_status & SCSCR_RIE)) {
|
||||
if (port->type == PORT_SCIF || port->type == PORT_HSCIF)
|
||||
sci_handle_fifo_overrun(port);
|
||||
ret = sci_rx_interrupt(irq, ptr);
|
||||
}
|
||||
|
||||
/* Error Interrupt */
|
||||
if ((ssr_status & SCxSR_ERRORS(port)) && err_enabled)
|
||||
@ -1026,6 +1032,12 @@ static irqreturn_t sci_mpxed_interrupt(int irq, void *ptr)
|
||||
if ((ssr_status & SCxSR_BRK(port)) && err_enabled)
|
||||
ret = sci_br_interrupt(irq, ptr);
|
||||
|
||||
/* Overrun Interrupt */
|
||||
if (port->type == PORT_SCIF || port->type == PORT_HSCIF) {
|
||||
if (slr_status & 0x01)
|
||||
sci_handle_fifo_overrun(port);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -2605,7 +2617,7 @@ static int sci_probe(struct platform_device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sci_suspend(struct device *dev)
|
||||
static __maybe_unused int sci_suspend(struct device *dev)
|
||||
{
|
||||
struct sci_port *sport = dev_get_drvdata(dev);
|
||||
|
||||
@ -2615,7 +2627,7 @@ static int sci_suspend(struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sci_resume(struct device *dev)
|
||||
static __maybe_unused int sci_resume(struct device *dev)
|
||||
{
|
||||
struct sci_port *sport = dev_get_drvdata(dev);
|
||||
|
||||
@ -2625,10 +2637,7 @@ static int sci_resume(struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops sci_dev_pm_ops = {
|
||||
.suspend = sci_suspend,
|
||||
.resume = sci_resume,
|
||||
};
|
||||
static SIMPLE_DEV_PM_OPS(sci_dev_pm_ops, sci_suspend, sci_resume);
|
||||
|
||||
static struct platform_driver sci_driver = {
|
||||
.probe = sci_probe,
|
||||
|
@ -177,7 +177,7 @@ static void sirfsoc_uart_stop_tx(struct uart_port *port)
|
||||
dmaengine_pause(sirfport->tx_dma_chan);
|
||||
sirfport->tx_dma_state = TX_DMA_PAUSE;
|
||||
} else {
|
||||
if (!sirfport->is_marco)
|
||||
if (!sirfport->is_atlas7)
|
||||
wr_regl(port, ureg->sirfsoc_int_en_reg,
|
||||
rd_regl(port, ureg->sirfsoc_int_en_reg) &
|
||||
~uint_en->sirfsoc_txfifo_empty_en);
|
||||
@ -186,7 +186,7 @@ static void sirfsoc_uart_stop_tx(struct uart_port *port)
|
||||
uint_en->sirfsoc_txfifo_empty_en);
|
||||
}
|
||||
} else {
|
||||
if (!sirfport->is_marco)
|
||||
if (!sirfport->is_atlas7)
|
||||
wr_regl(port, ureg->sirfsoc_int_en_reg,
|
||||
rd_regl(port, ureg->sirfsoc_int_en_reg) &
|
||||
~uint_en->sirfsoc_txfifo_empty_en);
|
||||
@ -217,7 +217,7 @@ static void sirfsoc_uart_tx_with_dma(struct sirfsoc_uart_port *sirfport)
|
||||
}
|
||||
if (sirfport->tx_dma_state == TX_DMA_RUNNING)
|
||||
return;
|
||||
if (!sirfport->is_marco)
|
||||
if (!sirfport->is_atlas7)
|
||||
wr_regl(port, ureg->sirfsoc_int_en_reg,
|
||||
rd_regl(port, ureg->sirfsoc_int_en_reg)&
|
||||
~(uint_en->sirfsoc_txfifo_empty_en));
|
||||
@ -244,7 +244,7 @@ static void sirfsoc_uart_tx_with_dma(struct sirfsoc_uart_port *sirfport)
|
||||
}
|
||||
if (tran_size < 4)
|
||||
sirfsoc_uart_pio_tx_chars(sirfport, tran_size);
|
||||
if (!sirfport->is_marco)
|
||||
if (!sirfport->is_atlas7)
|
||||
wr_regl(port, ureg->sirfsoc_int_en_reg,
|
||||
rd_regl(port, ureg->sirfsoc_int_en_reg)|
|
||||
uint_en->sirfsoc_txfifo_empty_en);
|
||||
@ -293,7 +293,7 @@ static void sirfsoc_uart_start_tx(struct uart_port *port)
|
||||
sirfsoc_uart_pio_tx_chars(sirfport,
|
||||
SIRFSOC_UART_IO_TX_REASONABLE_CNT);
|
||||
wr_regl(port, ureg->sirfsoc_tx_fifo_op, SIRFUART_FIFO_START);
|
||||
if (!sirfport->is_marco)
|
||||
if (!sirfport->is_atlas7)
|
||||
wr_regl(port, ureg->sirfsoc_int_en_reg,
|
||||
rd_regl(port, ureg->sirfsoc_int_en_reg)|
|
||||
uint_en->sirfsoc_txfifo_empty_en);
|
||||
@ -311,7 +311,7 @@ static void sirfsoc_uart_stop_rx(struct uart_port *port)
|
||||
|
||||
wr_regl(port, ureg->sirfsoc_rx_fifo_op, 0);
|
||||
if (sirfport->rx_dma_chan) {
|
||||
if (!sirfport->is_marco)
|
||||
if (!sirfport->is_atlas7)
|
||||
wr_regl(port, ureg->sirfsoc_int_en_reg,
|
||||
rd_regl(port, ureg->sirfsoc_int_en_reg) &
|
||||
~(SIRFUART_RX_DMA_INT_EN(port, uint_en) |
|
||||
@ -322,7 +322,7 @@ static void sirfsoc_uart_stop_rx(struct uart_port *port)
|
||||
uint_en->sirfsoc_rx_done_en);
|
||||
dmaengine_terminate_all(sirfport->rx_dma_chan);
|
||||
} else {
|
||||
if (!sirfport->is_marco)
|
||||
if (!sirfport->is_atlas7)
|
||||
wr_regl(port, ureg->sirfsoc_int_en_reg,
|
||||
rd_regl(port, ureg->sirfsoc_int_en_reg)&
|
||||
~(SIRFUART_RX_IO_INT_EN(port, uint_en)));
|
||||
@ -344,7 +344,7 @@ static void sirfsoc_uart_disable_ms(struct uart_port *port)
|
||||
if (sirfport->uart_reg->uart_type == SIRF_REAL_UART) {
|
||||
wr_regl(port, ureg->sirfsoc_afc_ctrl,
|
||||
rd_regl(port, ureg->sirfsoc_afc_ctrl) & ~0x3FF);
|
||||
if (!sirfport->is_marco)
|
||||
if (!sirfport->is_atlas7)
|
||||
wr_regl(port, ureg->sirfsoc_int_en_reg,
|
||||
rd_regl(port, ureg->sirfsoc_int_en_reg)&
|
||||
~uint_en->sirfsoc_cts_en);
|
||||
@ -380,7 +380,7 @@ static void sirfsoc_uart_enable_ms(struct uart_port *port)
|
||||
wr_regl(port, ureg->sirfsoc_afc_ctrl,
|
||||
rd_regl(port, ureg->sirfsoc_afc_ctrl) |
|
||||
SIRFUART_AFC_TX_EN | SIRFUART_AFC_RX_EN);
|
||||
if (!sirfport->is_marco)
|
||||
if (!sirfport->is_atlas7)
|
||||
wr_regl(port, ureg->sirfsoc_int_en_reg,
|
||||
rd_regl(port, ureg->sirfsoc_int_en_reg)
|
||||
| uint_en->sirfsoc_cts_en);
|
||||
@ -544,7 +544,7 @@ static void sirfsoc_rx_tmo_process_tl(unsigned long param)
|
||||
sirfport->rx_io_count = 0;
|
||||
wr_regl(port, ureg->sirfsoc_int_st_reg,
|
||||
uint_st->sirfsoc_rx_done);
|
||||
if (!sirfport->is_marco)
|
||||
if (!sirfport->is_atlas7)
|
||||
wr_regl(port, ureg->sirfsoc_int_en_reg,
|
||||
rd_regl(port, ureg->sirfsoc_int_en_reg) &
|
||||
~(uint_en->sirfsoc_rx_done_en));
|
||||
@ -555,7 +555,7 @@ static void sirfsoc_rx_tmo_process_tl(unsigned long param)
|
||||
} else {
|
||||
wr_regl(port, ureg->sirfsoc_int_st_reg,
|
||||
uint_st->sirfsoc_rx_done);
|
||||
if (!sirfport->is_marco)
|
||||
if (!sirfport->is_atlas7)
|
||||
wr_regl(port, ureg->sirfsoc_int_en_reg,
|
||||
rd_regl(port, ureg->sirfsoc_int_en_reg) |
|
||||
(uint_en->sirfsoc_rx_done_en));
|
||||
@ -578,7 +578,7 @@ static void sirfsoc_uart_handle_rx_tmo(struct sirfsoc_uart_port *sirfport)
|
||||
dmaengine_terminate_all(sirfport->rx_dma_chan);
|
||||
sirfport->rx_dma_items[sirfport->rx_issued].xmit.head =
|
||||
SIRFSOC_RX_DMA_BUF_SIZE - tx_state.residue;
|
||||
if (!sirfport->is_marco)
|
||||
if (!sirfport->is_atlas7)
|
||||
wr_regl(port, ureg->sirfsoc_int_en_reg,
|
||||
rd_regl(port, ureg->sirfsoc_int_en_reg) &
|
||||
~(uint_en->sirfsoc_rx_timeout_en));
|
||||
@ -598,7 +598,7 @@ static void sirfsoc_uart_handle_rx_done(struct sirfsoc_uart_port *sirfport)
|
||||
sirfsoc_uart_pio_rx_chars(port, 4 - sirfport->rx_io_count);
|
||||
if (sirfport->rx_io_count == 4) {
|
||||
sirfport->rx_io_count = 0;
|
||||
if (!sirfport->is_marco)
|
||||
if (!sirfport->is_atlas7)
|
||||
wr_regl(port, ureg->sirfsoc_int_en_reg,
|
||||
rd_regl(port, ureg->sirfsoc_int_en_reg) &
|
||||
~(uint_en->sirfsoc_rx_done_en));
|
||||
@ -748,7 +748,7 @@ static void sirfsoc_uart_start_next_rx_dma(struct uart_port *port)
|
||||
for (i = 0; i < SIRFSOC_RX_LOOP_BUF_CNT; i++)
|
||||
sirfsoc_rx_submit_one_dma_desc(port, i);
|
||||
sirfport->rx_completed = sirfport->rx_issued = 0;
|
||||
if (!sirfport->is_marco)
|
||||
if (!sirfport->is_atlas7)
|
||||
wr_regl(port, ureg->sirfsoc_int_en_reg,
|
||||
rd_regl(port, ureg->sirfsoc_int_en_reg) |
|
||||
SIRFUART_RX_DMA_INT_EN(port, uint_en));
|
||||
@ -770,7 +770,7 @@ static void sirfsoc_uart_start_rx(struct uart_port *port)
|
||||
if (sirfport->rx_dma_chan)
|
||||
sirfsoc_uart_start_next_rx_dma(port);
|
||||
else {
|
||||
if (!sirfport->is_marco)
|
||||
if (!sirfport->is_atlas7)
|
||||
wr_regl(port, ureg->sirfsoc_int_en_reg,
|
||||
rd_regl(port, ureg->sirfsoc_int_en_reg) |
|
||||
SIRFUART_RX_IO_INT_EN(port, uint_en));
|
||||
@ -1124,7 +1124,7 @@ static void sirfsoc_uart_shutdown(struct uart_port *port)
|
||||
{
|
||||
struct sirfsoc_uart_port *sirfport = to_sirfport(port);
|
||||
struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg;
|
||||
if (!sirfport->is_marco)
|
||||
if (!sirfport->is_atlas7)
|
||||
wr_regl(port, ureg->sirfsoc_int_en_reg, 0);
|
||||
else
|
||||
wr_regl(port, SIRFUART_INT_EN_CLR, ~0UL);
|
||||
@ -1271,7 +1271,7 @@ static struct uart_driver sirfsoc_uart_drv = {
|
||||
|
||||
static struct of_device_id sirfsoc_uart_ids[] = {
|
||||
{ .compatible = "sirf,prima2-uart", .data = &sirfsoc_uart,},
|
||||
{ .compatible = "sirf,marco-uart", .data = &sirfsoc_uart},
|
||||
{ .compatible = "sirf,atlas7-uart", .data = &sirfsoc_uart},
|
||||
{ .compatible = "sirf,prima2-usp-uart", .data = &sirfsoc_usp},
|
||||
{}
|
||||
};
|
||||
@ -1350,8 +1350,8 @@ static int sirfsoc_uart_probe(struct platform_device *pdev)
|
||||
gpio_direction_output(sirfport->rts_gpio, 1);
|
||||
}
|
||||
usp_no_flow_control:
|
||||
if (of_device_is_compatible(pdev->dev.of_node, "sirf,marco-uart"))
|
||||
sirfport->is_marco = true;
|
||||
if (of_device_is_compatible(pdev->dev.of_node, "sirf,atlas7-uart"))
|
||||
sirfport->is_atlas7 = true;
|
||||
|
||||
if (of_property_read_u32(pdev->dev.of_node,
|
||||
"fifosize",
|
||||
@ -1393,7 +1393,7 @@ usp_no_flow_control:
|
||||
goto err;
|
||||
}
|
||||
port->uartclk = clk_get_rate(sirfport->clk);
|
||||
if (of_device_is_compatible(pdev->dev.of_node, "sirf,marco-bt-uart")) {
|
||||
if (of_device_is_compatible(pdev->dev.of_node, "sirf,atlas7-bt-uart")) {
|
||||
sirfport->clk_general = devm_clk_get(&pdev->dev, "general");
|
||||
if (IS_ERR(sirfport->clk_general)) {
|
||||
ret = PTR_ERR(sirfport->clk_general);
|
||||
|
@ -421,8 +421,8 @@ struct sirfsoc_uart_port {
|
||||
bool is_bt_uart;
|
||||
struct clk *clk_general;
|
||||
struct clk *clk_noc;
|
||||
/* for SiRFmarco, there are SET/CLR for UART_INT_EN */
|
||||
bool is_marco;
|
||||
/* for SiRFatlas7, there are SET/CLR for UART_INT_EN */
|
||||
bool is_atlas7;
|
||||
struct sirfsoc_uart_register *uart_reg;
|
||||
struct dma_chan *rx_dma_chan;
|
||||
struct dma_chan *tx_dma_chan;
|
||||
|
793
drivers/tty/serial/sprd_serial.c
Normal file
793
drivers/tty/serial/sprd_serial.c
Normal file
@ -0,0 +1,793 @@
|
||||
/*
|
||||
* Copyright (C) 2012-2015 Spreadtrum Communications Inc.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#if defined(CONFIG_SERIAL_SPRD_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
|
||||
#define SUPPORT_SYSRQ
|
||||
#endif
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/console.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/serial_core.h>
|
||||
#include <linux/serial.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/tty.h>
|
||||
#include <linux/tty_flip.h>
|
||||
|
||||
/* device name */
|
||||
#define UART_NR_MAX 8
|
||||
#define SPRD_TTY_NAME "ttyS"
|
||||
#define SPRD_FIFO_SIZE 128
|
||||
#define SPRD_DEF_RATE 26000000
|
||||
#define SPRD_BAUD_IO_LIMIT 3000000
|
||||
#define SPRD_TIMEOUT 256
|
||||
|
||||
/* the offset of serial registers and BITs for them */
|
||||
/* data registers */
|
||||
#define SPRD_TXD 0x0000
|
||||
#define SPRD_RXD 0x0004
|
||||
|
||||
/* line status register and its BITs */
|
||||
#define SPRD_LSR 0x0008
|
||||
#define SPRD_LSR_OE BIT(4)
|
||||
#define SPRD_LSR_FE BIT(3)
|
||||
#define SPRD_LSR_PE BIT(2)
|
||||
#define SPRD_LSR_BI BIT(7)
|
||||
#define SPRD_LSR_TX_OVER BIT(15)
|
||||
|
||||
/* data number in TX and RX fifo */
|
||||
#define SPRD_STS1 0x000C
|
||||
|
||||
/* interrupt enable register and its BITs */
|
||||
#define SPRD_IEN 0x0010
|
||||
#define SPRD_IEN_RX_FULL BIT(0)
|
||||
#define SPRD_IEN_TX_EMPTY BIT(1)
|
||||
#define SPRD_IEN_BREAK_DETECT BIT(7)
|
||||
#define SPRD_IEN_TIMEOUT BIT(13)
|
||||
|
||||
/* interrupt clear register */
|
||||
#define SPRD_ICLR 0x0014
|
||||
|
||||
/* line control register */
|
||||
#define SPRD_LCR 0x0018
|
||||
#define SPRD_LCR_STOP_1BIT 0x10
|
||||
#define SPRD_LCR_STOP_2BIT 0x30
|
||||
#define SPRD_LCR_DATA_LEN (BIT(2) | BIT(3))
|
||||
#define SPRD_LCR_DATA_LEN5 0x0
|
||||
#define SPRD_LCR_DATA_LEN6 0x4
|
||||
#define SPRD_LCR_DATA_LEN7 0x8
|
||||
#define SPRD_LCR_DATA_LEN8 0xc
|
||||
#define SPRD_LCR_PARITY (BIT(0) | BIT(1))
|
||||
#define SPRD_LCR_PARITY_EN 0x2
|
||||
#define SPRD_LCR_EVEN_PAR 0x0
|
||||
#define SPRD_LCR_ODD_PAR 0x1
|
||||
|
||||
/* control register 1 */
|
||||
#define SPRD_CTL1 0x001C
|
||||
#define RX_HW_FLOW_CTL_THLD BIT(6)
|
||||
#define RX_HW_FLOW_CTL_EN BIT(7)
|
||||
#define TX_HW_FLOW_CTL_EN BIT(8)
|
||||
#define RX_TOUT_THLD_DEF 0x3E00
|
||||
#define RX_HFC_THLD_DEF 0x40
|
||||
|
||||
/* fifo threshold register */
|
||||
#define SPRD_CTL2 0x0020
|
||||
#define THLD_TX_EMPTY 0x40
|
||||
#define THLD_RX_FULL 0x40
|
||||
|
||||
/* config baud rate register */
|
||||
#define SPRD_CLKD0 0x0024
|
||||
#define SPRD_CLKD1 0x0028
|
||||
|
||||
/* interrupt mask status register */
|
||||
#define SPRD_IMSR 0x002C
|
||||
#define SPRD_IMSR_RX_FIFO_FULL BIT(0)
|
||||
#define SPRD_IMSR_TX_FIFO_EMPTY BIT(1)
|
||||
#define SPRD_IMSR_BREAK_DETECT BIT(7)
|
||||
#define SPRD_IMSR_TIMEOUT BIT(13)
|
||||
|
||||
struct reg_backup {
|
||||
u32 ien;
|
||||
u32 ctrl0;
|
||||
u32 ctrl1;
|
||||
u32 ctrl2;
|
||||
u32 clkd0;
|
||||
u32 clkd1;
|
||||
u32 dspwait;
|
||||
};
|
||||
|
||||
struct sprd_uart_port {
|
||||
struct uart_port port;
|
||||
struct reg_backup reg_bak;
|
||||
char name[16];
|
||||
};
|
||||
|
||||
static struct sprd_uart_port *sprd_port[UART_NR_MAX];
|
||||
static int sprd_ports_num;
|
||||
|
||||
static inline unsigned int serial_in(struct uart_port *port, int offset)
|
||||
{
|
||||
return readl_relaxed(port->membase + offset);
|
||||
}
|
||||
|
||||
static inline void serial_out(struct uart_port *port, int offset, int value)
|
||||
{
|
||||
writel_relaxed(value, port->membase + offset);
|
||||
}
|
||||
|
||||
static unsigned int sprd_tx_empty(struct uart_port *port)
|
||||
{
|
||||
if (serial_in(port, SPRD_STS1) & 0xff00)
|
||||
return 0;
|
||||
else
|
||||
return TIOCSER_TEMT;
|
||||
}
|
||||
|
||||
static unsigned int sprd_get_mctrl(struct uart_port *port)
|
||||
{
|
||||
return TIOCM_DSR | TIOCM_CTS;
|
||||
}
|
||||
|
||||
static void sprd_set_mctrl(struct uart_port *port, unsigned int mctrl)
|
||||
{
|
||||
/* nothing to do */
|
||||
}
|
||||
|
||||
static void sprd_stop_tx(struct uart_port *port)
|
||||
{
|
||||
unsigned int ien, iclr;
|
||||
|
||||
iclr = serial_in(port, SPRD_ICLR);
|
||||
ien = serial_in(port, SPRD_IEN);
|
||||
|
||||
iclr |= SPRD_IEN_TX_EMPTY;
|
||||
ien &= ~SPRD_IEN_TX_EMPTY;
|
||||
|
||||
serial_out(port, SPRD_ICLR, iclr);
|
||||
serial_out(port, SPRD_IEN, ien);
|
||||
}
|
||||
|
||||
static void sprd_start_tx(struct uart_port *port)
|
||||
{
|
||||
unsigned int ien;
|
||||
|
||||
ien = serial_in(port, SPRD_IEN);
|
||||
if (!(ien & SPRD_IEN_TX_EMPTY)) {
|
||||
ien |= SPRD_IEN_TX_EMPTY;
|
||||
serial_out(port, SPRD_IEN, ien);
|
||||
}
|
||||
}
|
||||
|
||||
static void sprd_stop_rx(struct uart_port *port)
|
||||
{
|
||||
unsigned int ien, iclr;
|
||||
|
||||
iclr = serial_in(port, SPRD_ICLR);
|
||||
ien = serial_in(port, SPRD_IEN);
|
||||
|
||||
ien &= ~(SPRD_IEN_RX_FULL | SPRD_IEN_BREAK_DETECT);
|
||||
iclr |= SPRD_IEN_RX_FULL | SPRD_IEN_BREAK_DETECT;
|
||||
|
||||
serial_out(port, SPRD_IEN, ien);
|
||||
serial_out(port, SPRD_ICLR, iclr);
|
||||
}
|
||||
|
||||
/* The Sprd serial does not support this function. */
|
||||
static void sprd_break_ctl(struct uart_port *port, int break_state)
|
||||
{
|
||||
/* nothing to do */
|
||||
}
|
||||
|
||||
static int handle_lsr_errors(struct uart_port *port,
|
||||
unsigned int *flag,
|
||||
unsigned int *lsr)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
/* statistics */
|
||||
if (*lsr & SPRD_LSR_BI) {
|
||||
*lsr &= ~(SPRD_LSR_FE | SPRD_LSR_PE);
|
||||
port->icount.brk++;
|
||||
ret = uart_handle_break(port);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else if (*lsr & SPRD_LSR_PE)
|
||||
port->icount.parity++;
|
||||
else if (*lsr & SPRD_LSR_FE)
|
||||
port->icount.frame++;
|
||||
if (*lsr & SPRD_LSR_OE)
|
||||
port->icount.overrun++;
|
||||
|
||||
/* mask off conditions which should be ignored */
|
||||
*lsr &= port->read_status_mask;
|
||||
if (*lsr & SPRD_LSR_BI)
|
||||
*flag = TTY_BREAK;
|
||||
else if (*lsr & SPRD_LSR_PE)
|
||||
*flag = TTY_PARITY;
|
||||
else if (*lsr & SPRD_LSR_FE)
|
||||
*flag = TTY_FRAME;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline void sprd_rx(struct uart_port *port)
|
||||
{
|
||||
struct tty_port *tty = &port->state->port;
|
||||
unsigned int ch, flag, lsr, max_count = SPRD_TIMEOUT;
|
||||
|
||||
while ((serial_in(port, SPRD_STS1) & 0x00ff) && max_count--) {
|
||||
lsr = serial_in(port, SPRD_LSR);
|
||||
ch = serial_in(port, SPRD_RXD);
|
||||
flag = TTY_NORMAL;
|
||||
port->icount.rx++;
|
||||
|
||||
if (lsr & (SPRD_LSR_BI | SPRD_LSR_PE |
|
||||
SPRD_LSR_FE | SPRD_LSR_OE))
|
||||
if (handle_lsr_errors(port, &lsr, &flag))
|
||||
continue;
|
||||
if (uart_handle_sysrq_char(port, ch))
|
||||
continue;
|
||||
|
||||
uart_insert_char(port, lsr, SPRD_LSR_OE, ch, flag);
|
||||
}
|
||||
|
||||
tty_flip_buffer_push(tty);
|
||||
}
|
||||
|
||||
static inline void sprd_tx(struct uart_port *port)
|
||||
{
|
||||
struct circ_buf *xmit = &port->state->xmit;
|
||||
int count;
|
||||
|
||||
if (port->x_char) {
|
||||
serial_out(port, SPRD_TXD, port->x_char);
|
||||
port->icount.tx++;
|
||||
port->x_char = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
|
||||
sprd_stop_tx(port);
|
||||
return;
|
||||
}
|
||||
|
||||
count = THLD_TX_EMPTY;
|
||||
do {
|
||||
serial_out(port, SPRD_TXD, xmit->buf[xmit->tail]);
|
||||
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
|
||||
port->icount.tx++;
|
||||
if (uart_circ_empty(xmit))
|
||||
break;
|
||||
} while (--count > 0);
|
||||
|
||||
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
|
||||
uart_write_wakeup(port);
|
||||
|
||||
if (uart_circ_empty(xmit))
|
||||
sprd_stop_tx(port);
|
||||
}
|
||||
|
||||
/* this handles the interrupt from one port */
|
||||
static irqreturn_t sprd_handle_irq(int irq, void *dev_id)
|
||||
{
|
||||
struct uart_port *port = dev_id;
|
||||
unsigned int ims;
|
||||
|
||||
spin_lock(&port->lock);
|
||||
|
||||
ims = serial_in(port, SPRD_IMSR);
|
||||
|
||||
if (!ims)
|
||||
return IRQ_NONE;
|
||||
|
||||
serial_out(port, SPRD_ICLR, ~0);
|
||||
|
||||
if (ims & (SPRD_IMSR_RX_FIFO_FULL |
|
||||
SPRD_IMSR_BREAK_DETECT | SPRD_IMSR_TIMEOUT))
|
||||
sprd_rx(port);
|
||||
|
||||
if (ims & SPRD_IMSR_TX_FIFO_EMPTY)
|
||||
sprd_tx(port);
|
||||
|
||||
spin_unlock(&port->lock);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int sprd_startup(struct uart_port *port)
|
||||
{
|
||||
int ret = 0;
|
||||
unsigned int ien, fc;
|
||||
unsigned int timeout;
|
||||
struct sprd_uart_port *sp;
|
||||
unsigned long flags;
|
||||
|
||||
serial_out(port, SPRD_CTL2, ((THLD_TX_EMPTY << 8) | THLD_RX_FULL));
|
||||
|
||||
/* clear rx fifo */
|
||||
timeout = SPRD_TIMEOUT;
|
||||
while (timeout-- && serial_in(port, SPRD_STS1) & 0x00ff)
|
||||
serial_in(port, SPRD_RXD);
|
||||
|
||||
/* clear tx fifo */
|
||||
timeout = SPRD_TIMEOUT;
|
||||
while (timeout-- && serial_in(port, SPRD_STS1) & 0xff00)
|
||||
cpu_relax();
|
||||
|
||||
/* clear interrupt */
|
||||
serial_out(port, SPRD_IEN, 0);
|
||||
serial_out(port, SPRD_ICLR, ~0);
|
||||
|
||||
/* allocate irq */
|
||||
sp = container_of(port, struct sprd_uart_port, port);
|
||||
snprintf(sp->name, sizeof(sp->name), "sprd_serial%d", port->line);
|
||||
ret = devm_request_irq(port->dev, port->irq, sprd_handle_irq,
|
||||
IRQF_SHARED, sp->name, port);
|
||||
if (ret) {
|
||||
dev_err(port->dev, "fail to request serial irq %d, ret=%d\n",
|
||||
port->irq, ret);
|
||||
return ret;
|
||||
}
|
||||
fc = serial_in(port, SPRD_CTL1);
|
||||
fc |= RX_TOUT_THLD_DEF | RX_HFC_THLD_DEF;
|
||||
serial_out(port, SPRD_CTL1, fc);
|
||||
|
||||
/* enable interrupt */
|
||||
spin_lock_irqsave(&port->lock, flags);
|
||||
ien = serial_in(port, SPRD_IEN);
|
||||
ien |= SPRD_IEN_RX_FULL | SPRD_IEN_BREAK_DETECT | SPRD_IEN_TIMEOUT;
|
||||
serial_out(port, SPRD_IEN, ien);
|
||||
spin_unlock_irqrestore(&port->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sprd_shutdown(struct uart_port *port)
|
||||
{
|
||||
serial_out(port, SPRD_IEN, 0);
|
||||
serial_out(port, SPRD_ICLR, ~0);
|
||||
devm_free_irq(port->dev, port->irq, port);
|
||||
}
|
||||
|
||||
static void sprd_set_termios(struct uart_port *port,
|
||||
struct ktermios *termios,
|
||||
struct ktermios *old)
|
||||
{
|
||||
unsigned int baud, quot;
|
||||
unsigned int lcr = 0, fc;
|
||||
unsigned long flags;
|
||||
|
||||
/* ask the core to calculate the divisor for us */
|
||||
baud = uart_get_baud_rate(port, termios, old, 0, SPRD_BAUD_IO_LIMIT);
|
||||
|
||||
quot = (unsigned int)((port->uartclk + baud / 2) / baud);
|
||||
|
||||
/* set data length */
|
||||
switch (termios->c_cflag & CSIZE) {
|
||||
case CS5:
|
||||
lcr |= SPRD_LCR_DATA_LEN5;
|
||||
break;
|
||||
case CS6:
|
||||
lcr |= SPRD_LCR_DATA_LEN6;
|
||||
break;
|
||||
case CS7:
|
||||
lcr |= SPRD_LCR_DATA_LEN7;
|
||||
break;
|
||||
case CS8:
|
||||
default:
|
||||
lcr |= SPRD_LCR_DATA_LEN8;
|
||||
break;
|
||||
}
|
||||
|
||||
/* calculate stop bits */
|
||||
lcr &= ~(SPRD_LCR_STOP_1BIT | SPRD_LCR_STOP_2BIT);
|
||||
if (termios->c_cflag & CSTOPB)
|
||||
lcr |= SPRD_LCR_STOP_2BIT;
|
||||
else
|
||||
lcr |= SPRD_LCR_STOP_1BIT;
|
||||
|
||||
/* calculate parity */
|
||||
lcr &= ~SPRD_LCR_PARITY;
|
||||
termios->c_cflag &= ~CMSPAR; /* no support mark/space */
|
||||
if (termios->c_cflag & PARENB) {
|
||||
lcr |= SPRD_LCR_PARITY_EN;
|
||||
if (termios->c_cflag & PARODD)
|
||||
lcr |= SPRD_LCR_ODD_PAR;
|
||||
else
|
||||
lcr |= SPRD_LCR_EVEN_PAR;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&port->lock, flags);
|
||||
|
||||
/* update the per-port timeout */
|
||||
uart_update_timeout(port, termios->c_cflag, baud);
|
||||
|
||||
port->read_status_mask = SPRD_LSR_OE;
|
||||
if (termios->c_iflag & INPCK)
|
||||
port->read_status_mask |= SPRD_LSR_FE | SPRD_LSR_PE;
|
||||
if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK))
|
||||
port->read_status_mask |= SPRD_LSR_BI;
|
||||
|
||||
/* characters to ignore */
|
||||
port->ignore_status_mask = 0;
|
||||
if (termios->c_iflag & IGNPAR)
|
||||
port->ignore_status_mask |= SPRD_LSR_PE | SPRD_LSR_FE;
|
||||
if (termios->c_iflag & IGNBRK) {
|
||||
port->ignore_status_mask |= SPRD_LSR_BI;
|
||||
/*
|
||||
* If we're ignoring parity and break indicators,
|
||||
* ignore overruns too (for real raw support).
|
||||
*/
|
||||
if (termios->c_iflag & IGNPAR)
|
||||
port->ignore_status_mask |= SPRD_LSR_OE;
|
||||
}
|
||||
|
||||
/* flow control */
|
||||
fc = serial_in(port, SPRD_CTL1);
|
||||
fc &= ~(RX_HW_FLOW_CTL_THLD | RX_HW_FLOW_CTL_EN | TX_HW_FLOW_CTL_EN);
|
||||
if (termios->c_cflag & CRTSCTS) {
|
||||
fc |= RX_HW_FLOW_CTL_THLD;
|
||||
fc |= RX_HW_FLOW_CTL_EN;
|
||||
fc |= TX_HW_FLOW_CTL_EN;
|
||||
}
|
||||
|
||||
/* clock divider bit0~bit15 */
|
||||
serial_out(port, SPRD_CLKD0, quot & 0xffff);
|
||||
|
||||
/* clock divider bit16~bit20 */
|
||||
serial_out(port, SPRD_CLKD1, (quot & 0x1f0000) >> 16);
|
||||
serial_out(port, SPRD_LCR, lcr);
|
||||
fc |= RX_TOUT_THLD_DEF | RX_HFC_THLD_DEF;
|
||||
serial_out(port, SPRD_CTL1, fc);
|
||||
|
||||
spin_unlock_irqrestore(&port->lock, flags);
|
||||
|
||||
/* Don't rewrite B0 */
|
||||
if (tty_termios_baud_rate(termios))
|
||||
tty_termios_encode_baud_rate(termios, baud, baud);
|
||||
}
|
||||
|
||||
static const char *sprd_type(struct uart_port *port)
|
||||
{
|
||||
return "SPX";
|
||||
}
|
||||
|
||||
static void sprd_release_port(struct uart_port *port)
|
||||
{
|
||||
/* nothing to do */
|
||||
}
|
||||
|
||||
static int sprd_request_port(struct uart_port *port)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sprd_config_port(struct uart_port *port, int flags)
|
||||
{
|
||||
if (flags & UART_CONFIG_TYPE)
|
||||
port->type = PORT_SPRD;
|
||||
}
|
||||
|
||||
static int sprd_verify_port(struct uart_port *port,
|
||||
struct serial_struct *ser)
|
||||
{
|
||||
if (ser->type != PORT_SPRD)
|
||||
return -EINVAL;
|
||||
if (port->irq != ser->irq)
|
||||
return -EINVAL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct uart_ops serial_sprd_ops = {
|
||||
.tx_empty = sprd_tx_empty,
|
||||
.get_mctrl = sprd_get_mctrl,
|
||||
.set_mctrl = sprd_set_mctrl,
|
||||
.stop_tx = sprd_stop_tx,
|
||||
.start_tx = sprd_start_tx,
|
||||
.stop_rx = sprd_stop_rx,
|
||||
.break_ctl = sprd_break_ctl,
|
||||
.startup = sprd_startup,
|
||||
.shutdown = sprd_shutdown,
|
||||
.set_termios = sprd_set_termios,
|
||||
.type = sprd_type,
|
||||
.release_port = sprd_release_port,
|
||||
.request_port = sprd_request_port,
|
||||
.config_port = sprd_config_port,
|
||||
.verify_port = sprd_verify_port,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_SERIAL_SPRD_CONSOLE
|
||||
static inline void wait_for_xmitr(struct uart_port *port)
|
||||
{
|
||||
unsigned int status, tmout = 10000;
|
||||
|
||||
/* wait up to 10ms for the character(s) to be sent */
|
||||
do {
|
||||
status = serial_in(port, SPRD_STS1);
|
||||
if (--tmout == 0)
|
||||
break;
|
||||
udelay(1);
|
||||
} while (status & 0xff00);
|
||||
}
|
||||
|
||||
static void sprd_console_putchar(struct uart_port *port, int ch)
|
||||
{
|
||||
wait_for_xmitr(port);
|
||||
serial_out(port, SPRD_TXD, ch);
|
||||
}
|
||||
|
||||
static void sprd_console_write(struct console *co, const char *s,
|
||||
unsigned int count)
|
||||
{
|
||||
struct uart_port *port = &sprd_port[co->index]->port;
|
||||
int locked = 1;
|
||||
unsigned long flags;
|
||||
|
||||
if (port->sysrq)
|
||||
locked = 0;
|
||||
else if (oops_in_progress)
|
||||
locked = spin_trylock_irqsave(&port->lock, flags);
|
||||
else
|
||||
spin_lock_irqsave(&port->lock, flags);
|
||||
|
||||
uart_console_write(port, s, count, sprd_console_putchar);
|
||||
|
||||
/* wait for transmitter to become empty */
|
||||
wait_for_xmitr(port);
|
||||
|
||||
if (locked)
|
||||
spin_unlock_irqrestore(&port->lock, flags);
|
||||
}
|
||||
|
||||
static int __init sprd_console_setup(struct console *co, char *options)
|
||||
{
|
||||
struct uart_port *port;
|
||||
int baud = 115200;
|
||||
int bits = 8;
|
||||
int parity = 'n';
|
||||
int flow = 'n';
|
||||
|
||||
if (co->index >= UART_NR_MAX || co->index < 0)
|
||||
co->index = 0;
|
||||
|
||||
port = &sprd_port[co->index]->port;
|
||||
if (port == NULL) {
|
||||
pr_info("serial port %d not yet initialized\n", co->index);
|
||||
return -ENODEV;
|
||||
}
|
||||
if (options)
|
||||
uart_parse_options(options, &baud, &parity, &bits, &flow);
|
||||
|
||||
return uart_set_options(port, co, baud, parity, bits, flow);
|
||||
}
|
||||
|
||||
static struct uart_driver sprd_uart_driver;
|
||||
static struct console sprd_console = {
|
||||
.name = SPRD_TTY_NAME,
|
||||
.write = sprd_console_write,
|
||||
.device = uart_console_device,
|
||||
.setup = sprd_console_setup,
|
||||
.flags = CON_PRINTBUFFER,
|
||||
.index = -1,
|
||||
.data = &sprd_uart_driver,
|
||||
};
|
||||
|
||||
#define SPRD_CONSOLE (&sprd_console)
|
||||
|
||||
/* Support for earlycon */
|
||||
static void sprd_putc(struct uart_port *port, int c)
|
||||
{
|
||||
unsigned int timeout = SPRD_TIMEOUT;
|
||||
|
||||
while (timeout-- &&
|
||||
!(readl(port->membase + SPRD_LSR) & SPRD_LSR_TX_OVER))
|
||||
cpu_relax();
|
||||
|
||||
writeb(c, port->membase + SPRD_TXD);
|
||||
}
|
||||
|
||||
static void sprd_early_write(struct console *con, const char *s,
|
||||
unsigned n)
|
||||
{
|
||||
struct earlycon_device *dev = con->data;
|
||||
|
||||
uart_console_write(&dev->port, s, n, sprd_putc);
|
||||
}
|
||||
|
||||
static int __init sprd_early_console_setup(
|
||||
struct earlycon_device *device,
|
||||
const char *opt)
|
||||
{
|
||||
if (!device->port.membase)
|
||||
return -ENODEV;
|
||||
|
||||
device->con->write = sprd_early_write;
|
||||
return 0;
|
||||
}
|
||||
|
||||
EARLYCON_DECLARE(sprd_serial, sprd_early_console_setup);
|
||||
OF_EARLYCON_DECLARE(sprd_serial, "sprd,sc9836-uart",
|
||||
sprd_early_console_setup);
|
||||
|
||||
#else /* !CONFIG_SERIAL_SPRD_CONSOLE */
|
||||
#define SPRD_CONSOLE NULL
|
||||
#endif
|
||||
|
||||
static struct uart_driver sprd_uart_driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.driver_name = "sprd_serial",
|
||||
.dev_name = SPRD_TTY_NAME,
|
||||
.major = 0,
|
||||
.minor = 0,
|
||||
.nr = UART_NR_MAX,
|
||||
.cons = SPRD_CONSOLE,
|
||||
};
|
||||
|
||||
static int sprd_probe_dt_alias(int index, struct device *dev)
|
||||
{
|
||||
struct device_node *np;
|
||||
int ret = index;
|
||||
|
||||
if (!IS_ENABLED(CONFIG_OF))
|
||||
return ret;
|
||||
|
||||
np = dev->of_node;
|
||||
if (!np)
|
||||
return ret;
|
||||
|
||||
ret = of_alias_get_id(np, "serial");
|
||||
if (IS_ERR_VALUE(ret))
|
||||
ret = index;
|
||||
else if (ret >= ARRAY_SIZE(sprd_port) || sprd_port[ret] != NULL) {
|
||||
dev_warn(dev, "requested serial port %d not available.\n", ret);
|
||||
ret = index;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sprd_remove(struct platform_device *dev)
|
||||
{
|
||||
struct sprd_uart_port *sup = platform_get_drvdata(dev);
|
||||
|
||||
if (sup) {
|
||||
uart_remove_one_port(&sprd_uart_driver, &sup->port);
|
||||
sprd_port[sup->port.line] = NULL;
|
||||
sprd_ports_num--;
|
||||
}
|
||||
|
||||
if (!sprd_ports_num)
|
||||
uart_unregister_driver(&sprd_uart_driver);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sprd_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *res;
|
||||
struct uart_port *up;
|
||||
struct clk *clk;
|
||||
int irq;
|
||||
int index;
|
||||
int ret;
|
||||
|
||||
for (index = 0; index < ARRAY_SIZE(sprd_port); index++)
|
||||
if (sprd_port[index] == NULL)
|
||||
break;
|
||||
|
||||
if (index == ARRAY_SIZE(sprd_port))
|
||||
return -EBUSY;
|
||||
|
||||
index = sprd_probe_dt_alias(index, &pdev->dev);
|
||||
|
||||
sprd_port[index] = devm_kzalloc(&pdev->dev,
|
||||
sizeof(*sprd_port[index]), GFP_KERNEL);
|
||||
if (!sprd_port[index])
|
||||
return -ENOMEM;
|
||||
|
||||
up = &sprd_port[index]->port;
|
||||
up->dev = &pdev->dev;
|
||||
up->line = index;
|
||||
up->type = PORT_SPRD;
|
||||
up->iotype = SERIAL_IO_PORT;
|
||||
up->uartclk = SPRD_DEF_RATE;
|
||||
up->fifosize = SPRD_FIFO_SIZE;
|
||||
up->ops = &serial_sprd_ops;
|
||||
up->flags = UPF_BOOT_AUTOCONF;
|
||||
|
||||
clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (!IS_ERR(clk))
|
||||
up->uartclk = clk_get_rate(clk);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "not provide mem resource\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
up->mapbase = res->start;
|
||||
up->membase = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(up->membase))
|
||||
return PTR_ERR(up->membase);
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
dev_err(&pdev->dev, "not provide irq resource\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
up->irq = irq;
|
||||
|
||||
if (!sprd_ports_num) {
|
||||
ret = uart_register_driver(&sprd_uart_driver);
|
||||
if (ret < 0) {
|
||||
pr_err("Failed to register SPRD-UART driver\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
sprd_ports_num++;
|
||||
|
||||
ret = uart_add_one_port(&sprd_uart_driver, up);
|
||||
if (ret) {
|
||||
sprd_port[index] = NULL;
|
||||
sprd_remove(pdev);
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, up);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sprd_suspend(struct device *dev)
|
||||
{
|
||||
struct sprd_uart_port *sup = dev_get_drvdata(dev);
|
||||
|
||||
uart_suspend_port(&sprd_uart_driver, &sup->port);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sprd_resume(struct device *dev)
|
||||
{
|
||||
struct sprd_uart_port *sup = dev_get_drvdata(dev);
|
||||
|
||||
uart_resume_port(&sprd_uart_driver, &sup->port);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(sprd_pm_ops, sprd_suspend, sprd_resume);
|
||||
|
||||
static const struct of_device_id serial_ids[] = {
|
||||
{.compatible = "sprd,sc9836-uart",},
|
||||
{}
|
||||
};
|
||||
|
||||
static struct platform_driver sprd_platform_driver = {
|
||||
.probe = sprd_probe,
|
||||
.remove = sprd_remove,
|
||||
.driver = {
|
||||
.name = "sprd_serial",
|
||||
.of_match_table = of_match_ptr(serial_ids),
|
||||
.pm = &sprd_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(sprd_platform_driver);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("Spreadtrum SoC serial driver series");
|
@ -637,10 +637,12 @@ static void cdns_uart_set_termios(struct uart_port *port,
|
||||
|
||||
spin_lock_irqsave(&port->lock, flags);
|
||||
|
||||
/* Empty the receive FIFO 1st before making changes */
|
||||
while ((cdns_uart_readl(CDNS_UART_SR_OFFSET) &
|
||||
CDNS_UART_SR_RXEMPTY) != CDNS_UART_SR_RXEMPTY) {
|
||||
cdns_uart_readl(CDNS_UART_FIFO_OFFSET);
|
||||
/* Wait for the transmit FIFO to empty before making changes */
|
||||
if (!(cdns_uart_readl(CDNS_UART_CR_OFFSET) & CDNS_UART_CR_TX_DIS)) {
|
||||
while (!(cdns_uart_readl(CDNS_UART_SR_OFFSET) &
|
||||
CDNS_UART_SR_TXEMPTY)) {
|
||||
cpu_relax();
|
||||
}
|
||||
}
|
||||
|
||||
/* Disable the TX and RX to set baud rate */
|
||||
|
@ -557,3 +557,9 @@ int tty_buffer_set_limit(struct tty_port *port, int limit)
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tty_buffer_set_limit);
|
||||
|
||||
/* slave ptys can claim nested buffer lock when handling BRK and INTR */
|
||||
void tty_buffer_set_lock_subclass(struct tty_port *port)
|
||||
{
|
||||
lockdep_set_subclass(&port->buf.lock, TTY_LOCK_SLAVE);
|
||||
}
|
||||
|
@ -530,7 +530,7 @@ EXPORT_SYMBOL(tty_termios_hw_change);
|
||||
* Locking: termios_rwsem
|
||||
*/
|
||||
|
||||
int tty_set_termios(struct tty_struct *tty, struct ktermios *new_termios)
|
||||
static int tty_set_termios(struct tty_struct *tty, struct ktermios *new_termios)
|
||||
{
|
||||
struct ktermios old_termios;
|
||||
struct tty_ldisc *ld;
|
||||
@ -563,7 +563,6 @@ int tty_set_termios(struct tty_struct *tty, struct ktermios *new_termios)
|
||||
up_write(&tty->termios_rwsem);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tty_set_termios);
|
||||
|
||||
/**
|
||||
* set_termios - set termios values for a tty
|
||||
|
@ -4,18 +4,8 @@
|
||||
#include <linux/semaphore.h>
|
||||
#include <linux/sched.h>
|
||||
|
||||
/*
|
||||
* Nested tty locks are necessary for releasing pty pairs.
|
||||
* The stable lock order is master pty first, then slave pty.
|
||||
*/
|
||||
|
||||
/* Legacy tty mutex glue */
|
||||
|
||||
enum {
|
||||
TTY_MUTEX_NORMAL,
|
||||
TTY_MUTEX_SLAVE,
|
||||
};
|
||||
|
||||
/*
|
||||
* Getting the big tty mutex.
|
||||
*/
|
||||
@ -46,12 +36,8 @@ EXPORT_SYMBOL(tty_unlock);
|
||||
|
||||
void __lockfunc tty_lock_slave(struct tty_struct *tty)
|
||||
{
|
||||
if (tty && tty != tty->link) {
|
||||
WARN_ON(!mutex_is_locked(&tty->link->legacy_mutex) ||
|
||||
!tty->driver->type == TTY_DRIVER_TYPE_PTY ||
|
||||
!tty->driver->type == PTY_TYPE_SLAVE);
|
||||
if (tty && tty != tty->link)
|
||||
tty_lock(tty);
|
||||
}
|
||||
}
|
||||
|
||||
void __lockfunc tty_unlock_slave(struct tty_struct *tty)
|
||||
@ -62,5 +48,5 @@ void __lockfunc tty_unlock_slave(struct tty_struct *tty)
|
||||
|
||||
void tty_set_lock_subclass(struct tty_struct *tty)
|
||||
{
|
||||
lockdep_set_subclass(&tty->legacy_mutex, TTY_MUTEX_SLAVE);
|
||||
lockdep_set_subclass(&tty->legacy_mutex, TTY_LOCK_SLAVE);
|
||||
}
|
||||
|
@ -500,6 +500,7 @@ void invert_screen(struct vc_data *vc, int offset, int count, int viewed)
|
||||
#endif
|
||||
if (DO_UPDATE(vc))
|
||||
do_update_region(vc, (unsigned long) p, count);
|
||||
notify_update(vc);
|
||||
}
|
||||
|
||||
/* used by selection: complement pointer position */
|
||||
@ -516,6 +517,7 @@ void complement_pos(struct vc_data *vc, int offset)
|
||||
scr_writew(old, screenpos(vc, old_offset, 1));
|
||||
if (DO_UPDATE(vc))
|
||||
vc->vc_sw->con_putc(vc, old, oldy, oldx);
|
||||
notify_update(vc);
|
||||
}
|
||||
|
||||
old_offset = offset;
|
||||
@ -533,8 +535,8 @@ void complement_pos(struct vc_data *vc, int offset)
|
||||
oldy = (offset >> 1) / vc->vc_cols;
|
||||
vc->vc_sw->con_putc(vc, new, oldy, oldx);
|
||||
}
|
||||
notify_update(vc);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void insert_char(struct vc_data *vc, unsigned int nr)
|
||||
@ -3318,11 +3320,8 @@ static int vt_bind(struct con_driver *con)
|
||||
if (first == 0 && last == MAX_NR_CONSOLES -1)
|
||||
deflt = 1;
|
||||
|
||||
if (first != -1) {
|
||||
console_lock();
|
||||
if (first != -1)
|
||||
do_bind_con_driver(csw, first, last, deflt);
|
||||
console_unlock();
|
||||
}
|
||||
|
||||
first = -1;
|
||||
last = -1;
|
||||
@ -3362,9 +3361,7 @@ static int vt_unbind(struct con_driver *con)
|
||||
deflt = 1;
|
||||
|
||||
if (first != -1) {
|
||||
console_lock();
|
||||
ret = do_unbind_con_driver(csw, first, last, deflt);
|
||||
console_unlock();
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
}
|
||||
@ -3394,11 +3391,15 @@ static ssize_t store_bind(struct device *dev, struct device_attribute *attr,
|
||||
struct con_driver *con = dev_get_drvdata(dev);
|
||||
int bind = simple_strtoul(buf, NULL, 0);
|
||||
|
||||
console_lock();
|
||||
|
||||
if (bind)
|
||||
vt_bind(con);
|
||||
else
|
||||
vt_unbind(con);
|
||||
|
||||
console_unlock();
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
@ -3665,8 +3666,7 @@ int do_unregister_con_driver(const struct consw *csw)
|
||||
for (i = 0; i < MAX_NR_CON_DRIVER; i++) {
|
||||
struct con_driver *con_driver = ®istered_con_driver[i];
|
||||
|
||||
if (con_driver->con == csw &&
|
||||
con_driver->flag & CON_DRIVER_FLAG_INIT) {
|
||||
if (con_driver->con == csw) {
|
||||
vtconsole_deinit_device(con_driver);
|
||||
device_destroy(vtconsole_class,
|
||||
MKDEV(0, con_driver->node));
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include <linux/list.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/console.h>
|
||||
|
||||
#define PNP_NAME_LEN 50
|
||||
|
||||
@ -309,15 +310,22 @@ struct pnp_fixup {
|
||||
#define PNP_DISABLE 0x0004
|
||||
#define PNP_CONFIGURABLE 0x0008
|
||||
#define PNP_REMOVABLE 0x0010
|
||||
#define PNP_CONSOLE 0x0020
|
||||
|
||||
#define pnp_can_read(dev) (((dev)->protocol->get) && \
|
||||
((dev)->capabilities & PNP_READ))
|
||||
#define pnp_can_write(dev) (((dev)->protocol->set) && \
|
||||
((dev)->capabilities & PNP_WRITE))
|
||||
#define pnp_can_disable(dev) (((dev)->protocol->disable) && \
|
||||
((dev)->capabilities & PNP_DISABLE))
|
||||
#define pnp_can_disable(dev) (((dev)->protocol->disable) && \
|
||||
((dev)->capabilities & PNP_DISABLE) && \
|
||||
(!((dev)->capabilities & PNP_CONSOLE) || \
|
||||
console_suspend_enabled))
|
||||
#define pnp_can_configure(dev) ((!(dev)->active) && \
|
||||
((dev)->capabilities & PNP_CONFIGURABLE))
|
||||
#define pnp_can_suspend(dev) (((dev)->protocol->suspend) && \
|
||||
(!((dev)->capabilities & PNP_CONSOLE) || \
|
||||
console_suspend_enabled))
|
||||
|
||||
|
||||
#ifdef CONFIG_ISAPNP
|
||||
extern struct pnp_protocol isapnp_protocol;
|
||||
|
@ -85,6 +85,9 @@ struct uart_8250_port {
|
||||
unsigned char mcr_force; /* mask of forced bits */
|
||||
unsigned char cur_iotype; /* Running I/O type */
|
||||
unsigned int rpm_tx_active;
|
||||
unsigned char canary; /* non-zero during system sleep
|
||||
* if no_console_suspend
|
||||
*/
|
||||
|
||||
/*
|
||||
* Some bits in registers are cleared on a read, so they must
|
||||
@ -126,6 +129,7 @@ extern int serial8250_do_startup(struct uart_port *port);
|
||||
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 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);
|
||||
|
@ -123,6 +123,7 @@ struct uart_port {
|
||||
void (*set_termios)(struct uart_port *,
|
||||
struct ktermios *new,
|
||||
struct ktermios *old);
|
||||
void (*set_mctrl)(struct uart_port *, unsigned int);
|
||||
int (*startup)(struct uart_port *port);
|
||||
void (*shutdown)(struct uart_port *port);
|
||||
void (*throttle)(struct uart_port *port);
|
||||
@ -190,8 +191,10 @@ struct uart_port {
|
||||
#define UPF_NO_TXEN_TEST ((__force upf_t) (1 << 15))
|
||||
#define UPF_MAGIC_MULTIPLIER ((__force upf_t) ASYNC_MAGIC_MULTIPLIER /* 16 */ )
|
||||
|
||||
/* Port has hardware-assisted h/w flow control (iow, auto-RTS *not* auto-CTS) */
|
||||
#define UPF_HARD_FLOW ((__force upf_t) (1 << 21))
|
||||
/* Port has hardware-assisted h/w flow control */
|
||||
#define UPF_AUTO_CTS ((__force upf_t) (1 << 20))
|
||||
#define UPF_AUTO_RTS ((__force upf_t) (1 << 21))
|
||||
#define UPF_HARD_FLOW ((__force upf_t) (UPF_AUTO_CTS | UPF_AUTO_RTS))
|
||||
/* Port has hardware-assisted s/w flow control */
|
||||
#define UPF_SOFT_FLOW ((__force upf_t) (1 << 22))
|
||||
#define UPF_CONS_FLOW ((__force upf_t) (1 << 23))
|
||||
@ -213,11 +216,17 @@ struct uart_port {
|
||||
#error Change mask not equivalent to userspace-visible bit defines
|
||||
#endif
|
||||
|
||||
/* status must be updated while holding port lock */
|
||||
/*
|
||||
* Must hold termios_rwsem, port mutex and port lock to change;
|
||||
* can hold any one lock to read.
|
||||
*/
|
||||
upstat_t status;
|
||||
|
||||
#define UPSTAT_CTS_ENABLE ((__force upstat_t) (1 << 0))
|
||||
#define UPSTAT_DCD_ENABLE ((__force upstat_t) (1 << 1))
|
||||
#define UPSTAT_AUTORTS ((__force upstat_t) (1 << 2))
|
||||
#define UPSTAT_AUTOCTS ((__force upstat_t) (1 << 3))
|
||||
#define UPSTAT_AUTOXOFF ((__force upstat_t) (1 << 4))
|
||||
|
||||
int hw_stopped; /* sw-assisted CTS flow state */
|
||||
unsigned int mctrl; /* current modem ctrl settings */
|
||||
@ -391,6 +400,13 @@ static inline bool uart_cts_enabled(struct uart_port *uport)
|
||||
return !!(uport->status & UPSTAT_CTS_ENABLE);
|
||||
}
|
||||
|
||||
static inline bool uart_softcts_mode(struct uart_port *uport)
|
||||
{
|
||||
upstat_t mask = UPSTAT_CTS_ENABLE | UPSTAT_AUTOCTS;
|
||||
|
||||
return ((uport->status & mask) == UPSTAT_CTS_ENABLE);
|
||||
}
|
||||
|
||||
/*
|
||||
* The following are helper functions for the low level drivers.
|
||||
*/
|
||||
|
@ -104,6 +104,31 @@
|
||||
S3C2410_UCON_RXIRQMODE | \
|
||||
S3C2410_UCON_RXFIFO_TOI)
|
||||
|
||||
#define S3C64XX_UCON_TXBURST_1 (0<<20)
|
||||
#define S3C64XX_UCON_TXBURST_4 (1<<20)
|
||||
#define S3C64XX_UCON_TXBURST_8 (2<<20)
|
||||
#define S3C64XX_UCON_TXBURST_16 (3<<20)
|
||||
#define S3C64XX_UCON_TXBURST_MASK (0xf<<20)
|
||||
#define S3C64XX_UCON_RXBURST_1 (0<<16)
|
||||
#define S3C64XX_UCON_RXBURST_4 (1<<16)
|
||||
#define S3C64XX_UCON_RXBURST_8 (2<<16)
|
||||
#define S3C64XX_UCON_RXBURST_16 (3<<16)
|
||||
#define S3C64XX_UCON_RXBURST_MASK (0xf<<16)
|
||||
#define S3C64XX_UCON_TIMEOUT_SHIFT (12)
|
||||
#define S3C64XX_UCON_TIMEOUT_MASK (0xf<<12)
|
||||
#define S3C64XX_UCON_EMPTYINT_EN (1<<11)
|
||||
#define S3C64XX_UCON_DMASUS_EN (1<<10)
|
||||
#define S3C64XX_UCON_TXINT_LEVEL (1<<9)
|
||||
#define S3C64XX_UCON_RXINT_LEVEL (1<<8)
|
||||
#define S3C64XX_UCON_TIMEOUT_EN (1<<7)
|
||||
#define S3C64XX_UCON_ERRINT_EN (1<<6)
|
||||
#define S3C64XX_UCON_TXMODE_DMA (2<<2)
|
||||
#define S3C64XX_UCON_TXMODE_CPU (1<<2)
|
||||
#define S3C64XX_UCON_TXMODE_MASK (3<<2)
|
||||
#define S3C64XX_UCON_RXMODE_DMA (2<<0)
|
||||
#define S3C64XX_UCON_RXMODE_CPU (1<<0)
|
||||
#define S3C64XX_UCON_RXMODE_MASK (3<<0)
|
||||
|
||||
#define S3C2410_UFCON_FIFOMODE (1<<0)
|
||||
#define S3C2410_UFCON_TXTRIG0 (0<<6)
|
||||
#define S3C2410_UFCON_RXTRIG8 (1<<4)
|
||||
@ -155,6 +180,7 @@
|
||||
#define S3C2440_UFSTAT_TXMASK (63<<8)
|
||||
#define S3C2440_UFSTAT_RXMASK (63)
|
||||
|
||||
#define S3C2410_UTRSTAT_TIMEOUT (1<<3)
|
||||
#define S3C2410_UTRSTAT_TXE (1<<2)
|
||||
#define S3C2410_UTRSTAT_TXFE (1<<1)
|
||||
#define S3C2410_UTRSTAT_RXDR (1<<0)
|
||||
@ -179,8 +205,10 @@
|
||||
#define S3C64XX_UINTM 0x38
|
||||
|
||||
#define S3C64XX_UINTM_RXD (0)
|
||||
#define S3C64XX_UINTM_ERROR (1)
|
||||
#define S3C64XX_UINTM_TXD (2)
|
||||
#define S3C64XX_UINTM_RXD_MSK (1 << S3C64XX_UINTM_RXD)
|
||||
#define S3C64XX_UINTM_ERR_MSK (1 << S3C64XX_UINTM_ERROR)
|
||||
#define S3C64XX_UINTM_TXD_MSK (1 << S3C64XX_UINTM_TXD)
|
||||
|
||||
/* Following are specific to S5PV210 */
|
||||
|
@ -14,6 +14,29 @@
|
||||
#include <linux/llist.h>
|
||||
|
||||
|
||||
/*
|
||||
* Lock subclasses for tty locks
|
||||
*
|
||||
* TTY_LOCK_NORMAL is for normal ttys and master ptys.
|
||||
* TTY_LOCK_SLAVE is for slave ptys only.
|
||||
*
|
||||
* Lock subclasses are necessary for handling nested locking with pty pairs.
|
||||
* tty locks which use nested locking:
|
||||
*
|
||||
* legacy_mutex - Nested tty locks are necessary for releasing pty pairs.
|
||||
* The stable lock order is master pty first, then slave pty.
|
||||
* termios_rwsem - The stable lock order is tty_buffer lock->termios_rwsem.
|
||||
* Subclassing this lock enables the slave pty to hold its
|
||||
* termios_rwsem when claiming the master tty_buffer lock.
|
||||
* tty_buffer lock - slave ptys can claim nested buffer lock when handling
|
||||
* signal chars. The stable lock order is slave pty, then
|
||||
* master.
|
||||
*/
|
||||
|
||||
enum {
|
||||
TTY_LOCK_NORMAL = 0,
|
||||
TTY_LOCK_SLAVE,
|
||||
};
|
||||
|
||||
/*
|
||||
* (Note: the *_driver.minor_start values 1, 64, 128, 192 are
|
||||
@ -443,6 +466,7 @@ extern void tty_flush_to_ldisc(struct tty_struct *tty);
|
||||
extern void tty_buffer_free_all(struct tty_port *port);
|
||||
extern void tty_buffer_flush(struct tty_struct *tty, struct tty_ldisc *ld);
|
||||
extern void tty_buffer_init(struct tty_port *port);
|
||||
extern void tty_buffer_set_lock_subclass(struct tty_port *port);
|
||||
extern speed_t tty_termios_baud_rate(struct ktermios *termios);
|
||||
extern speed_t tty_termios_input_baud_rate(struct ktermios *termios);
|
||||
extern void tty_termios_encode_baud_rate(struct ktermios *termios,
|
||||
@ -467,7 +491,6 @@ static inline speed_t tty_get_baud_rate(struct tty_struct *tty)
|
||||
|
||||
extern void tty_termios_copy_hw(struct ktermios *new, struct ktermios *old);
|
||||
extern int tty_termios_hw_change(struct ktermios *a, struct ktermios *b);
|
||||
extern int tty_set_termios(struct tty_struct *tty, struct ktermios *kt);
|
||||
|
||||
extern struct tty_ldisc *tty_ldisc_ref(struct tty_struct *);
|
||||
extern void tty_ldisc_deref(struct tty_ldisc *);
|
||||
|
@ -21,10 +21,6 @@
|
||||
#ifndef VT_BUF_HAVE_RW
|
||||
#define scr_writew(val, addr) (*(addr) = (val))
|
||||
#define scr_readw(addr) (*(addr))
|
||||
#define scr_memcpyw(d, s, c) memcpy(d, s, c)
|
||||
#define scr_memmovew(d, s, c) memmove(d, s, c)
|
||||
#define VT_BUF_HAVE_MEMCPYW
|
||||
#define VT_BUF_HAVE_MEMMOVEW
|
||||
#endif
|
||||
|
||||
#ifndef VT_BUF_HAVE_MEMSETW
|
||||
|
@ -55,7 +55,8 @@
|
||||
#define PORT_ALTR_16550_F64 27 /* Altera 16550 UART with 64 FIFOs */
|
||||
#define PORT_ALTR_16550_F128 28 /* Altera 16550 UART with 128 FIFOs */
|
||||
#define PORT_RT2880 29 /* Ralink RT2880 internal UART */
|
||||
#define PORT_MAX_8250 29 /* max port ID */
|
||||
#define PORT_16550A_FSL64 30 /* Freescale 16550 UART with 64 FIFOs */
|
||||
#define PORT_MAX_8250 30 /* max port ID */
|
||||
|
||||
/*
|
||||
* ARM specific type numbers. These are not currently guaranteed
|
||||
@ -248,4 +249,13 @@
|
||||
/* MESON */
|
||||
#define PORT_MESON 109
|
||||
|
||||
/* Conexant Digicolor */
|
||||
#define PORT_DIGICOLOR 110
|
||||
|
||||
/* SPRD SERIAL */
|
||||
#define PORT_SPRD 111
|
||||
|
||||
/* Cris v10 / v32 SoC */
|
||||
#define PORT_CRIS 112
|
||||
|
||||
#endif /* _UAPILINUX_SERIAL_CORE_H */
|
||||
|
@ -86,7 +86,8 @@
|
||||
#define UART_FCR6_T_TRIGGER_8 0x10 /* Mask for transmit trigger set at 8 */
|
||||
#define UART_FCR6_T_TRIGGER_24 0x20 /* Mask for transmit trigger set at 24 */
|
||||
#define UART_FCR6_T_TRIGGER_30 0x30 /* Mask for transmit trigger set at 30 */
|
||||
#define UART_FCR7_64BYTE 0x20 /* Go into 64 byte mode (TI16C750) */
|
||||
#define UART_FCR7_64BYTE 0x20 /* Go into 64 byte mode (TI16C750 and
|
||||
some Freescale UARTs) */
|
||||
|
||||
#define UART_FCR_R_TRIG_SHIFT 6
|
||||
#define UART_FCR_R_TRIG_BITS(x) \
|
||||
|
Loading…
Reference in New Issue
Block a user