mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-26 21:54:11 +08:00
TTY/Serial driver changes for 5.4-rc1
Even in this age, people are still making new serial port silicon, why... Anyway, here's the TTY and Serial driver update for 5.4-rc1. Lots of changes in here for a number of embedded serial port devices that are being worked on because people really like to see those console logs... Other than that, nothing major here, no core tty changes that anyone should care about. All of these have been in linux-next for a while with no reported issues. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCXYIXfw8cZ3JlZ0Brcm9h aC5jb20ACgkQMUfUDdst+ymmNACeIYq7038NXGBXBj1DfN90GeGNenwAnRkjzF+6 YwEhFRxOMglnbvDevgPU =E6jv -----END PGP SIGNATURE----- Merge tag 'tty-5.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty Pull tty/serial driver updates from Greg KH: "Even in this age, people are still making new serial port silicon, why... Anyway, here's the TTY and Serial driver update for 5.4-rc1. Lots of changes in here for a number of embedded serial port devices that are being worked on because people really like to see those console logs... Other than that, nothing major here, no core tty changes that anyone should care about. All of these have been in linux-next for a while with no reported issues" * tag 'tty-5.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty: (125 commits) serial: tegra: Add PIO mode support serial: tegra: report clk rate errors serial: tegra: add support to adjust baud rate serial: tegra: DT for Adjusted baud rates serial: tegra: add support to use 8 bytes trigger serial: tegra: set maximum num of uart ports to 8 serial: tegra: check for FIFO mode enabled status dt-binding: serial: tegra: add new chips serial: tegra: report error to upper tty layer serial: tegra: flush the RX fifo on frame error serial: tegra: avoid reg access when clk disabled serial: tegra: add support to ignore read serial: sprd: correct the wrong sequence of arguments dt-bindings: serial: Convert riscv,sifive-serial to json-schema serial: max310x: turn off transmitter before activating AutoCTS or auto transmitter flow control serial: max310x: Properly set flags in AutoCTS mode tty: serial: fix platform_no_drv_owner.cocci warnings dt-bindings: serial: Document Freescale LINFlexD UART serial: fsl_linflexuart: Update compatible string tty: n_gsm: avoid recursive locking with async port hangup ...
This commit is contained in:
commit
e444d51b14
@ -1094,6 +1094,12 @@
|
||||
the framebuffer, pass the 'ram' option so that it is
|
||||
mapped with the correct attributes.
|
||||
|
||||
linflex,<addr>
|
||||
Use early console provided by Freescale LinFlex UART
|
||||
serial driver for NXP S32V234 SoCs. A valid base
|
||||
address must be provided, and the serial port must
|
||||
already be setup and configured.
|
||||
|
||||
earlyprintk= [X86,SH,ARM,M68k,S390]
|
||||
earlyprintk=vga
|
||||
earlyprintk=sclp
|
||||
|
@ -0,0 +1,22 @@
|
||||
* Freescale LINFlexD UART
|
||||
|
||||
The LINFlexD controller implements several LIN protocol versions, as well as
|
||||
support for full-duplex UART communication through 8-bit and 9-bit frames.
|
||||
|
||||
See chapter 47 ("LINFlexD") in the reference manual[1].
|
||||
|
||||
Required properties:
|
||||
- compatible :
|
||||
- "fsl,s32v234-linflexuart" for LINFlexD configured in UART mode, which
|
||||
is compatible with the one integrated on S32V234 SoC
|
||||
- reg : Address and length of the register set for the device
|
||||
- interrupts : Should contain uart interrupt
|
||||
|
||||
Example:
|
||||
uart0: serial@40053000 {
|
||||
compatible = "fsl,s32v234-linflexuart";
|
||||
reg = <0x0 0x40053000 0x0 0x1000>;
|
||||
interrupts = <0 59 4>;
|
||||
};
|
||||
|
||||
[1] https://www.nxp.com/webapp/Download?colCode=S32V234RM
|
@ -1,7 +1,12 @@
|
||||
NVIDIA Tegra20/Tegra30 high speed (DMA based) UART controller driver.
|
||||
|
||||
Required properties:
|
||||
- compatible : should be "nvidia,tegra30-hsuart", "nvidia,tegra20-hsuart".
|
||||
- compatible : should be,
|
||||
"nvidia,tegra20-hsuart" for Tegra20,
|
||||
"nvidia,tegra30-hsuart" for Tegra30,
|
||||
"nvidia,tegra186-hsuart" for Tegra186,
|
||||
"nvidia,tegra194-hsuart" for Tegra194.
|
||||
|
||||
- reg: Should contain UART controller registers location and length.
|
||||
- interrupts: Should contain UART controller interrupts.
|
||||
- clocks: Must contain one entry, for the module clock.
|
||||
@ -19,6 +24,37 @@ Required properties:
|
||||
Optional properties:
|
||||
- nvidia,enable-modem-interrupt: Enable modem interrupts. Should be enable
|
||||
only if all 8 lines of UART controller are pinmuxed.
|
||||
- nvidia,adjust-baud-rates: List of entries providing percentage of baud rate
|
||||
adjustment within a range.
|
||||
Each entry contains sets of 3 values. Range low/high and adjusted rate.
|
||||
<range_low range_high adjusted_rate>
|
||||
When baud rate set on controller falls within the range mentioned in this
|
||||
field, baud rate will be adjusted by percentage mentioned here.
|
||||
Ex: <9600 115200 200>
|
||||
Increase baud rate by 2% when set baud rate falls within range 9600 to 115200
|
||||
|
||||
Baud Rate tolerance:
|
||||
Standard UART devices are expected to have tolerance for baud rate error by
|
||||
-4 to +4 %. All Tegra devices till Tegra210 had this support. However,
|
||||
Tegra186 chip has a known hardware issue. UART Rx baud rate tolerance level
|
||||
is 0% to +4% in 1-stop config. Otherwise, the received data will have
|
||||
corruption/invalid framing errors. Parker errata suggests adjusting baud
|
||||
rate to be higher than the deviations observed in Tx.
|
||||
|
||||
Tx deviation of connected device can be captured over scope (or noted from
|
||||
its spec) for valid range and Tegra baud rate has to be set above actual
|
||||
Tx baud rate observed. To do this we use nvidia,adjust-baud-rates
|
||||
|
||||
As an example, consider there is deviation observed in Tx for baud rates as
|
||||
listed below.
|
||||
0 to 9600 has 1% deviation
|
||||
9600 to 115200 2% deviation
|
||||
This slight deviation is expcted and Tegra UART is expected to handle it. Due
|
||||
to the issue stated above, baud rate on Tegra UART should be set equal to or
|
||||
above deviation observed for avoiding frame errors.
|
||||
Property should be set like this
|
||||
nvidia,adjust-baud-rates = <0 9600 100>,
|
||||
<9600 115200 200>;
|
||||
|
||||
Example:
|
||||
|
||||
@ -33,4 +69,5 @@ serial@70006000 {
|
||||
reset-names = "serial";
|
||||
dmas = <&apbdma 8>, <&apbdma 8>;
|
||||
dma-names = "rx", "tx";
|
||||
nvidia,adjust-baud-rates = <1000000 4000000 136>; /* 1.36% shift */
|
||||
};
|
||||
|
@ -1,33 +0,0 @@
|
||||
SiFive asynchronous serial interface (UART)
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible: should be something similar to
|
||||
"sifive,<chip>-uart" for the UART as integrated
|
||||
on a particular chip, and "sifive,uart<version>" for the
|
||||
general UART IP block programming model. Supported
|
||||
compatible strings as of the date of this writing are:
|
||||
"sifive,fu540-c000-uart" for the SiFive UART v0 as
|
||||
integrated onto the SiFive FU540 chip, or "sifive,uart0"
|
||||
for the SiFive UART v0 IP block with no chip integration
|
||||
tweaks (if any)
|
||||
- reg: address and length of the register space
|
||||
- interrupts: Should contain the UART interrupt identifier
|
||||
- clocks: Should contain a clock identifier for the UART's parent clock
|
||||
|
||||
|
||||
UART HDL that corresponds to the IP block version numbers can be found
|
||||
here:
|
||||
|
||||
https://github.com/sifive/sifive-blocks/tree/master/src/main/scala/devices/uart
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
uart0: serial@10010000 {
|
||||
compatible = "sifive,fu540-c000-uart", "sifive,uart0";
|
||||
interrupt-parent = <&plic0>;
|
||||
interrupts = <80>;
|
||||
reg = <0x0 0x10010000 0x0 0x1000>;
|
||||
clocks = <&prci PRCI_CLK_TLCLK>;
|
||||
};
|
62
Documentation/devicetree/bindings/serial/sifive-serial.yaml
Normal file
62
Documentation/devicetree/bindings/serial/sifive-serial.yaml
Normal file
@ -0,0 +1,62 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/serial/sifive-serial.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: SiFive asynchronous serial interface (UART)
|
||||
|
||||
maintainers:
|
||||
- Pragnesh Patel <pragnesh.patel@sifive.com>
|
||||
- Paul Walmsley <paul.walmsley@sifive.com>
|
||||
- Palmer Dabbelt <palmer@sifive.com>
|
||||
|
||||
allOf:
|
||||
- $ref: /schemas/serial.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
- const: sifive,fu540-c000-uart
|
||||
- const: sifive,uart0
|
||||
|
||||
description:
|
||||
Should be something similar to "sifive,<chip>-uart"
|
||||
for the UART as integrated on a particular chip,
|
||||
and "sifive,uart<version>" for the general UART IP
|
||||
block programming model.
|
||||
|
||||
UART HDL that corresponds to the IP block version
|
||||
numbers can be found here -
|
||||
|
||||
https://github.com/sifive/sifive-blocks/tree/master/src/main/scala/devices/uart
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- clocks
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/sifive-fu540-prci.h>
|
||||
serial@10010000 {
|
||||
compatible = "sifive,fu540-c000-uart", "sifive,uart0";
|
||||
interrupt-parent = <&plic0>;
|
||||
interrupts = <80>;
|
||||
reg = <0x0 0x10010000 0x0 0x1000>;
|
||||
clocks = <&prci PRCI_CLK_TLCLK>;
|
||||
};
|
||||
|
||||
...
|
@ -20,6 +20,11 @@ Optional properties:
|
||||
linux,rs485-enabled-at-boot-time: see rs485.txt.
|
||||
- dmas: phandle(s) to DMA controller node(s). Refer to stm32-dma.txt
|
||||
- dma-names: "rx" and/or "tx"
|
||||
- wakeup-source: bool flag to indicate this device has wakeup capabilities
|
||||
- interrupt-names, if optional wake-up interrupt is used, should be:
|
||||
- "event": the name for the interrupt line of the USART instance
|
||||
- "wakeup" the name for the optional wake-up interrupt
|
||||
|
||||
|
||||
Examples:
|
||||
usart4: serial@40004c00 {
|
||||
|
@ -18,18 +18,22 @@ How to use it
|
||||
2. switch the serial line to using the n_gsm line discipline by using
|
||||
TIOCSETD ioctl,
|
||||
3. configure the mux using GSMIOC_GETCONF / GSMIOC_SETCONF ioctl,
|
||||
4. obtain base gsmtty number for the used serial port,
|
||||
|
||||
Major parts of the initialization program :
|
||||
(a good starting point is util-linux-ng/sys-utils/ldattach.c)::
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <linux/gsmmux.h>
|
||||
#define N_GSM0710 21 /* GSM 0710 Mux */
|
||||
#include <linux/tty.h>
|
||||
#define DEFAULT_SPEED B115200
|
||||
#define SERIAL_PORT /dev/ttyS0
|
||||
|
||||
int ldisc = N_GSM0710;
|
||||
struct gsm_config c;
|
||||
struct termios configuration;
|
||||
uint32_t first;
|
||||
|
||||
/* open the serial port connected to the modem */
|
||||
fd = open(SERIAL_PORT, O_RDWR | O_NOCTTY | O_NDELAY);
|
||||
@ -58,21 +62,14 @@ Major parts of the initialization program :
|
||||
c.mtu = 127;
|
||||
/* set the new configuration */
|
||||
ioctl(fd, GSMIOC_SETCONF, &c);
|
||||
/* get first gsmtty device node */
|
||||
ioctl(fd, GSMIOC_GETFIRST, &first);
|
||||
printf("first muxed line: /dev/gsmtty%i\n", first);
|
||||
|
||||
/* and wait for ever to keep the line discipline enabled */
|
||||
daemon(0,0);
|
||||
pause();
|
||||
|
||||
4. create the devices corresponding to the "virtual" serial ports (take care,
|
||||
each modem has its configuration and some DLC have dedicated functions,
|
||||
for example GPS), starting with minor 1 (DLC0 is reserved for the management
|
||||
of the mux)::
|
||||
|
||||
MAJOR=`cat /proc/devices |grep gsmtty | awk '{print $1}`
|
||||
for i in `seq 1 4`; do
|
||||
mknod /dev/ttygsm$i c $MAJOR $i
|
||||
done
|
||||
|
||||
5. use these devices as plain serial ports.
|
||||
|
||||
for example, it's possible:
|
||||
|
@ -61,7 +61,10 @@ enum parport_pc_pci_cards {
|
||||
wch_ch382_0s1p,
|
||||
wch_ch382_2s1p,
|
||||
brainboxes_5s1p,
|
||||
sunix_2s1p,
|
||||
sunix_4008a,
|
||||
sunix_5069a,
|
||||
sunix_5079a,
|
||||
sunix_5099a,
|
||||
};
|
||||
|
||||
/* each element directly indexed from enum list, above */
|
||||
@ -151,7 +154,10 @@ static struct parport_pc_pci cards[] = {
|
||||
/* wch_ch382_0s1p*/ { 1, { { 2, -1}, } },
|
||||
/* wch_ch382_2s1p*/ { 1, { { 2, -1}, } },
|
||||
/* brainboxes_5s1p */ { 1, { { 3, -1 }, } },
|
||||
/* sunix_2s1p */ { 1, { { 3, -1 }, } },
|
||||
/* sunix_4008a */ { 1, { { 1, 2 }, } },
|
||||
/* sunix_5069a */ { 1, { { 1, 2 }, } },
|
||||
/* sunix_5079a */ { 1, { { 1, 2 }, } },
|
||||
/* sunix_5099a */ { 1, { { 1, 2 }, } },
|
||||
};
|
||||
|
||||
static struct pci_device_id parport_serial_pci_tbl[] = {
|
||||
@ -261,13 +267,15 @@ static struct pci_device_id parport_serial_pci_tbl[] = {
|
||||
{ PCI_VENDOR_ID_INTASHIELD, 0x4100,
|
||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0, brainboxes_5s1p },
|
||||
|
||||
/*
|
||||
* More SUNIX variations. At least one of these has part number
|
||||
* '5079A but subdevice 0x102. That board reports 0x0708 as
|
||||
* its PCI Class.
|
||||
*/
|
||||
/* Sunix boards */
|
||||
{ PCI_VENDOR_ID_SUNIX, PCI_DEVICE_ID_SUNIX_1999, PCI_VENDOR_ID_SUNIX,
|
||||
0x0102, 0, 0, sunix_2s1p },
|
||||
0x0100, 0, 0, sunix_4008a },
|
||||
{ PCI_VENDOR_ID_SUNIX, PCI_DEVICE_ID_SUNIX_1999, PCI_VENDOR_ID_SUNIX,
|
||||
0x0101, 0, 0, sunix_5069a },
|
||||
{ PCI_VENDOR_ID_SUNIX, PCI_DEVICE_ID_SUNIX_1999, PCI_VENDOR_ID_SUNIX,
|
||||
0x0102, 0, 0, sunix_5079a },
|
||||
{ PCI_VENDOR_ID_SUNIX, PCI_DEVICE_ID_SUNIX_1999, PCI_VENDOR_ID_SUNIX,
|
||||
0x0104, 0, 0, sunix_5099a },
|
||||
|
||||
{ 0, } /* terminate list */
|
||||
};
|
||||
@ -516,11 +524,23 @@ static struct pciserial_board pci_parport_serial_boards[] = {
|
||||
.base_baud = 921600,
|
||||
.uart_offset = 8,
|
||||
},
|
||||
[sunix_2s1p] = {
|
||||
.flags = FL_BASE0|FL_BASE_BARS,
|
||||
[sunix_4008a] = {
|
||||
.num_ports = 0,
|
||||
},
|
||||
[sunix_5069a] = {
|
||||
.num_ports = 1,
|
||||
.base_baud = 921600,
|
||||
.uart_offset = 0x8,
|
||||
},
|
||||
[sunix_5079a] = {
|
||||
.num_ports = 2,
|
||||
.base_baud = 921600,
|
||||
.uart_offset = 8,
|
||||
.base_baud = 921600,
|
||||
.uart_offset = 0x8,
|
||||
},
|
||||
[sunix_5099a] = {
|
||||
.num_ports = 4,
|
||||
.base_baud = 921600,
|
||||
.uart_offset = 0x8,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -871,8 +871,8 @@ static void hvcs_set_pi(struct hvcs_partner_info *pi, struct hvcs_struct *hvcsd)
|
||||
hvcsd->p_partition_ID = pi->partition_ID;
|
||||
|
||||
/* copy the null-term char too */
|
||||
strlcpy(&hvcsd->p_location_code[0],
|
||||
&pi->location_code[0], sizeof(hvcsd->p_location_code));
|
||||
strlcpy(hvcsd->p_location_code, pi->location_code,
|
||||
sizeof(hvcsd->p_location_code));
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -553,7 +553,6 @@ static irqreturn_t isicom_interrupt(int irq, void *dev_id)
|
||||
|
||||
tty = tty_port_tty_get(&port->port);
|
||||
if (tty == NULL) {
|
||||
word_count = byte_count >> 1;
|
||||
while (byte_count > 1) {
|
||||
inw(base);
|
||||
byte_count -= 2;
|
||||
|
@ -1716,7 +1716,7 @@ static void gsm_dlci_release(struct gsm_dlci *dlci)
|
||||
gsm_destroy_network(dlci);
|
||||
mutex_unlock(&dlci->mutex);
|
||||
|
||||
tty_vhangup(tty);
|
||||
tty_hangup(tty);
|
||||
|
||||
tty_port_tty_set(&dlci->port, NULL);
|
||||
tty_kref_put(tty);
|
||||
@ -2171,6 +2171,16 @@ static inline void mux_put(struct gsm_mux *gsm)
|
||||
kref_put(&gsm->ref, gsm_free_muxr);
|
||||
}
|
||||
|
||||
static inline unsigned int mux_num_to_base(struct gsm_mux *gsm)
|
||||
{
|
||||
return gsm->num * NUM_DLCI;
|
||||
}
|
||||
|
||||
static inline unsigned int mux_line_to_num(unsigned int line)
|
||||
{
|
||||
return line / NUM_DLCI;
|
||||
}
|
||||
|
||||
/**
|
||||
* gsm_alloc_mux - allocate a mux
|
||||
*
|
||||
@ -2351,7 +2361,8 @@ static int gsmld_output(struct gsm_mux *gsm, u8 *data, int len)
|
||||
|
||||
static int gsmld_attach_gsm(struct tty_struct *tty, struct gsm_mux *gsm)
|
||||
{
|
||||
int ret, i, base;
|
||||
unsigned int base;
|
||||
int ret, i;
|
||||
|
||||
gsm->tty = tty_kref_get(tty);
|
||||
gsm->output = gsmld_output;
|
||||
@ -2361,7 +2372,7 @@ static int gsmld_attach_gsm(struct tty_struct *tty, struct gsm_mux *gsm)
|
||||
else {
|
||||
/* Don't register device 0 - this is the control channel and not
|
||||
a usable tty interface */
|
||||
base = gsm->num << 6; /* Base for this MUX */
|
||||
base = mux_num_to_base(gsm); /* Base for this MUX */
|
||||
for (i = 1; i < NUM_DLCI; i++)
|
||||
tty_register_device(gsm_tty_driver, base + i, NULL);
|
||||
}
|
||||
@ -2379,8 +2390,8 @@ static int gsmld_attach_gsm(struct tty_struct *tty, struct gsm_mux *gsm)
|
||||
|
||||
static void gsmld_detach_gsm(struct tty_struct *tty, struct gsm_mux *gsm)
|
||||
{
|
||||
unsigned int base = mux_num_to_base(gsm); /* Base for this MUX */
|
||||
int i;
|
||||
int base = gsm->num << 6; /* Base for this MUX */
|
||||
|
||||
WARN_ON(tty != gsm->tty);
|
||||
for (i = 1; i < NUM_DLCI; i++)
|
||||
@ -2602,6 +2613,7 @@ static int gsmld_ioctl(struct tty_struct *tty, struct file *file,
|
||||
{
|
||||
struct gsm_config c;
|
||||
struct gsm_mux *gsm = tty->disc_data;
|
||||
unsigned int base;
|
||||
|
||||
switch (cmd) {
|
||||
case GSMIOC_GETCONF:
|
||||
@ -2613,6 +2625,9 @@ static int gsmld_ioctl(struct tty_struct *tty, struct file *file,
|
||||
if (copy_from_user(&c, (void *)arg, sizeof(c)))
|
||||
return -EFAULT;
|
||||
return gsm_config(gsm, &c);
|
||||
case GSMIOC_GETFIRST:
|
||||
base = mux_num_to_base(gsm);
|
||||
return put_user(base + 1, (__u32 __user *)arg);
|
||||
default:
|
||||
return n_tty_ioctl_helper(tty, file, cmd, arg);
|
||||
}
|
||||
@ -2908,7 +2923,7 @@ static int gsmtty_install(struct tty_driver *driver, struct tty_struct *tty)
|
||||
struct gsm_mux *gsm;
|
||||
struct gsm_dlci *dlci;
|
||||
unsigned int line = tty->index;
|
||||
unsigned int mux = line >> 6;
|
||||
unsigned int mux = mux_line_to_num(line);
|
||||
bool alloc = false;
|
||||
int ret;
|
||||
|
||||
|
@ -1282,7 +1282,7 @@ static void nozomi_setup_private_data(struct nozomi *dc)
|
||||
static ssize_t card_type_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
const struct nozomi *dc = pci_get_drvdata(to_pci_dev(dev));
|
||||
const struct nozomi *dc = dev_get_drvdata(dev);
|
||||
|
||||
return sprintf(buf, "%d\n", dc->card_type);
|
||||
}
|
||||
@ -1291,7 +1291,7 @@ static DEVICE_ATTR_RO(card_type);
|
||||
static ssize_t open_ttys_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
const struct nozomi *dc = pci_get_drvdata(to_pci_dev(dev));
|
||||
const struct nozomi *dc = dev_get_drvdata(dev);
|
||||
|
||||
return sprintf(buf, "%u\n", dc->open_ttys);
|
||||
}
|
||||
|
@ -56,10 +56,8 @@ static int bcm2835aux_serial_probe(struct platform_device *pdev)
|
||||
|
||||
/* get the interrupt */
|
||||
ret = platform_get_irq(pdev, 0);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "irq not found - %i", ret);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
data->uart.port.irq = ret;
|
||||
|
||||
/* map the main registers */
|
||||
|
@ -1026,10 +1026,8 @@ int serial8250_register_8250_port(struct uart_8250_port *up)
|
||||
if (!has_acpi_companion(uart->port.dev)) {
|
||||
gpios = mctrl_gpio_init(&uart->port, 0);
|
||||
if (IS_ERR(gpios)) {
|
||||
if (PTR_ERR(gpios) != -ENOSYS) {
|
||||
ret = PTR_ERR(gpios);
|
||||
goto out_unlock;
|
||||
}
|
||||
ret = PTR_ERR(gpios);
|
||||
goto out_unlock;
|
||||
} else {
|
||||
uart->gpios = gpios;
|
||||
}
|
||||
|
@ -27,66 +27,36 @@
|
||||
|
||||
#include <asm/byteorder.h>
|
||||
|
||||
#include "8250.h"
|
||||
#include "8250_dwlib.h"
|
||||
|
||||
/* Offsets for the DesignWare specific registers */
|
||||
#define DW_UART_USR 0x1f /* UART Status Register */
|
||||
#define DW_UART_DLF 0xc0 /* Divisor Latch Fraction Register */
|
||||
#define DW_UART_CPR 0xf4 /* Component Parameter Register */
|
||||
#define DW_UART_UCV 0xf8 /* UART Component Version */
|
||||
|
||||
/* Component Parameter Register bits */
|
||||
#define DW_UART_CPR_ABP_DATA_WIDTH (3 << 0)
|
||||
#define DW_UART_CPR_AFCE_MODE (1 << 4)
|
||||
#define DW_UART_CPR_THRE_MODE (1 << 5)
|
||||
#define DW_UART_CPR_SIR_MODE (1 << 6)
|
||||
#define DW_UART_CPR_SIR_LP_MODE (1 << 7)
|
||||
#define DW_UART_CPR_ADDITIONAL_FEATURES (1 << 8)
|
||||
#define DW_UART_CPR_FIFO_ACCESS (1 << 9)
|
||||
#define DW_UART_CPR_FIFO_STAT (1 << 10)
|
||||
#define DW_UART_CPR_SHADOW (1 << 11)
|
||||
#define DW_UART_CPR_ENCODED_PARMS (1 << 12)
|
||||
#define DW_UART_CPR_DMA_EXTRA (1 << 13)
|
||||
#define DW_UART_CPR_FIFO_MODE (0xff << 16)
|
||||
/* Helper for fifo size calculation */
|
||||
#define DW_UART_CPR_FIFO_SIZE(a) (((a >> 16) & 0xff) * 16)
|
||||
|
||||
/* DesignWare specific register fields */
|
||||
#define DW_UART_MCR_SIRE BIT(6)
|
||||
|
||||
struct dw8250_data {
|
||||
struct dw8250_port_data data;
|
||||
|
||||
u8 usr_reg;
|
||||
u8 dlf_size;
|
||||
int line;
|
||||
int msr_mask_on;
|
||||
int msr_mask_off;
|
||||
struct clk *clk;
|
||||
struct clk *pclk;
|
||||
struct reset_control *rst;
|
||||
struct uart_8250_dma dma;
|
||||
|
||||
unsigned int skip_autocfg:1;
|
||||
unsigned int uart_16550_compatible:1;
|
||||
};
|
||||
|
||||
static inline u32 dw8250_readl_ext(struct uart_port *p, int offset)
|
||||
static inline struct dw8250_data *to_dw8250_data(struct dw8250_port_data *data)
|
||||
{
|
||||
if (p->iotype == UPIO_MEM32BE)
|
||||
return ioread32be(p->membase + offset);
|
||||
return readl(p->membase + offset);
|
||||
}
|
||||
|
||||
static inline void dw8250_writel_ext(struct uart_port *p, int offset, u32 reg)
|
||||
{
|
||||
if (p->iotype == UPIO_MEM32BE)
|
||||
iowrite32be(reg, p->membase + offset);
|
||||
else
|
||||
writel(reg, p->membase + offset);
|
||||
return container_of(data, struct dw8250_data, data);
|
||||
}
|
||||
|
||||
static inline int dw8250_modify_msr(struct uart_port *p, int offset, int value)
|
||||
{
|
||||
struct dw8250_data *d = p->private_data;
|
||||
struct dw8250_data *d = to_dw8250_data(p->private_data);
|
||||
|
||||
/* Override any modem control signals if needed */
|
||||
if (offset == UART_MSR) {
|
||||
@ -160,7 +130,7 @@ static void dw8250_tx_wait_empty(struct uart_port *p)
|
||||
|
||||
static void dw8250_serial_out38x(struct uart_port *p, int offset, int value)
|
||||
{
|
||||
struct dw8250_data *d = p->private_data;
|
||||
struct dw8250_data *d = to_dw8250_data(p->private_data);
|
||||
|
||||
/* Allow the TX to drain before we reconfigure */
|
||||
if (offset == UART_LCR)
|
||||
@ -175,7 +145,7 @@ static void dw8250_serial_out38x(struct uart_port *p, int offset, int value)
|
||||
|
||||
static void dw8250_serial_out(struct uart_port *p, int offset, int value)
|
||||
{
|
||||
struct dw8250_data *d = p->private_data;
|
||||
struct dw8250_data *d = to_dw8250_data(p->private_data);
|
||||
|
||||
writeb(value, p->membase + (offset << p->regshift));
|
||||
|
||||
@ -202,7 +172,7 @@ static unsigned int dw8250_serial_inq(struct uart_port *p, int offset)
|
||||
|
||||
static void dw8250_serial_outq(struct uart_port *p, int offset, int value)
|
||||
{
|
||||
struct dw8250_data *d = p->private_data;
|
||||
struct dw8250_data *d = to_dw8250_data(p->private_data);
|
||||
|
||||
value &= 0xff;
|
||||
__raw_writeq(value, p->membase + (offset << p->regshift));
|
||||
@ -216,7 +186,7 @@ static void dw8250_serial_outq(struct uart_port *p, int offset, int value)
|
||||
|
||||
static void dw8250_serial_out32(struct uart_port *p, int offset, int value)
|
||||
{
|
||||
struct dw8250_data *d = p->private_data;
|
||||
struct dw8250_data *d = to_dw8250_data(p->private_data);
|
||||
|
||||
writel(value, p->membase + (offset << p->regshift));
|
||||
|
||||
@ -233,7 +203,7 @@ static unsigned int dw8250_serial_in32(struct uart_port *p, int offset)
|
||||
|
||||
static void dw8250_serial_out32be(struct uart_port *p, int offset, int value)
|
||||
{
|
||||
struct dw8250_data *d = p->private_data;
|
||||
struct dw8250_data *d = to_dw8250_data(p->private_data);
|
||||
|
||||
iowrite32be(value, p->membase + (offset << p->regshift));
|
||||
|
||||
@ -252,7 +222,7 @@ static unsigned int dw8250_serial_in32be(struct uart_port *p, int offset)
|
||||
static int dw8250_handle_irq(struct uart_port *p)
|
||||
{
|
||||
struct uart_8250_port *up = up_to_u8250p(p);
|
||||
struct dw8250_data *d = p->private_data;
|
||||
struct dw8250_data *d = to_dw8250_data(p->private_data);
|
||||
unsigned int iir = p->serial_in(p, UART_IIR);
|
||||
unsigned int status;
|
||||
unsigned long flags;
|
||||
@ -306,7 +276,7 @@ static void dw8250_set_termios(struct uart_port *p, struct ktermios *termios,
|
||||
struct ktermios *old)
|
||||
{
|
||||
unsigned int baud = tty_termios_baud_rate(termios);
|
||||
struct dw8250_data *d = p->private_data;
|
||||
struct dw8250_data *d = to_dw8250_data(p->private_data);
|
||||
long rate;
|
||||
int ret;
|
||||
|
||||
@ -368,37 +338,6 @@ static bool dw8250_idma_filter(struct dma_chan *chan, void *param)
|
||||
return param == chan->device->dev;
|
||||
}
|
||||
|
||||
/*
|
||||
* divisor = div(I) + div(F)
|
||||
* "I" means integer, "F" means fractional
|
||||
* quot = div(I) = clk / (16 * baud)
|
||||
* frac = div(F) * 2^dlf_size
|
||||
*
|
||||
* let rem = clk % (16 * baud)
|
||||
* we have: div(F) * (16 * baud) = rem
|
||||
* so frac = 2^dlf_size * rem / (16 * baud) = (rem << dlf_size) / (16 * baud)
|
||||
*/
|
||||
static unsigned int dw8250_get_divisor(struct uart_port *p,
|
||||
unsigned int baud,
|
||||
unsigned int *frac)
|
||||
{
|
||||
unsigned int quot, rem, base_baud = baud * 16;
|
||||
struct dw8250_data *d = p->private_data;
|
||||
|
||||
quot = p->uartclk / base_baud;
|
||||
rem = p->uartclk % base_baud;
|
||||
*frac = DIV_ROUND_CLOSEST(rem << d->dlf_size, base_baud);
|
||||
|
||||
return quot;
|
||||
}
|
||||
|
||||
static void dw8250_set_divisor(struct uart_port *p, unsigned int baud,
|
||||
unsigned int quot, unsigned int quot_frac)
|
||||
{
|
||||
dw8250_writel_ext(p, DW_UART_DLF, quot_frac);
|
||||
serial8250_do_set_divisor(p, baud, quot, quot_frac);
|
||||
}
|
||||
|
||||
static void dw8250_quirks(struct uart_port *p, struct dw8250_data *data)
|
||||
{
|
||||
if (p->dev->of_node) {
|
||||
@ -437,65 +376,18 @@ static void dw8250_quirks(struct uart_port *p, struct dw8250_data *data)
|
||||
/* Platforms with iDMA 64-bit */
|
||||
if (platform_get_resource_byname(to_platform_device(p->dev),
|
||||
IORESOURCE_MEM, "lpss_priv")) {
|
||||
data->dma.rx_param = p->dev->parent;
|
||||
data->dma.tx_param = p->dev->parent;
|
||||
data->dma.fn = dw8250_idma_filter;
|
||||
data->data.dma.rx_param = p->dev->parent;
|
||||
data->data.dma.tx_param = p->dev->parent;
|
||||
data->data.dma.fn = dw8250_idma_filter;
|
||||
}
|
||||
}
|
||||
|
||||
static void dw8250_setup_port(struct uart_port *p)
|
||||
{
|
||||
struct uart_8250_port *up = up_to_u8250p(p);
|
||||
u32 reg;
|
||||
|
||||
/*
|
||||
* If the Component Version Register returns zero, we know that
|
||||
* ADDITIONAL_FEATURES are not enabled. No need to go any further.
|
||||
*/
|
||||
reg = dw8250_readl_ext(p, DW_UART_UCV);
|
||||
if (!reg)
|
||||
return;
|
||||
|
||||
dev_dbg(p->dev, "Designware UART version %c.%c%c\n",
|
||||
(reg >> 24) & 0xff, (reg >> 16) & 0xff, (reg >> 8) & 0xff);
|
||||
|
||||
dw8250_writel_ext(p, DW_UART_DLF, ~0U);
|
||||
reg = dw8250_readl_ext(p, DW_UART_DLF);
|
||||
dw8250_writel_ext(p, DW_UART_DLF, 0);
|
||||
|
||||
if (reg) {
|
||||
struct dw8250_data *d = p->private_data;
|
||||
|
||||
d->dlf_size = fls(reg);
|
||||
p->get_divisor = dw8250_get_divisor;
|
||||
p->set_divisor = dw8250_set_divisor;
|
||||
}
|
||||
|
||||
reg = dw8250_readl_ext(p, DW_UART_CPR);
|
||||
if (!reg)
|
||||
return;
|
||||
|
||||
/* Select the type based on fifo */
|
||||
if (reg & DW_UART_CPR_FIFO_MODE) {
|
||||
p->type = PORT_16550A;
|
||||
p->flags |= UPF_FIXED_TYPE;
|
||||
p->fifosize = DW_UART_CPR_FIFO_SIZE(reg);
|
||||
up->capabilities = UART_CAP_FIFO;
|
||||
}
|
||||
|
||||
if (reg & DW_UART_CPR_AFCE_MODE)
|
||||
up->capabilities |= UART_CAP_AFE;
|
||||
|
||||
if (reg & DW_UART_CPR_SIR_MODE)
|
||||
up->capabilities |= UART_CAP_IRDA;
|
||||
}
|
||||
|
||||
static int dw8250_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct uart_8250_port uart = {};
|
||||
struct uart_8250_port uart = {}, *up = &uart;
|
||||
struct resource *regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
int irq = platform_get_irq(pdev, 0);
|
||||
struct uart_port *p = &uart.port;
|
||||
struct uart_port *p = &up->port;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct dw8250_data *data;
|
||||
int err;
|
||||
@ -534,9 +426,9 @@ static int dw8250_probe(struct platform_device *pdev)
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
data->dma.fn = dw8250_fallback_dma_filter;
|
||||
data->data.dma.fn = dw8250_fallback_dma_filter;
|
||||
data->usr_reg = DW_UART_USR;
|
||||
p->private_data = data;
|
||||
p->private_data = &data->data;
|
||||
|
||||
data->uart_16550_compatible = device_property_read_bool(dev,
|
||||
"snps,uart-16550-compatible");
|
||||
@ -632,14 +524,14 @@ static int dw8250_probe(struct platform_device *pdev)
|
||||
|
||||
/* If we have a valid fifosize, try hooking up DMA */
|
||||
if (p->fifosize) {
|
||||
data->dma.rxconf.src_maxburst = p->fifosize / 4;
|
||||
data->dma.txconf.dst_maxburst = p->fifosize / 4;
|
||||
uart.dma = &data->dma;
|
||||
data->data.dma.rxconf.src_maxburst = p->fifosize / 4;
|
||||
data->data.dma.txconf.dst_maxburst = p->fifosize / 4;
|
||||
up->dma = &data->data.dma;
|
||||
}
|
||||
|
||||
data->line = serial8250_register_8250_port(&uart);
|
||||
if (data->line < 0) {
|
||||
err = data->line;
|
||||
data->data.line = serial8250_register_8250_port(up);
|
||||
if (data->data.line < 0) {
|
||||
err = data->data.line;
|
||||
goto err_reset;
|
||||
}
|
||||
|
||||
@ -667,10 +559,11 @@ err_clk:
|
||||
static int dw8250_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct dw8250_data *data = platform_get_drvdata(pdev);
|
||||
struct device *dev = &pdev->dev;
|
||||
|
||||
pm_runtime_get_sync(&pdev->dev);
|
||||
pm_runtime_get_sync(dev);
|
||||
|
||||
serial8250_unregister_port(data->line);
|
||||
serial8250_unregister_port(data->data.line);
|
||||
|
||||
reset_control_assert(data->rst);
|
||||
|
||||
@ -680,8 +573,8 @@ static int dw8250_remove(struct platform_device *pdev)
|
||||
if (!IS_ERR(data->clk))
|
||||
clk_disable_unprepare(data->clk);
|
||||
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
pm_runtime_put_noidle(&pdev->dev);
|
||||
pm_runtime_disable(dev);
|
||||
pm_runtime_put_noidle(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -691,7 +584,7 @@ static int dw8250_suspend(struct device *dev)
|
||||
{
|
||||
struct dw8250_data *data = dev_get_drvdata(dev);
|
||||
|
||||
serial8250_suspend_port(data->line);
|
||||
serial8250_suspend_port(data->data.line);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -700,7 +593,7 @@ static int dw8250_resume(struct device *dev)
|
||||
{
|
||||
struct dw8250_data *data = dev_get_drvdata(dev);
|
||||
|
||||
serial8250_resume_port(data->line);
|
||||
serial8250_resume_port(data->data.line);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
126
drivers/tty/serial/8250/8250_dwlib.c
Normal file
126
drivers/tty/serial/8250/8250_dwlib.c
Normal file
@ -0,0 +1,126 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/* Synopsys DesignWare 8250 library. */
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/serial_8250.h>
|
||||
#include <linux/serial_core.h>
|
||||
|
||||
#include "8250_dwlib.h"
|
||||
|
||||
/* Offsets for the DesignWare specific registers */
|
||||
#define DW_UART_DLF 0xc0 /* Divisor Latch Fraction Register */
|
||||
#define DW_UART_CPR 0xf4 /* Component Parameter Register */
|
||||
#define DW_UART_UCV 0xf8 /* UART Component Version */
|
||||
|
||||
/* Component Parameter Register bits */
|
||||
#define DW_UART_CPR_ABP_DATA_WIDTH (3 << 0)
|
||||
#define DW_UART_CPR_AFCE_MODE (1 << 4)
|
||||
#define DW_UART_CPR_THRE_MODE (1 << 5)
|
||||
#define DW_UART_CPR_SIR_MODE (1 << 6)
|
||||
#define DW_UART_CPR_SIR_LP_MODE (1 << 7)
|
||||
#define DW_UART_CPR_ADDITIONAL_FEATURES (1 << 8)
|
||||
#define DW_UART_CPR_FIFO_ACCESS (1 << 9)
|
||||
#define DW_UART_CPR_FIFO_STAT (1 << 10)
|
||||
#define DW_UART_CPR_SHADOW (1 << 11)
|
||||
#define DW_UART_CPR_ENCODED_PARMS (1 << 12)
|
||||
#define DW_UART_CPR_DMA_EXTRA (1 << 13)
|
||||
#define DW_UART_CPR_FIFO_MODE (0xff << 16)
|
||||
|
||||
/* Helper for FIFO size calculation */
|
||||
#define DW_UART_CPR_FIFO_SIZE(a) (((a >> 16) & 0xff) * 16)
|
||||
|
||||
static inline u32 dw8250_readl_ext(struct uart_port *p, int offset)
|
||||
{
|
||||
if (p->iotype == UPIO_MEM32BE)
|
||||
return ioread32be(p->membase + offset);
|
||||
return readl(p->membase + offset);
|
||||
}
|
||||
|
||||
static inline void dw8250_writel_ext(struct uart_port *p, int offset, u32 reg)
|
||||
{
|
||||
if (p->iotype == UPIO_MEM32BE)
|
||||
iowrite32be(reg, p->membase + offset);
|
||||
else
|
||||
writel(reg, p->membase + offset);
|
||||
}
|
||||
|
||||
/*
|
||||
* divisor = div(I) + div(F)
|
||||
* "I" means integer, "F" means fractional
|
||||
* quot = div(I) = clk / (16 * baud)
|
||||
* frac = div(F) * 2^dlf_size
|
||||
*
|
||||
* let rem = clk % (16 * baud)
|
||||
* we have: div(F) * (16 * baud) = rem
|
||||
* so frac = 2^dlf_size * rem / (16 * baud) = (rem << dlf_size) / (16 * baud)
|
||||
*/
|
||||
static unsigned int dw8250_get_divisor(struct uart_port *p, unsigned int baud,
|
||||
unsigned int *frac)
|
||||
{
|
||||
unsigned int quot, rem, base_baud = baud * 16;
|
||||
struct dw8250_port_data *d = p->private_data;
|
||||
|
||||
quot = p->uartclk / base_baud;
|
||||
rem = p->uartclk % base_baud;
|
||||
*frac = DIV_ROUND_CLOSEST(rem << d->dlf_size, base_baud);
|
||||
|
||||
return quot;
|
||||
}
|
||||
|
||||
static void dw8250_set_divisor(struct uart_port *p, unsigned int baud,
|
||||
unsigned int quot, unsigned int quot_frac)
|
||||
{
|
||||
dw8250_writel_ext(p, DW_UART_DLF, quot_frac);
|
||||
serial8250_do_set_divisor(p, baud, quot, quot_frac);
|
||||
}
|
||||
|
||||
void dw8250_setup_port(struct uart_port *p)
|
||||
{
|
||||
struct uart_8250_port *up = up_to_u8250p(p);
|
||||
u32 reg;
|
||||
|
||||
/*
|
||||
* If the Component Version Register returns zero, we know that
|
||||
* ADDITIONAL_FEATURES are not enabled. No need to go any further.
|
||||
*/
|
||||
reg = dw8250_readl_ext(p, DW_UART_UCV);
|
||||
if (!reg)
|
||||
return;
|
||||
|
||||
dev_dbg(p->dev, "Designware UART version %c.%c%c\n",
|
||||
(reg >> 24) & 0xff, (reg >> 16) & 0xff, (reg >> 8) & 0xff);
|
||||
|
||||
dw8250_writel_ext(p, DW_UART_DLF, ~0U);
|
||||
reg = dw8250_readl_ext(p, DW_UART_DLF);
|
||||
dw8250_writel_ext(p, DW_UART_DLF, 0);
|
||||
|
||||
if (reg) {
|
||||
struct dw8250_port_data *d = p->private_data;
|
||||
|
||||
d->dlf_size = fls(reg);
|
||||
p->get_divisor = dw8250_get_divisor;
|
||||
p->set_divisor = dw8250_set_divisor;
|
||||
}
|
||||
|
||||
reg = dw8250_readl_ext(p, DW_UART_CPR);
|
||||
if (!reg)
|
||||
return;
|
||||
|
||||
/* Select the type based on FIFO */
|
||||
if (reg & DW_UART_CPR_FIFO_MODE) {
|
||||
p->type = PORT_16550A;
|
||||
p->flags |= UPF_FIXED_TYPE;
|
||||
p->fifosize = DW_UART_CPR_FIFO_SIZE(reg);
|
||||
up->capabilities = UART_CAP_FIFO;
|
||||
}
|
||||
|
||||
if (reg & DW_UART_CPR_AFCE_MODE)
|
||||
up->capabilities |= UART_CAP_AFE;
|
||||
|
||||
if (reg & DW_UART_CPR_SIR_MODE)
|
||||
up->capabilities |= UART_CAP_IRDA;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dw8250_setup_port);
|
19
drivers/tty/serial/8250/8250_dwlib.h
Normal file
19
drivers/tty/serial/8250/8250_dwlib.h
Normal file
@ -0,0 +1,19 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/* Synopsys DesignWare 8250 library header file. */
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
#include "8250.h"
|
||||
|
||||
struct dw8250_port_data {
|
||||
/* Port properties */
|
||||
int line;
|
||||
|
||||
/* DMA operations */
|
||||
struct uart_8250_dma dma;
|
||||
|
||||
/* Hardware configuration */
|
||||
u8 dlf_size;
|
||||
};
|
||||
|
||||
void dw8250_setup_port(struct uart_port *p);
|
@ -19,6 +19,7 @@
|
||||
#include <linux/string.h>
|
||||
#include <linux/tty.h>
|
||||
#include <linux/8250_pci.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include <asm/byteorder.h>
|
||||
|
||||
@ -36,6 +37,8 @@
|
||||
|
||||
#define UART_EXAR_INT0 0x80
|
||||
#define UART_EXAR_8XMODE 0x88 /* 8X sampling rate select */
|
||||
#define UART_EXAR_SLEEP 0x8b /* Sleep mode */
|
||||
#define UART_EXAR_DVID 0x8d /* Device identification */
|
||||
|
||||
#define UART_EXAR_FCTR 0x08 /* Feature Control Register */
|
||||
#define UART_FCTR_EXAR_IRDA 0x10 /* IrDa data encode select */
|
||||
@ -127,18 +130,95 @@ struct exar8250 {
|
||||
int line[0];
|
||||
};
|
||||
|
||||
static void exar_pm(struct uart_port *port, unsigned int state, unsigned int old)
|
||||
{
|
||||
/*
|
||||
* Exar UARTs have a SLEEP register that enables or disables each UART
|
||||
* to enter sleep mode separately. On the XR17V35x the register
|
||||
* is accessible to each UART at the UART_EXAR_SLEEP offset, but
|
||||
* the UART channel may only write to the corresponding bit.
|
||||
*/
|
||||
serial_port_out(port, UART_EXAR_SLEEP, state ? 0xff : 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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_port *p, unsigned int baud,
|
||||
unsigned int *frac)
|
||||
{
|
||||
unsigned int quot_16;
|
||||
|
||||
quot_16 = DIV_ROUND_CLOSEST(p->uartclk, baud);
|
||||
*frac = quot_16 & 0x0f;
|
||||
|
||||
return quot_16 >> 4;
|
||||
}
|
||||
|
||||
static void xr17v35x_set_divisor(struct uart_port *p, unsigned int baud,
|
||||
unsigned int quot, unsigned int quot_frac)
|
||||
{
|
||||
serial8250_do_set_divisor(p, baud, quot, quot_frac);
|
||||
|
||||
/* Preserve bits not related to baudrate; DLD[7:4]. */
|
||||
quot_frac |= serial_port_in(p, 0x2) & 0xf0;
|
||||
serial_port_out(p, 0x2, quot_frac);
|
||||
}
|
||||
|
||||
static void exar_shutdown(struct uart_port *port)
|
||||
{
|
||||
unsigned char lsr;
|
||||
bool tx_complete = 0;
|
||||
struct uart_8250_port *up = up_to_u8250p(port);
|
||||
struct circ_buf *xmit = &port->state->xmit;
|
||||
int i = 0;
|
||||
|
||||
do {
|
||||
lsr = serial_in(up, UART_LSR);
|
||||
if (lsr & (UART_LSR_TEMT | UART_LSR_THRE))
|
||||
tx_complete = 1;
|
||||
else
|
||||
tx_complete = 0;
|
||||
usleep_range(1000, 1100);
|
||||
} while (!uart_circ_empty(xmit) && !tx_complete && i++ < 1000);
|
||||
|
||||
serial8250_do_shutdown(port);
|
||||
}
|
||||
|
||||
static int default_setup(struct exar8250 *priv, struct pci_dev *pcidev,
|
||||
int idx, unsigned int offset,
|
||||
struct uart_8250_port *port)
|
||||
{
|
||||
const struct exar8250_board *board = priv->board;
|
||||
unsigned int bar = 0;
|
||||
unsigned char status;
|
||||
|
||||
port->port.iotype = UPIO_MEM;
|
||||
port->port.mapbase = pci_resource_start(pcidev, bar) + offset;
|
||||
port->port.membase = priv->virt + offset;
|
||||
port->port.regshift = board->reg_shift;
|
||||
|
||||
/*
|
||||
* XR17V35x UARTs have an extra divisor register, DLD that gets enabled
|
||||
* with when DLAB is set which will cause the device to incorrectly match
|
||||
* and assign port type to PORT_16650. The EFR for this UART is found
|
||||
* at offset 0x09. Instead check the Deice ID (DVID) register
|
||||
* for a 2, 4 or 8 port UART.
|
||||
*/
|
||||
status = readb(port->port.membase + UART_EXAR_DVID);
|
||||
if (status == 0x82 || status == 0x84 || status == 0x88) {
|
||||
port->port.type = PORT_XR17V35X;
|
||||
|
||||
port->port.get_divisor = xr17v35x_get_divisor;
|
||||
port->port.set_divisor = xr17v35x_set_divisor;
|
||||
} else {
|
||||
port->port.type = PORT_XR17D15X;
|
||||
}
|
||||
|
||||
port->port.pm = exar_pm;
|
||||
port->port.shutdown = exar_shutdown;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -434,6 +514,16 @@ static void pci_xr17v35x_exit(struct pci_dev *pcidev)
|
||||
port->port.private_data = NULL;
|
||||
}
|
||||
|
||||
static inline void exar_misc_clear(struct exar8250 *priv)
|
||||
{
|
||||
/* Clear all PCI interrupts by reading INT0. No effect on IIR */
|
||||
readb(priv->virt + UART_EXAR_INT0);
|
||||
|
||||
/* Clear INT0 for Expansion Interface slave ports, too */
|
||||
if (priv->board->num_ports > 8)
|
||||
readb(priv->virt + 0x2000 + UART_EXAR_INT0);
|
||||
}
|
||||
|
||||
/*
|
||||
* These Exar UARTs have an extra interrupt indicator that could fire for a
|
||||
* few interrupts that are not presented/cleared through IIR. One of which is
|
||||
@ -445,14 +535,7 @@ static void pci_xr17v35x_exit(struct pci_dev *pcidev)
|
||||
*/
|
||||
static irqreturn_t exar_misc_handler(int irq, void *data)
|
||||
{
|
||||
struct exar8250 *priv = data;
|
||||
|
||||
/* Clear all PCI interrupts by reading INT0. No effect on IIR */
|
||||
readb(priv->virt + UART_EXAR_INT0);
|
||||
|
||||
/* Clear INT0 for Expansion Interface slave ports, too */
|
||||
if (priv->board->num_ports > 8)
|
||||
readb(priv->virt + 0x2000 + UART_EXAR_INT0);
|
||||
exar_misc_clear(data);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
@ -478,9 +561,7 @@ exar_pci_probe(struct pci_dev *pcidev, const struct pci_device_id *ent)
|
||||
|
||||
nr_ports = board->num_ports ? board->num_ports : pcidev->device & 0x0f;
|
||||
|
||||
priv = devm_kzalloc(&pcidev->dev, sizeof(*priv) +
|
||||
sizeof(unsigned int) * nr_ports,
|
||||
GFP_KERNEL);
|
||||
priv = devm_kzalloc(&pcidev->dev, struct_size(priv, line, nr_ports), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
@ -496,8 +577,7 @@ exar_pci_probe(struct pci_dev *pcidev, const struct pci_device_id *ent)
|
||||
return rc;
|
||||
|
||||
memset(&uart, 0, sizeof(uart));
|
||||
uart.port.flags = UPF_SKIP_TEST | UPF_BOOT_AUTOCONF | UPF_SHARE_IRQ
|
||||
| UPF_EXAR_EFR;
|
||||
uart.port.flags = UPF_SHARE_IRQ | UPF_EXAR_EFR | UPF_FIXED_TYPE | UPF_FIXED_PORT;
|
||||
uart.port.irq = pci_irq_vector(pcidev, 0);
|
||||
uart.port.dev = &pcidev->dev;
|
||||
|
||||
@ -506,6 +586,9 @@ exar_pci_probe(struct pci_dev *pcidev, const struct pci_device_id *ent)
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* Clear interrupts */
|
||||
exar_misc_clear(priv);
|
||||
|
||||
for (i = 0; i < nr_ports && i < maxnr; i++) {
|
||||
rc = board->setup(priv, pcidev, &uart, i);
|
||||
if (rc) {
|
||||
@ -561,10 +644,11 @@ static int __maybe_unused exar_suspend(struct device *dev)
|
||||
|
||||
static int __maybe_unused exar_resume(struct device *dev)
|
||||
{
|
||||
struct pci_dev *pcidev = to_pci_dev(dev);
|
||||
struct exar8250 *priv = pci_get_drvdata(pcidev);
|
||||
struct exar8250 *priv = dev_get_drvdata(dev);
|
||||
unsigned int i;
|
||||
|
||||
exar_misc_clear(priv);
|
||||
|
||||
for (i = 0; i < priv->nr; i++)
|
||||
if (priv->line[i] >= 0)
|
||||
serial8250_resume_port(priv->line[i]);
|
||||
|
@ -106,10 +106,8 @@ static int lpc18xx_serial_probe(struct platform_device *pdev)
|
||||
int irq, ret;
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
dev_err(&pdev->dev, "irq not found");
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
|
@ -14,7 +14,7 @@
|
||||
#include <linux/dmaengine.h>
|
||||
#include <linux/dma/dw.h>
|
||||
|
||||
#include "8250.h"
|
||||
#include "8250_dwlib.h"
|
||||
|
||||
#define PCI_DEVICE_ID_INTEL_QRK_UARTx 0x0936
|
||||
|
||||
@ -24,6 +24,13 @@
|
||||
#define PCI_DEVICE_ID_INTEL_BSW_UART1 0x228a
|
||||
#define PCI_DEVICE_ID_INTEL_BSW_UART2 0x228c
|
||||
|
||||
#define PCI_DEVICE_ID_INTEL_EHL_UART0 0x4b96
|
||||
#define PCI_DEVICE_ID_INTEL_EHL_UART1 0x4b97
|
||||
#define PCI_DEVICE_ID_INTEL_EHL_UART2 0x4b98
|
||||
#define PCI_DEVICE_ID_INTEL_EHL_UART3 0x4b99
|
||||
#define PCI_DEVICE_ID_INTEL_EHL_UART4 0x4b9a
|
||||
#define PCI_DEVICE_ID_INTEL_EHL_UART5 0x4b9b
|
||||
|
||||
#define PCI_DEVICE_ID_INTEL_BDW_UART1 0x9ce3
|
||||
#define PCI_DEVICE_ID_INTEL_BDW_UART2 0x9ce4
|
||||
|
||||
@ -48,21 +55,25 @@ struct lpss8250_board {
|
||||
};
|
||||
|
||||
struct lpss8250 {
|
||||
int line;
|
||||
struct dw8250_port_data data;
|
||||
struct lpss8250_board *board;
|
||||
|
||||
/* DMA parameters */
|
||||
struct uart_8250_dma dma;
|
||||
struct dw_dma_chip dma_chip;
|
||||
struct dw_dma_slave dma_param;
|
||||
u8 dma_maxburst;
|
||||
};
|
||||
|
||||
static inline struct lpss8250 *to_lpss8250(struct dw8250_port_data *data)
|
||||
{
|
||||
return container_of(data, struct lpss8250, data);
|
||||
}
|
||||
|
||||
static void byt_set_termios(struct uart_port *p, struct ktermios *termios,
|
||||
struct ktermios *old)
|
||||
{
|
||||
unsigned int baud = tty_termios_baud_rate(termios);
|
||||
struct lpss8250 *lpss = p->private_data;
|
||||
struct lpss8250 *lpss = to_lpss8250(p->private_data);
|
||||
unsigned long fref = lpss->board->freq, fuart = baud * 16;
|
||||
unsigned long w = BIT(15) - 1;
|
||||
unsigned long m, n;
|
||||
@ -109,7 +120,6 @@ static unsigned int byt_get_mctrl(struct uart_port *port)
|
||||
static int byt_serial_setup(struct lpss8250 *lpss, struct uart_port *port)
|
||||
{
|
||||
struct dw_dma_slave *param = &lpss->dma_param;
|
||||
struct uart_8250_port *up = up_to_u8250p(port);
|
||||
struct pci_dev *pdev = to_pci_dev(port->dev);
|
||||
unsigned int dma_devfn = PCI_DEVFN(PCI_SLOT(pdev->devfn), 0);
|
||||
struct pci_dev *dma_dev = pci_get_slot(pdev->bus, dma_devfn);
|
||||
@ -135,10 +145,6 @@ static int byt_serial_setup(struct lpss8250 *lpss, struct uart_port *port)
|
||||
param->m_master = 0;
|
||||
param->p_master = 1;
|
||||
|
||||
/* TODO: Detect FIFO size automaticaly for DesignWare 8250 */
|
||||
port->fifosize = 64;
|
||||
up->tx_loadsz = 64;
|
||||
|
||||
lpss->dma_maxburst = 16;
|
||||
|
||||
port->set_termios = byt_set_termios;
|
||||
@ -163,16 +169,19 @@ static const struct dw_dma_platform_data qrk_serial_dma_pdata = {
|
||||
|
||||
static void qrk_serial_setup_dma(struct lpss8250 *lpss, struct uart_port *port)
|
||||
{
|
||||
struct uart_8250_dma *dma = &lpss->dma;
|
||||
struct uart_8250_dma *dma = &lpss->data.dma;
|
||||
struct dw_dma_chip *chip = &lpss->dma_chip;
|
||||
struct dw_dma_slave *param = &lpss->dma_param;
|
||||
struct pci_dev *pdev = to_pci_dev(port->dev);
|
||||
int ret;
|
||||
|
||||
chip->pdata = &qrk_serial_dma_pdata;
|
||||
chip->dev = &pdev->dev;
|
||||
chip->id = pdev->devfn;
|
||||
chip->irq = pci_irq_vector(pdev, 0);
|
||||
chip->regs = pci_ioremap_bar(pdev, 1);
|
||||
chip->pdata = &qrk_serial_dma_pdata;
|
||||
if (!chip->regs)
|
||||
return;
|
||||
|
||||
/* Falling back to PIO mode if DMA probing fails */
|
||||
ret = dw_dma_probe(chip);
|
||||
@ -195,11 +204,15 @@ static void qrk_serial_setup_dma(struct lpss8250 *lpss, struct uart_port *port)
|
||||
|
||||
static void qrk_serial_exit_dma(struct lpss8250 *lpss)
|
||||
{
|
||||
struct dw_dma_chip *chip = &lpss->dma_chip;
|
||||
struct dw_dma_slave *param = &lpss->dma_param;
|
||||
|
||||
if (!param->dma_dev)
|
||||
return;
|
||||
dw_dma_remove(&lpss->dma_chip);
|
||||
|
||||
dw_dma_remove(chip);
|
||||
|
||||
pci_iounmap(to_pci_dev(chip->dev), chip->regs);
|
||||
}
|
||||
#else /* CONFIG_SERIAL_8250_DMA */
|
||||
static void qrk_serial_setup_dma(struct lpss8250 *lpss, struct uart_port *port) {}
|
||||
@ -241,7 +254,7 @@ static bool lpss8250_dma_filter(struct dma_chan *chan, void *param)
|
||||
|
||||
static int lpss8250_dma_setup(struct lpss8250 *lpss, struct uart_8250_port *port)
|
||||
{
|
||||
struct uart_8250_dma *dma = &lpss->dma;
|
||||
struct uart_8250_dma *dma = &lpss->data.dma;
|
||||
struct dw_dma_slave *rx_param, *tx_param;
|
||||
struct device *dev = port->port.dev;
|
||||
|
||||
@ -290,7 +303,7 @@ static int lpss8250_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||
|
||||
uart.port.dev = &pdev->dev;
|
||||
uart.port.irq = pdev->irq;
|
||||
uart.port.private_data = lpss;
|
||||
uart.port.private_data = &lpss->data;
|
||||
uart.port.type = PORT_16550A;
|
||||
uart.port.iotype = UPIO_MEM;
|
||||
uart.port.regshift = 2;
|
||||
@ -306,6 +319,8 @@ static int lpss8250_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dw8250_setup_port(&uart.port);
|
||||
|
||||
ret = lpss8250_dma_setup(lpss, &uart);
|
||||
if (ret)
|
||||
goto err_exit;
|
||||
@ -314,7 +329,7 @@ static int lpss8250_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||
if (ret < 0)
|
||||
goto err_exit;
|
||||
|
||||
lpss->line = ret;
|
||||
lpss->data.line = ret;
|
||||
|
||||
pci_set_drvdata(pdev, lpss);
|
||||
return 0;
|
||||
@ -329,7 +344,7 @@ static void lpss8250_remove(struct pci_dev *pdev)
|
||||
{
|
||||
struct lpss8250 *lpss = pci_get_drvdata(pdev);
|
||||
|
||||
serial8250_unregister_port(lpss->line);
|
||||
serial8250_unregister_port(lpss->data.line);
|
||||
|
||||
if (lpss->board->exit)
|
||||
lpss->board->exit(lpss);
|
||||
@ -341,6 +356,11 @@ static const struct lpss8250_board byt_board = {
|
||||
.setup = byt_serial_setup,
|
||||
};
|
||||
|
||||
static const struct lpss8250_board ehl_board = {
|
||||
.freq = 200000000,
|
||||
.base_baud = 12500000,
|
||||
};
|
||||
|
||||
static const struct lpss8250_board qrk_board = {
|
||||
.freq = 44236800,
|
||||
.base_baud = 2764800,
|
||||
@ -348,17 +368,21 @@ static const struct lpss8250_board qrk_board = {
|
||||
.exit = qrk_serial_exit,
|
||||
};
|
||||
|
||||
#define LPSS_DEVICE(id, board) { PCI_VDEVICE(INTEL, id), (kernel_ulong_t)&board }
|
||||
|
||||
static const struct pci_device_id pci_ids[] = {
|
||||
LPSS_DEVICE(PCI_DEVICE_ID_INTEL_QRK_UARTx, qrk_board),
|
||||
LPSS_DEVICE(PCI_DEVICE_ID_INTEL_BYT_UART1, byt_board),
|
||||
LPSS_DEVICE(PCI_DEVICE_ID_INTEL_BYT_UART2, byt_board),
|
||||
LPSS_DEVICE(PCI_DEVICE_ID_INTEL_BSW_UART1, byt_board),
|
||||
LPSS_DEVICE(PCI_DEVICE_ID_INTEL_BSW_UART2, byt_board),
|
||||
LPSS_DEVICE(PCI_DEVICE_ID_INTEL_BDW_UART1, byt_board),
|
||||
LPSS_DEVICE(PCI_DEVICE_ID_INTEL_BDW_UART2, byt_board),
|
||||
{ },
|
||||
{ PCI_DEVICE_DATA(INTEL, QRK_UARTx, &qrk_board) },
|
||||
{ PCI_DEVICE_DATA(INTEL, EHL_UART0, &ehl_board) },
|
||||
{ PCI_DEVICE_DATA(INTEL, EHL_UART1, &ehl_board) },
|
||||
{ PCI_DEVICE_DATA(INTEL, EHL_UART2, &ehl_board) },
|
||||
{ PCI_DEVICE_DATA(INTEL, EHL_UART3, &ehl_board) },
|
||||
{ PCI_DEVICE_DATA(INTEL, EHL_UART4, &ehl_board) },
|
||||
{ PCI_DEVICE_DATA(INTEL, EHL_UART5, &ehl_board) },
|
||||
{ PCI_DEVICE_DATA(INTEL, BYT_UART1, &byt_board) },
|
||||
{ PCI_DEVICE_DATA(INTEL, BYT_UART2, &byt_board) },
|
||||
{ PCI_DEVICE_DATA(INTEL, BSW_UART1, &byt_board) },
|
||||
{ PCI_DEVICE_DATA(INTEL, BSW_UART2, &byt_board) },
|
||||
{ PCI_DEVICE_DATA(INTEL, BDW_UART1, &byt_board) },
|
||||
{ PCI_DEVICE_DATA(INTEL, BDW_UART2, &byt_board) },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, pci_ids);
|
||||
|
||||
|
@ -1,155 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* 8250_moxa.c - MOXA Smartio/Industio MUE multiport serial driver.
|
||||
*
|
||||
* Author: Mathieu OTHACEHE <m.othacehe@gmail.com>
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
|
||||
#include "8250.h"
|
||||
|
||||
#define PCI_DEVICE_ID_MOXA_CP102E 0x1024
|
||||
#define PCI_DEVICE_ID_MOXA_CP102EL 0x1025
|
||||
#define PCI_DEVICE_ID_MOXA_CP104EL_A 0x1045
|
||||
#define PCI_DEVICE_ID_MOXA_CP114EL 0x1144
|
||||
#define PCI_DEVICE_ID_MOXA_CP116E_A_A 0x1160
|
||||
#define PCI_DEVICE_ID_MOXA_CP116E_A_B 0x1161
|
||||
#define PCI_DEVICE_ID_MOXA_CP118EL_A 0x1182
|
||||
#define PCI_DEVICE_ID_MOXA_CP118E_A_I 0x1183
|
||||
#define PCI_DEVICE_ID_MOXA_CP132EL 0x1322
|
||||
#define PCI_DEVICE_ID_MOXA_CP134EL_A 0x1342
|
||||
#define PCI_DEVICE_ID_MOXA_CP138E_A 0x1381
|
||||
#define PCI_DEVICE_ID_MOXA_CP168EL_A 0x1683
|
||||
|
||||
#define MOXA_BASE_BAUD 921600
|
||||
#define MOXA_UART_OFFSET 0x200
|
||||
#define MOXA_BASE_BAR 1
|
||||
|
||||
struct moxa8250_board {
|
||||
unsigned int num_ports;
|
||||
int line[0];
|
||||
};
|
||||
|
||||
enum {
|
||||
moxa8250_2p = 0,
|
||||
moxa8250_4p,
|
||||
moxa8250_8p
|
||||
};
|
||||
|
||||
static struct moxa8250_board moxa8250_boards[] = {
|
||||
[moxa8250_2p] = { .num_ports = 2},
|
||||
[moxa8250_4p] = { .num_ports = 4},
|
||||
[moxa8250_8p] = { .num_ports = 8},
|
||||
};
|
||||
|
||||
static int moxa8250_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||
{
|
||||
struct uart_8250_port uart;
|
||||
struct moxa8250_board *brd;
|
||||
void __iomem *ioaddr;
|
||||
resource_size_t baseaddr;
|
||||
unsigned int i, nr_ports;
|
||||
unsigned int offset;
|
||||
int ret;
|
||||
|
||||
brd = &moxa8250_boards[id->driver_data];
|
||||
nr_ports = brd->num_ports;
|
||||
|
||||
ret = pcim_enable_device(pdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
brd = devm_kzalloc(&pdev->dev, sizeof(struct moxa8250_board) +
|
||||
sizeof(unsigned int) * nr_ports, GFP_KERNEL);
|
||||
if (!brd)
|
||||
return -ENOMEM;
|
||||
brd->num_ports = nr_ports;
|
||||
|
||||
memset(&uart, 0, sizeof(struct uart_8250_port));
|
||||
|
||||
uart.port.dev = &pdev->dev;
|
||||
uart.port.irq = pdev->irq;
|
||||
uart.port.uartclk = MOXA_BASE_BAUD * 16;
|
||||
uart.port.flags = UPF_SKIP_TEST | UPF_BOOT_AUTOCONF | UPF_SHARE_IRQ;
|
||||
|
||||
baseaddr = pci_resource_start(pdev, MOXA_BASE_BAR);
|
||||
ioaddr = pcim_iomap(pdev, MOXA_BASE_BAR, 0);
|
||||
if (!ioaddr)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < nr_ports; i++) {
|
||||
|
||||
/*
|
||||
* MOXA Smartio MUE boards with 4 ports have
|
||||
* a different offset for port #3
|
||||
*/
|
||||
if (nr_ports == 4 && i == 3)
|
||||
offset = 7 * MOXA_UART_OFFSET;
|
||||
else
|
||||
offset = i * MOXA_UART_OFFSET;
|
||||
|
||||
uart.port.iotype = UPIO_MEM;
|
||||
uart.port.iobase = 0;
|
||||
uart.port.mapbase = baseaddr + offset;
|
||||
uart.port.membase = ioaddr + offset;
|
||||
uart.port.regshift = 0;
|
||||
|
||||
dev_dbg(&pdev->dev, "Setup PCI port: port %lx, irq %d, type %d\n",
|
||||
uart.port.iobase, uart.port.irq, uart.port.iotype);
|
||||
|
||||
brd->line[i] = serial8250_register_8250_port(&uart);
|
||||
if (brd->line[i] < 0) {
|
||||
dev_err(&pdev->dev,
|
||||
"Couldn't register serial port %lx, irq %d, type %d, error %d\n",
|
||||
uart.port.iobase, uart.port.irq,
|
||||
uart.port.iotype, brd->line[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
pci_set_drvdata(pdev, brd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void moxa8250_remove(struct pci_dev *pdev)
|
||||
{
|
||||
struct moxa8250_board *brd = pci_get_drvdata(pdev);
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < brd->num_ports; i++)
|
||||
serial8250_unregister_port(brd->line[i]);
|
||||
}
|
||||
|
||||
#define MOXA_DEVICE(id, data) { PCI_VDEVICE(MOXA, id), (kernel_ulong_t)data }
|
||||
|
||||
static const struct pci_device_id pci_ids[] = {
|
||||
MOXA_DEVICE(PCI_DEVICE_ID_MOXA_CP102E, moxa8250_2p),
|
||||
MOXA_DEVICE(PCI_DEVICE_ID_MOXA_CP102EL, moxa8250_2p),
|
||||
MOXA_DEVICE(PCI_DEVICE_ID_MOXA_CP104EL_A, moxa8250_4p),
|
||||
MOXA_DEVICE(PCI_DEVICE_ID_MOXA_CP114EL, moxa8250_4p),
|
||||
MOXA_DEVICE(PCI_DEVICE_ID_MOXA_CP116E_A_A, moxa8250_8p),
|
||||
MOXA_DEVICE(PCI_DEVICE_ID_MOXA_CP116E_A_B, moxa8250_8p),
|
||||
MOXA_DEVICE(PCI_DEVICE_ID_MOXA_CP118EL_A, moxa8250_8p),
|
||||
MOXA_DEVICE(PCI_DEVICE_ID_MOXA_CP118E_A_I, moxa8250_8p),
|
||||
MOXA_DEVICE(PCI_DEVICE_ID_MOXA_CP132EL, moxa8250_2p),
|
||||
MOXA_DEVICE(PCI_DEVICE_ID_MOXA_CP134EL_A, moxa8250_4p),
|
||||
MOXA_DEVICE(PCI_DEVICE_ID_MOXA_CP138E_A, moxa8250_8p),
|
||||
MOXA_DEVICE(PCI_DEVICE_ID_MOXA_CP168EL_A, moxa8250_8p),
|
||||
{0}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, pci_ids);
|
||||
|
||||
static struct pci_driver moxa8250_pci_driver = {
|
||||
.name = "8250_moxa",
|
||||
.id_table = pci_ids,
|
||||
.probe = moxa8250_probe,
|
||||
.remove = moxa8250_remove,
|
||||
};
|
||||
|
||||
module_pci_driver(moxa8250_pci_driver);
|
||||
|
||||
MODULE_AUTHOR("Mathieu OTHACEHE");
|
||||
MODULE_DESCRIPTION("MOXA SmartIO MUE driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -1234,7 +1234,16 @@ static int omap8250_probe(struct platform_device *pdev)
|
||||
|
||||
device_init_wakeup(&pdev->dev, true);
|
||||
pm_runtime_use_autosuspend(&pdev->dev);
|
||||
pm_runtime_set_autosuspend_delay(&pdev->dev, -1);
|
||||
|
||||
/*
|
||||
* Disable runtime PM until autosuspend delay unless specifically
|
||||
* enabled by the user via sysfs. This is the historic way to
|
||||
* prevent an unsafe default policy with lossy characters on wake-up.
|
||||
* For serdev devices this is not needed, the policy can be managed by
|
||||
* the serdev driver.
|
||||
*/
|
||||
if (!of_get_available_child_count(pdev->dev.of_node))
|
||||
pm_runtime_set_autosuspend_delay(&pdev->dev, -1);
|
||||
|
||||
pm_runtime_irq_safe(&pdev->dev);
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
|
@ -43,6 +43,11 @@ struct pci_serial_quirk {
|
||||
void (*exit)(struct pci_dev *dev);
|
||||
};
|
||||
|
||||
struct f815xxa_data {
|
||||
spinlock_t lock;
|
||||
int idx;
|
||||
};
|
||||
|
||||
#define PCI_NUM_BAR_RESOURCES 6
|
||||
|
||||
struct serial_private {
|
||||
@ -53,6 +58,16 @@ struct serial_private {
|
||||
int line[0];
|
||||
};
|
||||
|
||||
static const struct pci_device_id pci_use_msi[] = {
|
||||
{ PCI_DEVICE_SUB(PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9900,
|
||||
0xA000, 0x1000) },
|
||||
{ PCI_DEVICE_SUB(PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9912,
|
||||
0xA000, 0x1000) },
|
||||
{ PCI_DEVICE_SUB(PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9922,
|
||||
0xA000, 0x1000) },
|
||||
{ }
|
||||
};
|
||||
|
||||
static int pci_default_setup(struct serial_private*,
|
||||
const struct pciserial_board*, struct uart_8250_port *, int);
|
||||
|
||||
@ -730,8 +745,16 @@ static int pci_ni8430_init(struct pci_dev *dev)
|
||||
}
|
||||
|
||||
/* UART Port Control Register */
|
||||
#define NI8430_PORTCON 0x0f
|
||||
#define NI8430_PORTCON_TXVR_ENABLE (1 << 3)
|
||||
#define NI16550_PCR_OFFSET 0x0f
|
||||
#define NI16550_PCR_RS422 0x00
|
||||
#define NI16550_PCR_ECHO_RS485 0x01
|
||||
#define NI16550_PCR_DTR_RS485 0x02
|
||||
#define NI16550_PCR_AUTO_RS485 0x03
|
||||
#define NI16550_PCR_WIRE_MODE_MASK 0x03
|
||||
#define NI16550_PCR_TXVR_ENABLE_BIT BIT(3)
|
||||
#define NI16550_PCR_RS485_TERMINATION_BIT BIT(6)
|
||||
#define NI16550_ACR_DTR_AUTO_DTR (0x2 << 3)
|
||||
#define NI16550_ACR_DTR_MANUAL_DTR (0x0 << 3)
|
||||
|
||||
static int
|
||||
pci_ni8430_setup(struct serial_private *priv,
|
||||
@ -753,14 +776,117 @@ pci_ni8430_setup(struct serial_private *priv,
|
||||
return -ENOMEM;
|
||||
|
||||
/* enable the transceiver */
|
||||
writeb(readb(p + offset + NI8430_PORTCON) | NI8430_PORTCON_TXVR_ENABLE,
|
||||
p + offset + NI8430_PORTCON);
|
||||
writeb(readb(p + offset + NI16550_PCR_OFFSET) | NI16550_PCR_TXVR_ENABLE_BIT,
|
||||
p + offset + NI16550_PCR_OFFSET);
|
||||
|
||||
iounmap(p);
|
||||
|
||||
return setup_port(priv, port, bar, offset, board->reg_shift);
|
||||
}
|
||||
|
||||
static int pci_ni8431_config_rs485(struct uart_port *port,
|
||||
struct serial_rs485 *rs485)
|
||||
{
|
||||
u8 pcr, acr;
|
||||
struct uart_8250_port *up;
|
||||
|
||||
up = container_of(port, struct uart_8250_port, port);
|
||||
acr = up->acr;
|
||||
pcr = port->serial_in(port, NI16550_PCR_OFFSET);
|
||||
pcr &= ~NI16550_PCR_WIRE_MODE_MASK;
|
||||
|
||||
if (rs485->flags & SER_RS485_ENABLED) {
|
||||
/* RS-485 */
|
||||
if ((rs485->flags & SER_RS485_RX_DURING_TX) &&
|
||||
(rs485->flags & SER_RS485_RTS_ON_SEND)) {
|
||||
dev_dbg(port->dev, "Invalid 2-wire mode\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (rs485->flags & SER_RS485_RX_DURING_TX) {
|
||||
/* Echo */
|
||||
dev_vdbg(port->dev, "2-wire DTR with echo\n");
|
||||
pcr |= NI16550_PCR_ECHO_RS485;
|
||||
acr |= NI16550_ACR_DTR_MANUAL_DTR;
|
||||
} else {
|
||||
/* Auto or DTR */
|
||||
if (rs485->flags & SER_RS485_RTS_ON_SEND) {
|
||||
/* Auto */
|
||||
dev_vdbg(port->dev, "2-wire Auto\n");
|
||||
pcr |= NI16550_PCR_AUTO_RS485;
|
||||
acr |= NI16550_ACR_DTR_AUTO_DTR;
|
||||
} else {
|
||||
/* DTR-controlled */
|
||||
/* No Echo */
|
||||
dev_vdbg(port->dev, "2-wire DTR no echo\n");
|
||||
pcr |= NI16550_PCR_DTR_RS485;
|
||||
acr |= NI16550_ACR_DTR_MANUAL_DTR;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* RS-422 */
|
||||
dev_vdbg(port->dev, "4-wire\n");
|
||||
pcr |= NI16550_PCR_RS422;
|
||||
acr |= NI16550_ACR_DTR_MANUAL_DTR;
|
||||
}
|
||||
|
||||
dev_dbg(port->dev, "write pcr: 0x%08x\n", pcr);
|
||||
port->serial_out(port, NI16550_PCR_OFFSET, pcr);
|
||||
|
||||
up->acr = acr;
|
||||
port->serial_out(port, UART_SCR, UART_ACR);
|
||||
port->serial_out(port, UART_ICR, up->acr);
|
||||
|
||||
/* Update the cache. */
|
||||
port->rs485 = *rs485;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pci_ni8431_setup(struct serial_private *priv,
|
||||
const struct pciserial_board *board,
|
||||
struct uart_8250_port *uart, int idx)
|
||||
{
|
||||
u8 pcr, acr;
|
||||
struct pci_dev *dev = priv->dev;
|
||||
void __iomem *addr;
|
||||
unsigned int bar, offset = board->first_offset;
|
||||
|
||||
if (idx >= board->num_ports)
|
||||
return 1;
|
||||
|
||||
bar = FL_GET_BASE(board->flags);
|
||||
offset += idx * board->uart_offset;
|
||||
|
||||
addr = pci_ioremap_bar(dev, bar);
|
||||
if (!addr)
|
||||
return -ENOMEM;
|
||||
|
||||
/* enable the transceiver */
|
||||
writeb(readb(addr + NI16550_PCR_OFFSET) | NI16550_PCR_TXVR_ENABLE_BIT,
|
||||
addr + NI16550_PCR_OFFSET);
|
||||
|
||||
pcr = readb(addr + NI16550_PCR_OFFSET);
|
||||
pcr &= ~NI16550_PCR_WIRE_MODE_MASK;
|
||||
|
||||
/* set wire mode to default RS-422 */
|
||||
pcr |= NI16550_PCR_RS422;
|
||||
acr = NI16550_ACR_DTR_MANUAL_DTR;
|
||||
|
||||
/* write port configuration to register */
|
||||
writeb(pcr, addr + NI16550_PCR_OFFSET);
|
||||
|
||||
/* access and write to UART acr register */
|
||||
writeb(UART_ACR, addr + UART_SCR);
|
||||
writeb(acr, addr + UART_ICR);
|
||||
|
||||
uart->port.rs485_config = &pci_ni8431_config_rs485;
|
||||
|
||||
iounmap(addr);
|
||||
|
||||
return setup_port(priv, uart, bar, offset, board->reg_shift);
|
||||
}
|
||||
|
||||
static int pci_netmos_9900_setup(struct serial_private *priv,
|
||||
const struct pciserial_board *board,
|
||||
struct uart_8250_port *port, int idx)
|
||||
@ -1596,6 +1722,77 @@ static int pci_fintek_init(struct pci_dev *dev)
|
||||
return max_port;
|
||||
}
|
||||
|
||||
static void f815xxa_mem_serial_out(struct uart_port *p, int offset, int value)
|
||||
{
|
||||
struct f815xxa_data *data = p->private_data;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&data->lock, flags);
|
||||
writeb(value, p->membase + offset);
|
||||
readb(p->membase + UART_SCR); /* Dummy read for flush pcie tx queue */
|
||||
spin_unlock_irqrestore(&data->lock, flags);
|
||||
}
|
||||
|
||||
static int pci_fintek_f815xxa_setup(struct serial_private *priv,
|
||||
const struct pciserial_board *board,
|
||||
struct uart_8250_port *port, int idx)
|
||||
{
|
||||
struct pci_dev *pdev = priv->dev;
|
||||
struct f815xxa_data *data;
|
||||
|
||||
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
data->idx = idx;
|
||||
spin_lock_init(&data->lock);
|
||||
|
||||
port->port.private_data = data;
|
||||
port->port.iotype = UPIO_MEM;
|
||||
port->port.flags |= UPF_IOREMAP;
|
||||
port->port.mapbase = pci_resource_start(pdev, 0) + 8 * idx;
|
||||
port->port.serial_out = f815xxa_mem_serial_out;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pci_fintek_f815xxa_init(struct pci_dev *dev)
|
||||
{
|
||||
u32 max_port, i;
|
||||
int config_base;
|
||||
|
||||
if (!(pci_resource_flags(dev, 0) & IORESOURCE_MEM))
|
||||
return -ENODEV;
|
||||
|
||||
switch (dev->device) {
|
||||
case 0x1204: /* 4 ports */
|
||||
case 0x1208: /* 8 ports */
|
||||
max_port = dev->device & 0xff;
|
||||
break;
|
||||
case 0x1212: /* 12 ports */
|
||||
max_port = 12;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Set to mmio decode */
|
||||
pci_write_config_byte(dev, 0x209, 0x40);
|
||||
|
||||
for (i = 0; i < max_port; ++i) {
|
||||
/* UART0 configuration offset start from 0x2A0 */
|
||||
config_base = 0x2A0 + 0x08 * i;
|
||||
|
||||
/* Select 128-byte FIFO and 8x FIFO threshold */
|
||||
pci_write_config_byte(dev, config_base + 0x01, 0x33);
|
||||
|
||||
/* Enable UART I/O port */
|
||||
pci_write_config_byte(dev, config_base + 0, 0x01);
|
||||
}
|
||||
|
||||
return max_port;
|
||||
}
|
||||
|
||||
static int skip_tx_en_setup(struct serial_private *priv,
|
||||
const struct pciserial_board *board,
|
||||
struct uart_8250_port *port, int idx)
|
||||
@ -1692,6 +1889,46 @@ pci_wch_ch38x_setup(struct serial_private *priv,
|
||||
return pci_default_setup(priv, board, port, idx);
|
||||
}
|
||||
|
||||
static int
|
||||
pci_sunix_setup(struct serial_private *priv,
|
||||
const struct pciserial_board *board,
|
||||
struct uart_8250_port *port, int idx)
|
||||
{
|
||||
int bar;
|
||||
int offset;
|
||||
|
||||
port->port.flags |= UPF_FIXED_TYPE;
|
||||
port->port.type = PORT_SUNIX;
|
||||
|
||||
if (idx < 4) {
|
||||
bar = 0;
|
||||
offset = idx * board->uart_offset;
|
||||
} else {
|
||||
bar = 1;
|
||||
idx -= 4;
|
||||
idx = div_s64_rem(idx, 4, &offset);
|
||||
offset = idx * 64 + offset * board->uart_offset;
|
||||
}
|
||||
|
||||
return setup_port(priv, port, bar, offset, 0);
|
||||
}
|
||||
|
||||
static int
|
||||
pci_moxa_setup(struct serial_private *priv,
|
||||
const struct pciserial_board *board,
|
||||
struct uart_8250_port *port, int idx)
|
||||
{
|
||||
unsigned int bar = FL_GET_BASE(board->flags);
|
||||
int offset;
|
||||
|
||||
if (board->num_ports == 4 && idx == 3)
|
||||
offset = 7 * board->uart_offset;
|
||||
else
|
||||
offset = idx * board->uart_offset;
|
||||
|
||||
return setup_port(priv, port, bar, offset, 0);
|
||||
}
|
||||
|
||||
#define PCI_VENDOR_ID_SBSMODULARIO 0x124B
|
||||
#define PCI_SUBVENDOR_ID_SBSMODULARIO 0x124B
|
||||
#define PCI_DEVICE_ID_OCTPRO 0x0001
|
||||
@ -1786,7 +2023,28 @@ pci_wch_ch38x_setup(struct serial_private *priv,
|
||||
#define PCI_DEVICE_ID_ACCESIO_PCIE_COM_8SM 0x10E9
|
||||
#define PCI_DEVICE_ID_ACCESIO_PCIE_ICM_4SM 0x11D8
|
||||
|
||||
#define PCIE_DEVICE_ID_NI_PXIE8430_2328 0x74C2
|
||||
#define PCIE_DEVICE_ID_NI_PXIE8430_23216 0x74C1
|
||||
#define PCI_DEVICE_ID_NI_PXI8431_4852 0x7081
|
||||
#define PCI_DEVICE_ID_NI_PXI8431_4854 0x70DE
|
||||
#define PCI_DEVICE_ID_NI_PXI8431_4858 0x70E3
|
||||
#define PCI_DEVICE_ID_NI_PXI8433_4852 0x70E9
|
||||
#define PCI_DEVICE_ID_NI_PXI8433_4854 0x70ED
|
||||
#define PCIE_DEVICE_ID_NI_PXIE8431_4858 0x74C4
|
||||
#define PCIE_DEVICE_ID_NI_PXIE8431_48516 0x74C3
|
||||
|
||||
#define PCI_DEVICE_ID_MOXA_CP102E 0x1024
|
||||
#define PCI_DEVICE_ID_MOXA_CP102EL 0x1025
|
||||
#define PCI_DEVICE_ID_MOXA_CP104EL_A 0x1045
|
||||
#define PCI_DEVICE_ID_MOXA_CP114EL 0x1144
|
||||
#define PCI_DEVICE_ID_MOXA_CP116E_A_A 0x1160
|
||||
#define PCI_DEVICE_ID_MOXA_CP116E_A_B 0x1161
|
||||
#define PCI_DEVICE_ID_MOXA_CP118EL_A 0x1182
|
||||
#define PCI_DEVICE_ID_MOXA_CP118E_A_I 0x1183
|
||||
#define PCI_DEVICE_ID_MOXA_CP132EL 0x1322
|
||||
#define PCI_DEVICE_ID_MOXA_CP134EL_A 0x1342
|
||||
#define PCI_DEVICE_ID_MOXA_CP138E_A 0x1381
|
||||
#define PCI_DEVICE_ID_MOXA_CP168EL_A 0x1683
|
||||
|
||||
/* Unknown vendors/cards - this should not be in linux/pci_ids.h */
|
||||
#define PCI_SUBDEVICE_ID_UNKNOWN_0x1584 0x1584
|
||||
@ -2011,6 +2269,87 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = {
|
||||
.setup = pci_ni8430_setup,
|
||||
.exit = pci_ni8430_exit,
|
||||
},
|
||||
{
|
||||
.vendor = PCI_VENDOR_ID_NI,
|
||||
.device = PCIE_DEVICE_ID_NI_PXIE8430_2328,
|
||||
.subvendor = PCI_ANY_ID,
|
||||
.subdevice = PCI_ANY_ID,
|
||||
.init = pci_ni8430_init,
|
||||
.setup = pci_ni8430_setup,
|
||||
.exit = pci_ni8430_exit,
|
||||
},
|
||||
{
|
||||
.vendor = PCI_VENDOR_ID_NI,
|
||||
.device = PCIE_DEVICE_ID_NI_PXIE8430_23216,
|
||||
.subvendor = PCI_ANY_ID,
|
||||
.subdevice = PCI_ANY_ID,
|
||||
.init = pci_ni8430_init,
|
||||
.setup = pci_ni8430_setup,
|
||||
.exit = pci_ni8430_exit,
|
||||
},
|
||||
{
|
||||
.vendor = PCI_VENDOR_ID_NI,
|
||||
.device = PCI_DEVICE_ID_NI_PXI8431_4852,
|
||||
.subvendor = PCI_ANY_ID,
|
||||
.subdevice = PCI_ANY_ID,
|
||||
.init = pci_ni8430_init,
|
||||
.setup = pci_ni8431_setup,
|
||||
.exit = pci_ni8430_exit,
|
||||
},
|
||||
{
|
||||
.vendor = PCI_VENDOR_ID_NI,
|
||||
.device = PCI_DEVICE_ID_NI_PXI8431_4854,
|
||||
.subvendor = PCI_ANY_ID,
|
||||
.subdevice = PCI_ANY_ID,
|
||||
.init = pci_ni8430_init,
|
||||
.setup = pci_ni8431_setup,
|
||||
.exit = pci_ni8430_exit,
|
||||
},
|
||||
{
|
||||
.vendor = PCI_VENDOR_ID_NI,
|
||||
.device = PCI_DEVICE_ID_NI_PXI8431_4858,
|
||||
.subvendor = PCI_ANY_ID,
|
||||
.subdevice = PCI_ANY_ID,
|
||||
.init = pci_ni8430_init,
|
||||
.setup = pci_ni8431_setup,
|
||||
.exit = pci_ni8430_exit,
|
||||
},
|
||||
{
|
||||
.vendor = PCI_VENDOR_ID_NI,
|
||||
.device = PCI_DEVICE_ID_NI_PXI8433_4852,
|
||||
.subvendor = PCI_ANY_ID,
|
||||
.subdevice = PCI_ANY_ID,
|
||||
.init = pci_ni8430_init,
|
||||
.setup = pci_ni8431_setup,
|
||||
.exit = pci_ni8430_exit,
|
||||
},
|
||||
{
|
||||
.vendor = PCI_VENDOR_ID_NI,
|
||||
.device = PCI_DEVICE_ID_NI_PXI8433_4854,
|
||||
.subvendor = PCI_ANY_ID,
|
||||
.subdevice = PCI_ANY_ID,
|
||||
.init = pci_ni8430_init,
|
||||
.setup = pci_ni8431_setup,
|
||||
.exit = pci_ni8430_exit,
|
||||
},
|
||||
{
|
||||
.vendor = PCI_VENDOR_ID_NI,
|
||||
.device = PCIE_DEVICE_ID_NI_PXIE8431_4858,
|
||||
.subvendor = PCI_ANY_ID,
|
||||
.subdevice = PCI_ANY_ID,
|
||||
.init = pci_ni8430_init,
|
||||
.setup = pci_ni8431_setup,
|
||||
.exit = pci_ni8430_exit,
|
||||
},
|
||||
{
|
||||
.vendor = PCI_VENDOR_ID_NI,
|
||||
.device = PCIE_DEVICE_ID_NI_PXIE8431_48516,
|
||||
.subvendor = PCI_ANY_ID,
|
||||
.subdevice = PCI_ANY_ID,
|
||||
.init = pci_ni8430_init,
|
||||
.setup = pci_ni8431_setup,
|
||||
.exit = pci_ni8430_exit,
|
||||
},
|
||||
/* Quatech */
|
||||
{
|
||||
.vendor = PCI_VENDOR_ID_QUATECH,
|
||||
@ -2289,21 +2628,14 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = {
|
||||
.setup = pci_timedia_setup,
|
||||
},
|
||||
/*
|
||||
* SUNIX (Timedia) cards
|
||||
* Do not "probe" for these cards as there is at least one combination
|
||||
* card that should be handled by parport_pc that doesn't match the
|
||||
* rule in pci_timedia_probe.
|
||||
* It is part number is MIO5079A but its subdevice ID is 0x0102.
|
||||
* There are some boards with part number SER5037AL that report
|
||||
* subdevice ID 0x0002.
|
||||
* Sunix PCI serial boards
|
||||
*/
|
||||
{
|
||||
.vendor = PCI_VENDOR_ID_SUNIX,
|
||||
.device = PCI_DEVICE_ID_SUNIX_1999,
|
||||
.subvendor = PCI_VENDOR_ID_SUNIX,
|
||||
.subdevice = PCI_ANY_ID,
|
||||
.init = pci_timedia_init,
|
||||
.setup = pci_timedia_setup,
|
||||
.setup = pci_sunix_setup,
|
||||
},
|
||||
/*
|
||||
* Xircom cards
|
||||
@ -2563,6 +2895,40 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = {
|
||||
.setup = pci_fintek_setup,
|
||||
.init = pci_fintek_init,
|
||||
},
|
||||
/*
|
||||
* MOXA
|
||||
*/
|
||||
{
|
||||
.vendor = PCI_VENDOR_ID_MOXA,
|
||||
.device = PCI_ANY_ID,
|
||||
.subvendor = PCI_ANY_ID,
|
||||
.subdevice = PCI_ANY_ID,
|
||||
.setup = pci_moxa_setup,
|
||||
},
|
||||
{
|
||||
.vendor = 0x1c29,
|
||||
.device = 0x1204,
|
||||
.subvendor = PCI_ANY_ID,
|
||||
.subdevice = PCI_ANY_ID,
|
||||
.setup = pci_fintek_f815xxa_setup,
|
||||
.init = pci_fintek_f815xxa_init,
|
||||
},
|
||||
{
|
||||
.vendor = 0x1c29,
|
||||
.device = 0x1208,
|
||||
.subvendor = PCI_ANY_ID,
|
||||
.subdevice = PCI_ANY_ID,
|
||||
.setup = pci_fintek_f815xxa_setup,
|
||||
.init = pci_fintek_f815xxa_init,
|
||||
},
|
||||
{
|
||||
.vendor = 0x1c29,
|
||||
.device = 0x1212,
|
||||
.subvendor = PCI_ANY_ID,
|
||||
.subdevice = PCI_ANY_ID,
|
||||
.setup = pci_fintek_f815xxa_setup,
|
||||
.init = pci_fintek_f815xxa_init,
|
||||
},
|
||||
|
||||
/*
|
||||
* Default "match everything" terminator entry
|
||||
@ -2740,6 +3106,13 @@ enum pci_board_num_t {
|
||||
pbn_ni8430_4,
|
||||
pbn_ni8430_8,
|
||||
pbn_ni8430_16,
|
||||
pbn_ni8430_pxie_8,
|
||||
pbn_ni8430_pxie_16,
|
||||
pbn_ni8431_2,
|
||||
pbn_ni8431_4,
|
||||
pbn_ni8431_8,
|
||||
pbn_ni8431_pxie_8,
|
||||
pbn_ni8431_pxie_16,
|
||||
pbn_ADDIDATA_PCIe_1_3906250,
|
||||
pbn_ADDIDATA_PCIe_2_3906250,
|
||||
pbn_ADDIDATA_PCIe_4_3906250,
|
||||
@ -2751,12 +3124,23 @@ enum pci_board_num_t {
|
||||
pbn_fintek_4,
|
||||
pbn_fintek_8,
|
||||
pbn_fintek_12,
|
||||
pbn_fintek_F81504A,
|
||||
pbn_fintek_F81508A,
|
||||
pbn_fintek_F81512A,
|
||||
pbn_wch382_2,
|
||||
pbn_wch384_4,
|
||||
pbn_pericom_PI7C9X7951,
|
||||
pbn_pericom_PI7C9X7952,
|
||||
pbn_pericom_PI7C9X7954,
|
||||
pbn_pericom_PI7C9X7958,
|
||||
pbn_sunix_pci_1s,
|
||||
pbn_sunix_pci_2s,
|
||||
pbn_sunix_pci_4s,
|
||||
pbn_sunix_pci_8s,
|
||||
pbn_sunix_pci_16s,
|
||||
pbn_moxa8250_2p,
|
||||
pbn_moxa8250_4p,
|
||||
pbn_moxa8250_8p,
|
||||
};
|
||||
|
||||
/*
|
||||
@ -3381,6 +3765,55 @@ static struct pciserial_board pci_boards[] = {
|
||||
.uart_offset = 0x10,
|
||||
.first_offset = 0x800,
|
||||
},
|
||||
[pbn_ni8430_pxie_16] = {
|
||||
.flags = FL_BASE0,
|
||||
.num_ports = 16,
|
||||
.base_baud = 3125000,
|
||||
.uart_offset = 0x10,
|
||||
.first_offset = 0x800,
|
||||
},
|
||||
[pbn_ni8430_pxie_8] = {
|
||||
.flags = FL_BASE0,
|
||||
.num_ports = 8,
|
||||
.base_baud = 3125000,
|
||||
.uart_offset = 0x10,
|
||||
.first_offset = 0x800,
|
||||
},
|
||||
[pbn_ni8431_8] = {
|
||||
.flags = FL_BASE0,
|
||||
.num_ports = 8,
|
||||
.base_baud = 3686400,
|
||||
.uart_offset = 0x10,
|
||||
.first_offset = 0x800,
|
||||
},
|
||||
[pbn_ni8431_4] = {
|
||||
.flags = FL_BASE0,
|
||||
.num_ports = 4,
|
||||
.base_baud = 3686400,
|
||||
.uart_offset = 0x10,
|
||||
.first_offset = 0x800,
|
||||
},
|
||||
[pbn_ni8431_2] = {
|
||||
.flags = FL_BASE0,
|
||||
.num_ports = 2,
|
||||
.base_baud = 3686400,
|
||||
.uart_offset = 0x10,
|
||||
.first_offset = 0x800,
|
||||
},
|
||||
[pbn_ni8431_pxie_16] = {
|
||||
.flags = FL_BASE0,
|
||||
.num_ports = 16,
|
||||
.base_baud = 3125000,
|
||||
.uart_offset = 0x10,
|
||||
.first_offset = 0x800,
|
||||
},
|
||||
[pbn_ni8431_pxie_8] = {
|
||||
.flags = FL_BASE0,
|
||||
.num_ports = 8,
|
||||
.base_baud = 3125000,
|
||||
.uart_offset = 0x10,
|
||||
.first_offset = 0x800,
|
||||
},
|
||||
/*
|
||||
* ADDI-DATA GmbH PCI-Express communication cards <info@addi-data.com>
|
||||
*/
|
||||
@ -3453,6 +3886,21 @@ static struct pciserial_board pci_boards[] = {
|
||||
.base_baud = 115200,
|
||||
.first_offset = 0x40,
|
||||
},
|
||||
[pbn_fintek_F81504A] = {
|
||||
.num_ports = 4,
|
||||
.uart_offset = 8,
|
||||
.base_baud = 115200,
|
||||
},
|
||||
[pbn_fintek_F81508A] = {
|
||||
.num_ports = 8,
|
||||
.uart_offset = 8,
|
||||
.base_baud = 115200,
|
||||
},
|
||||
[pbn_fintek_F81512A] = {
|
||||
.num_ports = 12,
|
||||
.uart_offset = 8,
|
||||
.base_baud = 115200,
|
||||
},
|
||||
[pbn_wch382_2] = {
|
||||
.flags = FL_BASE0,
|
||||
.num_ports = 2,
|
||||
@ -3494,6 +3942,49 @@ static struct pciserial_board pci_boards[] = {
|
||||
.base_baud = 921600,
|
||||
.uart_offset = 0x8,
|
||||
},
|
||||
[pbn_sunix_pci_1s] = {
|
||||
.num_ports = 1,
|
||||
.base_baud = 921600,
|
||||
.uart_offset = 0x8,
|
||||
},
|
||||
[pbn_sunix_pci_2s] = {
|
||||
.num_ports = 2,
|
||||
.base_baud = 921600,
|
||||
.uart_offset = 0x8,
|
||||
},
|
||||
[pbn_sunix_pci_4s] = {
|
||||
.num_ports = 4,
|
||||
.base_baud = 921600,
|
||||
.uart_offset = 0x8,
|
||||
},
|
||||
[pbn_sunix_pci_8s] = {
|
||||
.num_ports = 8,
|
||||
.base_baud = 921600,
|
||||
.uart_offset = 0x8,
|
||||
},
|
||||
[pbn_sunix_pci_16s] = {
|
||||
.num_ports = 16,
|
||||
.base_baud = 921600,
|
||||
.uart_offset = 0x8,
|
||||
},
|
||||
[pbn_moxa8250_2p] = {
|
||||
.flags = FL_BASE1,
|
||||
.num_ports = 2,
|
||||
.base_baud = 921600,
|
||||
.uart_offset = 0x200,
|
||||
},
|
||||
[pbn_moxa8250_4p] = {
|
||||
.flags = FL_BASE1,
|
||||
.num_ports = 4,
|
||||
.base_baud = 921600,
|
||||
.uart_offset = 0x200,
|
||||
},
|
||||
[pbn_moxa8250_8p] = {
|
||||
.flags = FL_BASE1,
|
||||
.num_ports = 8,
|
||||
.base_baud = 921600,
|
||||
.uart_offset = 0x200,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct pci_device_id blacklist[] = {
|
||||
@ -3507,20 +3998,6 @@ static const struct pci_device_id blacklist[] = {
|
||||
{ PCI_DEVICE(0x4348, 0x5053), }, /* WCH CH353 1S1P */
|
||||
{ PCI_DEVICE(0x1c00, 0x3250), }, /* WCH CH382 2S1P */
|
||||
|
||||
/* Moxa Smartio MUE boards handled by 8250_moxa */
|
||||
{ PCI_VDEVICE(MOXA, 0x1024), },
|
||||
{ PCI_VDEVICE(MOXA, 0x1025), },
|
||||
{ PCI_VDEVICE(MOXA, 0x1045), },
|
||||
{ PCI_VDEVICE(MOXA, 0x1144), },
|
||||
{ PCI_VDEVICE(MOXA, 0x1160), },
|
||||
{ PCI_VDEVICE(MOXA, 0x1161), },
|
||||
{ PCI_VDEVICE(MOXA, 0x1182), },
|
||||
{ PCI_VDEVICE(MOXA, 0x1183), },
|
||||
{ PCI_VDEVICE(MOXA, 0x1322), },
|
||||
{ PCI_VDEVICE(MOXA, 0x1342), },
|
||||
{ PCI_VDEVICE(MOXA, 0x1381), },
|
||||
{ PCI_VDEVICE(MOXA, 0x1683), },
|
||||
|
||||
/* Intel platforms with MID UART */
|
||||
{ PCI_VDEVICE(INTEL, 0x081b), },
|
||||
{ PCI_VDEVICE(INTEL, 0x081c), },
|
||||
@ -3688,7 +4165,22 @@ pciserial_init_ports(struct pci_dev *dev, const struct pciserial_board *board)
|
||||
memset(&uart, 0, sizeof(uart));
|
||||
uart.port.flags = UPF_SKIP_TEST | UPF_BOOT_AUTOCONF | UPF_SHARE_IRQ;
|
||||
uart.port.uartclk = board->base_baud * 16;
|
||||
uart.port.irq = get_pci_irq(dev, board);
|
||||
|
||||
if (pci_match_id(pci_use_msi, dev)) {
|
||||
dev_dbg(&dev->dev, "Using MSI(-X) interrupts\n");
|
||||
pci_set_master(dev);
|
||||
rc = pci_alloc_irq_vectors(dev, 1, 1, PCI_IRQ_ALL_TYPES);
|
||||
} else {
|
||||
dev_dbg(&dev->dev, "Using legacy interrupts\n");
|
||||
rc = pci_alloc_irq_vectors(dev, 1, 1, PCI_IRQ_LEGACY);
|
||||
}
|
||||
if (rc < 0) {
|
||||
kfree(priv);
|
||||
priv = ERR_PTR(rc);
|
||||
goto err_deinit;
|
||||
}
|
||||
|
||||
uart.port.irq = pci_irq_vector(dev, 0);
|
||||
uart.port.dev = &dev->dev;
|
||||
|
||||
for (i = 0; i < nr_ports; i++) {
|
||||
@ -3859,8 +4351,7 @@ static void pciserial_remove_one(struct pci_dev *dev)
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int pciserial_suspend_one(struct device *dev)
|
||||
{
|
||||
struct pci_dev *pdev = to_pci_dev(dev);
|
||||
struct serial_private *priv = pci_get_drvdata(pdev);
|
||||
struct serial_private *priv = dev_get_drvdata(dev);
|
||||
|
||||
if (priv)
|
||||
pciserial_suspend_ports(priv);
|
||||
@ -4532,17 +5023,29 @@ static const struct pci_device_id serial_pci_tbl[] = {
|
||||
pbn_b0_bt_1_921600 },
|
||||
|
||||
/*
|
||||
* SUNIX (TIMEDIA)
|
||||
* Sunix PCI serial boards
|
||||
*/
|
||||
{ PCI_VENDOR_ID_SUNIX, PCI_DEVICE_ID_SUNIX_1999,
|
||||
PCI_VENDOR_ID_SUNIX, PCI_ANY_ID,
|
||||
PCI_CLASS_COMMUNICATION_SERIAL << 8, 0xffff00,
|
||||
pbn_b0_bt_1_921600 },
|
||||
|
||||
PCI_VENDOR_ID_SUNIX, 0x0001, 0, 0,
|
||||
pbn_sunix_pci_1s },
|
||||
{ PCI_VENDOR_ID_SUNIX, PCI_DEVICE_ID_SUNIX_1999,
|
||||
PCI_VENDOR_ID_SUNIX, PCI_ANY_ID,
|
||||
PCI_CLASS_COMMUNICATION_MULTISERIAL << 8, 0xffff00,
|
||||
pbn_b0_bt_1_921600 },
|
||||
PCI_VENDOR_ID_SUNIX, 0x0002, 0, 0,
|
||||
pbn_sunix_pci_2s },
|
||||
{ PCI_VENDOR_ID_SUNIX, PCI_DEVICE_ID_SUNIX_1999,
|
||||
PCI_VENDOR_ID_SUNIX, 0x0004, 0, 0,
|
||||
pbn_sunix_pci_4s },
|
||||
{ PCI_VENDOR_ID_SUNIX, PCI_DEVICE_ID_SUNIX_1999,
|
||||
PCI_VENDOR_ID_SUNIX, 0x0084, 0, 0,
|
||||
pbn_sunix_pci_4s },
|
||||
{ PCI_VENDOR_ID_SUNIX, PCI_DEVICE_ID_SUNIX_1999,
|
||||
PCI_VENDOR_ID_SUNIX, 0x0008, 0, 0,
|
||||
pbn_sunix_pci_8s },
|
||||
{ PCI_VENDOR_ID_SUNIX, PCI_DEVICE_ID_SUNIX_1999,
|
||||
PCI_VENDOR_ID_SUNIX, 0x0088, 0, 0,
|
||||
pbn_sunix_pci_8s },
|
||||
{ PCI_VENDOR_ID_SUNIX, PCI_DEVICE_ID_SUNIX_1999,
|
||||
PCI_VENDOR_ID_SUNIX, 0x0010, 0, 0,
|
||||
pbn_sunix_pci_16s },
|
||||
|
||||
/*
|
||||
* AFAVLAB serial card, from Harald Welte <laforge@gnumonks.org>
|
||||
@ -5064,6 +5567,73 @@ static const struct pci_device_id serial_pci_tbl[] = {
|
||||
{ PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI8432_2324,
|
||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
|
||||
pbn_ni8430_4 },
|
||||
{ PCI_VENDOR_ID_NI, PCIE_DEVICE_ID_NI_PXIE8430_2328,
|
||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
|
||||
pbn_ni8430_pxie_8 },
|
||||
{ PCI_VENDOR_ID_NI, PCIE_DEVICE_ID_NI_PXIE8430_23216,
|
||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
|
||||
pbn_ni8430_pxie_16 },
|
||||
{ PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8431_4852,
|
||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
|
||||
pbn_ni8431_2 },
|
||||
{ PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8431_4854,
|
||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
|
||||
pbn_ni8431_4 },
|
||||
{ PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8431_4858,
|
||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
|
||||
pbn_ni8431_8 },
|
||||
{ PCI_VENDOR_ID_NI, PCIE_DEVICE_ID_NI_PXIE8431_4858,
|
||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
|
||||
pbn_ni8431_pxie_8 },
|
||||
{ PCI_VENDOR_ID_NI, PCIE_DEVICE_ID_NI_PXIE8431_48516,
|
||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
|
||||
pbn_ni8431_pxie_16 },
|
||||
{ PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8433_4852,
|
||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
|
||||
pbn_ni8431_2 },
|
||||
{ PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8433_4854,
|
||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
|
||||
pbn_ni8431_4 },
|
||||
|
||||
/*
|
||||
* MOXA
|
||||
*/
|
||||
{ PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_CP102E,
|
||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
|
||||
pbn_moxa8250_2p },
|
||||
{ PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_CP102EL,
|
||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
|
||||
pbn_moxa8250_2p },
|
||||
{ PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_CP104EL_A,
|
||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
|
||||
pbn_moxa8250_4p },
|
||||
{ PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_CP114EL,
|
||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
|
||||
pbn_moxa8250_4p },
|
||||
{ PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_CP116E_A_A,
|
||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
|
||||
pbn_moxa8250_8p },
|
||||
{ PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_CP116E_A_B,
|
||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
|
||||
pbn_moxa8250_8p },
|
||||
{ PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_CP118EL_A,
|
||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
|
||||
pbn_moxa8250_8p },
|
||||
{ PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_CP118E_A_I,
|
||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
|
||||
pbn_moxa8250_8p },
|
||||
{ PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_CP132EL,
|
||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
|
||||
pbn_moxa8250_2p },
|
||||
{ PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_CP134EL_A,
|
||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
|
||||
pbn_moxa8250_4p },
|
||||
{ PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_CP138E_A,
|
||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
|
||||
pbn_moxa8250_8p },
|
||||
{ PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_CP168EL_A,
|
||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
|
||||
pbn_moxa8250_8p },
|
||||
|
||||
/*
|
||||
* ADDI-DATA GmbH communication cards <info@addi-data.com>
|
||||
@ -5292,6 +5862,9 @@ static const struct pci_device_id serial_pci_tbl[] = {
|
||||
{ PCI_DEVICE(0x1c29, 0x1104), .driver_data = pbn_fintek_4 },
|
||||
{ PCI_DEVICE(0x1c29, 0x1108), .driver_data = pbn_fintek_8 },
|
||||
{ PCI_DEVICE(0x1c29, 0x1112), .driver_data = pbn_fintek_12 },
|
||||
{ PCI_DEVICE(0x1c29, 0x1204), .driver_data = pbn_fintek_F81504A },
|
||||
{ PCI_DEVICE(0x1c29, 0x1208), .driver_data = pbn_fintek_F81508A },
|
||||
{ PCI_DEVICE(0x1c29, 0x1212), .driver_data = pbn_fintek_F81512A },
|
||||
|
||||
/* MKS Tenta SCOM-080x serial cards */
|
||||
{ PCI_DEVICE(0x1601, 0x0800), .driver_data = pbn_b0_4_1250000 },
|
||||
|
@ -498,10 +498,9 @@ static void serial_pnp_remove(struct pnp_dev *dev)
|
||||
serial8250_unregister_port(line - 1);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int serial_pnp_suspend(struct pnp_dev *dev, pm_message_t state)
|
||||
static int __maybe_unused serial_pnp_suspend(struct device *dev)
|
||||
{
|
||||
long line = (long)pnp_get_drvdata(dev);
|
||||
long line = (long)dev_get_drvdata(dev);
|
||||
|
||||
if (!line)
|
||||
return -ENODEV;
|
||||
@ -509,26 +508,25 @@ static int serial_pnp_suspend(struct pnp_dev *dev, pm_message_t state)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int serial_pnp_resume(struct pnp_dev *dev)
|
||||
static int __maybe_unused serial_pnp_resume(struct device *dev)
|
||||
{
|
||||
long line = (long)pnp_get_drvdata(dev);
|
||||
long line = (long)dev_get_drvdata(dev);
|
||||
|
||||
if (!line)
|
||||
return -ENODEV;
|
||||
serial8250_resume_port(line - 1);
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define serial_pnp_suspend NULL
|
||||
#define serial_pnp_resume NULL
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(serial_pnp_pm_ops, serial_pnp_suspend, serial_pnp_resume);
|
||||
|
||||
static struct pnp_driver serial_pnp_driver = {
|
||||
.name = "serial",
|
||||
.probe = serial_pnp_probe,
|
||||
.remove = serial_pnp_remove,
|
||||
.suspend = serial_pnp_suspend,
|
||||
.resume = serial_pnp_resume,
|
||||
.driver = {
|
||||
.pm = &serial_pnp_pm_ops,
|
||||
},
|
||||
.id_table = pnp_dev_table,
|
||||
};
|
||||
|
||||
|
@ -40,13 +40,6 @@
|
||||
|
||||
#include "8250.h"
|
||||
|
||||
/*
|
||||
* These are definitions for the Exar XR17V35X and XR17(C|D)15X
|
||||
*/
|
||||
#define UART_EXAR_INT0 0x80
|
||||
#define UART_EXAR_SLEEP 0x8b /* Sleep mode */
|
||||
#define UART_EXAR_DVID 0x8d /* Device identification */
|
||||
|
||||
/* Nuvoton NPCM timeout register */
|
||||
#define UART_NPCM_TOR 7
|
||||
#define UART_NPCM_TOIE BIT(7) /* Timeout Interrupt Enable */
|
||||
@ -308,6 +301,14 @@ static const struct serial8250_config uart_config[] = {
|
||||
.rxtrig_bytes = {1, 4, 8, 14},
|
||||
.flags = UART_CAP_FIFO,
|
||||
},
|
||||
[PORT_SUNIX] = {
|
||||
.name = "Sunix",
|
||||
.fifo_size = 128,
|
||||
.tx_loadsz = 128,
|
||||
.fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
|
||||
.rxtrig_bytes = {1, 32, 64, 112},
|
||||
.flags = UART_CAP_FIFO | UART_CAP_SLEEP,
|
||||
},
|
||||
};
|
||||
|
||||
/* Uart divisor latch read */
|
||||
@ -709,19 +710,8 @@ EXPORT_SYMBOL_GPL(serial8250_rpm_put_tx);
|
||||
static void serial8250_set_sleep(struct uart_8250_port *p, int sleep)
|
||||
{
|
||||
unsigned char lcr = 0, efr = 0;
|
||||
/*
|
||||
* Exar UARTs have a SLEEP register that enables or disables
|
||||
* each UART to enter sleep mode separately. On the XR17V35x the
|
||||
* register is accessible to each UART at the UART_EXAR_SLEEP
|
||||
* offset but the UART channel may only write to the corresponding
|
||||
* bit.
|
||||
*/
|
||||
|
||||
serial8250_rpm_get(p);
|
||||
if ((p->port.type == PORT_XR17V35X) ||
|
||||
(p->port.type == PORT_XR17D15X)) {
|
||||
serial_out(p, UART_EXAR_SLEEP, sleep ? 0xff : 0);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (p->capabilities & UART_CAP_SLEEP) {
|
||||
if (p->capabilities & UART_CAP_EFR) {
|
||||
@ -738,7 +728,7 @@ static void serial8250_set_sleep(struct uart_8250_port *p, int sleep)
|
||||
serial_out(p, UART_LCR, lcr);
|
||||
}
|
||||
}
|
||||
out:
|
||||
|
||||
serial8250_rpm_put(p);
|
||||
}
|
||||
|
||||
@ -1011,27 +1001,6 @@ static void autoconfig_16550a(struct uart_8250_port *up)
|
||||
up->port.type = PORT_16550A;
|
||||
up->capabilities |= UART_CAP_FIFO;
|
||||
|
||||
/*
|
||||
* XR17V35x UARTs have an extra divisor register, DLD
|
||||
* that gets enabled with when DLAB is set which will
|
||||
* cause the device to incorrectly match and assign
|
||||
* port type to PORT_16650. The EFR for this UART is
|
||||
* found at offset 0x09. Instead check the Deice ID (DVID)
|
||||
* register for a 2, 4 or 8 port UART.
|
||||
*/
|
||||
if (up->port.flags & UPF_EXAR_EFR) {
|
||||
status1 = serial_in(up, UART_EXAR_DVID);
|
||||
if (status1 == 0x82 || status1 == 0x84 || status1 == 0x88) {
|
||||
DEBUG_AUTOCONF("Exar XR17V35x ");
|
||||
up->port.type = PORT_XR17V35X;
|
||||
up->capabilities |= UART_CAP_AFE | UART_CAP_EFR |
|
||||
UART_CAP_SLEEP;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Check for presence of the EFR when DLAB is set.
|
||||
* Only ST16C650V1 UARTs pass this test.
|
||||
@ -1170,18 +1139,6 @@ static void autoconfig_16550a(struct uart_8250_port *up)
|
||||
}
|
||||
serial_out(up, UART_IER, iersave);
|
||||
|
||||
/*
|
||||
* Exar uarts have EFR in a weird location
|
||||
*/
|
||||
if (up->port.flags & UPF_EXAR_EFR) {
|
||||
DEBUG_AUTOCONF("Exar XR17D15x ");
|
||||
up->port.type = PORT_XR17D15X;
|
||||
up->capabilities |= UART_CAP_AFE | UART_CAP_EFR |
|
||||
UART_CAP_SLEEP;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* We distinguish between 16550A and U6 16550A by counting
|
||||
* how many bytes are in the FIFO.
|
||||
@ -2184,8 +2141,6 @@ int serial8250_do_startup(struct uart_port *port)
|
||||
serial_port_in(port, UART_RX);
|
||||
serial_port_in(port, UART_IIR);
|
||||
serial_port_in(port, UART_MSR);
|
||||
if ((port->type == PORT_XR17V35X) || (port->type == PORT_XR17D15X))
|
||||
serial_port_in(port, UART_EXAR_INT0);
|
||||
|
||||
/*
|
||||
* At this point, there's no way the LSR could still be 0xff;
|
||||
@ -2343,8 +2298,6 @@ dont_test_tx_en:
|
||||
serial_port_in(port, UART_RX);
|
||||
serial_port_in(port, UART_IIR);
|
||||
serial_port_in(port, UART_MSR);
|
||||
if ((port->type == PORT_XR17V35X) || (port->type == PORT_XR17D15X))
|
||||
serial_port_in(port, UART_EXAR_INT0);
|
||||
up->lsr_saved_flags = 0;
|
||||
up->msr_saved_flags = 0;
|
||||
|
||||
@ -2453,23 +2406,6 @@ static void serial8250_shutdown(struct uart_port *port)
|
||||
serial8250_do_shutdown(port);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
/* Nuvoton NPCM UARTs have a custom divisor calculation */
|
||||
static unsigned int npcm_get_divisor(struct uart_8250_port *up,
|
||||
unsigned int baud)
|
||||
@ -2497,8 +2433,6 @@ static unsigned int serial8250_do_get_divisor(struct uart_port *port,
|
||||
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 if (up->port.type == PORT_NPCM)
|
||||
quot = npcm_get_divisor(up, baud);
|
||||
else
|
||||
@ -2585,13 +2519,6 @@ void serial8250_do_set_divisor(struct uart_port *port, unsigned int baud,
|
||||
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) {
|
||||
/* Preserve bits not related to baudrate; DLD[7:4]. */
|
||||
quot_frac |= serial_port_in(port, 0x2) & 0xf0;
|
||||
serial_port_out(port, 0x2, quot_frac);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(serial8250_do_set_divisor);
|
||||
|
||||
|
@ -176,10 +176,8 @@ static int uniphier_uart_probe(struct platform_device *pdev)
|
||||
return -ENOMEM;
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
dev_err(dev, "failed to get IRQ number\n");
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
}
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
|
@ -314,6 +314,9 @@ config SERIAL_8250_RSA
|
||||
|
||||
If you don't have such card, or if unsure, say N.
|
||||
|
||||
config SERIAL_8250_DWLIB
|
||||
bool
|
||||
|
||||
config SERIAL_8250_ACORN
|
||||
tristate "Acorn expansion card serial port support"
|
||||
depends on ARCH_ACORN && SERIAL_8250
|
||||
@ -354,6 +357,7 @@ config SERIAL_8250_FSL
|
||||
config SERIAL_8250_DW
|
||||
tristate "Support for Synopsys DesignWare 8250 quirks"
|
||||
depends on SERIAL_8250
|
||||
select SERIAL_8250_DWLIB
|
||||
help
|
||||
Selecting this option will enable handling of the extra features
|
||||
present in the Synopsys DesignWare APB UART.
|
||||
@ -440,6 +444,7 @@ config SERIAL_8250_LPSS
|
||||
default SERIAL_8250
|
||||
depends on SERIAL_8250 && PCI
|
||||
depends on X86 || COMPILE_TEST
|
||||
select SERIAL_8250_DWLIB
|
||||
select DW_DMAC_CORE if SERIAL_8250_DMA
|
||||
select DW_DMAC_PCI if (SERIAL_8250_DMA && X86_INTEL_LPSS)
|
||||
select RATIONAL
|
||||
@ -463,16 +468,6 @@ config SERIAL_8250_MID
|
||||
present on the UART found on Intel Medfield SOC and various other
|
||||
Intel platforms.
|
||||
|
||||
config SERIAL_8250_MOXA
|
||||
tristate "MOXA SmartIO MUE support"
|
||||
depends on SERIAL_8250 && PCI
|
||||
help
|
||||
Say Y here if you have a Moxa SmartIO MUE multiport serial card.
|
||||
If unsure, say N.
|
||||
|
||||
This driver can also be built as a module. The module will be called
|
||||
8250_moxa. If you want to do that, say M here.
|
||||
|
||||
config SERIAL_8250_PXA
|
||||
tristate "PXA serial port support"
|
||||
depends on SERIAL_8250
|
||||
|
@ -8,6 +8,7 @@ obj-$(CONFIG_SERIAL_8250) += 8250.o 8250_base.o
|
||||
8250-$(CONFIG_SERIAL_8250_PNP) += 8250_pnp.o
|
||||
8250_base-y := 8250_port.o
|
||||
8250_base-$(CONFIG_SERIAL_8250_DMA) += 8250_dma.o
|
||||
8250_base-$(CONFIG_SERIAL_8250_DWLIB) += 8250_dwlib.o
|
||||
8250_base-$(CONFIG_SERIAL_8250_FINTEK) += 8250_fintek.o
|
||||
obj-$(CONFIG_SERIAL_8250_GSC) += 8250_gsc.o
|
||||
obj-$(CONFIG_SERIAL_8250_PCI) += 8250_pci.o
|
||||
@ -34,7 +35,6 @@ obj-$(CONFIG_SERIAL_8250_UNIPHIER) += 8250_uniphier.o
|
||||
obj-$(CONFIG_SERIAL_8250_INGENIC) += 8250_ingenic.o
|
||||
obj-$(CONFIG_SERIAL_8250_LPSS) += 8250_lpss.o
|
||||
obj-$(CONFIG_SERIAL_8250_MID) += 8250_mid.o
|
||||
obj-$(CONFIG_SERIAL_8250_MOXA) += 8250_moxa.o
|
||||
obj-$(CONFIG_SERIAL_8250_PXA) += 8250_pxa.o
|
||||
obj-$(CONFIG_SERIAL_OF_PLATFORM) += 8250_of.o
|
||||
|
||||
|
@ -197,23 +197,6 @@ config SERIAL_KGDB_NMI
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config SERIAL_KS8695
|
||||
bool "Micrel KS8695 (Centaur) serial port support"
|
||||
depends on ARCH_KS8695
|
||||
select SERIAL_CORE
|
||||
help
|
||||
This selects the Micrel Centaur KS8695 UART. Say Y here.
|
||||
|
||||
config SERIAL_KS8695_CONSOLE
|
||||
bool "Support for console on KS8695 (Centaur) serial port"
|
||||
depends on SERIAL_KS8695=y
|
||||
select SERIAL_CORE_CONSOLE
|
||||
help
|
||||
Say Y here if you wish to use a KS8695 (Centaur) UART as the
|
||||
system console (the system console is the device which
|
||||
receives all kernel messages and warnings and which allows
|
||||
logins in single user mode).
|
||||
|
||||
config SERIAL_MESON
|
||||
tristate "Meson serial port support"
|
||||
depends on ARCH_MESON
|
||||
@ -1407,6 +1390,22 @@ 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_FSL_LINFLEXUART
|
||||
tristate "Freescale linflexuart serial port support"
|
||||
depends on PRINTK
|
||||
select SERIAL_CORE
|
||||
help
|
||||
Support for the on-chip linflexuart on some Freescale SOCs.
|
||||
|
||||
config SERIAL_FSL_LINFLEXUART_CONSOLE
|
||||
bool "Console on Freescale linflexuart serial port"
|
||||
depends on SERIAL_FSL_LINFLEXUART=y
|
||||
select SERIAL_CORE_CONSOLE
|
||||
select SERIAL_EARLYCON
|
||||
help
|
||||
If you have enabled the linflexuart 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
|
||||
|
@ -56,7 +56,6 @@ obj-$(CONFIG_SERIAL_ATMEL) += atmel_serial.o
|
||||
obj-$(CONFIG_SERIAL_UARTLITE) += uartlite.o
|
||||
obj-$(CONFIG_SERIAL_MSM) += msm_serial.o
|
||||
obj-$(CONFIG_SERIAL_QCOM_GENI) += qcom_geni_serial.o
|
||||
obj-$(CONFIG_SERIAL_KS8695) += serial_ks8695.o
|
||||
obj-$(CONFIG_SERIAL_OMAP) += omap-serial.o
|
||||
obj-$(CONFIG_SERIAL_ALTERA_UART) += altera_uart.o
|
||||
obj-$(CONFIG_SERIAL_ST_ASC) += st-asc.o
|
||||
@ -78,6 +77,7 @@ 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_FSL_LINFLEXUART) += fsl_linflexuart.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
|
||||
|
@ -2718,11 +2718,8 @@ static int sbsa_uart_probe(struct platform_device *pdev)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = platform_get_irq(pdev, 0);
|
||||
if (ret < 0) {
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(&pdev->dev, "cannot obtain irq\n");
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
uap->port.irq = ret;
|
||||
|
||||
#ifdef CONFIG_ACPI_SPCR_TABLE
|
||||
|
@ -294,50 +294,6 @@ static void atmel_tasklet_schedule(struct atmel_uart_port *atmel_port,
|
||||
tasklet_schedule(t);
|
||||
}
|
||||
|
||||
static unsigned int atmel_get_lines_status(struct uart_port *port)
|
||||
{
|
||||
struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
|
||||
unsigned int status, ret = 0;
|
||||
|
||||
status = atmel_uart_readl(port, ATMEL_US_CSR);
|
||||
|
||||
mctrl_gpio_get(atmel_port->gpios, &ret);
|
||||
|
||||
if (!IS_ERR_OR_NULL(mctrl_gpio_to_gpiod(atmel_port->gpios,
|
||||
UART_GPIO_CTS))) {
|
||||
if (ret & TIOCM_CTS)
|
||||
status &= ~ATMEL_US_CTS;
|
||||
else
|
||||
status |= ATMEL_US_CTS;
|
||||
}
|
||||
|
||||
if (!IS_ERR_OR_NULL(mctrl_gpio_to_gpiod(atmel_port->gpios,
|
||||
UART_GPIO_DSR))) {
|
||||
if (ret & TIOCM_DSR)
|
||||
status &= ~ATMEL_US_DSR;
|
||||
else
|
||||
status |= ATMEL_US_DSR;
|
||||
}
|
||||
|
||||
if (!IS_ERR_OR_NULL(mctrl_gpio_to_gpiod(atmel_port->gpios,
|
||||
UART_GPIO_RI))) {
|
||||
if (ret & TIOCM_RI)
|
||||
status &= ~ATMEL_US_RI;
|
||||
else
|
||||
status |= ATMEL_US_RI;
|
||||
}
|
||||
|
||||
if (!IS_ERR_OR_NULL(mctrl_gpio_to_gpiod(atmel_port->gpios,
|
||||
UART_GPIO_DCD))) {
|
||||
if (ret & TIOCM_CD)
|
||||
status &= ~ATMEL_US_DCD;
|
||||
else
|
||||
status |= ATMEL_US_DCD;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/* Enable or disable the rs485 support */
|
||||
static int atmel_config_rs485(struct uart_port *port,
|
||||
struct serial_rs485 *rs485conf)
|
||||
@ -1400,7 +1356,6 @@ atmel_handle_transmit(struct uart_port *port, unsigned int pending)
|
||||
|
||||
atmel_port->hd_start_rx = false;
|
||||
atmel_start_rx(port);
|
||||
return;
|
||||
}
|
||||
|
||||
atmel_tasklet_schedule(atmel_port, &atmel_port->tasklet_tx);
|
||||
@ -1454,7 +1409,7 @@ static irqreturn_t atmel_interrupt(int irq, void *dev_id)
|
||||
spin_lock(&atmel_port->lock_suspended);
|
||||
|
||||
do {
|
||||
status = atmel_get_lines_status(port);
|
||||
status = atmel_uart_readl(port, ATMEL_US_CSR);
|
||||
mask = atmel_uart_readl(port, ATMEL_US_IMR);
|
||||
pending = status & mask;
|
||||
if (!pending)
|
||||
@ -2003,7 +1958,7 @@ static int atmel_startup(struct uart_port *port)
|
||||
}
|
||||
|
||||
/* Save current CSR for comparison in atmel_tasklet_func() */
|
||||
atmel_port->irq_status_prev = atmel_get_lines_status(port);
|
||||
atmel_port->irq_status_prev = atmel_uart_readl(port, ATMEL_US_CSR);
|
||||
|
||||
/*
|
||||
* Finally, enable the serial port
|
||||
@ -2888,7 +2843,7 @@ static int atmel_serial_probe(struct platform_device *pdev)
|
||||
struct atmel_uart_port *atmel_port;
|
||||
struct device_node *np = pdev->dev.parent->of_node;
|
||||
void *data;
|
||||
int ret = -ENODEV;
|
||||
int ret;
|
||||
bool rs485_enabled;
|
||||
|
||||
BUILD_BUG_ON(ATMEL_SERIAL_RINGSIZE & (ATMEL_SERIAL_RINGSIZE - 1));
|
||||
|
937
drivers/tty/serial/fsl_linflexuart.c
Normal file
937
drivers/tty/serial/fsl_linflexuart.c
Normal file
@ -0,0 +1,937 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Freescale linflexuart serial port driver
|
||||
*
|
||||
* Copyright 2012-2016 Freescale Semiconductor, Inc.
|
||||
* Copyright 2017-2018 NXP
|
||||
*/
|
||||
|
||||
#if defined(CONFIG_SERIAL_FSL_LINFLEXUART_CONSOLE) && \
|
||||
defined(CONFIG_MAGIC_SYSRQ)
|
||||
#define SUPPORT_SYSRQ
|
||||
#endif
|
||||
|
||||
#include <linux/console.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/serial_core.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/tty_flip.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
/* All registers are 32-bit width */
|
||||
|
||||
#define LINCR1 0x0000 /* LIN control register */
|
||||
#define LINIER 0x0004 /* LIN interrupt enable register */
|
||||
#define LINSR 0x0008 /* LIN status register */
|
||||
#define LINESR 0x000C /* LIN error status register */
|
||||
#define UARTCR 0x0010 /* UART mode control register */
|
||||
#define UARTSR 0x0014 /* UART mode status register */
|
||||
#define LINTCSR 0x0018 /* LIN timeout control status register */
|
||||
#define LINOCR 0x001C /* LIN output compare register */
|
||||
#define LINTOCR 0x0020 /* LIN timeout control register */
|
||||
#define LINFBRR 0x0024 /* LIN fractional baud rate register */
|
||||
#define LINIBRR 0x0028 /* LIN integer baud rate register */
|
||||
#define LINCFR 0x002C /* LIN checksum field register */
|
||||
#define LINCR2 0x0030 /* LIN control register 2 */
|
||||
#define BIDR 0x0034 /* Buffer identifier register */
|
||||
#define BDRL 0x0038 /* Buffer data register least significant */
|
||||
#define BDRM 0x003C /* Buffer data register most significant */
|
||||
#define IFER 0x0040 /* Identifier filter enable register */
|
||||
#define IFMI 0x0044 /* Identifier filter match index */
|
||||
#define IFMR 0x0048 /* Identifier filter mode register */
|
||||
#define GCR 0x004C /* Global control register */
|
||||
#define UARTPTO 0x0050 /* UART preset timeout register */
|
||||
#define UARTCTO 0x0054 /* UART current timeout register */
|
||||
|
||||
/*
|
||||
* Register field definitions
|
||||
*/
|
||||
|
||||
#define LINFLEXD_LINCR1_INIT BIT(0)
|
||||
#define LINFLEXD_LINCR1_MME BIT(4)
|
||||
#define LINFLEXD_LINCR1_BF BIT(7)
|
||||
|
||||
#define LINFLEXD_LINSR_LINS_INITMODE BIT(12)
|
||||
#define LINFLEXD_LINSR_LINS_MASK (0xF << 12)
|
||||
|
||||
#define LINFLEXD_LINIER_SZIE BIT(15)
|
||||
#define LINFLEXD_LINIER_OCIE BIT(14)
|
||||
#define LINFLEXD_LINIER_BEIE BIT(13)
|
||||
#define LINFLEXD_LINIER_CEIE BIT(12)
|
||||
#define LINFLEXD_LINIER_HEIE BIT(11)
|
||||
#define LINFLEXD_LINIER_FEIE BIT(8)
|
||||
#define LINFLEXD_LINIER_BOIE BIT(7)
|
||||
#define LINFLEXD_LINIER_LSIE BIT(6)
|
||||
#define LINFLEXD_LINIER_WUIE BIT(5)
|
||||
#define LINFLEXD_LINIER_DBFIE BIT(4)
|
||||
#define LINFLEXD_LINIER_DBEIETOIE BIT(3)
|
||||
#define LINFLEXD_LINIER_DRIE BIT(2)
|
||||
#define LINFLEXD_LINIER_DTIE BIT(1)
|
||||
#define LINFLEXD_LINIER_HRIE BIT(0)
|
||||
|
||||
#define LINFLEXD_UARTCR_OSR_MASK (0xF << 24)
|
||||
#define LINFLEXD_UARTCR_OSR(uartcr) (((uartcr) \
|
||||
& LINFLEXD_UARTCR_OSR_MASK) >> 24)
|
||||
|
||||
#define LINFLEXD_UARTCR_ROSE BIT(23)
|
||||
|
||||
#define LINFLEXD_UARTCR_RFBM BIT(9)
|
||||
#define LINFLEXD_UARTCR_TFBM BIT(8)
|
||||
#define LINFLEXD_UARTCR_WL1 BIT(7)
|
||||
#define LINFLEXD_UARTCR_PC1 BIT(6)
|
||||
|
||||
#define LINFLEXD_UARTCR_RXEN BIT(5)
|
||||
#define LINFLEXD_UARTCR_TXEN BIT(4)
|
||||
#define LINFLEXD_UARTCR_PC0 BIT(3)
|
||||
|
||||
#define LINFLEXD_UARTCR_PCE BIT(2)
|
||||
#define LINFLEXD_UARTCR_WL0 BIT(1)
|
||||
#define LINFLEXD_UARTCR_UART BIT(0)
|
||||
|
||||
#define LINFLEXD_UARTSR_SZF BIT(15)
|
||||
#define LINFLEXD_UARTSR_OCF BIT(14)
|
||||
#define LINFLEXD_UARTSR_PE3 BIT(13)
|
||||
#define LINFLEXD_UARTSR_PE2 BIT(12)
|
||||
#define LINFLEXD_UARTSR_PE1 BIT(11)
|
||||
#define LINFLEXD_UARTSR_PE0 BIT(10)
|
||||
#define LINFLEXD_UARTSR_RMB BIT(9)
|
||||
#define LINFLEXD_UARTSR_FEF BIT(8)
|
||||
#define LINFLEXD_UARTSR_BOF BIT(7)
|
||||
#define LINFLEXD_UARTSR_RPS BIT(6)
|
||||
#define LINFLEXD_UARTSR_WUF BIT(5)
|
||||
#define LINFLEXD_UARTSR_4 BIT(4)
|
||||
|
||||
#define LINFLEXD_UARTSR_TO BIT(3)
|
||||
|
||||
#define LINFLEXD_UARTSR_DRFRFE BIT(2)
|
||||
#define LINFLEXD_UARTSR_DTFTFF BIT(1)
|
||||
#define LINFLEXD_UARTSR_NF BIT(0)
|
||||
#define LINFLEXD_UARTSR_PE (LINFLEXD_UARTSR_PE0 |\
|
||||
LINFLEXD_UARTSR_PE1 |\
|
||||
LINFLEXD_UARTSR_PE2 |\
|
||||
LINFLEXD_UARTSR_PE3)
|
||||
|
||||
#define LINFLEX_LDIV_MULTIPLIER (16)
|
||||
|
||||
#define DRIVER_NAME "fsl-linflexuart"
|
||||
#define DEV_NAME "ttyLF"
|
||||
#define UART_NR 4
|
||||
|
||||
#define EARLYCON_BUFFER_INITIAL_CAP 8
|
||||
|
||||
#define PREINIT_DELAY 2000 /* us */
|
||||
|
||||
static const struct of_device_id linflex_dt_ids[] = {
|
||||
{
|
||||
.compatible = "fsl,s32v234-linflexuart",
|
||||
},
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, linflex_dt_ids);
|
||||
|
||||
#ifdef CONFIG_SERIAL_FSL_LINFLEXUART_CONSOLE
|
||||
static struct uart_port *earlycon_port;
|
||||
static bool linflex_earlycon_same_instance;
|
||||
static DEFINE_SPINLOCK(init_lock);
|
||||
static bool during_init;
|
||||
|
||||
static struct {
|
||||
char *content;
|
||||
unsigned int len, cap;
|
||||
} earlycon_buf;
|
||||
#endif
|
||||
|
||||
static void linflex_stop_tx(struct uart_port *port)
|
||||
{
|
||||
unsigned long ier;
|
||||
|
||||
ier = readl(port->membase + LINIER);
|
||||
ier &= ~(LINFLEXD_LINIER_DTIE);
|
||||
writel(ier, port->membase + LINIER);
|
||||
}
|
||||
|
||||
static void linflex_stop_rx(struct uart_port *port)
|
||||
{
|
||||
unsigned long ier;
|
||||
|
||||
ier = readl(port->membase + LINIER);
|
||||
writel(ier & ~LINFLEXD_LINIER_DRIE, port->membase + LINIER);
|
||||
}
|
||||
|
||||
static inline void linflex_transmit_buffer(struct uart_port *sport)
|
||||
{
|
||||
struct circ_buf *xmit = &sport->state->xmit;
|
||||
unsigned char c;
|
||||
unsigned long status;
|
||||
|
||||
while (!uart_circ_empty(xmit)) {
|
||||
c = xmit->buf[xmit->tail];
|
||||
writeb(c, sport->membase + BDRL);
|
||||
|
||||
/* Waiting for data transmission completed. */
|
||||
while (((status = readl(sport->membase + UARTSR)) &
|
||||
LINFLEXD_UARTSR_DTFTFF) !=
|
||||
LINFLEXD_UARTSR_DTFTFF)
|
||||
;
|
||||
|
||||
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
|
||||
sport->icount.tx++;
|
||||
|
||||
writel(status | LINFLEXD_UARTSR_DTFTFF,
|
||||
sport->membase + UARTSR);
|
||||
}
|
||||
|
||||
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
|
||||
uart_write_wakeup(sport);
|
||||
|
||||
if (uart_circ_empty(xmit))
|
||||
linflex_stop_tx(sport);
|
||||
}
|
||||
|
||||
static void linflex_start_tx(struct uart_port *port)
|
||||
{
|
||||
unsigned long ier;
|
||||
|
||||
linflex_transmit_buffer(port);
|
||||
ier = readl(port->membase + LINIER);
|
||||
writel(ier | LINFLEXD_LINIER_DTIE, port->membase + LINIER);
|
||||
}
|
||||
|
||||
static irqreturn_t linflex_txint(int irq, void *dev_id)
|
||||
{
|
||||
struct uart_port *sport = dev_id;
|
||||
struct circ_buf *xmit = &sport->state->xmit;
|
||||
unsigned long flags;
|
||||
unsigned long status;
|
||||
|
||||
spin_lock_irqsave(&sport->lock, flags);
|
||||
|
||||
if (sport->x_char) {
|
||||
writeb(sport->x_char, sport->membase + BDRL);
|
||||
|
||||
/* waiting for data transmission completed */
|
||||
while (((status = readl(sport->membase + UARTSR)) &
|
||||
LINFLEXD_UARTSR_DTFTFF) != LINFLEXD_UARTSR_DTFTFF)
|
||||
;
|
||||
|
||||
writel(status | LINFLEXD_UARTSR_DTFTFF,
|
||||
sport->membase + UARTSR);
|
||||
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (uart_circ_empty(xmit) || uart_tx_stopped(sport)) {
|
||||
linflex_stop_tx(sport);
|
||||
goto out;
|
||||
}
|
||||
|
||||
linflex_transmit_buffer(sport);
|
||||
|
||||
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
|
||||
uart_write_wakeup(sport);
|
||||
|
||||
out:
|
||||
spin_unlock_irqrestore(&sport->lock, flags);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t linflex_rxint(int irq, void *dev_id)
|
||||
{
|
||||
struct uart_port *sport = dev_id;
|
||||
unsigned int flg;
|
||||
struct tty_port *port = &sport->state->port;
|
||||
unsigned long flags, status;
|
||||
unsigned char rx;
|
||||
|
||||
spin_lock_irqsave(&sport->lock, flags);
|
||||
|
||||
status = readl(sport->membase + UARTSR);
|
||||
while (status & LINFLEXD_UARTSR_RMB) {
|
||||
rx = readb(sport->membase + BDRM);
|
||||
flg = TTY_NORMAL;
|
||||
sport->icount.rx++;
|
||||
|
||||
if (status & (LINFLEXD_UARTSR_BOF | LINFLEXD_UARTSR_SZF |
|
||||
LINFLEXD_UARTSR_FEF | LINFLEXD_UARTSR_PE)) {
|
||||
if (status & LINFLEXD_UARTSR_SZF)
|
||||
status |= LINFLEXD_UARTSR_SZF;
|
||||
if (status & LINFLEXD_UARTSR_BOF)
|
||||
status |= LINFLEXD_UARTSR_BOF;
|
||||
if (status & LINFLEXD_UARTSR_FEF)
|
||||
status |= LINFLEXD_UARTSR_FEF;
|
||||
if (status & LINFLEXD_UARTSR_PE)
|
||||
status |= LINFLEXD_UARTSR_PE;
|
||||
}
|
||||
|
||||
writel(status | LINFLEXD_UARTSR_RMB | LINFLEXD_UARTSR_DRFRFE,
|
||||
sport->membase + UARTSR);
|
||||
status = readl(sport->membase + UARTSR);
|
||||
|
||||
if (uart_handle_sysrq_char(sport, (unsigned char)rx))
|
||||
continue;
|
||||
|
||||
#ifdef SUPPORT_SYSRQ
|
||||
sport->sysrq = 0;
|
||||
#endif
|
||||
tty_insert_flip_char(port, rx, flg);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&sport->lock, flags);
|
||||
|
||||
tty_flip_buffer_push(port);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t linflex_int(int irq, void *dev_id)
|
||||
{
|
||||
struct uart_port *sport = dev_id;
|
||||
unsigned long status;
|
||||
|
||||
status = readl(sport->membase + UARTSR);
|
||||
|
||||
if (status & LINFLEXD_UARTSR_DRFRFE)
|
||||
linflex_rxint(irq, dev_id);
|
||||
if (status & LINFLEXD_UARTSR_DTFTFF)
|
||||
linflex_txint(irq, dev_id);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/* return TIOCSER_TEMT when transmitter is not busy */
|
||||
static unsigned int linflex_tx_empty(struct uart_port *port)
|
||||
{
|
||||
unsigned long status;
|
||||
|
||||
status = readl(port->membase + UARTSR) & LINFLEXD_UARTSR_DTFTFF;
|
||||
|
||||
return status ? TIOCSER_TEMT : 0;
|
||||
}
|
||||
|
||||
static unsigned int linflex_get_mctrl(struct uart_port *port)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void linflex_set_mctrl(struct uart_port *port, unsigned int mctrl)
|
||||
{
|
||||
}
|
||||
|
||||
static void linflex_break_ctl(struct uart_port *port, int break_state)
|
||||
{
|
||||
}
|
||||
|
||||
static void linflex_setup_watermark(struct uart_port *sport)
|
||||
{
|
||||
unsigned long cr, ier, cr1;
|
||||
|
||||
/* Disable transmission/reception */
|
||||
ier = readl(sport->membase + LINIER);
|
||||
ier &= ~(LINFLEXD_LINIER_DRIE | LINFLEXD_LINIER_DTIE);
|
||||
writel(ier, sport->membase + LINIER);
|
||||
|
||||
cr = readl(sport->membase + UARTCR);
|
||||
cr &= ~(LINFLEXD_UARTCR_RXEN | LINFLEXD_UARTCR_TXEN);
|
||||
writel(cr, sport->membase + UARTCR);
|
||||
|
||||
/* Enter initialization mode by setting INIT bit */
|
||||
|
||||
/* set the Linflex in master mode and activate by-pass filter */
|
||||
cr1 = LINFLEXD_LINCR1_BF | LINFLEXD_LINCR1_MME
|
||||
| LINFLEXD_LINCR1_INIT;
|
||||
writel(cr1, sport->membase + LINCR1);
|
||||
|
||||
/* wait for init mode entry */
|
||||
while ((readl(sport->membase + LINSR)
|
||||
& LINFLEXD_LINSR_LINS_MASK)
|
||||
!= LINFLEXD_LINSR_LINS_INITMODE)
|
||||
;
|
||||
|
||||
/*
|
||||
* UART = 0x1; - Linflex working in UART mode
|
||||
* TXEN = 0x1; - Enable transmission of data now
|
||||
* RXEn = 0x1; - Receiver enabled
|
||||
* WL0 = 0x1; - 8 bit data
|
||||
* PCE = 0x0; - No parity
|
||||
*/
|
||||
|
||||
/* set UART bit to allow writing other bits */
|
||||
writel(LINFLEXD_UARTCR_UART, sport->membase + UARTCR);
|
||||
|
||||
cr = (LINFLEXD_UARTCR_RXEN | LINFLEXD_UARTCR_TXEN |
|
||||
LINFLEXD_UARTCR_WL0 | LINFLEXD_UARTCR_UART);
|
||||
|
||||
writel(cr, sport->membase + UARTCR);
|
||||
|
||||
cr1 &= ~(LINFLEXD_LINCR1_INIT);
|
||||
|
||||
writel(cr1, sport->membase + LINCR1);
|
||||
|
||||
ier = readl(sport->membase + LINIER);
|
||||
ier |= LINFLEXD_LINIER_DRIE;
|
||||
ier |= LINFLEXD_LINIER_DTIE;
|
||||
|
||||
writel(ier, sport->membase + LINIER);
|
||||
}
|
||||
|
||||
static int linflex_startup(struct uart_port *port)
|
||||
{
|
||||
int ret = 0;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&port->lock, flags);
|
||||
|
||||
linflex_setup_watermark(port);
|
||||
|
||||
spin_unlock_irqrestore(&port->lock, flags);
|
||||
|
||||
ret = devm_request_irq(port->dev, port->irq, linflex_int, 0,
|
||||
DRIVER_NAME, port);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void linflex_shutdown(struct uart_port *port)
|
||||
{
|
||||
unsigned long ier;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&port->lock, flags);
|
||||
|
||||
/* disable interrupts */
|
||||
ier = readl(port->membase + LINIER);
|
||||
ier &= ~(LINFLEXD_LINIER_DRIE | LINFLEXD_LINIER_DTIE);
|
||||
writel(ier, port->membase + LINIER);
|
||||
|
||||
spin_unlock_irqrestore(&port->lock, flags);
|
||||
|
||||
devm_free_irq(port->dev, port->irq, port);
|
||||
}
|
||||
|
||||
static void
|
||||
linflex_set_termios(struct uart_port *port, struct ktermios *termios,
|
||||
struct ktermios *old)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned long cr, old_cr, cr1;
|
||||
unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8;
|
||||
|
||||
cr = readl(port->membase + UARTCR);
|
||||
old_cr = cr;
|
||||
|
||||
/* Enter initialization mode by setting INIT bit */
|
||||
cr1 = readl(port->membase + LINCR1);
|
||||
cr1 |= LINFLEXD_LINCR1_INIT;
|
||||
writel(cr1, port->membase + LINCR1);
|
||||
|
||||
/* wait for init mode entry */
|
||||
while ((readl(port->membase + LINSR)
|
||||
& LINFLEXD_LINSR_LINS_MASK)
|
||||
!= LINFLEXD_LINSR_LINS_INITMODE)
|
||||
;
|
||||
|
||||
/*
|
||||
* only support CS8 and CS7, and for CS7 must enable PE.
|
||||
* supported mode:
|
||||
* - (7,e/o,1)
|
||||
* - (8,n,1)
|
||||
* - (8,e/o,1)
|
||||
*/
|
||||
/* enter the UART into configuration mode */
|
||||
|
||||
while ((termios->c_cflag & CSIZE) != CS8 &&
|
||||
(termios->c_cflag & CSIZE) != CS7) {
|
||||
termios->c_cflag &= ~CSIZE;
|
||||
termios->c_cflag |= old_csize;
|
||||
old_csize = CS8;
|
||||
}
|
||||
|
||||
if ((termios->c_cflag & CSIZE) == CS7) {
|
||||
/* Word length: WL1WL0:00 */
|
||||
cr = old_cr & ~LINFLEXD_UARTCR_WL1 & ~LINFLEXD_UARTCR_WL0;
|
||||
}
|
||||
|
||||
if ((termios->c_cflag & CSIZE) == CS8) {
|
||||
/* Word length: WL1WL0:01 */
|
||||
cr = (old_cr | LINFLEXD_UARTCR_WL0) & ~LINFLEXD_UARTCR_WL1;
|
||||
}
|
||||
|
||||
if (termios->c_cflag & CMSPAR) {
|
||||
if ((termios->c_cflag & CSIZE) != CS8) {
|
||||
termios->c_cflag &= ~CSIZE;
|
||||
termios->c_cflag |= CS8;
|
||||
}
|
||||
/* has a space/sticky bit */
|
||||
cr |= LINFLEXD_UARTCR_WL0;
|
||||
}
|
||||
|
||||
if (termios->c_cflag & CSTOPB)
|
||||
termios->c_cflag &= ~CSTOPB;
|
||||
|
||||
/* parity must be enabled when CS7 to match 8-bits format */
|
||||
if ((termios->c_cflag & CSIZE) == CS7)
|
||||
termios->c_cflag |= PARENB;
|
||||
|
||||
if ((termios->c_cflag & PARENB)) {
|
||||
cr |= LINFLEXD_UARTCR_PCE;
|
||||
if (termios->c_cflag & PARODD)
|
||||
cr = (cr | LINFLEXD_UARTCR_PC0) &
|
||||
(~LINFLEXD_UARTCR_PC1);
|
||||
else
|
||||
cr = cr & (~LINFLEXD_UARTCR_PC1 &
|
||||
~LINFLEXD_UARTCR_PC0);
|
||||
} else {
|
||||
cr &= ~LINFLEXD_UARTCR_PCE;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&port->lock, flags);
|
||||
|
||||
port->read_status_mask = 0;
|
||||
|
||||
if (termios->c_iflag & INPCK)
|
||||
port->read_status_mask |= (LINFLEXD_UARTSR_FEF |
|
||||
LINFLEXD_UARTSR_PE0 |
|
||||
LINFLEXD_UARTSR_PE1 |
|
||||
LINFLEXD_UARTSR_PE2 |
|
||||
LINFLEXD_UARTSR_PE3);
|
||||
if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK))
|
||||
port->read_status_mask |= LINFLEXD_UARTSR_FEF;
|
||||
|
||||
/* characters to ignore */
|
||||
port->ignore_status_mask = 0;
|
||||
if (termios->c_iflag & IGNPAR)
|
||||
port->ignore_status_mask |= LINFLEXD_UARTSR_PE;
|
||||
if (termios->c_iflag & IGNBRK) {
|
||||
port->ignore_status_mask |= LINFLEXD_UARTSR_PE;
|
||||
/*
|
||||
* if we're ignoring parity and break indicators,
|
||||
* ignore overruns too (for real raw support).
|
||||
*/
|
||||
if (termios->c_iflag & IGNPAR)
|
||||
port->ignore_status_mask |= LINFLEXD_UARTSR_BOF;
|
||||
}
|
||||
|
||||
writel(cr, port->membase + UARTCR);
|
||||
|
||||
cr1 &= ~(LINFLEXD_LINCR1_INIT);
|
||||
|
||||
writel(cr1, port->membase + LINCR1);
|
||||
|
||||
spin_unlock_irqrestore(&port->lock, flags);
|
||||
}
|
||||
|
||||
static const char *linflex_type(struct uart_port *port)
|
||||
{
|
||||
return "FSL_LINFLEX";
|
||||
}
|
||||
|
||||
static void linflex_release_port(struct uart_port *port)
|
||||
{
|
||||
/* nothing to do */
|
||||
}
|
||||
|
||||
static int linflex_request_port(struct uart_port *port)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* configure/auto-configure the port */
|
||||
static void linflex_config_port(struct uart_port *port, int flags)
|
||||
{
|
||||
if (flags & UART_CONFIG_TYPE)
|
||||
port->type = PORT_LINFLEXUART;
|
||||
}
|
||||
|
||||
static const struct uart_ops linflex_pops = {
|
||||
.tx_empty = linflex_tx_empty,
|
||||
.set_mctrl = linflex_set_mctrl,
|
||||
.get_mctrl = linflex_get_mctrl,
|
||||
.stop_tx = linflex_stop_tx,
|
||||
.start_tx = linflex_start_tx,
|
||||
.stop_rx = linflex_stop_rx,
|
||||
.break_ctl = linflex_break_ctl,
|
||||
.startup = linflex_startup,
|
||||
.shutdown = linflex_shutdown,
|
||||
.set_termios = linflex_set_termios,
|
||||
.type = linflex_type,
|
||||
.request_port = linflex_request_port,
|
||||
.release_port = linflex_release_port,
|
||||
.config_port = linflex_config_port,
|
||||
};
|
||||
|
||||
static struct uart_port *linflex_ports[UART_NR];
|
||||
|
||||
#ifdef CONFIG_SERIAL_FSL_LINFLEXUART_CONSOLE
|
||||
static void linflex_console_putchar(struct uart_port *port, int ch)
|
||||
{
|
||||
unsigned long cr;
|
||||
|
||||
cr = readl(port->membase + UARTCR);
|
||||
|
||||
writeb(ch, port->membase + BDRL);
|
||||
|
||||
if (!(cr & LINFLEXD_UARTCR_TFBM))
|
||||
while ((readl(port->membase + UARTSR) &
|
||||
LINFLEXD_UARTSR_DTFTFF)
|
||||
!= LINFLEXD_UARTSR_DTFTFF)
|
||||
;
|
||||
else
|
||||
while (readl(port->membase + UARTSR) &
|
||||
LINFLEXD_UARTSR_DTFTFF)
|
||||
;
|
||||
|
||||
if (!(cr & LINFLEXD_UARTCR_TFBM)) {
|
||||
writel((readl(port->membase + UARTSR) |
|
||||
LINFLEXD_UARTSR_DTFTFF),
|
||||
port->membase + UARTSR);
|
||||
}
|
||||
}
|
||||
|
||||
static void linflex_earlycon_putchar(struct uart_port *port, int ch)
|
||||
{
|
||||
unsigned long flags;
|
||||
char *ret;
|
||||
|
||||
if (!linflex_earlycon_same_instance) {
|
||||
linflex_console_putchar(port, ch);
|
||||
return;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&init_lock, flags);
|
||||
if (!during_init)
|
||||
goto outside_init;
|
||||
|
||||
if (earlycon_buf.len >= 1 << CONFIG_LOG_BUF_SHIFT)
|
||||
goto init_release;
|
||||
|
||||
if (!earlycon_buf.cap) {
|
||||
earlycon_buf.content = kmalloc(EARLYCON_BUFFER_INITIAL_CAP,
|
||||
GFP_ATOMIC);
|
||||
earlycon_buf.cap = earlycon_buf.content ?
|
||||
EARLYCON_BUFFER_INITIAL_CAP : 0;
|
||||
} else if (earlycon_buf.len == earlycon_buf.cap) {
|
||||
ret = krealloc(earlycon_buf.content, earlycon_buf.cap << 1,
|
||||
GFP_ATOMIC);
|
||||
if (ret) {
|
||||
earlycon_buf.content = ret;
|
||||
earlycon_buf.cap <<= 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (earlycon_buf.len < earlycon_buf.cap)
|
||||
earlycon_buf.content[earlycon_buf.len++] = ch;
|
||||
|
||||
goto init_release;
|
||||
|
||||
outside_init:
|
||||
linflex_console_putchar(port, ch);
|
||||
init_release:
|
||||
spin_unlock_irqrestore(&init_lock, flags);
|
||||
}
|
||||
|
||||
static void linflex_string_write(struct uart_port *sport, const char *s,
|
||||
unsigned int count)
|
||||
{
|
||||
unsigned long cr, ier = 0;
|
||||
|
||||
ier = readl(sport->membase + LINIER);
|
||||
linflex_stop_tx(sport);
|
||||
|
||||
cr = readl(sport->membase + UARTCR);
|
||||
cr |= (LINFLEXD_UARTCR_TXEN);
|
||||
writel(cr, sport->membase + UARTCR);
|
||||
|
||||
uart_console_write(sport, s, count, linflex_console_putchar);
|
||||
|
||||
writel(ier, sport->membase + LINIER);
|
||||
}
|
||||
|
||||
static void
|
||||
linflex_console_write(struct console *co, const char *s, unsigned int count)
|
||||
{
|
||||
struct uart_port *sport = linflex_ports[co->index];
|
||||
unsigned long flags;
|
||||
int locked = 1;
|
||||
|
||||
if (sport->sysrq)
|
||||
locked = 0;
|
||||
else if (oops_in_progress)
|
||||
locked = spin_trylock_irqsave(&sport->lock, flags);
|
||||
else
|
||||
spin_lock_irqsave(&sport->lock, flags);
|
||||
|
||||
linflex_string_write(sport, s, count);
|
||||
|
||||
if (locked)
|
||||
spin_unlock_irqrestore(&sport->lock, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* if the port was already initialised (eg, by a boot loader),
|
||||
* try to determine the current setup.
|
||||
*/
|
||||
static void __init
|
||||
linflex_console_get_options(struct uart_port *sport, int *parity, int *bits)
|
||||
{
|
||||
unsigned long cr;
|
||||
|
||||
cr = readl(sport->membase + UARTCR);
|
||||
cr &= LINFLEXD_UARTCR_RXEN | LINFLEXD_UARTCR_TXEN;
|
||||
|
||||
if (!cr)
|
||||
return;
|
||||
|
||||
/* ok, the port was enabled */
|
||||
|
||||
*parity = 'n';
|
||||
if (cr & LINFLEXD_UARTCR_PCE) {
|
||||
if (cr & LINFLEXD_UARTCR_PC0)
|
||||
*parity = 'o';
|
||||
else
|
||||
*parity = 'e';
|
||||
}
|
||||
|
||||
if ((cr & LINFLEXD_UARTCR_WL0) && ((cr & LINFLEXD_UARTCR_WL1) == 0)) {
|
||||
if (cr & LINFLEXD_UARTCR_PCE)
|
||||
*bits = 9;
|
||||
else
|
||||
*bits = 8;
|
||||
}
|
||||
}
|
||||
|
||||
static int __init linflex_console_setup(struct console *co, char *options)
|
||||
{
|
||||
struct uart_port *sport;
|
||||
int baud = 115200;
|
||||
int bits = 8;
|
||||
int parity = 'n';
|
||||
int flow = 'n';
|
||||
int ret;
|
||||
int i;
|
||||
unsigned long flags;
|
||||
/*
|
||||
* check whether an invalid uart number has been specified, and
|
||||
* if so, search for the first available port that does have
|
||||
* console support.
|
||||
*/
|
||||
if (co->index == -1 || co->index >= ARRAY_SIZE(linflex_ports))
|
||||
co->index = 0;
|
||||
|
||||
sport = linflex_ports[co->index];
|
||||
if (!sport)
|
||||
return -ENODEV;
|
||||
|
||||
if (options)
|
||||
uart_parse_options(options, &baud, &parity, &bits, &flow);
|
||||
else
|
||||
linflex_console_get_options(sport, &parity, &bits);
|
||||
|
||||
if (earlycon_port && sport->mapbase == earlycon_port->mapbase) {
|
||||
linflex_earlycon_same_instance = true;
|
||||
|
||||
spin_lock_irqsave(&init_lock, flags);
|
||||
during_init = true;
|
||||
spin_unlock_irqrestore(&init_lock, flags);
|
||||
|
||||
/* Workaround for character loss or output of many invalid
|
||||
* characters, when INIT mode is entered shortly after a
|
||||
* character has just been printed.
|
||||
*/
|
||||
udelay(PREINIT_DELAY);
|
||||
}
|
||||
|
||||
linflex_setup_watermark(sport);
|
||||
|
||||
ret = uart_set_options(sport, co, baud, parity, bits, flow);
|
||||
|
||||
if (!linflex_earlycon_same_instance)
|
||||
goto done;
|
||||
|
||||
spin_lock_irqsave(&init_lock, flags);
|
||||
|
||||
/* Emptying buffer */
|
||||
if (earlycon_buf.len) {
|
||||
for (i = 0; i < earlycon_buf.len; i++)
|
||||
linflex_console_putchar(earlycon_port,
|
||||
earlycon_buf.content[i]);
|
||||
|
||||
kfree(earlycon_buf.content);
|
||||
earlycon_buf.len = 0;
|
||||
}
|
||||
|
||||
during_init = false;
|
||||
spin_unlock_irqrestore(&init_lock, flags);
|
||||
|
||||
done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct uart_driver linflex_reg;
|
||||
static struct console linflex_console = {
|
||||
.name = DEV_NAME,
|
||||
.write = linflex_console_write,
|
||||
.device = uart_console_device,
|
||||
.setup = linflex_console_setup,
|
||||
.flags = CON_PRINTBUFFER,
|
||||
.index = -1,
|
||||
.data = &linflex_reg,
|
||||
};
|
||||
|
||||
static void linflex_earlycon_write(struct console *con, const char *s,
|
||||
unsigned int n)
|
||||
{
|
||||
struct earlycon_device *dev = con->data;
|
||||
|
||||
uart_console_write(&dev->port, s, n, linflex_earlycon_putchar);
|
||||
}
|
||||
|
||||
static int __init linflex_early_console_setup(struct earlycon_device *device,
|
||||
const char *options)
|
||||
{
|
||||
if (!device->port.membase)
|
||||
return -ENODEV;
|
||||
|
||||
device->con->write = linflex_earlycon_write;
|
||||
earlycon_port = &device->port;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
OF_EARLYCON_DECLARE(linflex, "fsl,s32v234-linflexuart",
|
||||
linflex_early_console_setup);
|
||||
|
||||
#define LINFLEX_CONSOLE (&linflex_console)
|
||||
#else
|
||||
#define LINFLEX_CONSOLE NULL
|
||||
#endif
|
||||
|
||||
static struct uart_driver linflex_reg = {
|
||||
.owner = THIS_MODULE,
|
||||
.driver_name = DRIVER_NAME,
|
||||
.dev_name = DEV_NAME,
|
||||
.nr = ARRAY_SIZE(linflex_ports),
|
||||
.cons = LINFLEX_CONSOLE,
|
||||
};
|
||||
|
||||
static int linflex_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct uart_port *sport;
|
||||
struct resource *res;
|
||||
int ret;
|
||||
|
||||
sport = devm_kzalloc(&pdev->dev, sizeof(*sport), GFP_KERNEL);
|
||||
if (!sport)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = of_alias_get_id(np, "serial");
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "failed to get alias id, errno %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
if (ret >= UART_NR) {
|
||||
dev_err(&pdev->dev, "driver limited to %d serial ports\n",
|
||||
UART_NR);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
sport->line = ret;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res)
|
||||
return -ENODEV;
|
||||
|
||||
sport->mapbase = res->start;
|
||||
sport->membase = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(sport->membase))
|
||||
return PTR_ERR(sport->membase);
|
||||
|
||||
sport->dev = &pdev->dev;
|
||||
sport->type = PORT_LINFLEXUART;
|
||||
sport->iotype = UPIO_MEM;
|
||||
sport->irq = platform_get_irq(pdev, 0);
|
||||
sport->ops = &linflex_pops;
|
||||
sport->flags = UPF_BOOT_AUTOCONF;
|
||||
|
||||
linflex_ports[sport->line] = sport;
|
||||
|
||||
platform_set_drvdata(pdev, sport);
|
||||
|
||||
ret = uart_add_one_port(&linflex_reg, sport);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int linflex_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct uart_port *sport = platform_get_drvdata(pdev);
|
||||
|
||||
uart_remove_one_port(&linflex_reg, sport);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int linflex_suspend(struct device *dev)
|
||||
{
|
||||
struct uart_port *sport = dev_get_drvdata(dev);
|
||||
|
||||
uart_suspend_port(&linflex_reg, sport);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int linflex_resume(struct device *dev)
|
||||
{
|
||||
struct uart_port *sport = dev_get_drvdata(dev);
|
||||
|
||||
uart_resume_port(&linflex_reg, sport);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(linflex_pm_ops, linflex_suspend, linflex_resume);
|
||||
|
||||
static struct platform_driver linflex_driver = {
|
||||
.probe = linflex_probe,
|
||||
.remove = linflex_remove,
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.of_match_table = linflex_dt_ids,
|
||||
.pm = &linflex_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init linflex_serial_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = uart_register_driver(&linflex_reg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = platform_driver_register(&linflex_driver);
|
||||
if (ret)
|
||||
uart_unregister_driver(&linflex_reg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit linflex_serial_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&linflex_driver);
|
||||
uart_unregister_driver(&linflex_reg);
|
||||
}
|
||||
|
||||
module_init(linflex_serial_init);
|
||||
module_exit(linflex_serial_exit);
|
||||
|
||||
MODULE_DESCRIPTION("Freescale linflex serial port driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -214,6 +214,7 @@
|
||||
#define UARTFIFO_TXSIZE_OFF 4
|
||||
#define UARTFIFO_RXFE 0x00000008
|
||||
#define UARTFIFO_RXSIZE_OFF 0
|
||||
#define UARTFIFO_DEPTH(x) (0x1 << ((x) ? ((x) + 1) : 0))
|
||||
|
||||
#define UARTWATER_COUNT_MASK 0xff
|
||||
#define UARTWATER_TXCNT_OFF 8
|
||||
@ -451,6 +452,11 @@ static void lpuart_dma_tx(struct lpuart_port *sport)
|
||||
dma_async_issue_pending(sport->dma_tx_chan);
|
||||
}
|
||||
|
||||
static bool lpuart_stopped_or_empty(struct uart_port *port)
|
||||
{
|
||||
return uart_circ_empty(&port->state->xmit) || uart_tx_stopped(port);
|
||||
}
|
||||
|
||||
static void lpuart_dma_tx_complete(void *arg)
|
||||
{
|
||||
struct lpuart_port *sport = arg;
|
||||
@ -478,7 +484,7 @@ static void lpuart_dma_tx_complete(void *arg)
|
||||
|
||||
spin_lock_irqsave(&sport->port.lock, flags);
|
||||
|
||||
if (!uart_circ_empty(xmit) && !uart_tx_stopped(&sport->port))
|
||||
if (!lpuart_stopped_or_empty(&sport->port))
|
||||
lpuart_dma_tx(sport);
|
||||
|
||||
spin_unlock_irqrestore(&sport->port.lock, flags);
|
||||
@ -517,9 +523,16 @@ static int lpuart_dma_tx_request(struct uart_port *port)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool lpuart_is_32(struct lpuart_port *sport)
|
||||
{
|
||||
return sport->port.iotype == UPIO_MEM32 ||
|
||||
sport->port.iotype == UPIO_MEM32BE;
|
||||
}
|
||||
|
||||
static void lpuart_flush_buffer(struct uart_port *port)
|
||||
{
|
||||
struct lpuart_port *sport = container_of(port, struct lpuart_port, port);
|
||||
u32 val;
|
||||
|
||||
if (sport->lpuart_dma_tx_use) {
|
||||
if (sport->dma_tx_in_progress) {
|
||||
@ -529,6 +542,30 @@ static void lpuart_flush_buffer(struct uart_port *port)
|
||||
}
|
||||
dmaengine_terminate_all(sport->dma_tx_chan);
|
||||
}
|
||||
|
||||
if (lpuart_is_32(sport)) {
|
||||
val = lpuart32_read(&sport->port, UARTFIFO);
|
||||
val |= UARTFIFO_TXFLUSH | UARTFIFO_RXFLUSH;
|
||||
lpuart32_write(&sport->port, val, UARTFIFO);
|
||||
} else {
|
||||
val = readb(sport->port.membase + UARTPFIFO);
|
||||
val |= UARTCFIFO_TXFLUSH | UARTCFIFO_RXFLUSH;
|
||||
writeb(val, sport->port.membase + UARTCFIFO);
|
||||
}
|
||||
}
|
||||
|
||||
static void lpuart_wait_bit_set(struct uart_port *port, unsigned int offset,
|
||||
u8 bit)
|
||||
{
|
||||
while (!(readb(port->membase + offset) & bit))
|
||||
cpu_relax();
|
||||
}
|
||||
|
||||
static void lpuart32_wait_bit_set(struct uart_port *port, unsigned int offset,
|
||||
u32 bit)
|
||||
{
|
||||
while (!(lpuart32_read(port, offset) & bit))
|
||||
cpu_relax();
|
||||
}
|
||||
|
||||
#if defined(CONFIG_CONSOLE_POLL)
|
||||
@ -574,9 +611,7 @@ static int lpuart_poll_init(struct uart_port *port)
|
||||
static void lpuart_poll_put_char(struct uart_port *port, unsigned char c)
|
||||
{
|
||||
/* drain */
|
||||
while (!(readb(port->membase + UARTSR1) & UARTSR1_TDRE))
|
||||
barrier();
|
||||
|
||||
lpuart_wait_bit_set(port, UARTSR1, UARTSR1_TDRE);
|
||||
writeb(c, port->membase + UARTDR);
|
||||
}
|
||||
|
||||
@ -599,26 +634,26 @@ static int lpuart32_poll_init(struct uart_port *port)
|
||||
spin_lock_irqsave(&sport->port.lock, flags);
|
||||
|
||||
/* Disable Rx & Tx */
|
||||
writel(0, sport->port.membase + UARTCTRL);
|
||||
lpuart32_write(&sport->port, UARTCTRL, 0);
|
||||
|
||||
temp = readl(sport->port.membase + UARTFIFO);
|
||||
temp = lpuart32_read(&sport->port, UARTFIFO);
|
||||
|
||||
/* Enable Rx and Tx FIFO */
|
||||
writel(temp | UARTFIFO_RXFE | UARTFIFO_TXFE,
|
||||
sport->port.membase + UARTFIFO);
|
||||
lpuart32_write(&sport->port, UARTFIFO,
|
||||
temp | UARTFIFO_RXFE | UARTFIFO_TXFE);
|
||||
|
||||
/* flush Tx and Rx FIFO */
|
||||
writel(UARTFIFO_TXFLUSH | UARTFIFO_RXFLUSH,
|
||||
sport->port.membase + UARTFIFO);
|
||||
lpuart32_write(&sport->port, UARTFIFO,
|
||||
UARTFIFO_TXFLUSH | UARTFIFO_RXFLUSH);
|
||||
|
||||
/* explicitly clear RDRF */
|
||||
if (readl(sport->port.membase + UARTSTAT) & UARTSTAT_RDRF) {
|
||||
readl(sport->port.membase + UARTDATA);
|
||||
writel(UARTFIFO_RXUF, sport->port.membase + UARTFIFO);
|
||||
if (lpuart32_read(&sport->port, UARTSTAT) & UARTSTAT_RDRF) {
|
||||
lpuart32_read(&sport->port, UARTDATA);
|
||||
lpuart32_write(&sport->port, UARTFIFO, UARTFIFO_RXUF);
|
||||
}
|
||||
|
||||
/* Enable Rx and Tx */
|
||||
writel(UARTCTRL_RE | UARTCTRL_TE, sport->port.membase + UARTCTRL);
|
||||
lpuart32_write(&sport->port, UARTCTRL, UARTCTRL_RE | UARTCTRL_TE);
|
||||
spin_unlock_irqrestore(&sport->port.lock, flags);
|
||||
|
||||
return 0;
|
||||
@ -626,18 +661,16 @@ static int lpuart32_poll_init(struct uart_port *port)
|
||||
|
||||
static void lpuart32_poll_put_char(struct uart_port *port, unsigned char c)
|
||||
{
|
||||
while (!(readl(port->membase + UARTSTAT) & UARTSTAT_TDRE))
|
||||
barrier();
|
||||
|
||||
writel(c, port->membase + UARTDATA);
|
||||
lpuart32_wait_bit_set(port, UARTSTAT, UARTSTAT_TDRE);
|
||||
lpuart32_write(port, UARTDATA, c);
|
||||
}
|
||||
|
||||
static int lpuart32_poll_get_char(struct uart_port *port)
|
||||
{
|
||||
if (!(readl(port->membase + UARTSTAT) & UARTSTAT_RDRF))
|
||||
if (!(lpuart32_read(port, UARTSTAT) & UARTSTAT_RDRF))
|
||||
return NO_POLL_CHAR;
|
||||
|
||||
return readl(port->membase + UARTDATA);
|
||||
return lpuart32_read(port, UARTDATA);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -645,6 +678,18 @@ static inline void lpuart_transmit_buffer(struct lpuart_port *sport)
|
||||
{
|
||||
struct circ_buf *xmit = &sport->port.state->xmit;
|
||||
|
||||
if (sport->port.x_char) {
|
||||
writeb(sport->port.x_char, sport->port.membase + UARTDR);
|
||||
sport->port.icount.tx++;
|
||||
sport->port.x_char = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (lpuart_stopped_or_empty(&sport->port)) {
|
||||
lpuart_stop_tx(&sport->port);
|
||||
return;
|
||||
}
|
||||
|
||||
while (!uart_circ_empty(xmit) &&
|
||||
(readb(sport->port.membase + UARTTCFIFO) < sport->txfifo_size)) {
|
||||
writeb(xmit->buf[xmit->tail], sport->port.membase + UARTDR);
|
||||
@ -664,6 +709,18 @@ static inline void lpuart32_transmit_buffer(struct lpuart_port *sport)
|
||||
struct circ_buf *xmit = &sport->port.state->xmit;
|
||||
unsigned long txcnt;
|
||||
|
||||
if (sport->port.x_char) {
|
||||
lpuart32_write(&sport->port, sport->port.x_char, UARTDATA);
|
||||
sport->port.icount.tx++;
|
||||
sport->port.x_char = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (lpuart_stopped_or_empty(&sport->port)) {
|
||||
lpuart32_stop_tx(&sport->port);
|
||||
return;
|
||||
}
|
||||
|
||||
txcnt = lpuart32_read(&sport->port, UARTWATER);
|
||||
txcnt = txcnt >> UARTWATER_TXCNT_OFF;
|
||||
txcnt &= UARTWATER_COUNT_MASK;
|
||||
@ -687,14 +744,13 @@ static void lpuart_start_tx(struct uart_port *port)
|
||||
{
|
||||
struct lpuart_port *sport = container_of(port,
|
||||
struct lpuart_port, port);
|
||||
struct circ_buf *xmit = &sport->port.state->xmit;
|
||||
unsigned char temp;
|
||||
|
||||
temp = readb(port->membase + UARTCR2);
|
||||
writeb(temp | UARTCR2_TIE, port->membase + UARTCR2);
|
||||
|
||||
if (sport->lpuart_dma_tx_use) {
|
||||
if (!uart_circ_empty(xmit) && !uart_tx_stopped(port))
|
||||
if (!lpuart_stopped_or_empty(port))
|
||||
lpuart_dma_tx(sport);
|
||||
} else {
|
||||
if (readb(port->membase + UARTSR1) & UARTSR1_TDRE)
|
||||
@ -705,11 +761,10 @@ static void lpuart_start_tx(struct uart_port *port)
|
||||
static void lpuart32_start_tx(struct uart_port *port)
|
||||
{
|
||||
struct lpuart_port *sport = container_of(port, struct lpuart_port, port);
|
||||
struct circ_buf *xmit = &sport->port.state->xmit;
|
||||
unsigned long temp;
|
||||
|
||||
if (sport->lpuart_dma_tx_use) {
|
||||
if (!uart_circ_empty(xmit) && !uart_tx_stopped(port))
|
||||
if (!lpuart_stopped_or_empty(port))
|
||||
lpuart_dma_tx(sport);
|
||||
} else {
|
||||
temp = lpuart32_read(port, UARTCTRL);
|
||||
@ -753,52 +808,18 @@ static unsigned int lpuart32_tx_empty(struct uart_port *port)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool lpuart_is_32(struct lpuart_port *sport)
|
||||
static void lpuart_txint(struct lpuart_port *sport)
|
||||
{
|
||||
return sport->port.iotype == UPIO_MEM32 ||
|
||||
sport->port.iotype == UPIO_MEM32BE;
|
||||
}
|
||||
|
||||
static irqreturn_t lpuart_txint(int irq, void *dev_id)
|
||||
{
|
||||
struct lpuart_port *sport = dev_id;
|
||||
struct circ_buf *xmit = &sport->port.state->xmit;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&sport->port.lock, flags);
|
||||
if (sport->port.x_char) {
|
||||
if (lpuart_is_32(sport))
|
||||
lpuart32_write(&sport->port, sport->port.x_char, UARTDATA);
|
||||
else
|
||||
writeb(sport->port.x_char, sport->port.membase + UARTDR);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (uart_circ_empty(xmit) || uart_tx_stopped(&sport->port)) {
|
||||
if (lpuart_is_32(sport))
|
||||
lpuart32_stop_tx(&sport->port);
|
||||
else
|
||||
lpuart_stop_tx(&sport->port);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (lpuart_is_32(sport))
|
||||
lpuart32_transmit_buffer(sport);
|
||||
else
|
||||
lpuart_transmit_buffer(sport);
|
||||
|
||||
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
|
||||
uart_write_wakeup(&sport->port);
|
||||
|
||||
out:
|
||||
lpuart_transmit_buffer(sport);
|
||||
spin_unlock_irqrestore(&sport->port.lock, flags);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t lpuart_rxint(int irq, void *dev_id)
|
||||
static void lpuart_rxint(struct lpuart_port *sport)
|
||||
{
|
||||
struct lpuart_port *sport = dev_id;
|
||||
unsigned int flg, ignored = 0;
|
||||
unsigned int flg, ignored = 0, overrun = 0;
|
||||
struct tty_port *port = &sport->port.state->port;
|
||||
unsigned long flags;
|
||||
unsigned char rx, sr;
|
||||
@ -825,7 +846,7 @@ static irqreturn_t lpuart_rxint(int irq, void *dev_id)
|
||||
sport->port.icount.frame++;
|
||||
|
||||
if (sr & UARTSR1_OR)
|
||||
sport->port.icount.overrun++;
|
||||
overrun++;
|
||||
|
||||
if (sr & sport->port.ignore_status_mask) {
|
||||
if (++ignored > 100)
|
||||
@ -852,15 +873,33 @@ static irqreturn_t lpuart_rxint(int irq, void *dev_id)
|
||||
}
|
||||
|
||||
out:
|
||||
if (overrun) {
|
||||
sport->port.icount.overrun += overrun;
|
||||
|
||||
/*
|
||||
* Overruns cause FIFO pointers to become missaligned.
|
||||
* Flushing the receive FIFO reinitializes the pointers.
|
||||
*/
|
||||
writeb(UARTCFIFO_RXFLUSH, sport->port.membase + UARTCFIFO);
|
||||
writeb(UARTSFIFO_RXOF, sport->port.membase + UARTSFIFO);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&sport->port.lock, flags);
|
||||
|
||||
tty_flip_buffer_push(port);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t lpuart32_rxint(int irq, void *dev_id)
|
||||
static void lpuart32_txint(struct lpuart_port *sport)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&sport->port.lock, flags);
|
||||
lpuart32_transmit_buffer(sport);
|
||||
spin_unlock_irqrestore(&sport->port.lock, flags);
|
||||
}
|
||||
|
||||
static void lpuart32_rxint(struct lpuart_port *sport)
|
||||
{
|
||||
struct lpuart_port *sport = dev_id;
|
||||
unsigned int flg, ignored = 0;
|
||||
struct tty_port *port = &sport->port.state->port;
|
||||
unsigned long flags;
|
||||
@ -919,7 +958,6 @@ out:
|
||||
spin_unlock_irqrestore(&sport->port.lock, flags);
|
||||
|
||||
tty_flip_buffer_push(port);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t lpuart_int(int irq, void *dev_id)
|
||||
@ -929,11 +967,11 @@ static irqreturn_t lpuart_int(int irq, void *dev_id)
|
||||
|
||||
sts = readb(sport->port.membase + UARTSR1);
|
||||
|
||||
if (sts & UARTSR1_RDRF)
|
||||
lpuart_rxint(irq, dev_id);
|
||||
if (sts & UARTSR1_RDRF && !sport->lpuart_dma_rx_use)
|
||||
lpuart_rxint(sport);
|
||||
|
||||
if (sts & UARTSR1_TDRE)
|
||||
lpuart_txint(irq, dev_id);
|
||||
if (sts & UARTSR1_TDRE && !sport->lpuart_dma_tx_use)
|
||||
lpuart_txint(sport);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
@ -948,10 +986,10 @@ static irqreturn_t lpuart32_int(int irq, void *dev_id)
|
||||
rxcount = rxcount >> UARTWATER_RXCNT_OFF;
|
||||
|
||||
if ((sts & UARTSTAT_RDRF || rxcount > 0) && !sport->lpuart_dma_rx_use)
|
||||
lpuart32_rxint(irq, dev_id);
|
||||
lpuart32_rxint(sport);
|
||||
|
||||
if ((sts & UARTSTAT_TDRE) && !sport->lpuart_dma_tx_use)
|
||||
lpuart_txint(irq, dev_id);
|
||||
lpuart32_txint(sport);
|
||||
|
||||
lpuart32_write(&sport->port, sts, UARTSTAT);
|
||||
return IRQ_HANDLED;
|
||||
@ -982,6 +1020,13 @@ static void lpuart_copy_rx_to_tty(struct lpuart_port *sport)
|
||||
unsigned char sr = readb(sport->port.membase + UARTSR1);
|
||||
|
||||
if (sr & (UARTSR1_PE | UARTSR1_FE)) {
|
||||
unsigned char cr2;
|
||||
|
||||
/* Disable receiver during this operation... */
|
||||
cr2 = readb(sport->port.membase + UARTCR2);
|
||||
cr2 &= ~UARTCR2_RE;
|
||||
writeb(cr2, sport->port.membase + UARTCR2);
|
||||
|
||||
/* Read DR to clear the error flags */
|
||||
readb(sport->port.membase + UARTDR);
|
||||
|
||||
@ -989,6 +1034,25 @@ static void lpuart_copy_rx_to_tty(struct lpuart_port *sport)
|
||||
sport->port.icount.parity++;
|
||||
else if (sr & UARTSR1_FE)
|
||||
sport->port.icount.frame++;
|
||||
/*
|
||||
* At this point parity/framing error is
|
||||
* cleared However, since the DMA already read
|
||||
* the data register and we had to read it
|
||||
* again after reading the status register to
|
||||
* properly clear the flags, the FIFO actually
|
||||
* underflowed... This requires a clearing of
|
||||
* the FIFO...
|
||||
*/
|
||||
if (readb(sport->port.membase + UARTSFIFO) &
|
||||
UARTSFIFO_RXUF) {
|
||||
writeb(UARTSFIFO_RXUF,
|
||||
sport->port.membase + UARTSFIFO);
|
||||
writeb(UARTCFIFO_RXFLUSH,
|
||||
sport->port.membase + UARTCFIFO);
|
||||
}
|
||||
|
||||
cr2 |= UARTCR2_RE;
|
||||
writeb(cr2, sport->port.membase + UARTCR2);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1097,12 +1161,11 @@ static inline int lpuart_start_rx_dma(struct lpuart_port *sport)
|
||||
if (sport->rx_dma_rng_buf_len < 16)
|
||||
sport->rx_dma_rng_buf_len = 16;
|
||||
|
||||
ring->buf = kmalloc(sport->rx_dma_rng_buf_len, GFP_ATOMIC);
|
||||
ring->buf = kzalloc(sport->rx_dma_rng_buf_len, GFP_ATOMIC);
|
||||
if (!ring->buf)
|
||||
return -ENOMEM;
|
||||
|
||||
sg_init_one(&sport->rx_sgl, ring->buf, sport->rx_dma_rng_buf_len);
|
||||
sg_set_buf(&sport->rx_sgl, ring->buf, sport->rx_dma_rng_buf_len);
|
||||
nent = dma_map_sg(sport->port.dev, &sport->rx_sgl, 1, DMA_FROM_DEVICE);
|
||||
|
||||
if (!nent) {
|
||||
@ -1340,6 +1403,17 @@ static void lpuart_setup_watermark(struct lpuart_port *sport)
|
||||
writeb(cr2_saved, sport->port.membase + UARTCR2);
|
||||
}
|
||||
|
||||
static void lpuart_setup_watermark_enable(struct lpuart_port *sport)
|
||||
{
|
||||
unsigned char cr2;
|
||||
|
||||
lpuart_setup_watermark(sport);
|
||||
|
||||
cr2 = readb(sport->port.membase + UARTCR2);
|
||||
cr2 |= UARTCR2_RIE | UARTCR2_RE | UARTCR2_TE;
|
||||
writeb(cr2, sport->port.membase + UARTCR2);
|
||||
}
|
||||
|
||||
static void lpuart32_setup_watermark(struct lpuart_port *sport)
|
||||
{
|
||||
unsigned long val, ctrl;
|
||||
@ -1365,89 +1439,46 @@ static void lpuart32_setup_watermark(struct lpuart_port *sport)
|
||||
lpuart32_write(&sport->port, ctrl_saved, UARTCTRL);
|
||||
}
|
||||
|
||||
static void rx_dma_timer_init(struct lpuart_port *sport)
|
||||
static void lpuart32_setup_watermark_enable(struct lpuart_port *sport)
|
||||
{
|
||||
timer_setup(&sport->lpuart_timer, lpuart_timer_func, 0);
|
||||
sport->lpuart_timer.expires = jiffies + sport->dma_rx_timeout;
|
||||
add_timer(&sport->lpuart_timer);
|
||||
}
|
||||
|
||||
static int lpuart_startup(struct uart_port *port)
|
||||
{
|
||||
struct lpuart_port *sport = container_of(port, struct lpuart_port, port);
|
||||
unsigned long flags;
|
||||
unsigned char temp;
|
||||
|
||||
/* determine FIFO size and enable FIFO mode */
|
||||
temp = readb(sport->port.membase + UARTPFIFO);
|
||||
|
||||
sport->txfifo_size = 0x1 << (((temp >> UARTPFIFO_TXSIZE_OFF) &
|
||||
UARTPFIFO_FIFOSIZE_MASK) + 1);
|
||||
|
||||
sport->port.fifosize = sport->txfifo_size;
|
||||
|
||||
sport->rxfifo_size = 0x1 << (((temp >> UARTPFIFO_RXSIZE_OFF) &
|
||||
UARTPFIFO_FIFOSIZE_MASK) + 1);
|
||||
|
||||
spin_lock_irqsave(&sport->port.lock, flags);
|
||||
|
||||
lpuart_setup_watermark(sport);
|
||||
|
||||
temp = readb(sport->port.membase + UARTCR2);
|
||||
temp |= (UARTCR2_RIE | UARTCR2_TIE | UARTCR2_RE | UARTCR2_TE);
|
||||
writeb(temp, sport->port.membase + UARTCR2);
|
||||
|
||||
if (sport->dma_rx_chan && !lpuart_start_rx_dma(sport)) {
|
||||
/* set Rx DMA timeout */
|
||||
sport->dma_rx_timeout = msecs_to_jiffies(DMA_RX_TIMEOUT);
|
||||
if (!sport->dma_rx_timeout)
|
||||
sport->dma_rx_timeout = 1;
|
||||
|
||||
sport->lpuart_dma_rx_use = true;
|
||||
rx_dma_timer_init(sport);
|
||||
} else {
|
||||
sport->lpuart_dma_rx_use = false;
|
||||
}
|
||||
|
||||
if (sport->dma_tx_chan && !lpuart_dma_tx_request(port)) {
|
||||
init_waitqueue_head(&sport->dma_wait);
|
||||
sport->lpuart_dma_tx_use = true;
|
||||
temp = readb(port->membase + UARTCR5);
|
||||
writeb(temp | UARTCR5_TDMAS, port->membase + UARTCR5);
|
||||
} else {
|
||||
sport->lpuart_dma_tx_use = false;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&sport->port.lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lpuart32_startup(struct uart_port *port)
|
||||
{
|
||||
struct lpuart_port *sport = container_of(port, struct lpuart_port, port);
|
||||
unsigned long flags;
|
||||
unsigned long temp;
|
||||
|
||||
/* determine FIFO size */
|
||||
temp = lpuart32_read(&sport->port, UARTFIFO);
|
||||
|
||||
sport->txfifo_size = 0x1 << (((temp >> UARTFIFO_TXSIZE_OFF) &
|
||||
UARTFIFO_FIFOSIZE_MASK) - 1);
|
||||
|
||||
sport->port.fifosize = sport->txfifo_size;
|
||||
|
||||
sport->rxfifo_size = 0x1 << (((temp >> UARTFIFO_RXSIZE_OFF) &
|
||||
UARTFIFO_FIFOSIZE_MASK) - 1);
|
||||
|
||||
spin_lock_irqsave(&sport->port.lock, flags);
|
||||
u32 temp;
|
||||
|
||||
lpuart32_setup_watermark(sport);
|
||||
|
||||
temp = lpuart32_read(&sport->port, UARTCTRL);
|
||||
temp |= UARTCTRL_RE | UARTCTRL_TE | UARTCTRL_ILIE;
|
||||
lpuart32_write(&sport->port, temp, UARTCTRL);
|
||||
}
|
||||
|
||||
static void rx_dma_timer_init(struct lpuart_port *sport)
|
||||
{
|
||||
timer_setup(&sport->lpuart_timer, lpuart_timer_func, 0);
|
||||
sport->lpuart_timer.expires = jiffies + sport->dma_rx_timeout;
|
||||
add_timer(&sport->lpuart_timer);
|
||||
}
|
||||
|
||||
static void lpuart_tx_dma_startup(struct lpuart_port *sport)
|
||||
{
|
||||
u32 uartbaud;
|
||||
|
||||
if (sport->dma_tx_chan && !lpuart_dma_tx_request(&sport->port)) {
|
||||
init_waitqueue_head(&sport->dma_wait);
|
||||
sport->lpuart_dma_tx_use = true;
|
||||
if (lpuart_is_32(sport)) {
|
||||
uartbaud = lpuart32_read(&sport->port, UARTBAUD);
|
||||
lpuart32_write(&sport->port,
|
||||
uartbaud | UARTBAUD_TDMAE, UARTBAUD);
|
||||
} else {
|
||||
writeb(readb(sport->port.membase + UARTCR5) |
|
||||
UARTCR5_TDMAS, sport->port.membase + UARTCR5);
|
||||
}
|
||||
} else {
|
||||
sport->lpuart_dma_tx_use = false;
|
||||
}
|
||||
}
|
||||
|
||||
static void lpuart_rx_dma_startup(struct lpuart_port *sport)
|
||||
{
|
||||
if (sport->dma_rx_chan && !lpuart_start_rx_dma(sport)) {
|
||||
/* set Rx DMA timeout */
|
||||
sport->dma_rx_timeout = msecs_to_jiffies(DMA_RX_TIMEOUT);
|
||||
@ -1459,15 +1490,39 @@ static int lpuart32_startup(struct uart_port *port)
|
||||
} else {
|
||||
sport->lpuart_dma_rx_use = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (sport->dma_tx_chan && !lpuart_dma_tx_request(port)) {
|
||||
init_waitqueue_head(&sport->dma_wait);
|
||||
sport->lpuart_dma_tx_use = true;
|
||||
temp = lpuart32_read(&sport->port, UARTBAUD);
|
||||
lpuart32_write(&sport->port, temp | UARTBAUD_TDMAE, UARTBAUD);
|
||||
} else {
|
||||
sport->lpuart_dma_tx_use = false;
|
||||
}
|
||||
static int lpuart_startup(struct uart_port *port)
|
||||
{
|
||||
struct lpuart_port *sport = container_of(port, struct lpuart_port, port);
|
||||
unsigned long flags;
|
||||
unsigned char temp;
|
||||
|
||||
/* determine FIFO size and enable FIFO mode */
|
||||
temp = readb(sport->port.membase + UARTPFIFO);
|
||||
|
||||
sport->txfifo_size = UARTFIFO_DEPTH((temp >> UARTPFIFO_TXSIZE_OFF) &
|
||||
UARTPFIFO_FIFOSIZE_MASK);
|
||||
sport->port.fifosize = sport->txfifo_size;
|
||||
|
||||
sport->rxfifo_size = UARTFIFO_DEPTH((temp >> UARTPFIFO_RXSIZE_OFF) &
|
||||
UARTPFIFO_FIFOSIZE_MASK);
|
||||
|
||||
spin_lock_irqsave(&sport->port.lock, flags);
|
||||
|
||||
lpuart_setup_watermark_enable(sport);
|
||||
|
||||
lpuart_rx_dma_startup(sport);
|
||||
lpuart_tx_dma_startup(sport);
|
||||
|
||||
spin_unlock_irqrestore(&sport->port.lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void lpuart32_configure(struct lpuart_port *sport)
|
||||
{
|
||||
unsigned long temp;
|
||||
|
||||
if (sport->lpuart_dma_rx_use) {
|
||||
/* RXWATER must be 0 */
|
||||
@ -1481,11 +1536,54 @@ static int lpuart32_startup(struct uart_port *port)
|
||||
if (!sport->lpuart_dma_tx_use)
|
||||
temp |= UARTCTRL_TIE;
|
||||
lpuart32_write(&sport->port, temp, UARTCTRL);
|
||||
}
|
||||
|
||||
static int lpuart32_startup(struct uart_port *port)
|
||||
{
|
||||
struct lpuart_port *sport = container_of(port, struct lpuart_port, port);
|
||||
unsigned long flags;
|
||||
unsigned long temp;
|
||||
|
||||
/* determine FIFO size */
|
||||
temp = lpuart32_read(&sport->port, UARTFIFO);
|
||||
|
||||
sport->txfifo_size = UARTFIFO_DEPTH((temp >> UARTFIFO_TXSIZE_OFF) &
|
||||
UARTFIFO_FIFOSIZE_MASK);
|
||||
sport->port.fifosize = sport->txfifo_size;
|
||||
|
||||
sport->rxfifo_size = UARTFIFO_DEPTH((temp >> UARTFIFO_RXSIZE_OFF) &
|
||||
UARTFIFO_FIFOSIZE_MASK);
|
||||
|
||||
spin_lock_irqsave(&sport->port.lock, flags);
|
||||
|
||||
lpuart32_setup_watermark_enable(sport);
|
||||
|
||||
|
||||
lpuart_rx_dma_startup(sport);
|
||||
lpuart_tx_dma_startup(sport);
|
||||
|
||||
lpuart32_configure(sport);
|
||||
|
||||
spin_unlock_irqrestore(&sport->port.lock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void lpuart_dma_shutdown(struct lpuart_port *sport)
|
||||
{
|
||||
if (sport->lpuart_dma_rx_use) {
|
||||
del_timer_sync(&sport->lpuart_timer);
|
||||
lpuart_dma_rx_free(&sport->port);
|
||||
}
|
||||
|
||||
if (sport->lpuart_dma_tx_use) {
|
||||
if (wait_event_interruptible(sport->dma_wait,
|
||||
!sport->dma_tx_in_progress) != false) {
|
||||
sport->dma_tx_in_progress = false;
|
||||
dmaengine_terminate_all(sport->dma_tx_chan);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void lpuart_shutdown(struct uart_port *port)
|
||||
{
|
||||
struct lpuart_port *sport = container_of(port, struct lpuart_port, port);
|
||||
@ -1502,20 +1600,7 @@ static void lpuart_shutdown(struct uart_port *port)
|
||||
|
||||
spin_unlock_irqrestore(&port->lock, flags);
|
||||
|
||||
if (sport->lpuart_dma_rx_use) {
|
||||
del_timer_sync(&sport->lpuart_timer);
|
||||
lpuart_dma_rx_free(&sport->port);
|
||||
}
|
||||
|
||||
if (sport->lpuart_dma_tx_use) {
|
||||
if (wait_event_interruptible(sport->dma_wait,
|
||||
!sport->dma_tx_in_progress) != false) {
|
||||
sport->dma_tx_in_progress = false;
|
||||
dmaengine_terminate_all(sport->dma_tx_chan);
|
||||
}
|
||||
|
||||
lpuart_stop_tx(port);
|
||||
}
|
||||
lpuart_dma_shutdown(sport);
|
||||
}
|
||||
|
||||
static void lpuart32_shutdown(struct uart_port *port)
|
||||
@ -1535,20 +1620,7 @@ static void lpuart32_shutdown(struct uart_port *port)
|
||||
|
||||
spin_unlock_irqrestore(&port->lock, flags);
|
||||
|
||||
if (sport->lpuart_dma_rx_use) {
|
||||
del_timer_sync(&sport->lpuart_timer);
|
||||
lpuart_dma_rx_free(&sport->port);
|
||||
}
|
||||
|
||||
if (sport->lpuart_dma_tx_use) {
|
||||
if (wait_event_interruptible(sport->dma_wait,
|
||||
!sport->dma_tx_in_progress)) {
|
||||
sport->dma_tx_in_progress = false;
|
||||
dmaengine_terminate_all(sport->dma_tx_chan);
|
||||
}
|
||||
|
||||
lpuart32_stop_tx(port);
|
||||
}
|
||||
lpuart_dma_shutdown(sport);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -1602,21 +1674,18 @@ lpuart_set_termios(struct uart_port *port, struct ktermios *termios,
|
||||
if (sport->port.rs485.flags & SER_RS485_ENABLED)
|
||||
termios->c_cflag &= ~CRTSCTS;
|
||||
|
||||
if (termios->c_cflag & CRTSCTS) {
|
||||
modem |= (UARTMODEM_RXRTSE | UARTMODEM_TXCTSE);
|
||||
} else {
|
||||
termios->c_cflag &= ~CRTSCTS;
|
||||
if (termios->c_cflag & CRTSCTS)
|
||||
modem |= UARTMODEM_RXRTSE | UARTMODEM_TXCTSE;
|
||||
else
|
||||
modem &= ~(UARTMODEM_RXRTSE | UARTMODEM_TXCTSE);
|
||||
}
|
||||
|
||||
if (termios->c_cflag & CSTOPB)
|
||||
termios->c_cflag &= ~CSTOPB;
|
||||
termios->c_cflag &= ~CSTOPB;
|
||||
|
||||
/* parity must be enabled when CS7 to match 8-bits format */
|
||||
if ((termios->c_cflag & CSIZE) == CS7)
|
||||
termios->c_cflag |= PARENB;
|
||||
|
||||
if ((termios->c_cflag & PARENB)) {
|
||||
if (termios->c_cflag & PARENB) {
|
||||
if (termios->c_cflag & CMSPAR) {
|
||||
cr1 &= ~UARTCR1_PE;
|
||||
if (termios->c_cflag & PARODD)
|
||||
@ -1655,7 +1724,7 @@ lpuart_set_termios(struct uart_port *port, struct ktermios *termios,
|
||||
|
||||
sport->port.read_status_mask = 0;
|
||||
if (termios->c_iflag & INPCK)
|
||||
sport->port.read_status_mask |= (UARTSR1_FE | UARTSR1_PE);
|
||||
sport->port.read_status_mask |= UARTSR1_FE | UARTSR1_PE;
|
||||
if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK))
|
||||
sport->port.read_status_mask |= UARTSR1_FE;
|
||||
|
||||
@ -1677,8 +1746,7 @@ lpuart_set_termios(struct uart_port *port, struct ktermios *termios,
|
||||
uart_update_timeout(port, termios->c_cflag, baud);
|
||||
|
||||
/* wait transmit engin complete */
|
||||
while (!(readb(sport->port.membase + UARTSR1) & UARTSR1_TC))
|
||||
barrier();
|
||||
lpuart_wait_bit_set(&sport->port, UARTSR1, UARTSR1_TC);
|
||||
|
||||
/* disable transmit and receive */
|
||||
writeb(old_cr2 & ~(UARTCR2_TE | UARTCR2_RE),
|
||||
@ -1769,7 +1837,7 @@ lpuart32_serial_setbrg(struct lpuart_port *sport, unsigned int baudrate)
|
||||
tmp |= UARTBAUD_BOTHEDGE;
|
||||
|
||||
tmp &= ~(UARTBAUD_OSR_MASK << UARTBAUD_OSR_SHIFT);
|
||||
tmp |= (((osr-1) & UARTBAUD_OSR_MASK) << UARTBAUD_OSR_SHIFT);
|
||||
tmp |= ((osr-1) & UARTBAUD_OSR_MASK) << UARTBAUD_OSR_SHIFT;
|
||||
|
||||
tmp &= ~UARTBAUD_SBR_MASK;
|
||||
tmp |= sbr & UARTBAUD_SBR_MASK;
|
||||
@ -1822,7 +1890,7 @@ lpuart32_set_termios(struct uart_port *port, struct ktermios *termios,
|
||||
}
|
||||
|
||||
if (termios->c_cflag & CRTSCTS) {
|
||||
modem |= (UARTMODEM_RXRTSE | UARTMODEM_TXCTSE);
|
||||
modem |= UARTMODEM_RXRTSE | UARTMODEM_TXCTSE;
|
||||
} else {
|
||||
termios->c_cflag &= ~CRTSCTS;
|
||||
modem &= ~(UARTMODEM_RXRTSE | UARTMODEM_TXCTSE);
|
||||
@ -1871,7 +1939,7 @@ lpuart32_set_termios(struct uart_port *port, struct ktermios *termios,
|
||||
|
||||
sport->port.read_status_mask = 0;
|
||||
if (termios->c_iflag & INPCK)
|
||||
sport->port.read_status_mask |= (UARTSTAT_FE | UARTSTAT_PE);
|
||||
sport->port.read_status_mask |= UARTSTAT_FE | UARTSTAT_PE;
|
||||
if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK))
|
||||
sport->port.read_status_mask |= UARTSTAT_FE;
|
||||
|
||||
@ -1893,8 +1961,7 @@ lpuart32_set_termios(struct uart_port *port, struct ktermios *termios,
|
||||
uart_update_timeout(port, termios->c_cflag, baud);
|
||||
|
||||
/* wait transmit engin complete */
|
||||
while (!(lpuart32_read(&sport->port, UARTSTAT) & UARTSTAT_TC))
|
||||
barrier();
|
||||
lpuart32_wait_bit_set(&sport->port, UARTSTAT, UARTSTAT_TC);
|
||||
|
||||
/* disable transmit and receive */
|
||||
lpuart32_write(&sport->port, old_ctrl & ~(UARTCTRL_TE | UARTCTRL_RE),
|
||||
@ -2009,17 +2076,13 @@ static struct lpuart_port *lpuart_ports[UART_NR];
|
||||
#ifdef CONFIG_SERIAL_FSL_LPUART_CONSOLE
|
||||
static void lpuart_console_putchar(struct uart_port *port, int ch)
|
||||
{
|
||||
while (!(readb(port->membase + UARTSR1) & UARTSR1_TDRE))
|
||||
barrier();
|
||||
|
||||
lpuart_wait_bit_set(port, UARTSR1, UARTSR1_TDRE);
|
||||
writeb(ch, port->membase + UARTDR);
|
||||
}
|
||||
|
||||
static void lpuart32_console_putchar(struct uart_port *port, int ch)
|
||||
{
|
||||
while (!(lpuart32_read(port, UARTSTAT) & UARTSTAT_TDRE))
|
||||
barrier();
|
||||
|
||||
lpuart32_wait_bit_set(port, UARTSTAT, UARTSTAT_TDRE);
|
||||
lpuart32_write(port, ch, UARTDATA);
|
||||
}
|
||||
|
||||
@ -2038,15 +2101,14 @@ lpuart_console_write(struct console *co, const char *s, unsigned int count)
|
||||
|
||||
/* first save CR2 and then disable interrupts */
|
||||
cr2 = old_cr2 = readb(sport->port.membase + UARTCR2);
|
||||
cr2 |= (UARTCR2_TE | UARTCR2_RE);
|
||||
cr2 |= UARTCR2_TE | UARTCR2_RE;
|
||||
cr2 &= ~(UARTCR2_TIE | UARTCR2_TCIE | UARTCR2_RIE);
|
||||
writeb(cr2, sport->port.membase + UARTCR2);
|
||||
|
||||
uart_console_write(&sport->port, s, count, lpuart_console_putchar);
|
||||
|
||||
/* wait for transmitter finish complete and restore CR2 */
|
||||
while (!(readb(sport->port.membase + UARTSR1) & UARTSR1_TC))
|
||||
barrier();
|
||||
lpuart_wait_bit_set(&sport->port, UARTSR1, UARTSR1_TC);
|
||||
|
||||
writeb(old_cr2, sport->port.membase + UARTCR2);
|
||||
|
||||
@ -2069,15 +2131,14 @@ lpuart32_console_write(struct console *co, const char *s, unsigned int count)
|
||||
|
||||
/* first save CR2 and then disable interrupts */
|
||||
cr = old_cr = lpuart32_read(&sport->port, UARTCTRL);
|
||||
cr |= (UARTCTRL_TE | UARTCTRL_RE);
|
||||
cr |= UARTCTRL_TE | UARTCTRL_RE;
|
||||
cr &= ~(UARTCTRL_TIE | UARTCTRL_TCIE | UARTCTRL_RIE);
|
||||
lpuart32_write(&sport->port, cr, UARTCTRL);
|
||||
|
||||
uart_console_write(&sport->port, s, count, lpuart32_console_putchar);
|
||||
|
||||
/* wait for transmitter finish complete and restore CR2 */
|
||||
while (!(lpuart32_read(&sport->port, UARTSTAT) & UARTSTAT_TC))
|
||||
barrier();
|
||||
lpuart32_wait_bit_set(&sport->port, UARTSTAT, UARTSTAT_TC);
|
||||
|
||||
lpuart32_write(&sport->port, old_cr, UARTCTRL);
|
||||
|
||||
@ -2288,6 +2349,7 @@ static int __init lpuart32_imx_early_console_setup(struct earlycon_device *devic
|
||||
OF_EARLYCON_DECLARE(lpuart, "fsl,vf610-lpuart", lpuart_early_console_setup);
|
||||
OF_EARLYCON_DECLARE(lpuart32, "fsl,ls1021a-lpuart", lpuart32_early_console_setup);
|
||||
OF_EARLYCON_DECLARE(lpuart32, "fsl,imx7ulp-lpuart", lpuart32_imx_early_console_setup);
|
||||
OF_EARLYCON_DECLARE(lpuart32, "fsl,imx8qxp-lpuart", lpuart32_imx_early_console_setup);
|
||||
EARLYCON_DECLARE(lpuart, lpuart_early_console_setup);
|
||||
EARLYCON_DECLARE(lpuart32, lpuart32_early_console_setup);
|
||||
|
||||
@ -2320,8 +2382,6 @@ static int lpuart_probe(struct platform_device *pdev)
|
||||
if (!sport)
|
||||
return -ENOMEM;
|
||||
|
||||
pdev->dev.coherent_dma_mask = 0;
|
||||
|
||||
ret = of_alias_get_id(np, "serial");
|
||||
if (ret < 0) {
|
||||
ret = ida_simple_get(&fsl_lpuart_ida, 0, UART_NR, GFP_KERNEL);
|
||||
@ -2346,10 +2406,8 @@ static int lpuart_probe(struct platform_device *pdev)
|
||||
sport->port.type = PORT_LPUART;
|
||||
sport->devtype = sdata->devtype;
|
||||
ret = platform_get_irq(pdev, 0);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "cannot obtain irq\n");
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
sport->port.irq = ret;
|
||||
sport->port.iotype = sdata->iotype;
|
||||
if (lpuart_is_32(sport))
|
||||
@ -2514,22 +2572,14 @@ static int lpuart_resume(struct device *dev)
|
||||
{
|
||||
struct lpuart_port *sport = dev_get_drvdata(dev);
|
||||
bool irq_wake = irqd_is_wakeup_set(irq_get_irq_data(sport->port.irq));
|
||||
unsigned long temp;
|
||||
|
||||
if (sport->port.suspended && !irq_wake)
|
||||
lpuart_enable_clks(sport);
|
||||
|
||||
if (lpuart_is_32(sport)) {
|
||||
lpuart32_setup_watermark(sport);
|
||||
temp = lpuart32_read(&sport->port, UARTCTRL);
|
||||
temp |= UARTCTRL_RE | UARTCTRL_TE | UARTCTRL_ILIE;
|
||||
lpuart32_write(&sport->port, temp, UARTCTRL);
|
||||
} else {
|
||||
lpuart_setup_watermark(sport);
|
||||
temp = readb(sport->port.membase + UARTCR2);
|
||||
temp |= (UARTCR2_RIE | UARTCR2_TIE | UARTCR2_RE | UARTCR2_TE);
|
||||
writeb(temp, sport->port.membase + UARTCR2);
|
||||
}
|
||||
if (lpuart_is_32(sport))
|
||||
lpuart32_setup_watermark_enable(sport);
|
||||
else
|
||||
lpuart_setup_watermark_enable(sport);
|
||||
|
||||
if (sport->lpuart_dma_rx_use) {
|
||||
if (irq_wake) {
|
||||
@ -2540,36 +2590,10 @@ static int lpuart_resume(struct device *dev)
|
||||
}
|
||||
}
|
||||
|
||||
if (sport->dma_tx_chan && !lpuart_dma_tx_request(&sport->port)) {
|
||||
init_waitqueue_head(&sport->dma_wait);
|
||||
sport->lpuart_dma_tx_use = true;
|
||||
if (lpuart_is_32(sport)) {
|
||||
temp = lpuart32_read(&sport->port, UARTBAUD);
|
||||
lpuart32_write(&sport->port,
|
||||
temp | UARTBAUD_TDMAE, UARTBAUD);
|
||||
} else {
|
||||
writeb(readb(sport->port.membase + UARTCR5) |
|
||||
UARTCR5_TDMAS, sport->port.membase + UARTCR5);
|
||||
}
|
||||
} else {
|
||||
sport->lpuart_dma_tx_use = false;
|
||||
}
|
||||
lpuart_tx_dma_startup(sport);
|
||||
|
||||
if (lpuart_is_32(sport)) {
|
||||
if (sport->lpuart_dma_rx_use) {
|
||||
/* RXWATER must be 0 */
|
||||
temp = lpuart32_read(&sport->port, UARTWATER);
|
||||
temp &= ~(UARTWATER_WATER_MASK <<
|
||||
UARTWATER_RXWATER_OFF);
|
||||
lpuart32_write(&sport->port, temp, UARTWATER);
|
||||
}
|
||||
temp = lpuart32_read(&sport->port, UARTCTRL);
|
||||
if (!sport->lpuart_dma_rx_use)
|
||||
temp |= UARTCTRL_RIE;
|
||||
if (!sport->lpuart_dma_tx_use)
|
||||
temp |= UARTCTRL_TIE;
|
||||
lpuart32_write(&sport->port, temp, UARTCTRL);
|
||||
}
|
||||
if (lpuart_is_32(sport))
|
||||
lpuart32_configure(sport);
|
||||
|
||||
uart_resume_port(&lpuart_reg, &sport->port);
|
||||
|
||||
|
@ -207,8 +207,6 @@ static int get_port_memory(struct icom_port *icom_port)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
memset(icom_port->statStg, 0, 4096);
|
||||
|
||||
/* FODs: Frame Out Descriptor Queue, this is a FIFO queue that
|
||||
indicates that frames are to be transmitted
|
||||
*/
|
||||
|
@ -402,12 +402,6 @@ static void imx_uart_rts_inactive(struct imx_port *sport, u32 *ucr2)
|
||||
mctrl_gpio_set(sport->gpios, sport->port.mctrl);
|
||||
}
|
||||
|
||||
/* called with port.lock taken and irqs caller dependent */
|
||||
static void imx_uart_rts_auto(struct imx_port *sport, u32 *ucr2)
|
||||
{
|
||||
*ucr2 |= UCR2_CTSC;
|
||||
}
|
||||
|
||||
/* called with port.lock taken and irqs off */
|
||||
static void imx_uart_start_rx(struct uart_port *port)
|
||||
{
|
||||
@ -445,7 +439,7 @@ static void imx_uart_stop_tx(struct uart_port *port)
|
||||
return;
|
||||
|
||||
ucr1 = imx_uart_readl(sport, UCR1);
|
||||
imx_uart_writel(sport, ucr1 & ~UCR1_TXMPTYEN, UCR1);
|
||||
imx_uart_writel(sport, ucr1 & ~UCR1_TRDYEN, UCR1);
|
||||
|
||||
/* in rs485 mode disable transmitter if shifter is empty */
|
||||
if (port->rs485.flags & SER_RS485_ENABLED &&
|
||||
@ -523,7 +517,7 @@ static inline void imx_uart_transmit_buffer(struct imx_port *sport)
|
||||
* and the TX IRQ is disabled.
|
||||
**/
|
||||
ucr1 = imx_uart_readl(sport, UCR1);
|
||||
ucr1 &= ~UCR1_TXMPTYEN;
|
||||
ucr1 &= ~UCR1_TRDYEN;
|
||||
if (sport->dma_is_txing) {
|
||||
ucr1 |= UCR1_TXDMAEN;
|
||||
imx_uart_writel(sport, ucr1, UCR1);
|
||||
@ -685,7 +679,7 @@ static void imx_uart_start_tx(struct uart_port *port)
|
||||
|
||||
if (!sport->dma_is_enabled) {
|
||||
ucr1 = imx_uart_readl(sport, UCR1);
|
||||
imx_uart_writel(sport, ucr1 | UCR1_TXMPTYEN, UCR1);
|
||||
imx_uart_writel(sport, ucr1 | UCR1_TRDYEN, UCR1);
|
||||
}
|
||||
|
||||
if (sport->dma_is_enabled) {
|
||||
@ -694,7 +688,7 @@ static void imx_uart_start_tx(struct uart_port *port)
|
||||
* disable TX DMA to let TX interrupt to send X-char */
|
||||
ucr1 = imx_uart_readl(sport, UCR1);
|
||||
ucr1 &= ~UCR1_TXDMAEN;
|
||||
ucr1 |= UCR1_TXMPTYEN;
|
||||
ucr1 |= UCR1_TRDYEN;
|
||||
imx_uart_writel(sport, ucr1, UCR1);
|
||||
return;
|
||||
}
|
||||
@ -880,7 +874,7 @@ static irqreturn_t imx_uart_int(int irq, void *dev_id)
|
||||
usr1 &= ~USR1_RRDY;
|
||||
if ((ucr2 & UCR2_ATEN) == 0)
|
||||
usr1 &= ~USR1_AGTIM;
|
||||
if ((ucr1 & UCR1_TXMPTYEN) == 0)
|
||||
if ((ucr1 & UCR1_TRDYEN) == 0)
|
||||
usr1 &= ~USR1_TRDY;
|
||||
if ((ucr4 & UCR4_TCEN) == 0)
|
||||
usr2 &= ~USR2_TXDC;
|
||||
@ -969,10 +963,22 @@ static void imx_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
|
||||
if (!(port->rs485.flags & SER_RS485_ENABLED)) {
|
||||
u32 ucr2;
|
||||
|
||||
/*
|
||||
* Turn off autoRTS if RTS is lowered and restore autoRTS
|
||||
* setting if RTS is raised.
|
||||
*/
|
||||
ucr2 = imx_uart_readl(sport, UCR2);
|
||||
ucr2 &= ~(UCR2_CTS | UCR2_CTSC);
|
||||
if (mctrl & TIOCM_RTS)
|
||||
ucr2 |= UCR2_CTS | UCR2_CTSC;
|
||||
if (mctrl & TIOCM_RTS) {
|
||||
ucr2 |= UCR2_CTS;
|
||||
/*
|
||||
* UCR2_IRTS is unset if and only if the port is
|
||||
* configured for CRTSCTS, so we use inverted UCR2_IRTS
|
||||
* to get the state to restore to.
|
||||
*/
|
||||
if (!(ucr2 & UCR2_IRTS))
|
||||
ucr2 |= UCR2_CTSC;
|
||||
}
|
||||
imx_uart_writel(sport, ucr2, UCR2);
|
||||
}
|
||||
|
||||
@ -1468,7 +1474,7 @@ static void imx_uart_shutdown(struct uart_port *port)
|
||||
|
||||
spin_lock_irqsave(&sport->port.lock, flags);
|
||||
ucr1 = imx_uart_readl(sport, UCR1);
|
||||
ucr1 &= ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN | UCR1_UARTEN | UCR1_RXDMAEN | UCR1_ATDMAEN);
|
||||
ucr1 &= ~(UCR1_TRDYEN | UCR1_RRDYEN | UCR1_RTSDEN | UCR1_UARTEN | UCR1_RXDMAEN | UCR1_ATDMAEN);
|
||||
|
||||
imx_uart_writel(sport, ucr1, UCR1);
|
||||
spin_unlock_irqrestore(&sport->port.lock, flags);
|
||||
@ -1535,11 +1541,11 @@ imx_uart_set_termios(struct uart_port *port, struct ktermios *termios,
|
||||
{
|
||||
struct imx_port *sport = (struct imx_port *)port;
|
||||
unsigned long flags;
|
||||
u32 ucr2, old_ucr1, old_ucr2, ufcr;
|
||||
u32 ucr2, old_ucr2, ufcr;
|
||||
unsigned int baud, quot;
|
||||
unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8;
|
||||
unsigned long div;
|
||||
unsigned long num, denom;
|
||||
unsigned long num, denom, old_ubir, old_ubmr;
|
||||
uint64_t tdiv64;
|
||||
|
||||
/*
|
||||
@ -1587,8 +1593,14 @@ imx_uart_set_termios(struct uart_port *port, struct ktermios *termios,
|
||||
else
|
||||
imx_uart_rts_inactive(sport, &ucr2);
|
||||
|
||||
} else if (termios->c_cflag & CRTSCTS)
|
||||
imx_uart_rts_auto(sport, &ucr2);
|
||||
} else if (termios->c_cflag & CRTSCTS) {
|
||||
/*
|
||||
* Only let receiver control RTS output if we were not requested
|
||||
* to have RTS inactive (which then should take precedence).
|
||||
*/
|
||||
if (ucr2 & UCR2_CTS)
|
||||
ucr2 |= UCR2_CTSC;
|
||||
}
|
||||
|
||||
if (termios->c_cflag & CRTSCTS)
|
||||
ucr2 &= ~UCR2_IRTS;
|
||||
@ -1631,21 +1643,6 @@ imx_uart_set_termios(struct uart_port *port, struct ktermios *termios,
|
||||
*/
|
||||
uart_update_timeout(port, termios->c_cflag, baud);
|
||||
|
||||
/*
|
||||
* disable interrupts and drain transmitter
|
||||
*/
|
||||
old_ucr1 = imx_uart_readl(sport, UCR1);
|
||||
imx_uart_writel(sport,
|
||||
old_ucr1 & ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN),
|
||||
UCR1);
|
||||
imx_uart_writel(sport, old_ucr2 & ~UCR2_ATEN, UCR2);
|
||||
|
||||
while (!(imx_uart_readl(sport, USR2) & USR2_TXDC))
|
||||
barrier();
|
||||
|
||||
/* then, disable everything */
|
||||
imx_uart_writel(sport, old_ucr2 & ~(UCR2_TXEN | UCR2_RXEN | UCR2_ATEN), UCR2);
|
||||
|
||||
/* custom-baudrate handling */
|
||||
div = sport->port.uartclk / (baud * 16);
|
||||
if (baud == 38400 && quot != div)
|
||||
@ -1673,15 +1670,26 @@ imx_uart_set_termios(struct uart_port *port, struct ktermios *termios,
|
||||
ufcr = (ufcr & (~UFCR_RFDIV)) | UFCR_RFDIV_REG(div);
|
||||
imx_uart_writel(sport, ufcr, UFCR);
|
||||
|
||||
imx_uart_writel(sport, num, UBIR);
|
||||
imx_uart_writel(sport, denom, UBMR);
|
||||
/*
|
||||
* Two registers below should always be written both and in this
|
||||
* particular order. One consequence is that we need to check if any of
|
||||
* them changes and then update both. We do need the check for change
|
||||
* as even writing the same values seem to "restart"
|
||||
* transmission/receiving logic in the hardware, that leads to data
|
||||
* breakage even when rate doesn't in fact change. E.g., user switches
|
||||
* RTS/CTS handshake and suddenly gets broken bytes.
|
||||
*/
|
||||
old_ubir = imx_uart_readl(sport, UBIR);
|
||||
old_ubmr = imx_uart_readl(sport, UBMR);
|
||||
if (old_ubir != num || old_ubmr != denom) {
|
||||
imx_uart_writel(sport, num, UBIR);
|
||||
imx_uart_writel(sport, denom, UBMR);
|
||||
}
|
||||
|
||||
if (!imx_uart_is_imx1(sport))
|
||||
imx_uart_writel(sport, sport->port.uartclk / div / 1000,
|
||||
IMX21_ONEMS);
|
||||
|
||||
imx_uart_writel(sport, old_ucr1, UCR1);
|
||||
|
||||
imx_uart_writel(sport, ucr2, UCR2);
|
||||
|
||||
if (UART_ENABLE_MS(&sport->port, termios->c_cflag))
|
||||
@ -1770,7 +1778,7 @@ static int imx_uart_poll_init(struct uart_port *port)
|
||||
ucr1 |= IMX1_UCR1_UARTCLKEN;
|
||||
|
||||
ucr1 |= UCR1_UARTEN;
|
||||
ucr1 &= ~(UCR1_TXMPTYEN | UCR1_RTSDEN | UCR1_RRDYEN);
|
||||
ucr1 &= ~(UCR1_TRDYEN | UCR1_RTSDEN | UCR1_RRDYEN);
|
||||
|
||||
ucr2 |= UCR2_RXEN;
|
||||
ucr2 &= ~UCR2_ATEN;
|
||||
@ -1930,7 +1938,7 @@ imx_uart_console_write(struct console *co, const char *s, unsigned int count)
|
||||
if (imx_uart_is_imx1(sport))
|
||||
ucr1 |= IMX1_UCR1_UARTCLKEN;
|
||||
ucr1 |= UCR1_UARTEN;
|
||||
ucr1 &= ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN);
|
||||
ucr1 &= ~(UCR1_TRDYEN | UCR1_RRDYEN | UCR1_RTSDEN);
|
||||
|
||||
imx_uart_writel(sport, ucr1, UCR1);
|
||||
|
||||
@ -2286,7 +2294,7 @@ static int imx_uart_probe(struct platform_device *pdev)
|
||||
/* Disable interrupts before requesting them */
|
||||
ucr1 = imx_uart_readl(sport, UCR1);
|
||||
ucr1 &= ~(UCR1_ADEN | UCR1_TRDYEN | UCR1_IDEN | UCR1_RRDYEN |
|
||||
UCR1_TXMPTYEN | UCR1_RTSDEN);
|
||||
UCR1_TRDYEN | UCR1_RTSDEN);
|
||||
imx_uart_writel(sport, ucr1, UCR1);
|
||||
|
||||
if (!imx_uart_is_imx1(sport) && sport->dte_mode) {
|
||||
|
@ -57,6 +57,7 @@
|
||||
#define ASC_IRNCR_TIR 0x1
|
||||
#define ASC_IRNCR_RIR 0x2
|
||||
#define ASC_IRNCR_EIR 0x4
|
||||
#define ASC_IRNCR_MASK GENMASK(2, 0)
|
||||
|
||||
#define ASCOPT_CSIZE 0x3
|
||||
#define TXFIFO_FL 1
|
||||
@ -99,7 +100,12 @@
|
||||
static void lqasc_tx_chars(struct uart_port *port);
|
||||
static struct ltq_uart_port *lqasc_port[MAXPORTS];
|
||||
static struct uart_driver lqasc_reg;
|
||||
static DEFINE_SPINLOCK(ltq_asc_lock);
|
||||
|
||||
struct ltq_soc_data {
|
||||
int (*fetch_irq)(struct device *dev, struct ltq_uart_port *ltq_port);
|
||||
int (*request_irq)(struct uart_port *port);
|
||||
void (*free_irq)(struct uart_port *port);
|
||||
};
|
||||
|
||||
struct ltq_uart_port {
|
||||
struct uart_port port;
|
||||
@ -110,6 +116,10 @@ struct ltq_uart_port {
|
||||
unsigned int tx_irq;
|
||||
unsigned int rx_irq;
|
||||
unsigned int err_irq;
|
||||
unsigned int common_irq;
|
||||
spinlock_t lock; /* exclusive access for multi core */
|
||||
|
||||
const struct ltq_soc_data *soc;
|
||||
};
|
||||
|
||||
static inline void asc_update_bits(u32 clear, u32 set, void __iomem *reg)
|
||||
@ -135,9 +145,11 @@ static void
|
||||
lqasc_start_tx(struct uart_port *port)
|
||||
{
|
||||
unsigned long flags;
|
||||
spin_lock_irqsave(<q_asc_lock, flags);
|
||||
struct ltq_uart_port *ltq_port = to_ltq_uart_port(port);
|
||||
|
||||
spin_lock_irqsave(<q_port->lock, flags);
|
||||
lqasc_tx_chars(port);
|
||||
spin_unlock_irqrestore(<q_asc_lock, flags);
|
||||
spin_unlock_irqrestore(<q_port->lock, flags);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -245,9 +257,11 @@ lqasc_tx_int(int irq, void *_port)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct uart_port *port = (struct uart_port *)_port;
|
||||
spin_lock_irqsave(<q_asc_lock, flags);
|
||||
struct ltq_uart_port *ltq_port = to_ltq_uart_port(port);
|
||||
|
||||
spin_lock_irqsave(<q_port->lock, flags);
|
||||
__raw_writel(ASC_IRNCR_TIR, port->membase + LTQ_ASC_IRNCR);
|
||||
spin_unlock_irqrestore(<q_asc_lock, flags);
|
||||
spin_unlock_irqrestore(<q_port->lock, flags);
|
||||
lqasc_start_tx(port);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
@ -257,11 +271,13 @@ lqasc_err_int(int irq, void *_port)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct uart_port *port = (struct uart_port *)_port;
|
||||
spin_lock_irqsave(<q_asc_lock, flags);
|
||||
struct ltq_uart_port *ltq_port = to_ltq_uart_port(port);
|
||||
|
||||
spin_lock_irqsave(<q_port->lock, flags);
|
||||
/* clear any pending interrupts */
|
||||
asc_update_bits(0, ASCWHBSTATE_CLRPE | ASCWHBSTATE_CLRFE |
|
||||
ASCWHBSTATE_CLRROE, port->membase + LTQ_ASC_WHBSTATE);
|
||||
spin_unlock_irqrestore(<q_asc_lock, flags);
|
||||
spin_unlock_irqrestore(<q_port->lock, flags);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
@ -270,10 +286,37 @@ lqasc_rx_int(int irq, void *_port)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct uart_port *port = (struct uart_port *)_port;
|
||||
spin_lock_irqsave(<q_asc_lock, flags);
|
||||
struct ltq_uart_port *ltq_port = to_ltq_uart_port(port);
|
||||
|
||||
spin_lock_irqsave(<q_port->lock, flags);
|
||||
__raw_writel(ASC_IRNCR_RIR, port->membase + LTQ_ASC_IRNCR);
|
||||
lqasc_rx_chars(port);
|
||||
spin_unlock_irqrestore(<q_asc_lock, flags);
|
||||
spin_unlock_irqrestore(<q_port->lock, flags);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t lqasc_irq(int irq, void *p)
|
||||
{
|
||||
unsigned long flags;
|
||||
u32 stat;
|
||||
struct uart_port *port = p;
|
||||
struct ltq_uart_port *ltq_port = to_ltq_uart_port(port);
|
||||
|
||||
spin_lock_irqsave(<q_port->lock, flags);
|
||||
stat = readl(port->membase + LTQ_ASC_IRNCR);
|
||||
spin_unlock_irqrestore(<q_port->lock, flags);
|
||||
if (!(stat & ASC_IRNCR_MASK))
|
||||
return IRQ_NONE;
|
||||
|
||||
if (stat & ASC_IRNCR_TIR)
|
||||
lqasc_tx_int(irq, p);
|
||||
|
||||
if (stat & ASC_IRNCR_RIR)
|
||||
lqasc_rx_int(irq, p);
|
||||
|
||||
if (stat & ASC_IRNCR_EIR)
|
||||
lqasc_err_int(irq, p);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
@ -307,11 +350,13 @@ lqasc_startup(struct uart_port *port)
|
||||
{
|
||||
struct ltq_uart_port *ltq_port = to_ltq_uart_port(port);
|
||||
int retval;
|
||||
unsigned long flags;
|
||||
|
||||
if (!IS_ERR(ltq_port->clk))
|
||||
clk_prepare_enable(ltq_port->clk);
|
||||
port->uartclk = clk_get_rate(ltq_port->freqclk);
|
||||
|
||||
spin_lock_irqsave(<q_port->lock, flags);
|
||||
asc_update_bits(ASCCLC_DISS | ASCCLC_RMCMASK, (1 << ASCCLC_RMCOFFSET),
|
||||
port->membase + LTQ_ASC_CLC);
|
||||
|
||||
@ -331,35 +376,14 @@ lqasc_startup(struct uart_port *port)
|
||||
asc_update_bits(0, ASCCON_M_8ASYNC | ASCCON_FEN | ASCCON_TOEN |
|
||||
ASCCON_ROEN, port->membase + LTQ_ASC_CON);
|
||||
|
||||
retval = request_irq(ltq_port->tx_irq, lqasc_tx_int,
|
||||
0, "asc_tx", port);
|
||||
if (retval) {
|
||||
pr_err("failed to request lqasc_tx_int\n");
|
||||
spin_unlock_irqrestore(<q_port->lock, flags);
|
||||
|
||||
retval = ltq_port->soc->request_irq(port);
|
||||
if (retval)
|
||||
return retval;
|
||||
}
|
||||
|
||||
retval = request_irq(ltq_port->rx_irq, lqasc_rx_int,
|
||||
0, "asc_rx", port);
|
||||
if (retval) {
|
||||
pr_err("failed to request lqasc_rx_int\n");
|
||||
goto err1;
|
||||
}
|
||||
|
||||
retval = request_irq(ltq_port->err_irq, lqasc_err_int,
|
||||
0, "asc_err", port);
|
||||
if (retval) {
|
||||
pr_err("failed to request lqasc_err_int\n");
|
||||
goto err2;
|
||||
}
|
||||
|
||||
__raw_writel(ASC_IRNREN_RX | ASC_IRNREN_ERR | ASC_IRNREN_TX,
|
||||
port->membase + LTQ_ASC_IRNREN);
|
||||
return 0;
|
||||
|
||||
err2:
|
||||
free_irq(ltq_port->rx_irq, port);
|
||||
err1:
|
||||
free_irq(ltq_port->tx_irq, port);
|
||||
return retval;
|
||||
}
|
||||
|
||||
@ -367,15 +391,17 @@ static void
|
||||
lqasc_shutdown(struct uart_port *port)
|
||||
{
|
||||
struct ltq_uart_port *ltq_port = to_ltq_uart_port(port);
|
||||
free_irq(ltq_port->tx_irq, port);
|
||||
free_irq(ltq_port->rx_irq, port);
|
||||
free_irq(ltq_port->err_irq, port);
|
||||
unsigned long flags;
|
||||
|
||||
ltq_port->soc->free_irq(port);
|
||||
|
||||
spin_lock_irqsave(<q_port->lock, flags);
|
||||
__raw_writel(0, port->membase + LTQ_ASC_CON);
|
||||
asc_update_bits(ASCRXFCON_RXFEN, ASCRXFCON_RXFFLU,
|
||||
port->membase + LTQ_ASC_RXFCON);
|
||||
asc_update_bits(ASCTXFCON_TXFEN, ASCTXFCON_TXFFLU,
|
||||
port->membase + LTQ_ASC_TXFCON);
|
||||
spin_unlock_irqrestore(<q_port->lock, flags);
|
||||
if (!IS_ERR(ltq_port->clk))
|
||||
clk_disable_unprepare(ltq_port->clk);
|
||||
}
|
||||
@ -390,6 +416,7 @@ lqasc_set_termios(struct uart_port *port,
|
||||
unsigned int baud;
|
||||
unsigned int con = 0;
|
||||
unsigned long flags;
|
||||
struct ltq_uart_port *ltq_port = to_ltq_uart_port(port);
|
||||
|
||||
cflag = new->c_cflag;
|
||||
iflag = new->c_iflag;
|
||||
@ -443,7 +470,7 @@ lqasc_set_termios(struct uart_port *port,
|
||||
/* set error signals - framing, parity and overrun, enable receiver */
|
||||
con |= ASCCON_FEN | ASCCON_TOEN | ASCCON_ROEN;
|
||||
|
||||
spin_lock_irqsave(<q_asc_lock, flags);
|
||||
spin_lock_irqsave(<q_port->lock, flags);
|
||||
|
||||
/* set up CON */
|
||||
asc_update_bits(0, con, port->membase + LTQ_ASC_CON);
|
||||
@ -471,7 +498,7 @@ lqasc_set_termios(struct uart_port *port,
|
||||
/* enable rx */
|
||||
__raw_writel(ASCWHBSTATE_SETREN, port->membase + LTQ_ASC_WHBSTATE);
|
||||
|
||||
spin_unlock_irqrestore(<q_asc_lock, flags);
|
||||
spin_unlock_irqrestore(<q_port->lock, flags);
|
||||
|
||||
/* Don't rewrite B0 */
|
||||
if (tty_termios_baud_rate(new))
|
||||
@ -589,17 +616,14 @@ lqasc_console_putchar(struct uart_port *port, int ch)
|
||||
static void lqasc_serial_port_write(struct uart_port *port, const char *s,
|
||||
u_int count)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(<q_asc_lock, flags);
|
||||
uart_console_write(port, s, count, lqasc_console_putchar);
|
||||
spin_unlock_irqrestore(<q_asc_lock, flags);
|
||||
}
|
||||
|
||||
static void
|
||||
lqasc_console_write(struct console *co, const char *s, u_int count)
|
||||
{
|
||||
struct ltq_uart_port *ltq_port;
|
||||
unsigned long flags;
|
||||
|
||||
if (co->index >= MAXPORTS)
|
||||
return;
|
||||
@ -608,7 +632,9 @@ lqasc_console_write(struct console *co, const char *s, u_int count)
|
||||
if (!ltq_port)
|
||||
return;
|
||||
|
||||
spin_lock_irqsave(<q_port->lock, flags);
|
||||
lqasc_serial_port_write(<q_port->port, s, count);
|
||||
spin_unlock_irqrestore(<q_port->lock, flags);
|
||||
}
|
||||
|
||||
static int __init
|
||||
@ -677,7 +703,8 @@ lqasc_serial_early_console_setup(struct earlycon_device *device,
|
||||
device->con->write = lqasc_serial_early_console_write;
|
||||
return 0;
|
||||
}
|
||||
OF_EARLYCON_DECLARE(lantiq, DRVNAME, lqasc_serial_early_console_setup);
|
||||
OF_EARLYCON_DECLARE(lantiq, "lantiq,asc", lqasc_serial_early_console_setup);
|
||||
OF_EARLYCON_DECLARE(lantiq, "intel,lgm-asc", lqasc_serial_early_console_setup);
|
||||
|
||||
static struct uart_driver lqasc_reg = {
|
||||
.owner = THIS_MODULE,
|
||||
@ -689,24 +716,134 @@ static struct uart_driver lqasc_reg = {
|
||||
.cons = &lqasc_console,
|
||||
};
|
||||
|
||||
static int fetch_irq_lantiq(struct device *dev, struct ltq_uart_port *ltq_port)
|
||||
{
|
||||
struct uart_port *port = <q_port->port;
|
||||
struct resource irqres[3];
|
||||
int ret;
|
||||
|
||||
ret = of_irq_to_resource_table(dev->of_node, irqres, 3);
|
||||
if (ret != 3) {
|
||||
dev_err(dev,
|
||||
"failed to get IRQs for serial port\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
ltq_port->tx_irq = irqres[0].start;
|
||||
ltq_port->rx_irq = irqres[1].start;
|
||||
ltq_port->err_irq = irqres[2].start;
|
||||
port->irq = irqres[0].start;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int request_irq_lantiq(struct uart_port *port)
|
||||
{
|
||||
struct ltq_uart_port *ltq_port = to_ltq_uart_port(port);
|
||||
int retval;
|
||||
|
||||
retval = request_irq(ltq_port->tx_irq, lqasc_tx_int,
|
||||
0, "asc_tx", port);
|
||||
if (retval) {
|
||||
dev_err(port->dev, "failed to request asc_tx\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
retval = request_irq(ltq_port->rx_irq, lqasc_rx_int,
|
||||
0, "asc_rx", port);
|
||||
if (retval) {
|
||||
dev_err(port->dev, "failed to request asc_rx\n");
|
||||
goto err1;
|
||||
}
|
||||
|
||||
retval = request_irq(ltq_port->err_irq, lqasc_err_int,
|
||||
0, "asc_err", port);
|
||||
if (retval) {
|
||||
dev_err(port->dev, "failed to request asc_err\n");
|
||||
goto err2;
|
||||
}
|
||||
return 0;
|
||||
|
||||
err2:
|
||||
free_irq(ltq_port->rx_irq, port);
|
||||
err1:
|
||||
free_irq(ltq_port->tx_irq, port);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void free_irq_lantiq(struct uart_port *port)
|
||||
{
|
||||
struct ltq_uart_port *ltq_port = to_ltq_uart_port(port);
|
||||
|
||||
free_irq(ltq_port->tx_irq, port);
|
||||
free_irq(ltq_port->rx_irq, port);
|
||||
free_irq(ltq_port->err_irq, port);
|
||||
}
|
||||
|
||||
static int fetch_irq_intel(struct device *dev, struct ltq_uart_port *ltq_port)
|
||||
{
|
||||
struct uart_port *port = <q_port->port;
|
||||
int ret;
|
||||
|
||||
ret = of_irq_get(dev->of_node, 0);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to fetch IRQ for serial port\n");
|
||||
return ret;
|
||||
}
|
||||
ltq_port->common_irq = ret;
|
||||
port->irq = ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int request_irq_intel(struct uart_port *port)
|
||||
{
|
||||
struct ltq_uart_port *ltq_port = to_ltq_uart_port(port);
|
||||
int retval;
|
||||
|
||||
retval = request_irq(ltq_port->common_irq, lqasc_irq, 0,
|
||||
"asc_irq", port);
|
||||
if (retval)
|
||||
dev_err(port->dev, "failed to request asc_irq\n");
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void free_irq_intel(struct uart_port *port)
|
||||
{
|
||||
struct ltq_uart_port *ltq_port = to_ltq_uart_port(port);
|
||||
|
||||
free_irq(ltq_port->common_irq, port);
|
||||
}
|
||||
|
||||
static int __init
|
||||
lqasc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
struct ltq_uart_port *ltq_port;
|
||||
struct uart_port *port;
|
||||
struct resource *mmres, irqres[3];
|
||||
struct resource *mmres;
|
||||
int line;
|
||||
int ret;
|
||||
|
||||
mmres = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
ret = of_irq_to_resource_table(node, irqres, 3);
|
||||
if (!mmres || (ret != 3)) {
|
||||
if (!mmres) {
|
||||
dev_err(&pdev->dev,
|
||||
"failed to get memory/irq for serial port\n");
|
||||
"failed to get memory for serial port\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ltq_port = devm_kzalloc(&pdev->dev, sizeof(struct ltq_uart_port),
|
||||
GFP_KERNEL);
|
||||
if (!ltq_port)
|
||||
return -ENOMEM;
|
||||
|
||||
port = <q_port->port;
|
||||
|
||||
ltq_port->soc = of_device_get_match_data(&pdev->dev);
|
||||
ret = ltq_port->soc->fetch_irq(&pdev->dev, ltq_port);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* get serial id */
|
||||
line = of_alias_get_id(node, "serial");
|
||||
if (line < 0) {
|
||||
@ -727,13 +864,6 @@ lqasc_probe(struct platform_device *pdev)
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
ltq_port = devm_kzalloc(&pdev->dev, sizeof(struct ltq_uart_port),
|
||||
GFP_KERNEL);
|
||||
if (!ltq_port)
|
||||
return -ENOMEM;
|
||||
|
||||
port = <q_port->port;
|
||||
|
||||
port->iotype = SERIAL_IO_MEM;
|
||||
port->flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP;
|
||||
port->ops = &lqasc_pops;
|
||||
@ -742,7 +872,6 @@ lqasc_probe(struct platform_device *pdev)
|
||||
port->line = line;
|
||||
port->dev = &pdev->dev;
|
||||
/* unused, just to be backward-compatible */
|
||||
port->irq = irqres[0].start;
|
||||
port->mapbase = mmres->start;
|
||||
|
||||
if (IS_ENABLED(CONFIG_LANTIQ) && !IS_ENABLED(CONFIG_COMMON_CLK))
|
||||
@ -762,10 +891,7 @@ lqasc_probe(struct platform_device *pdev)
|
||||
else
|
||||
ltq_port->clk = devm_clk_get(&pdev->dev, "asc");
|
||||
|
||||
ltq_port->tx_irq = irqres[0].start;
|
||||
ltq_port->rx_irq = irqres[1].start;
|
||||
ltq_port->err_irq = irqres[2].start;
|
||||
|
||||
spin_lock_init(<q_port->lock);
|
||||
lqasc_port[line] = ltq_port;
|
||||
platform_set_drvdata(pdev, ltq_port);
|
||||
|
||||
@ -774,8 +900,21 @@ lqasc_probe(struct platform_device *pdev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct ltq_soc_data soc_data_lantiq = {
|
||||
.fetch_irq = fetch_irq_lantiq,
|
||||
.request_irq = request_irq_lantiq,
|
||||
.free_irq = free_irq_lantiq,
|
||||
};
|
||||
|
||||
static const struct ltq_soc_data soc_data_intel = {
|
||||
.fetch_irq = fetch_irq_intel,
|
||||
.request_irq = request_irq_intel,
|
||||
.free_irq = free_irq_intel,
|
||||
};
|
||||
|
||||
static const struct of_device_id ltq_asc_match[] = {
|
||||
{ .compatible = DRVNAME },
|
||||
{ .compatible = "lantiq,asc", .data = &soc_data_lantiq },
|
||||
{ .compatible = "intel,lgm-asc", .data = &soc_data_intel },
|
||||
{},
|
||||
};
|
||||
|
||||
|
@ -658,11 +658,8 @@ static int serial_hs_lpc32xx_probe(struct platform_device *pdev)
|
||||
p->port.membase = NULL;
|
||||
|
||||
ret = platform_get_irq(pdev, 0);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "Error getting irq for HS UART port %d\n",
|
||||
uarts_registered);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
p->port.irq = ret;
|
||||
|
||||
p->port.iotype = UPIO_MEM32;
|
||||
|
@ -689,7 +689,7 @@ static void max310x_handle_rx(struct uart_port *port, unsigned int rxlen)
|
||||
* tail.
|
||||
*/
|
||||
uart_insert_char(port, sts, MAX310X_LSR_RXOVR_BIT,
|
||||
one->rx_buf[rxlen], flag);
|
||||
one->rx_buf[rxlen-1], flag);
|
||||
|
||||
} else {
|
||||
if (unlikely(rxlen >= port->fifosize)) {
|
||||
@ -955,17 +955,43 @@ static void max310x_set_termios(struct uart_port *port,
|
||||
/* Configure flow control */
|
||||
max310x_port_write(port, MAX310X_XON1_REG, termios->c_cc[VSTART]);
|
||||
max310x_port_write(port, MAX310X_XOFF1_REG, termios->c_cc[VSTOP]);
|
||||
if (termios->c_cflag & CRTSCTS)
|
||||
|
||||
/* Disable transmitter before enabling AutoCTS or auto transmitter
|
||||
* flow control
|
||||
*/
|
||||
if (termios->c_cflag & CRTSCTS || termios->c_iflag & IXOFF) {
|
||||
max310x_port_update(port, MAX310X_MODE1_REG,
|
||||
MAX310X_MODE1_TXDIS_BIT,
|
||||
MAX310X_MODE1_TXDIS_BIT);
|
||||
}
|
||||
|
||||
port->status &= ~(UPSTAT_AUTOCTS | UPSTAT_AUTORTS | UPSTAT_AUTOXOFF);
|
||||
|
||||
if (termios->c_cflag & CRTSCTS) {
|
||||
/* Enable AUTORTS and AUTOCTS */
|
||||
port->status |= UPSTAT_AUTOCTS | UPSTAT_AUTORTS;
|
||||
flow |= MAX310X_FLOWCTRL_AUTOCTS_BIT |
|
||||
MAX310X_FLOWCTRL_AUTORTS_BIT;
|
||||
}
|
||||
if (termios->c_iflag & IXON)
|
||||
flow |= MAX310X_FLOWCTRL_SWFLOW3_BIT |
|
||||
MAX310X_FLOWCTRL_SWFLOWEN_BIT;
|
||||
if (termios->c_iflag & IXOFF)
|
||||
if (termios->c_iflag & IXOFF) {
|
||||
port->status |= UPSTAT_AUTOXOFF;
|
||||
flow |= MAX310X_FLOWCTRL_SWFLOW1_BIT |
|
||||
MAX310X_FLOWCTRL_SWFLOWEN_BIT;
|
||||
}
|
||||
max310x_port_write(port, MAX310X_FLOWCTRL_REG, flow);
|
||||
|
||||
/* Enable transmitter after disabling AutoCTS and auto transmitter
|
||||
* flow control
|
||||
*/
|
||||
if (!(termios->c_cflag & CRTSCTS) && !(termios->c_iflag & IXOFF)) {
|
||||
max310x_port_update(port, MAX310X_MODE1_REG,
|
||||
MAX310X_MODE1_TXDIS_BIT,
|
||||
0);
|
||||
}
|
||||
|
||||
/* Get baud rate generator configuration */
|
||||
baud = uart_get_baud_rate(port, termios, old,
|
||||
port->uartclk / 16 / 0xffff,
|
||||
|
@ -884,10 +884,8 @@ static int mvebu_uart_probe(struct platform_device *pdev)
|
||||
if (platform_irq_count(pdev) == 1) {
|
||||
/* Old bindings: no name on the single unamed UART0 IRQ */
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
dev_err(&pdev->dev, "unable to get UART IRQ\n");
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
}
|
||||
|
||||
mvuart->irq[UART_IRQ_SUM] = irq;
|
||||
} else {
|
||||
@ -897,18 +895,14 @@ static int mvebu_uart_probe(struct platform_device *pdev)
|
||||
* uart-sum of UART0 port.
|
||||
*/
|
||||
irq = platform_get_irq_byname(pdev, "uart-rx");
|
||||
if (irq < 0) {
|
||||
dev_err(&pdev->dev, "unable to get 'uart-rx' IRQ\n");
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
}
|
||||
|
||||
mvuart->irq[UART_RX_IRQ] = irq;
|
||||
|
||||
irq = platform_get_irq_byname(pdev, "uart-tx");
|
||||
if (irq < 0) {
|
||||
dev_err(&pdev->dev, "unable to get 'uart-tx' IRQ\n");
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
}
|
||||
|
||||
mvuart->irq[UART_TX_IRQ] = irq;
|
||||
}
|
||||
|
@ -969,10 +969,8 @@ err_out:
|
||||
|
||||
}
|
||||
|
||||
#define RTS_AT_AUART() IS_ERR_OR_NULL(mctrl_gpio_to_gpiod(s->gpios, \
|
||||
UART_GPIO_RTS))
|
||||
#define CTS_AT_AUART() IS_ERR_OR_NULL(mctrl_gpio_to_gpiod(s->gpios, \
|
||||
UART_GPIO_CTS))
|
||||
#define RTS_AT_AUART() !mctrl_gpio_to_gpiod(s->gpios, UART_GPIO_RTS)
|
||||
#define CTS_AT_AUART() !mctrl_gpio_to_gpiod(s->gpios, UART_GPIO_CTS)
|
||||
static void mxs_auart_settermios(struct uart_port *u,
|
||||
struct ktermios *termios,
|
||||
struct ktermios *old)
|
||||
|
@ -662,10 +662,8 @@ static int owl_uart_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
dev_err(&pdev->dev, "could not get irq\n");
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
}
|
||||
|
||||
if (owl_uart_ports[pdev->id]) {
|
||||
dev_err(&pdev->dev, "port %d already allocated\n", pdev->id);
|
||||
|
@ -198,10 +198,8 @@ static int qcom_geni_serial_request_port(struct uart_port *uport)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(uport->dev);
|
||||
struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
|
||||
struct resource *res;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
uport->membase = devm_ioremap_resource(&pdev->dev, res);
|
||||
uport->membase = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(uport->membase))
|
||||
return PTR_ERR(uport->membase);
|
||||
port->se.base = uport->membase;
|
||||
@ -920,12 +918,13 @@ static unsigned long get_clk_cfg(unsigned long clk_freq)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned long get_clk_div_rate(unsigned int baud, unsigned int *clk_div)
|
||||
static unsigned long get_clk_div_rate(unsigned int baud,
|
||||
unsigned int sampling_rate, unsigned int *clk_div)
|
||||
{
|
||||
unsigned long ser_clk;
|
||||
unsigned long desired_clk;
|
||||
|
||||
desired_clk = baud * UART_OVERSAMPLING;
|
||||
desired_clk = baud * sampling_rate;
|
||||
ser_clk = get_clk_cfg(desired_clk);
|
||||
if (!ser_clk) {
|
||||
pr_err("%s: Can't find matching DFS entry for baud %d\n",
|
||||
@ -951,12 +950,20 @@ static void qcom_geni_serial_set_termios(struct uart_port *uport,
|
||||
u32 ser_clk_cfg;
|
||||
struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
|
||||
unsigned long clk_rate;
|
||||
u32 ver, sampling_rate;
|
||||
|
||||
qcom_geni_serial_stop_rx(uport);
|
||||
/* baud rate */
|
||||
baud = uart_get_baud_rate(uport, termios, old, 300, 4000000);
|
||||
port->baud = baud;
|
||||
clk_rate = get_clk_div_rate(baud, &clk_div);
|
||||
|
||||
sampling_rate = UART_OVERSAMPLING;
|
||||
/* Sampling rate is halved for IP versions >= 2.5 */
|
||||
ver = geni_se_get_qup_hw_version(&port->se);
|
||||
if (GENI_SE_VERSION_MAJOR(ver) >= 2 && GENI_SE_VERSION_MINOR(ver) >= 5)
|
||||
sampling_rate /= 2;
|
||||
|
||||
clk_rate = get_clk_div_rate(baud, sampling_rate, &clk_div);
|
||||
if (!clk_rate)
|
||||
goto out_restart_rx;
|
||||
|
||||
@ -1291,10 +1298,8 @@ static int qcom_geni_serial_probe(struct platform_device *pdev)
|
||||
port->tx_fifo_width = DEF_FIFO_WIDTH_BITS;
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
dev_err(&pdev->dev, "Failed to get IRQ %d\n", irq);
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
}
|
||||
uport->irq = irq;
|
||||
|
||||
uport->private_data = drv;
|
||||
|
@ -735,10 +735,8 @@ static int rda_uart_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
dev_err(&pdev->dev, "could not get irq\n");
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
}
|
||||
|
||||
if (rda_uart_ports[pdev->id]) {
|
||||
dev_err(&pdev->dev, "port %d already allocated\n", pdev->id);
|
||||
|
@ -961,7 +961,6 @@ static int sccnxp_probe(struct platform_device *pdev)
|
||||
if (!s->poll) {
|
||||
s->irq = platform_get_irq(pdev, 0);
|
||||
if (s->irq < 0) {
|
||||
dev_err(&pdev->dev, "Missing irq resource data\n");
|
||||
ret = -ENXIO;
|
||||
goto err_out;
|
||||
}
|
||||
|
@ -4,7 +4,7 @@
|
||||
*
|
||||
* High-speed serial driver for NVIDIA Tegra SoCs
|
||||
*
|
||||
* Copyright (c) 2012-2013, NVIDIA CORPORATION. All rights reserved.
|
||||
* Copyright (c) 2012-2019, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Author: Laxman Dewangan <ldewangan@nvidia.com>
|
||||
*/
|
||||
@ -62,7 +62,7 @@
|
||||
#define TEGRA_UART_TX_TRIG_4B 0x20
|
||||
#define TEGRA_UART_TX_TRIG_1B 0x30
|
||||
|
||||
#define TEGRA_UART_MAXIMUM 5
|
||||
#define TEGRA_UART_MAXIMUM 8
|
||||
|
||||
/* Default UART setting when started: 115200 no parity, stop, 8 data bits */
|
||||
#define TEGRA_UART_DEFAULT_BAUD 115200
|
||||
@ -72,6 +72,8 @@
|
||||
#define TEGRA_TX_PIO 1
|
||||
#define TEGRA_TX_DMA 2
|
||||
|
||||
#define TEGRA_UART_FCR_IIR_FIFO_EN 0x40
|
||||
|
||||
/**
|
||||
* tegra_uart_chip_data: SOC specific data.
|
||||
*
|
||||
@ -84,6 +86,17 @@ struct tegra_uart_chip_data {
|
||||
bool tx_fifo_full_status;
|
||||
bool allow_txfifo_reset_fifo_mode;
|
||||
bool support_clk_src_div;
|
||||
bool fifo_mode_enable_status;
|
||||
int uart_max_port;
|
||||
int max_dma_burst_bytes;
|
||||
int error_tolerance_low_range;
|
||||
int error_tolerance_high_range;
|
||||
};
|
||||
|
||||
struct tegra_baud_tolerance {
|
||||
u32 lower_range_baud;
|
||||
u32 upper_range_baud;
|
||||
s32 tolerance;
|
||||
};
|
||||
|
||||
struct tegra_uart_port {
|
||||
@ -122,10 +135,18 @@ struct tegra_uart_port {
|
||||
dma_cookie_t rx_cookie;
|
||||
unsigned int tx_bytes_requested;
|
||||
unsigned int rx_bytes_requested;
|
||||
struct tegra_baud_tolerance *baud_tolerance;
|
||||
int n_adjustable_baud_rates;
|
||||
int required_rate;
|
||||
int configured_rate;
|
||||
bool use_rx_pio;
|
||||
bool use_tx_pio;
|
||||
};
|
||||
|
||||
static void tegra_uart_start_next_tx(struct tegra_uart_port *tup);
|
||||
static int tegra_uart_start_rx_dma(struct tegra_uart_port *tup);
|
||||
static void tegra_uart_dma_channel_free(struct tegra_uart_port *tup,
|
||||
bool dma_to_memory);
|
||||
|
||||
static inline unsigned long tegra_uart_read(struct tegra_uart_port *tup,
|
||||
unsigned long reg)
|
||||
@ -192,16 +213,34 @@ static void set_dtr(struct tegra_uart_port *tup, bool active)
|
||||
}
|
||||
}
|
||||
|
||||
static void set_loopbk(struct tegra_uart_port *tup, bool active)
|
||||
{
|
||||
unsigned long mcr = tup->mcr_shadow;
|
||||
|
||||
if (active)
|
||||
mcr |= UART_MCR_LOOP;
|
||||
else
|
||||
mcr &= ~UART_MCR_LOOP;
|
||||
|
||||
if (mcr != tup->mcr_shadow) {
|
||||
tegra_uart_write(tup, mcr, UART_MCR);
|
||||
tup->mcr_shadow = mcr;
|
||||
}
|
||||
}
|
||||
|
||||
static void tegra_uart_set_mctrl(struct uart_port *u, unsigned int mctrl)
|
||||
{
|
||||
struct tegra_uart_port *tup = to_tegra_uport(u);
|
||||
int dtr_enable;
|
||||
int enable;
|
||||
|
||||
tup->rts_active = !!(mctrl & TIOCM_RTS);
|
||||
set_rts(tup, tup->rts_active);
|
||||
|
||||
dtr_enable = !!(mctrl & TIOCM_DTR);
|
||||
set_dtr(tup, dtr_enable);
|
||||
enable = !!(mctrl & TIOCM_DTR);
|
||||
set_dtr(tup, enable);
|
||||
|
||||
enable = !!(mctrl & TIOCM_LOOP);
|
||||
set_loopbk(tup, enable);
|
||||
}
|
||||
|
||||
static void tegra_uart_break_ctl(struct uart_port *u, int break_ctl)
|
||||
@ -243,9 +282,28 @@ static void tegra_uart_wait_sym_time(struct tegra_uart_port *tup,
|
||||
tup->current_baud));
|
||||
}
|
||||
|
||||
static int tegra_uart_wait_fifo_mode_enabled(struct tegra_uart_port *tup)
|
||||
{
|
||||
unsigned long iir;
|
||||
unsigned int tmout = 100;
|
||||
|
||||
do {
|
||||
iir = tegra_uart_read(tup, UART_IIR);
|
||||
if (iir & TEGRA_UART_FCR_IIR_FIFO_EN)
|
||||
return 0;
|
||||
udelay(1);
|
||||
} while (--tmout);
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static void tegra_uart_fifo_reset(struct tegra_uart_port *tup, u8 fcr_bits)
|
||||
{
|
||||
unsigned long fcr = tup->fcr_shadow;
|
||||
unsigned int lsr, tmout = 10000;
|
||||
|
||||
if (tup->rts_active)
|
||||
set_rts(tup, false);
|
||||
|
||||
if (tup->cdata->allow_txfifo_reset_fifo_mode) {
|
||||
fcr |= fcr_bits & (UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
|
||||
@ -258,6 +316,8 @@ static void tegra_uart_fifo_reset(struct tegra_uart_port *tup, u8 fcr_bits)
|
||||
tegra_uart_write(tup, fcr, UART_FCR);
|
||||
fcr |= UART_FCR_ENABLE_FIFO;
|
||||
tegra_uart_write(tup, fcr, UART_FCR);
|
||||
if (tup->cdata->fifo_mode_enable_status)
|
||||
tegra_uart_wait_fifo_mode_enabled(tup);
|
||||
}
|
||||
|
||||
/* Dummy read to ensure the write is posted */
|
||||
@ -269,6 +329,47 @@ static void tegra_uart_fifo_reset(struct tegra_uart_port *tup, u8 fcr_bits)
|
||||
* to propagate, otherwise data could be lost.
|
||||
*/
|
||||
tegra_uart_wait_cycle_time(tup, 32);
|
||||
|
||||
do {
|
||||
lsr = tegra_uart_read(tup, UART_LSR);
|
||||
if ((lsr | UART_LSR_TEMT) && !(lsr & UART_LSR_DR))
|
||||
break;
|
||||
udelay(1);
|
||||
} while (--tmout);
|
||||
|
||||
if (tup->rts_active)
|
||||
set_rts(tup, true);
|
||||
}
|
||||
|
||||
static long tegra_get_tolerance_rate(struct tegra_uart_port *tup,
|
||||
unsigned int baud, long rate)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < tup->n_adjustable_baud_rates; ++i) {
|
||||
if (baud >= tup->baud_tolerance[i].lower_range_baud &&
|
||||
baud <= tup->baud_tolerance[i].upper_range_baud)
|
||||
return (rate + (rate *
|
||||
tup->baud_tolerance[i].tolerance) / 10000);
|
||||
}
|
||||
|
||||
return rate;
|
||||
}
|
||||
|
||||
static int tegra_check_rate_in_range(struct tegra_uart_port *tup)
|
||||
{
|
||||
long diff;
|
||||
|
||||
diff = ((long)(tup->configured_rate - tup->required_rate) * 10000)
|
||||
/ tup->required_rate;
|
||||
if (diff < (tup->cdata->error_tolerance_low_range * 100) ||
|
||||
diff > (tup->cdata->error_tolerance_high_range * 100)) {
|
||||
dev_err(tup->uport.dev,
|
||||
"configured baud rate is out of range by %ld", diff);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra_set_baudrate(struct tegra_uart_port *tup, unsigned int baud)
|
||||
@ -276,6 +377,7 @@ static int tegra_set_baudrate(struct tegra_uart_port *tup, unsigned int baud)
|
||||
unsigned long rate;
|
||||
unsigned int divisor;
|
||||
unsigned long lcr;
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
if (tup->current_baud == baud)
|
||||
@ -283,18 +385,28 @@ static int tegra_set_baudrate(struct tegra_uart_port *tup, unsigned int baud)
|
||||
|
||||
if (tup->cdata->support_clk_src_div) {
|
||||
rate = baud * 16;
|
||||
tup->required_rate = rate;
|
||||
|
||||
if (tup->n_adjustable_baud_rates)
|
||||
rate = tegra_get_tolerance_rate(tup, baud, rate);
|
||||
|
||||
ret = clk_set_rate(tup->uart_clk, rate);
|
||||
if (ret < 0) {
|
||||
dev_err(tup->uport.dev,
|
||||
"clk_set_rate() failed for rate %lu\n", rate);
|
||||
return ret;
|
||||
}
|
||||
tup->configured_rate = clk_get_rate(tup->uart_clk);
|
||||
divisor = 1;
|
||||
ret = tegra_check_rate_in_range(tup);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
} else {
|
||||
rate = clk_get_rate(tup->uart_clk);
|
||||
divisor = DIV_ROUND_CLOSEST(rate, baud * 16);
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&tup->uport.lock, flags);
|
||||
lcr = tup->lcr_shadow;
|
||||
lcr |= UART_LCR_DLAB;
|
||||
tegra_uart_write(tup, lcr, UART_LCR);
|
||||
@ -307,6 +419,7 @@ static int tegra_set_baudrate(struct tegra_uart_port *tup, unsigned int baud)
|
||||
|
||||
/* Dummy read to ensure the write is posted */
|
||||
tegra_uart_read(tup, UART_SCR);
|
||||
spin_unlock_irqrestore(&tup->uport.lock, flags);
|
||||
|
||||
tup->current_baud = baud;
|
||||
|
||||
@ -336,13 +449,21 @@ static char tegra_uart_decode_rx_error(struct tegra_uart_port *tup,
|
||||
tup->uport.icount.frame++;
|
||||
dev_err(tup->uport.dev, "Got frame errors\n");
|
||||
} else if (lsr & UART_LSR_BI) {
|
||||
dev_err(tup->uport.dev, "Got Break\n");
|
||||
tup->uport.icount.brk++;
|
||||
/* If FIFO read error without any data, reset Rx FIFO */
|
||||
/*
|
||||
* Break error
|
||||
* If FIFO read error without any data, reset Rx FIFO
|
||||
*/
|
||||
if (!(lsr & UART_LSR_DR) && (lsr & UART_LSR_FIFOE))
|
||||
tegra_uart_fifo_reset(tup, UART_FCR_CLEAR_RCVR);
|
||||
if (tup->uport.ignore_status_mask & UART_LSR_BI)
|
||||
return TTY_BREAK;
|
||||
flag = TTY_BREAK;
|
||||
tup->uport.icount.brk++;
|
||||
dev_dbg(tup->uport.dev, "Got Break\n");
|
||||
}
|
||||
uart_insert_char(&tup->uport, lsr, UART_LSR_OE, 0, flag);
|
||||
}
|
||||
|
||||
return flag;
|
||||
}
|
||||
|
||||
@ -440,12 +561,15 @@ static void tegra_uart_start_next_tx(struct tegra_uart_port *tup)
|
||||
unsigned long count;
|
||||
struct circ_buf *xmit = &tup->uport.state->xmit;
|
||||
|
||||
if (!tup->current_baud)
|
||||
return;
|
||||
|
||||
tail = (unsigned long)&xmit->buf[xmit->tail];
|
||||
count = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
|
||||
if (!count)
|
||||
return;
|
||||
|
||||
if (count < TEGRA_UART_MIN_DMA)
|
||||
if (tup->use_tx_pio || count < TEGRA_UART_MIN_DMA)
|
||||
tegra_uart_start_pio_tx(tup, count);
|
||||
else if (BYTES_TO_ALIGN(tail) > 0)
|
||||
tegra_uart_start_pio_tx(tup, BYTES_TO_ALIGN(tail));
|
||||
@ -521,11 +645,17 @@ static void tegra_uart_handle_rx_pio(struct tegra_uart_port *tup,
|
||||
break;
|
||||
|
||||
flag = tegra_uart_decode_rx_error(tup, lsr);
|
||||
if (flag != TTY_NORMAL)
|
||||
continue;
|
||||
|
||||
ch = (unsigned char) tegra_uart_read(tup, UART_RX);
|
||||
tup->uport.icount.rx++;
|
||||
|
||||
if (!uart_handle_sysrq_char(&tup->uport, ch) && tty)
|
||||
tty_insert_flip_char(tty, ch, flag);
|
||||
|
||||
if (tup->uport.ignore_status_mask & UART_LSR_DR)
|
||||
continue;
|
||||
} while (1);
|
||||
}
|
||||
|
||||
@ -544,6 +674,10 @@ static void tegra_uart_copy_rx_to_tty(struct tegra_uart_port *tup,
|
||||
dev_err(tup->uport.dev, "No tty port\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (tup->uport.ignore_status_mask & UART_LSR_DR)
|
||||
return;
|
||||
|
||||
dma_sync_single_for_cpu(tup->uport.dev, tup->rx_dma_buf_phys,
|
||||
TEGRA_UART_RX_DMA_BUFFER_SIZE, DMA_FROM_DEVICE);
|
||||
copied = tty_insert_flip_string(tty,
|
||||
@ -668,6 +802,18 @@ static void tegra_uart_handle_modem_signal_change(struct uart_port *u)
|
||||
uart_handle_cts_change(&tup->uport, msr & UART_MSR_CTS);
|
||||
}
|
||||
|
||||
static void do_handle_rx_pio(struct tegra_uart_port *tup)
|
||||
{
|
||||
struct tty_struct *tty = tty_port_tty_get(&tup->uport.state->port);
|
||||
struct tty_port *port = &tup->uport.state->port;
|
||||
|
||||
tegra_uart_handle_rx_pio(tup, port);
|
||||
if (tty) {
|
||||
tty_flip_buffer_push(port);
|
||||
tty_kref_put(tty);
|
||||
}
|
||||
}
|
||||
|
||||
static irqreturn_t tegra_uart_isr(int irq, void *data)
|
||||
{
|
||||
struct tegra_uart_port *tup = data;
|
||||
@ -681,7 +827,7 @@ static irqreturn_t tegra_uart_isr(int irq, void *data)
|
||||
while (1) {
|
||||
iir = tegra_uart_read(tup, UART_IIR);
|
||||
if (iir & UART_IIR_NO_INT) {
|
||||
if (is_rx_int) {
|
||||
if (!tup->use_rx_pio && is_rx_int) {
|
||||
tegra_uart_handle_rx_dma(tup);
|
||||
if (tup->rx_in_progress) {
|
||||
ier = tup->ier_shadow;
|
||||
@ -709,7 +855,7 @@ static irqreturn_t tegra_uart_isr(int irq, void *data)
|
||||
case 4: /* End of data */
|
||||
case 6: /* Rx timeout */
|
||||
case 2: /* Receive */
|
||||
if (!is_rx_int) {
|
||||
if (!tup->use_rx_pio && !is_rx_int) {
|
||||
is_rx_int = true;
|
||||
/* Disable Rx interrupts */
|
||||
ier = tup->ier_shadow;
|
||||
@ -719,6 +865,8 @@ static irqreturn_t tegra_uart_isr(int irq, void *data)
|
||||
UART_IER_RTOIE | TEGRA_UART_IER_EORD);
|
||||
tup->ier_shadow = ier;
|
||||
tegra_uart_write(tup, ier, UART_IER);
|
||||
} else {
|
||||
do_handle_rx_pio(tup);
|
||||
}
|
||||
break;
|
||||
|
||||
@ -737,6 +885,7 @@ static irqreturn_t tegra_uart_isr(int irq, void *data)
|
||||
static void tegra_uart_stop_rx(struct uart_port *u)
|
||||
{
|
||||
struct tegra_uart_port *tup = to_tegra_uport(u);
|
||||
struct tty_port *port = &tup->uport.state->port;
|
||||
struct dma_tx_state state;
|
||||
unsigned long ier;
|
||||
|
||||
@ -754,9 +903,13 @@ static void tegra_uart_stop_rx(struct uart_port *u)
|
||||
tup->ier_shadow = ier;
|
||||
tegra_uart_write(tup, ier, UART_IER);
|
||||
tup->rx_in_progress = 0;
|
||||
dmaengine_terminate_all(tup->rx_dma_chan);
|
||||
dmaengine_tx_status(tup->rx_dma_chan, tup->rx_cookie, &state);
|
||||
tegra_uart_rx_buffer_push(tup, state.residue);
|
||||
if (tup->rx_dma_chan && !tup->use_rx_pio) {
|
||||
dmaengine_terminate_all(tup->rx_dma_chan);
|
||||
dmaengine_tx_status(tup->rx_dma_chan, tup->rx_cookie, &state);
|
||||
tegra_uart_rx_buffer_push(tup, state.residue);
|
||||
} else {
|
||||
tegra_uart_handle_rx_pio(tup, port);
|
||||
}
|
||||
}
|
||||
|
||||
static void tegra_uart_hw_deinit(struct tegra_uart_port *tup)
|
||||
@ -804,6 +957,14 @@ static void tegra_uart_hw_deinit(struct tegra_uart_port *tup)
|
||||
tup->current_baud = 0;
|
||||
spin_unlock_irqrestore(&tup->uport.lock, flags);
|
||||
|
||||
tup->rx_in_progress = 0;
|
||||
tup->tx_in_progress = 0;
|
||||
|
||||
if (!tup->use_rx_pio)
|
||||
tegra_uart_dma_channel_free(tup, true);
|
||||
if (!tup->use_tx_pio)
|
||||
tegra_uart_dma_channel_free(tup, false);
|
||||
|
||||
clk_disable_unprepare(tup->uart_clk);
|
||||
}
|
||||
|
||||
@ -846,35 +1007,60 @@ static int tegra_uart_hw_init(struct tegra_uart_port *tup)
|
||||
* programmed in the DMA registers.
|
||||
*/
|
||||
tup->fcr_shadow = UART_FCR_ENABLE_FIFO;
|
||||
tup->fcr_shadow |= UART_FCR_R_TRIG_01;
|
||||
|
||||
if (tup->use_rx_pio) {
|
||||
tup->fcr_shadow |= UART_FCR_R_TRIG_11;
|
||||
} else {
|
||||
if (tup->cdata->max_dma_burst_bytes == 8)
|
||||
tup->fcr_shadow |= UART_FCR_R_TRIG_10;
|
||||
else
|
||||
tup->fcr_shadow |= UART_FCR_R_TRIG_01;
|
||||
}
|
||||
|
||||
tup->fcr_shadow |= TEGRA_UART_TX_TRIG_16B;
|
||||
tegra_uart_write(tup, tup->fcr_shadow, UART_FCR);
|
||||
|
||||
/* Dummy read to ensure the write is posted */
|
||||
tegra_uart_read(tup, UART_SCR);
|
||||
|
||||
/*
|
||||
* For all tegra devices (up to t210), there is a hardware issue that
|
||||
* requires software to wait for 3 UART clock periods after enabling
|
||||
* the TX fifo, otherwise data could be lost.
|
||||
*/
|
||||
tegra_uart_wait_cycle_time(tup, 3);
|
||||
if (tup->cdata->fifo_mode_enable_status) {
|
||||
ret = tegra_uart_wait_fifo_mode_enabled(tup);
|
||||
dev_err(tup->uport.dev, "FIFO mode not enabled\n");
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
} else {
|
||||
/*
|
||||
* For all tegra devices (up to t210), there is a hardware
|
||||
* issue that requires software to wait for 3 UART clock
|
||||
* periods after enabling the TX fifo, otherwise data could
|
||||
* be lost.
|
||||
*/
|
||||
tegra_uart_wait_cycle_time(tup, 3);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize the UART with default configuration
|
||||
* (115200, N, 8, 1) so that the receive DMA buffer may be
|
||||
* enqueued
|
||||
*/
|
||||
tup->lcr_shadow = TEGRA_UART_DEFAULT_LSR;
|
||||
tegra_set_baudrate(tup, TEGRA_UART_DEFAULT_BAUD);
|
||||
tup->fcr_shadow |= UART_FCR_DMA_SELECT;
|
||||
tegra_uart_write(tup, tup->fcr_shadow, UART_FCR);
|
||||
|
||||
ret = tegra_uart_start_rx_dma(tup);
|
||||
ret = tegra_set_baudrate(tup, TEGRA_UART_DEFAULT_BAUD);
|
||||
if (ret < 0) {
|
||||
dev_err(tup->uport.dev, "Not able to start Rx DMA\n");
|
||||
dev_err(tup->uport.dev, "Failed to set baud rate\n");
|
||||
return ret;
|
||||
}
|
||||
if (!tup->use_rx_pio) {
|
||||
tup->lcr_shadow = TEGRA_UART_DEFAULT_LSR;
|
||||
tup->fcr_shadow |= UART_FCR_DMA_SELECT;
|
||||
tegra_uart_write(tup, tup->fcr_shadow, UART_FCR);
|
||||
|
||||
ret = tegra_uart_start_rx_dma(tup);
|
||||
if (ret < 0) {
|
||||
dev_err(tup->uport.dev, "Not able to start Rx DMA\n");
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
tegra_uart_write(tup, tup->fcr_shadow, UART_FCR);
|
||||
}
|
||||
tup->rx_in_progress = 1;
|
||||
|
||||
/*
|
||||
@ -895,7 +1081,12 @@ static int tegra_uart_hw_init(struct tegra_uart_port *tup)
|
||||
* both the EORD as well as RX_TIMEOUT - SW sees RX_TIMEOUT first
|
||||
* then the EORD.
|
||||
*/
|
||||
tup->ier_shadow = UART_IER_RLSI | UART_IER_RTOIE | TEGRA_UART_IER_EORD;
|
||||
if (!tup->use_rx_pio)
|
||||
tup->ier_shadow = UART_IER_RLSI | UART_IER_RTOIE |
|
||||
TEGRA_UART_IER_EORD;
|
||||
else
|
||||
tup->ier_shadow = UART_IER_RLSI | UART_IER_RTOIE | UART_IER_RDI;
|
||||
|
||||
tegra_uart_write(tup, tup->ier_shadow, UART_IER);
|
||||
return 0;
|
||||
}
|
||||
@ -952,7 +1143,7 @@ static int tegra_uart_dma_channel_allocate(struct tegra_uart_port *tup,
|
||||
}
|
||||
dma_sconfig.src_addr = tup->uport.mapbase;
|
||||
dma_sconfig.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
|
||||
dma_sconfig.src_maxburst = 4;
|
||||
dma_sconfig.src_maxburst = tup->cdata->max_dma_burst_bytes;
|
||||
tup->rx_dma_chan = dma_chan;
|
||||
tup->rx_dma_buf_virt = dma_buf;
|
||||
tup->rx_dma_buf_phys = dma_phys;
|
||||
@ -990,16 +1181,22 @@ static int tegra_uart_startup(struct uart_port *u)
|
||||
struct tegra_uart_port *tup = to_tegra_uport(u);
|
||||
int ret;
|
||||
|
||||
ret = tegra_uart_dma_channel_allocate(tup, false);
|
||||
if (ret < 0) {
|
||||
dev_err(u->dev, "Tx Dma allocation failed, err = %d\n", ret);
|
||||
return ret;
|
||||
if (!tup->use_tx_pio) {
|
||||
ret = tegra_uart_dma_channel_allocate(tup, false);
|
||||
if (ret < 0) {
|
||||
dev_err(u->dev, "Tx Dma allocation failed, err = %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = tegra_uart_dma_channel_allocate(tup, true);
|
||||
if (ret < 0) {
|
||||
dev_err(u->dev, "Rx Dma allocation failed, err = %d\n", ret);
|
||||
goto fail_rx_dma;
|
||||
if (!tup->use_rx_pio) {
|
||||
ret = tegra_uart_dma_channel_allocate(tup, true);
|
||||
if (ret < 0) {
|
||||
dev_err(u->dev, "Rx Dma allocation failed, err = %d\n",
|
||||
ret);
|
||||
goto fail_rx_dma;
|
||||
}
|
||||
}
|
||||
|
||||
ret = tegra_uart_hw_init(tup);
|
||||
@ -1017,9 +1214,11 @@ static int tegra_uart_startup(struct uart_port *u)
|
||||
return 0;
|
||||
|
||||
fail_hw_init:
|
||||
tegra_uart_dma_channel_free(tup, true);
|
||||
if (!tup->use_rx_pio)
|
||||
tegra_uart_dma_channel_free(tup, true);
|
||||
fail_rx_dma:
|
||||
tegra_uart_dma_channel_free(tup, false);
|
||||
if (!tup->use_tx_pio)
|
||||
tegra_uart_dma_channel_free(tup, false);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -1041,12 +1240,6 @@ static void tegra_uart_shutdown(struct uart_port *u)
|
||||
struct tegra_uart_port *tup = to_tegra_uport(u);
|
||||
|
||||
tegra_uart_hw_deinit(tup);
|
||||
|
||||
tup->rx_in_progress = 0;
|
||||
tup->tx_in_progress = 0;
|
||||
|
||||
tegra_uart_dma_channel_free(tup, true);
|
||||
tegra_uart_dma_channel_free(tup, false);
|
||||
free_irq(u->irq, tup);
|
||||
}
|
||||
|
||||
@ -1071,6 +1264,7 @@ static void tegra_uart_set_termios(struct uart_port *u,
|
||||
struct clk *parent_clk = clk_get_parent(tup->uart_clk);
|
||||
unsigned long parent_clk_rate = clk_get_rate(parent_clk);
|
||||
int max_divider = (tup->cdata->support_clk_src_div) ? 0x7FFF : 0xFFFF;
|
||||
int ret;
|
||||
|
||||
max_divider *= 16;
|
||||
spin_lock_irqsave(&u->lock, flags);
|
||||
@ -1143,7 +1337,11 @@ static void tegra_uart_set_termios(struct uart_port *u,
|
||||
parent_clk_rate/max_divider,
|
||||
parent_clk_rate/16);
|
||||
spin_unlock_irqrestore(&u->lock, flags);
|
||||
tegra_set_baudrate(tup, baud);
|
||||
ret = tegra_set_baudrate(tup, baud);
|
||||
if (ret < 0) {
|
||||
dev_err(tup->uport.dev, "Failed to set baud rate\n");
|
||||
return;
|
||||
}
|
||||
if (tty_termios_baud_rate(termios))
|
||||
tty_termios_encode_baud_rate(termios, baud, baud);
|
||||
spin_lock_irqsave(&u->lock, flags);
|
||||
@ -1172,6 +1370,13 @@ static void tegra_uart_set_termios(struct uart_port *u,
|
||||
tegra_uart_write(tup, tup->ier_shadow, UART_IER);
|
||||
tegra_uart_read(tup, UART_IER);
|
||||
|
||||
tup->uport.ignore_status_mask = 0;
|
||||
/* Ignore all characters if CREAD is not set */
|
||||
if ((termios->c_cflag & CREAD) == 0)
|
||||
tup->uport.ignore_status_mask |= UART_LSR_DR;
|
||||
if (termios->c_iflag & IGNBRK)
|
||||
tup->uport.ignore_status_mask |= UART_LSR_BI;
|
||||
|
||||
spin_unlock_irqrestore(&u->lock, flags);
|
||||
}
|
||||
|
||||
@ -1211,6 +1416,11 @@ static int tegra_uart_parse_dt(struct platform_device *pdev,
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
int port;
|
||||
int ret;
|
||||
int index;
|
||||
u32 pval;
|
||||
int count;
|
||||
int n_entries;
|
||||
|
||||
port = of_alias_get_id(np, "serial");
|
||||
if (port < 0) {
|
||||
@ -1221,6 +1431,54 @@ static int tegra_uart_parse_dt(struct platform_device *pdev,
|
||||
|
||||
tup->enable_modem_interrupt = of_property_read_bool(np,
|
||||
"nvidia,enable-modem-interrupt");
|
||||
|
||||
index = of_property_match_string(np, "dma-names", "rx");
|
||||
if (index < 0) {
|
||||
tup->use_rx_pio = true;
|
||||
dev_info(&pdev->dev, "RX in PIO mode\n");
|
||||
}
|
||||
index = of_property_match_string(np, "dma-names", "tx");
|
||||
if (index < 0) {
|
||||
tup->use_tx_pio = true;
|
||||
dev_info(&pdev->dev, "TX in PIO mode\n");
|
||||
}
|
||||
|
||||
n_entries = of_property_count_u32_elems(np, "nvidia,adjust-baud-rates");
|
||||
if (n_entries > 0) {
|
||||
tup->n_adjustable_baud_rates = n_entries / 3;
|
||||
tup->baud_tolerance =
|
||||
devm_kzalloc(&pdev->dev, (tup->n_adjustable_baud_rates) *
|
||||
sizeof(*tup->baud_tolerance), GFP_KERNEL);
|
||||
if (!tup->baud_tolerance)
|
||||
return -ENOMEM;
|
||||
for (count = 0, index = 0; count < n_entries; count += 3,
|
||||
index++) {
|
||||
ret =
|
||||
of_property_read_u32_index(np,
|
||||
"nvidia,adjust-baud-rates",
|
||||
count, &pval);
|
||||
if (!ret)
|
||||
tup->baud_tolerance[index].lower_range_baud =
|
||||
pval;
|
||||
ret =
|
||||
of_property_read_u32_index(np,
|
||||
"nvidia,adjust-baud-rates",
|
||||
count + 1, &pval);
|
||||
if (!ret)
|
||||
tup->baud_tolerance[index].upper_range_baud =
|
||||
pval;
|
||||
ret =
|
||||
of_property_read_u32_index(np,
|
||||
"nvidia,adjust-baud-rates",
|
||||
count + 2, &pval);
|
||||
if (!ret)
|
||||
tup->baud_tolerance[index].tolerance =
|
||||
(s32)pval;
|
||||
}
|
||||
} else {
|
||||
tup->n_adjustable_baud_rates = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1228,12 +1486,44 @@ static struct tegra_uart_chip_data tegra20_uart_chip_data = {
|
||||
.tx_fifo_full_status = false,
|
||||
.allow_txfifo_reset_fifo_mode = true,
|
||||
.support_clk_src_div = false,
|
||||
.fifo_mode_enable_status = false,
|
||||
.uart_max_port = 5,
|
||||
.max_dma_burst_bytes = 4,
|
||||
.error_tolerance_low_range = 0,
|
||||
.error_tolerance_high_range = 4,
|
||||
};
|
||||
|
||||
static struct tegra_uart_chip_data tegra30_uart_chip_data = {
|
||||
.tx_fifo_full_status = true,
|
||||
.allow_txfifo_reset_fifo_mode = false,
|
||||
.support_clk_src_div = true,
|
||||
.fifo_mode_enable_status = false,
|
||||
.uart_max_port = 5,
|
||||
.max_dma_burst_bytes = 4,
|
||||
.error_tolerance_low_range = 0,
|
||||
.error_tolerance_high_range = 4,
|
||||
};
|
||||
|
||||
static struct tegra_uart_chip_data tegra186_uart_chip_data = {
|
||||
.tx_fifo_full_status = true,
|
||||
.allow_txfifo_reset_fifo_mode = false,
|
||||
.support_clk_src_div = true,
|
||||
.fifo_mode_enable_status = true,
|
||||
.uart_max_port = 8,
|
||||
.max_dma_burst_bytes = 8,
|
||||
.error_tolerance_low_range = 0,
|
||||
.error_tolerance_high_range = 4,
|
||||
};
|
||||
|
||||
static struct tegra_uart_chip_data tegra194_uart_chip_data = {
|
||||
.tx_fifo_full_status = true,
|
||||
.allow_txfifo_reset_fifo_mode = false,
|
||||
.support_clk_src_div = true,
|
||||
.fifo_mode_enable_status = true,
|
||||
.uart_max_port = 8,
|
||||
.max_dma_burst_bytes = 8,
|
||||
.error_tolerance_low_range = -2,
|
||||
.error_tolerance_high_range = 2,
|
||||
};
|
||||
|
||||
static const struct of_device_id tegra_uart_of_match[] = {
|
||||
@ -1243,6 +1533,12 @@ static const struct of_device_id tegra_uart_of_match[] = {
|
||||
}, {
|
||||
.compatible = "nvidia,tegra20-hsuart",
|
||||
.data = &tegra20_uart_chip_data,
|
||||
}, {
|
||||
.compatible = "nvidia,tegra186-hsuart",
|
||||
.data = &tegra186_uart_chip_data,
|
||||
}, {
|
||||
.compatible = "nvidia,tegra194-hsuart",
|
||||
.data = &tegra194_uart_chip_data,
|
||||
}, {
|
||||
},
|
||||
};
|
||||
@ -1307,10 +1603,8 @@ static int tegra_uart_probe(struct platform_device *pdev)
|
||||
|
||||
u->iotype = UPIO_MEM32;
|
||||
ret = platform_get_irq(pdev, 0);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "Couldn't get IRQ\n");
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
u->irq = ret;
|
||||
u->regshift = 2;
|
||||
ret = uart_add_one_port(&tegra_uart_driver, u);
|
||||
@ -1365,11 +1659,22 @@ static struct platform_driver tegra_uart_platform_driver = {
|
||||
static int __init tegra_uart_init(void)
|
||||
{
|
||||
int ret;
|
||||
struct device_node *node;
|
||||
const struct of_device_id *match = NULL;
|
||||
const struct tegra_uart_chip_data *cdata = NULL;
|
||||
|
||||
node = of_find_matching_node(NULL, tegra_uart_of_match);
|
||||
if (node)
|
||||
match = of_match_node(tegra_uart_of_match, node);
|
||||
if (match)
|
||||
cdata = match->data;
|
||||
if (cdata)
|
||||
tegra_uart_driver.nr = cdata->uart_max_port;
|
||||
|
||||
ret = uart_register_driver(&tegra_uart_driver);
|
||||
if (ret < 0) {
|
||||
pr_err("Could not register %s driver\n",
|
||||
tegra_uart_driver.driver_name);
|
||||
tegra_uart_driver.driver_name);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -1,698 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Driver for KS8695 serial ports
|
||||
*
|
||||
* Based on drivers/serial/serial_amba.c, by Kam Lee.
|
||||
*
|
||||
* Copyright 2002-2005 Micrel Inc.
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/tty.h>
|
||||
#include <linux/tty_flip.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/serial.h>
|
||||
#include <linux/console.h>
|
||||
#include <linux/sysrq.h>
|
||||
#include <linux/device.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/mach/irq.h>
|
||||
|
||||
#include <mach/regs-uart.h>
|
||||
#include <mach/regs-irq.h>
|
||||
|
||||
#if defined(CONFIG_SERIAL_KS8695_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
|
||||
#define SUPPORT_SYSRQ
|
||||
#endif
|
||||
|
||||
#include <linux/serial_core.h>
|
||||
|
||||
|
||||
#define SERIAL_KS8695_MAJOR 204
|
||||
#define SERIAL_KS8695_MINOR 16
|
||||
#define SERIAL_KS8695_DEVNAME "ttyAM"
|
||||
|
||||
#define SERIAL_KS8695_NR 1
|
||||
|
||||
/*
|
||||
* Access macros for the KS8695 UART
|
||||
*/
|
||||
#define UART_GET_CHAR(p) (__raw_readl((p)->membase + KS8695_URRB) & 0xFF)
|
||||
#define UART_PUT_CHAR(p, c) __raw_writel((c), (p)->membase + KS8695_URTH)
|
||||
#define UART_GET_FCR(p) __raw_readl((p)->membase + KS8695_URFC)
|
||||
#define UART_PUT_FCR(p, c) __raw_writel((c), (p)->membase + KS8695_URFC)
|
||||
#define UART_GET_MSR(p) __raw_readl((p)->membase + KS8695_URMS)
|
||||
#define UART_GET_LSR(p) __raw_readl((p)->membase + KS8695_URLS)
|
||||
#define UART_GET_LCR(p) __raw_readl((p)->membase + KS8695_URLC)
|
||||
#define UART_PUT_LCR(p, c) __raw_writel((c), (p)->membase + KS8695_URLC)
|
||||
#define UART_GET_MCR(p) __raw_readl((p)->membase + KS8695_URMC)
|
||||
#define UART_PUT_MCR(p, c) __raw_writel((c), (p)->membase + KS8695_URMC)
|
||||
#define UART_GET_BRDR(p) __raw_readl((p)->membase + KS8695_URBD)
|
||||
#define UART_PUT_BRDR(p, c) __raw_writel((c), (p)->membase + KS8695_URBD)
|
||||
|
||||
#define KS8695_CLR_TX_INT() __raw_writel(1 << KS8695_IRQ_UART_TX, KS8695_IRQ_VA + KS8695_INTST)
|
||||
|
||||
#define UART_DUMMY_LSR_RX 0x100
|
||||
#define UART_PORT_SIZE (KS8695_USR - KS8695_URRB + 4)
|
||||
|
||||
static inline int tx_enabled(struct uart_port *port)
|
||||
{
|
||||
return port->unused[0] & 1;
|
||||
}
|
||||
|
||||
static inline int rx_enabled(struct uart_port *port)
|
||||
{
|
||||
return port->unused[0] & 2;
|
||||
}
|
||||
|
||||
static inline int ms_enabled(struct uart_port *port)
|
||||
{
|
||||
return port->unused[0] & 4;
|
||||
}
|
||||
|
||||
static inline void ms_enable(struct uart_port *port, int enabled)
|
||||
{
|
||||
if(enabled)
|
||||
port->unused[0] |= 4;
|
||||
else
|
||||
port->unused[0] &= ~4;
|
||||
}
|
||||
|
||||
static inline void rx_enable(struct uart_port *port, int enabled)
|
||||
{
|
||||
if(enabled)
|
||||
port->unused[0] |= 2;
|
||||
else
|
||||
port->unused[0] &= ~2;
|
||||
}
|
||||
|
||||
static inline void tx_enable(struct uart_port *port, int enabled)
|
||||
{
|
||||
if(enabled)
|
||||
port->unused[0] |= 1;
|
||||
else
|
||||
port->unused[0] &= ~1;
|
||||
}
|
||||
|
||||
|
||||
#ifdef SUPPORT_SYSRQ
|
||||
static struct console ks8695_console;
|
||||
#endif
|
||||
|
||||
static void ks8695uart_stop_tx(struct uart_port *port)
|
||||
{
|
||||
if (tx_enabled(port)) {
|
||||
/* use disable_irq_nosync() and not disable_irq() to avoid self
|
||||
* imposed deadlock by not waiting for irq handler to end,
|
||||
* since this ks8695uart_stop_tx() is called from interrupt context.
|
||||
*/
|
||||
disable_irq_nosync(KS8695_IRQ_UART_TX);
|
||||
tx_enable(port, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void ks8695uart_start_tx(struct uart_port *port)
|
||||
{
|
||||
if (!tx_enabled(port)) {
|
||||
enable_irq(KS8695_IRQ_UART_TX);
|
||||
tx_enable(port, 1);
|
||||
}
|
||||
}
|
||||
|
||||
static void ks8695uart_stop_rx(struct uart_port *port)
|
||||
{
|
||||
if (rx_enabled(port)) {
|
||||
disable_irq(KS8695_IRQ_UART_RX);
|
||||
rx_enable(port, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void ks8695uart_enable_ms(struct uart_port *port)
|
||||
{
|
||||
if (!ms_enabled(port)) {
|
||||
enable_irq(KS8695_IRQ_UART_MODEM_STATUS);
|
||||
ms_enable(port,1);
|
||||
}
|
||||
}
|
||||
|
||||
static void ks8695uart_disable_ms(struct uart_port *port)
|
||||
{
|
||||
if (ms_enabled(port)) {
|
||||
disable_irq(KS8695_IRQ_UART_MODEM_STATUS);
|
||||
ms_enable(port,0);
|
||||
}
|
||||
}
|
||||
|
||||
static irqreturn_t ks8695uart_rx_chars(int irq, void *dev_id)
|
||||
{
|
||||
struct uart_port *port = dev_id;
|
||||
unsigned int status, ch, lsr, flg, max_count = 256;
|
||||
|
||||
status = UART_GET_LSR(port); /* clears pending LSR interrupts */
|
||||
while ((status & URLS_URDR) && max_count--) {
|
||||
ch = UART_GET_CHAR(port);
|
||||
flg = TTY_NORMAL;
|
||||
|
||||
port->icount.rx++;
|
||||
|
||||
/*
|
||||
* Note that the error handling code is
|
||||
* out of the main execution path
|
||||
*/
|
||||
lsr = UART_GET_LSR(port) | UART_DUMMY_LSR_RX;
|
||||
if (unlikely(lsr & (URLS_URBI | URLS_URPE | URLS_URFE | URLS_URROE))) {
|
||||
if (lsr & URLS_URBI) {
|
||||
lsr &= ~(URLS_URFE | URLS_URPE);
|
||||
port->icount.brk++;
|
||||
if (uart_handle_break(port))
|
||||
goto ignore_char;
|
||||
}
|
||||
if (lsr & URLS_URPE)
|
||||
port->icount.parity++;
|
||||
if (lsr & URLS_URFE)
|
||||
port->icount.frame++;
|
||||
if (lsr & URLS_URROE)
|
||||
port->icount.overrun++;
|
||||
|
||||
lsr &= port->read_status_mask;
|
||||
|
||||
if (lsr & URLS_URBI)
|
||||
flg = TTY_BREAK;
|
||||
else if (lsr & URLS_URPE)
|
||||
flg = TTY_PARITY;
|
||||
else if (lsr & URLS_URFE)
|
||||
flg = TTY_FRAME;
|
||||
}
|
||||
|
||||
if (uart_handle_sysrq_char(port, ch))
|
||||
goto ignore_char;
|
||||
|
||||
uart_insert_char(port, lsr, URLS_URROE, ch, flg);
|
||||
|
||||
ignore_char:
|
||||
status = UART_GET_LSR(port);
|
||||
}
|
||||
tty_flip_buffer_push(&port->state->port);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
|
||||
static irqreturn_t ks8695uart_tx_chars(int irq, void *dev_id)
|
||||
{
|
||||
struct uart_port *port = dev_id;
|
||||
struct circ_buf *xmit = &port->state->xmit;
|
||||
unsigned int count;
|
||||
|
||||
if (port->x_char) {
|
||||
KS8695_CLR_TX_INT();
|
||||
UART_PUT_CHAR(port, port->x_char);
|
||||
port->icount.tx++;
|
||||
port->x_char = 0;
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
if (uart_tx_stopped(port) || uart_circ_empty(xmit)) {
|
||||
ks8695uart_stop_tx(port);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
count = 16; /* fifo size */
|
||||
while (!uart_circ_empty(xmit) && (count-- > 0)) {
|
||||
KS8695_CLR_TX_INT();
|
||||
UART_PUT_CHAR(port, xmit->buf[xmit->tail]);
|
||||
|
||||
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
|
||||
port->icount.tx++;
|
||||
}
|
||||
|
||||
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
|
||||
uart_write_wakeup(port);
|
||||
|
||||
if (uart_circ_empty(xmit))
|
||||
ks8695uart_stop_tx(port);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t ks8695uart_modem_status(int irq, void *dev_id)
|
||||
{
|
||||
struct uart_port *port = dev_id;
|
||||
unsigned int status;
|
||||
|
||||
/*
|
||||
* clear modem interrupt by reading MSR
|
||||
*/
|
||||
status = UART_GET_MSR(port);
|
||||
|
||||
if (status & URMS_URDDCD)
|
||||
uart_handle_dcd_change(port, status & URMS_URDDCD);
|
||||
|
||||
if (status & URMS_URDDST)
|
||||
port->icount.dsr++;
|
||||
|
||||
if (status & URMS_URDCTS)
|
||||
uart_handle_cts_change(port, status & URMS_URDCTS);
|
||||
|
||||
if (status & URMS_URTERI)
|
||||
port->icount.rng++;
|
||||
|
||||
wake_up_interruptible(&port->state->port.delta_msr_wait);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static unsigned int ks8695uart_tx_empty(struct uart_port *port)
|
||||
{
|
||||
return (UART_GET_LSR(port) & URLS_URTE) ? TIOCSER_TEMT : 0;
|
||||
}
|
||||
|
||||
static unsigned int ks8695uart_get_mctrl(struct uart_port *port)
|
||||
{
|
||||
unsigned int result = 0;
|
||||
unsigned int status;
|
||||
|
||||
status = UART_GET_MSR(port);
|
||||
if (status & URMS_URDCD)
|
||||
result |= TIOCM_CAR;
|
||||
if (status & URMS_URDSR)
|
||||
result |= TIOCM_DSR;
|
||||
if (status & URMS_URCTS)
|
||||
result |= TIOCM_CTS;
|
||||
if (status & URMS_URRI)
|
||||
result |= TIOCM_RI;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void ks8695uart_set_mctrl(struct uart_port *port, u_int mctrl)
|
||||
{
|
||||
unsigned int mcr;
|
||||
|
||||
mcr = UART_GET_MCR(port);
|
||||
if (mctrl & TIOCM_RTS)
|
||||
mcr |= URMC_URRTS;
|
||||
else
|
||||
mcr &= ~URMC_URRTS;
|
||||
|
||||
if (mctrl & TIOCM_DTR)
|
||||
mcr |= URMC_URDTR;
|
||||
else
|
||||
mcr &= ~URMC_URDTR;
|
||||
|
||||
UART_PUT_MCR(port, mcr);
|
||||
}
|
||||
|
||||
static void ks8695uart_break_ctl(struct uart_port *port, int break_state)
|
||||
{
|
||||
unsigned int lcr;
|
||||
|
||||
lcr = UART_GET_LCR(port);
|
||||
|
||||
if (break_state == -1)
|
||||
lcr |= URLC_URSBC;
|
||||
else
|
||||
lcr &= ~URLC_URSBC;
|
||||
|
||||
UART_PUT_LCR(port, lcr);
|
||||
}
|
||||
|
||||
static int ks8695uart_startup(struct uart_port *port)
|
||||
{
|
||||
int retval;
|
||||
|
||||
irq_modify_status(KS8695_IRQ_UART_TX, IRQ_NOREQUEST, IRQ_NOAUTOEN);
|
||||
tx_enable(port, 0);
|
||||
rx_enable(port, 1);
|
||||
ms_enable(port, 1);
|
||||
|
||||
/*
|
||||
* Allocate the IRQ
|
||||
*/
|
||||
retval = request_irq(KS8695_IRQ_UART_TX, ks8695uart_tx_chars, 0, "UART TX", port);
|
||||
if (retval)
|
||||
goto err_tx;
|
||||
|
||||
retval = request_irq(KS8695_IRQ_UART_RX, ks8695uart_rx_chars, 0, "UART RX", port);
|
||||
if (retval)
|
||||
goto err_rx;
|
||||
|
||||
retval = request_irq(KS8695_IRQ_UART_LINE_STATUS, ks8695uart_rx_chars, 0, "UART LineStatus", port);
|
||||
if (retval)
|
||||
goto err_ls;
|
||||
|
||||
retval = request_irq(KS8695_IRQ_UART_MODEM_STATUS, ks8695uart_modem_status, 0, "UART ModemStatus", port);
|
||||
if (retval)
|
||||
goto err_ms;
|
||||
|
||||
return 0;
|
||||
|
||||
err_ms:
|
||||
free_irq(KS8695_IRQ_UART_LINE_STATUS, port);
|
||||
err_ls:
|
||||
free_irq(KS8695_IRQ_UART_RX, port);
|
||||
err_rx:
|
||||
free_irq(KS8695_IRQ_UART_TX, port);
|
||||
err_tx:
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void ks8695uart_shutdown(struct uart_port *port)
|
||||
{
|
||||
/*
|
||||
* Free the interrupt
|
||||
*/
|
||||
free_irq(KS8695_IRQ_UART_RX, port);
|
||||
free_irq(KS8695_IRQ_UART_TX, port);
|
||||
free_irq(KS8695_IRQ_UART_MODEM_STATUS, port);
|
||||
free_irq(KS8695_IRQ_UART_LINE_STATUS, port);
|
||||
|
||||
/* disable break condition and fifos */
|
||||
UART_PUT_LCR(port, UART_GET_LCR(port) & ~URLC_URSBC);
|
||||
UART_PUT_FCR(port, UART_GET_FCR(port) & ~URFC_URFE);
|
||||
}
|
||||
|
||||
static void ks8695uart_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old)
|
||||
{
|
||||
unsigned int lcr, fcr = 0;
|
||||
unsigned long flags;
|
||||
unsigned int baud, quot;
|
||||
|
||||
/*
|
||||
* Ask the core to calculate the divisor for us.
|
||||
*/
|
||||
baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16);
|
||||
quot = uart_get_divisor(port, baud);
|
||||
|
||||
switch (termios->c_cflag & CSIZE) {
|
||||
case CS5:
|
||||
lcr = URCL_5;
|
||||
break;
|
||||
case CS6:
|
||||
lcr = URCL_6;
|
||||
break;
|
||||
case CS7:
|
||||
lcr = URCL_7;
|
||||
break;
|
||||
default:
|
||||
lcr = URCL_8;
|
||||
break;
|
||||
}
|
||||
|
||||
/* stop bits */
|
||||
if (termios->c_cflag & CSTOPB)
|
||||
lcr |= URLC_URSB;
|
||||
|
||||
/* parity */
|
||||
if (termios->c_cflag & PARENB) {
|
||||
if (termios->c_cflag & CMSPAR) { /* Mark or Space parity */
|
||||
if (termios->c_cflag & PARODD)
|
||||
lcr |= URPE_MARK;
|
||||
else
|
||||
lcr |= URPE_SPACE;
|
||||
}
|
||||
else if (termios->c_cflag & PARODD)
|
||||
lcr |= URPE_ODD;
|
||||
else
|
||||
lcr |= URPE_EVEN;
|
||||
}
|
||||
|
||||
if (port->fifosize > 1)
|
||||
fcr = URFC_URFRT_8 | URFC_URTFR | URFC_URRFR | URFC_URFE;
|
||||
|
||||
spin_lock_irqsave(&port->lock, flags);
|
||||
|
||||
/*
|
||||
* Update the per-port timeout.
|
||||
*/
|
||||
uart_update_timeout(port, termios->c_cflag, baud);
|
||||
|
||||
port->read_status_mask = URLS_URROE;
|
||||
if (termios->c_iflag & INPCK)
|
||||
port->read_status_mask |= (URLS_URFE | URLS_URPE);
|
||||
if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK))
|
||||
port->read_status_mask |= URLS_URBI;
|
||||
|
||||
/*
|
||||
* Characters to ignore
|
||||
*/
|
||||
port->ignore_status_mask = 0;
|
||||
if (termios->c_iflag & IGNPAR)
|
||||
port->ignore_status_mask |= (URLS_URFE | URLS_URPE);
|
||||
if (termios->c_iflag & IGNBRK) {
|
||||
port->ignore_status_mask |= URLS_URBI;
|
||||
/*
|
||||
* If we're ignoring parity and break indicators,
|
||||
* ignore overruns too (for real raw support).
|
||||
*/
|
||||
if (termios->c_iflag & IGNPAR)
|
||||
port->ignore_status_mask |= URLS_URROE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ignore all characters if CREAD is not set.
|
||||
*/
|
||||
if ((termios->c_cflag & CREAD) == 0)
|
||||
port->ignore_status_mask |= UART_DUMMY_LSR_RX;
|
||||
|
||||
/* first, disable everything */
|
||||
if (UART_ENABLE_MS(port, termios->c_cflag))
|
||||
ks8695uart_enable_ms(port);
|
||||
else
|
||||
ks8695uart_disable_ms(port);
|
||||
|
||||
/* Set baud rate */
|
||||
UART_PUT_BRDR(port, quot);
|
||||
|
||||
UART_PUT_LCR(port, lcr);
|
||||
UART_PUT_FCR(port, fcr);
|
||||
|
||||
spin_unlock_irqrestore(&port->lock, flags);
|
||||
}
|
||||
|
||||
static const char *ks8695uart_type(struct uart_port *port)
|
||||
{
|
||||
return port->type == PORT_KS8695 ? "KS8695" : NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Release the memory region(s) being used by 'port'
|
||||
*/
|
||||
static void ks8695uart_release_port(struct uart_port *port)
|
||||
{
|
||||
release_mem_region(port->mapbase, UART_PORT_SIZE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Request the memory region(s) being used by 'port'
|
||||
*/
|
||||
static int ks8695uart_request_port(struct uart_port *port)
|
||||
{
|
||||
return request_mem_region(port->mapbase, UART_PORT_SIZE,
|
||||
"serial_ks8695") != NULL ? 0 : -EBUSY;
|
||||
}
|
||||
|
||||
/*
|
||||
* Configure/autoconfigure the port.
|
||||
*/
|
||||
static void ks8695uart_config_port(struct uart_port *port, int flags)
|
||||
{
|
||||
if (flags & UART_CONFIG_TYPE) {
|
||||
port->type = PORT_KS8695;
|
||||
ks8695uart_request_port(port);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* verify the new serial_struct (for TIOCSSERIAL).
|
||||
*/
|
||||
static int ks8695uart_verify_port(struct uart_port *port, struct serial_struct *ser)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (ser->type != PORT_UNKNOWN && ser->type != PORT_KS8695)
|
||||
ret = -EINVAL;
|
||||
if (ser->irq != port->irq)
|
||||
ret = -EINVAL;
|
||||
if (ser->baud_base < 9600)
|
||||
ret = -EINVAL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct uart_ops ks8695uart_pops = {
|
||||
.tx_empty = ks8695uart_tx_empty,
|
||||
.set_mctrl = ks8695uart_set_mctrl,
|
||||
.get_mctrl = ks8695uart_get_mctrl,
|
||||
.stop_tx = ks8695uart_stop_tx,
|
||||
.start_tx = ks8695uart_start_tx,
|
||||
.stop_rx = ks8695uart_stop_rx,
|
||||
.enable_ms = ks8695uart_enable_ms,
|
||||
.break_ctl = ks8695uart_break_ctl,
|
||||
.startup = ks8695uart_startup,
|
||||
.shutdown = ks8695uart_shutdown,
|
||||
.set_termios = ks8695uart_set_termios,
|
||||
.type = ks8695uart_type,
|
||||
.release_port = ks8695uart_release_port,
|
||||
.request_port = ks8695uart_request_port,
|
||||
.config_port = ks8695uart_config_port,
|
||||
.verify_port = ks8695uart_verify_port,
|
||||
};
|
||||
|
||||
static struct uart_port ks8695uart_ports[SERIAL_KS8695_NR] = {
|
||||
{
|
||||
.membase = KS8695_UART_VA,
|
||||
.mapbase = KS8695_UART_PA,
|
||||
.iotype = SERIAL_IO_MEM,
|
||||
.irq = KS8695_IRQ_UART_TX,
|
||||
.uartclk = KS8695_CLOCK_RATE * 16,
|
||||
.fifosize = 16,
|
||||
.ops = &ks8695uart_pops,
|
||||
.flags = UPF_BOOT_AUTOCONF,
|
||||
.line = 0,
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef CONFIG_SERIAL_KS8695_CONSOLE
|
||||
static void ks8695_console_putchar(struct uart_port *port, int ch)
|
||||
{
|
||||
while (!(UART_GET_LSR(port) & URLS_URTHRE))
|
||||
barrier();
|
||||
|
||||
UART_PUT_CHAR(port, ch);
|
||||
}
|
||||
|
||||
static void ks8695_console_write(struct console *co, const char *s, u_int count)
|
||||
{
|
||||
struct uart_port *port = ks8695uart_ports + co->index;
|
||||
|
||||
uart_console_write(port, s, count, ks8695_console_putchar);
|
||||
}
|
||||
|
||||
static void __init ks8695_console_get_options(struct uart_port *port, int *baud, int *parity, int *bits)
|
||||
{
|
||||
unsigned int lcr;
|
||||
|
||||
lcr = UART_GET_LCR(port);
|
||||
|
||||
switch (lcr & URLC_PARITY) {
|
||||
case URPE_ODD:
|
||||
*parity = 'o';
|
||||
break;
|
||||
case URPE_EVEN:
|
||||
*parity = 'e';
|
||||
break;
|
||||
default:
|
||||
*parity = 'n';
|
||||
}
|
||||
|
||||
switch (lcr & URLC_URCL) {
|
||||
case URCL_5:
|
||||
*bits = 5;
|
||||
break;
|
||||
case URCL_6:
|
||||
*bits = 6;
|
||||
break;
|
||||
case URCL_7:
|
||||
*bits = 7;
|
||||
break;
|
||||
default:
|
||||
*bits = 8;
|
||||
}
|
||||
|
||||
*baud = port->uartclk / (UART_GET_BRDR(port) & 0x0FFF);
|
||||
*baud /= 16;
|
||||
*baud &= 0xFFFFFFF0;
|
||||
}
|
||||
|
||||
static int __init ks8695_console_setup(struct console *co, char *options)
|
||||
{
|
||||
struct uart_port *port;
|
||||
int baud = 115200;
|
||||
int bits = 8;
|
||||
int parity = 'n';
|
||||
int flow = 'n';
|
||||
|
||||
/*
|
||||
* Check whether an invalid uart number has been specified, and
|
||||
* if so, search for the first available port that does have
|
||||
* console support.
|
||||
*/
|
||||
port = uart_get_console(ks8695uart_ports, SERIAL_KS8695_NR, co);
|
||||
|
||||
if (options)
|
||||
uart_parse_options(options, &baud, &parity, &bits, &flow);
|
||||
else
|
||||
ks8695_console_get_options(port, &baud, &parity, &bits);
|
||||
|
||||
return uart_set_options(port, co, baud, parity, bits, flow);
|
||||
}
|
||||
|
||||
static struct uart_driver ks8695_reg;
|
||||
|
||||
static struct console ks8695_console = {
|
||||
.name = SERIAL_KS8695_DEVNAME,
|
||||
.write = ks8695_console_write,
|
||||
.device = uart_console_device,
|
||||
.setup = ks8695_console_setup,
|
||||
.flags = CON_PRINTBUFFER,
|
||||
.index = -1,
|
||||
.data = &ks8695_reg,
|
||||
};
|
||||
|
||||
static int __init ks8695_console_init(void)
|
||||
{
|
||||
add_preferred_console(SERIAL_KS8695_DEVNAME, 0, NULL);
|
||||
register_console(&ks8695_console);
|
||||
return 0;
|
||||
}
|
||||
|
||||
console_initcall(ks8695_console_init);
|
||||
|
||||
#define KS8695_CONSOLE &ks8695_console
|
||||
#else
|
||||
#define KS8695_CONSOLE NULL
|
||||
#endif
|
||||
|
||||
static struct uart_driver ks8695_reg = {
|
||||
.owner = THIS_MODULE,
|
||||
.driver_name = "serial_ks8695",
|
||||
.dev_name = SERIAL_KS8695_DEVNAME,
|
||||
.major = SERIAL_KS8695_MAJOR,
|
||||
.minor = SERIAL_KS8695_MINOR,
|
||||
.nr = SERIAL_KS8695_NR,
|
||||
.cons = KS8695_CONSOLE,
|
||||
};
|
||||
|
||||
static int __init ks8695uart_init(void)
|
||||
{
|
||||
int i, ret;
|
||||
|
||||
printk(KERN_INFO "Serial: Micrel KS8695 UART driver\n");
|
||||
|
||||
ret = uart_register_driver(&ks8695_reg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < SERIAL_KS8695_NR; i++)
|
||||
uart_add_one_port(&ks8695_reg, &ks8695uart_ports[0]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit ks8695uart_exit(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < SERIAL_KS8695_NR; i++)
|
||||
uart_remove_one_port(&ks8695_reg, &ks8695uart_ports[0]);
|
||||
uart_unregister_driver(&ks8695_reg);
|
||||
}
|
||||
|
||||
module_init(ks8695uart_init);
|
||||
module_exit(ks8695uart_exit);
|
||||
|
||||
MODULE_DESCRIPTION("KS8695 serial port driver");
|
||||
MODULE_AUTHOR("Micrel Inc.");
|
||||
MODULE_LICENSE("GPL");
|
@ -27,16 +27,21 @@ struct mctrl_gpios {
|
||||
static const struct {
|
||||
const char *name;
|
||||
unsigned int mctrl;
|
||||
bool dir_out;
|
||||
enum gpiod_flags flags;
|
||||
} mctrl_gpios_desc[UART_GPIO_MAX] = {
|
||||
{ "cts", TIOCM_CTS, false, },
|
||||
{ "dsr", TIOCM_DSR, false, },
|
||||
{ "dcd", TIOCM_CD, false, },
|
||||
{ "rng", TIOCM_RNG, false, },
|
||||
{ "rts", TIOCM_RTS, true, },
|
||||
{ "dtr", TIOCM_DTR, true, },
|
||||
{ "cts", TIOCM_CTS, GPIOD_IN, },
|
||||
{ "dsr", TIOCM_DSR, GPIOD_IN, },
|
||||
{ "dcd", TIOCM_CD, GPIOD_IN, },
|
||||
{ "rng", TIOCM_RNG, GPIOD_IN, },
|
||||
{ "rts", TIOCM_RTS, GPIOD_OUT_LOW, },
|
||||
{ "dtr", TIOCM_DTR, GPIOD_OUT_LOW, },
|
||||
};
|
||||
|
||||
static bool mctrl_gpio_flags_is_dir_out(unsigned int idx)
|
||||
{
|
||||
return mctrl_gpios_desc[idx].flags & GPIOD_FLAGS_BIT_DIR_OUT;
|
||||
}
|
||||
|
||||
void mctrl_gpio_set(struct mctrl_gpios *gpios, unsigned int mctrl)
|
||||
{
|
||||
enum mctrl_gpio_idx i;
|
||||
@ -48,7 +53,7 @@ void mctrl_gpio_set(struct mctrl_gpios *gpios, unsigned int mctrl)
|
||||
return;
|
||||
|
||||
for (i = 0; i < UART_GPIO_MAX; i++)
|
||||
if (gpios->gpio[i] && mctrl_gpios_desc[i].dir_out) {
|
||||
if (gpios->gpio[i] && mctrl_gpio_flags_is_dir_out(i)) {
|
||||
desc_array[count] = gpios->gpio[i];
|
||||
__assign_bit(count, values,
|
||||
mctrl & mctrl_gpios_desc[i].mctrl);
|
||||
@ -73,7 +78,7 @@ unsigned int mctrl_gpio_get(struct mctrl_gpios *gpios, unsigned int *mctrl)
|
||||
return *mctrl;
|
||||
|
||||
for (i = 0; i < UART_GPIO_MAX; i++) {
|
||||
if (gpios->gpio[i] && !mctrl_gpios_desc[i].dir_out) {
|
||||
if (gpios->gpio[i] && !mctrl_gpio_flags_is_dir_out(i)) {
|
||||
if (gpiod_get_value(gpios->gpio[i]))
|
||||
*mctrl |= mctrl_gpios_desc[i].mctrl;
|
||||
else
|
||||
@ -94,7 +99,7 @@ mctrl_gpio_get_outputs(struct mctrl_gpios *gpios, unsigned int *mctrl)
|
||||
return *mctrl;
|
||||
|
||||
for (i = 0; i < UART_GPIO_MAX; i++) {
|
||||
if (gpios->gpio[i] && mctrl_gpios_desc[i].dir_out) {
|
||||
if (gpios->gpio[i] && mctrl_gpio_flags_is_dir_out(i)) {
|
||||
if (gpiod_get_value(gpios->gpio[i]))
|
||||
*mctrl |= mctrl_gpios_desc[i].mctrl;
|
||||
else
|
||||
@ -116,7 +121,6 @@ struct mctrl_gpios *mctrl_gpio_init_noauto(struct device *dev, unsigned int idx)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
for (i = 0; i < UART_GPIO_MAX; i++) {
|
||||
enum gpiod_flags flags;
|
||||
char *gpio_str;
|
||||
bool present;
|
||||
|
||||
@ -131,15 +135,11 @@ struct mctrl_gpios *mctrl_gpio_init_noauto(struct device *dev, unsigned int idx)
|
||||
if (!present)
|
||||
continue;
|
||||
|
||||
if (mctrl_gpios_desc[i].dir_out)
|
||||
flags = GPIOD_OUT_LOW;
|
||||
else
|
||||
flags = GPIOD_IN;
|
||||
|
||||
gpios->gpio[i] =
|
||||
devm_gpiod_get_index_optional(dev,
|
||||
mctrl_gpios_desc[i].name,
|
||||
idx, flags);
|
||||
idx,
|
||||
mctrl_gpios_desc[i].flags);
|
||||
|
||||
if (IS_ERR(gpios->gpio[i]))
|
||||
return ERR_CAST(gpios->gpio[i]);
|
||||
@ -200,7 +200,7 @@ struct mctrl_gpios *mctrl_gpio_init(struct uart_port *port, unsigned int idx)
|
||||
for (i = 0; i < UART_GPIO_MAX; ++i) {
|
||||
int ret;
|
||||
|
||||
if (!gpios->gpio[i] || mctrl_gpios_desc[i].dir_out)
|
||||
if (!gpios->gpio[i] || mctrl_gpio_flags_is_dir_out(i))
|
||||
continue;
|
||||
|
||||
ret = gpiod_to_irq(gpios->gpio[i]);
|
||||
|
@ -114,19 +114,19 @@ static inline
|
||||
struct gpio_desc *mctrl_gpio_to_gpiod(struct mctrl_gpios *gpios,
|
||||
enum mctrl_gpio_idx gidx)
|
||||
{
|
||||
return ERR_PTR(-ENOSYS);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline
|
||||
struct mctrl_gpios *mctrl_gpio_init(struct uart_port *port, unsigned int idx)
|
||||
{
|
||||
return ERR_PTR(-ENOSYS);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline
|
||||
struct mctrl_gpios *mctrl_gpio_init_noauto(struct device *dev, unsigned int idx)
|
||||
{
|
||||
return ERR_PTR(-ENOSYS);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline
|
||||
|
@ -1092,9 +1092,8 @@ static void rx_fifo_timer_fn(struct timer_list *t)
|
||||
scif_set_rtrg(port, 1);
|
||||
}
|
||||
|
||||
static ssize_t rx_trigger_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
static ssize_t rx_fifo_trigger_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct uart_port *port = dev_get_drvdata(dev);
|
||||
struct sci_port *sci = to_sci_port(port);
|
||||
@ -1102,10 +1101,9 @@ static ssize_t rx_trigger_show(struct device *dev,
|
||||
return sprintf(buf, "%d\n", sci->rx_trigger);
|
||||
}
|
||||
|
||||
static ssize_t rx_trigger_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf,
|
||||
size_t count)
|
||||
static ssize_t rx_fifo_trigger_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct uart_port *port = dev_get_drvdata(dev);
|
||||
struct sci_port *sci = to_sci_port(port);
|
||||
@ -1123,7 +1121,7 @@ static ssize_t rx_trigger_store(struct device *dev,
|
||||
return count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(rx_fifo_trigger, 0644, rx_trigger_show, rx_trigger_store);
|
||||
static DEVICE_ATTR_RW(rx_fifo_trigger);
|
||||
|
||||
static ssize_t rx_fifo_timeout_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
@ -2101,12 +2099,12 @@ static unsigned int sci_get_mctrl(struct uart_port *port)
|
||||
if (s->autorts) {
|
||||
if (sci_get_cts(port))
|
||||
mctrl |= TIOCM_CTS;
|
||||
} else if (IS_ERR_OR_NULL(mctrl_gpio_to_gpiod(gpios, UART_GPIO_CTS))) {
|
||||
} else if (!mctrl_gpio_to_gpiod(gpios, UART_GPIO_CTS)) {
|
||||
mctrl |= TIOCM_CTS;
|
||||
}
|
||||
if (IS_ERR_OR_NULL(mctrl_gpio_to_gpiod(gpios, UART_GPIO_DSR)))
|
||||
if (!mctrl_gpio_to_gpiod(gpios, UART_GPIO_DSR))
|
||||
mctrl |= TIOCM_DSR;
|
||||
if (IS_ERR_OR_NULL(mctrl_gpio_to_gpiod(gpios, UART_GPIO_DCD)))
|
||||
if (!mctrl_gpio_to_gpiod(gpios, UART_GPIO_DCD))
|
||||
mctrl |= TIOCM_CAR;
|
||||
|
||||
return mctrl;
|
||||
@ -3152,14 +3150,10 @@ static int sci_remove(struct platform_device *dev)
|
||||
|
||||
sci_cleanup_single(port);
|
||||
|
||||
if (port->port.fifosize > 1) {
|
||||
sysfs_remove_file(&dev->dev.kobj,
|
||||
&dev_attr_rx_fifo_trigger.attr);
|
||||
}
|
||||
if (type == PORT_SCIFA || type == PORT_SCIFB || type == PORT_HSCIF) {
|
||||
sysfs_remove_file(&dev->dev.kobj,
|
||||
&dev_attr_rx_fifo_timeout.attr);
|
||||
}
|
||||
if (port->port.fifosize > 1)
|
||||
device_remove_file(&dev->dev, &dev_attr_rx_fifo_trigger);
|
||||
if (type == PORT_SCIFA || type == PORT_SCIFB || type == PORT_HSCIF)
|
||||
device_remove_file(&dev->dev, &dev_attr_rx_fifo_timeout);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -3287,14 +3281,12 @@ static int sci_probe_single(struct platform_device *dev,
|
||||
return ret;
|
||||
|
||||
sciport->gpios = mctrl_gpio_init(&sciport->port, 0);
|
||||
if (IS_ERR(sciport->gpios) && PTR_ERR(sciport->gpios) != -ENOSYS)
|
||||
if (IS_ERR(sciport->gpios))
|
||||
return PTR_ERR(sciport->gpios);
|
||||
|
||||
if (sciport->has_rtscts) {
|
||||
if (!IS_ERR_OR_NULL(mctrl_gpio_to_gpiod(sciport->gpios,
|
||||
UART_GPIO_CTS)) ||
|
||||
!IS_ERR_OR_NULL(mctrl_gpio_to_gpiod(sciport->gpios,
|
||||
UART_GPIO_RTS))) {
|
||||
if (mctrl_gpio_to_gpiod(sciport->gpios, UART_GPIO_CTS) ||
|
||||
mctrl_gpio_to_gpiod(sciport->gpios, UART_GPIO_RTS)) {
|
||||
dev_err(&dev->dev, "Conflicting RTS/CTS config\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -3347,19 +3339,17 @@ static int sci_probe(struct platform_device *dev)
|
||||
return ret;
|
||||
|
||||
if (sp->port.fifosize > 1) {
|
||||
ret = sysfs_create_file(&dev->dev.kobj,
|
||||
&dev_attr_rx_fifo_trigger.attr);
|
||||
ret = device_create_file(&dev->dev, &dev_attr_rx_fifo_trigger);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
if (sp->port.type == PORT_SCIFA || sp->port.type == PORT_SCIFB ||
|
||||
sp->port.type == PORT_HSCIF) {
|
||||
ret = sysfs_create_file(&dev->dev.kobj,
|
||||
&dev_attr_rx_fifo_timeout.attr);
|
||||
ret = device_create_file(&dev->dev, &dev_attr_rx_fifo_timeout);
|
||||
if (ret) {
|
||||
if (sp->port.fifosize > 1) {
|
||||
sysfs_remove_file(&dev->dev.kobj,
|
||||
&dev_attr_rx_fifo_trigger.attr);
|
||||
device_remove_file(&dev->dev,
|
||||
&dev_attr_rx_fifo_trigger);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
@ -896,10 +896,8 @@ static int sifive_serial_probe(struct platform_device *pdev)
|
||||
int irq, id, r;
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
dev_err(&pdev->dev, "could not acquire interrupt\n");
|
||||
if (irq < 0)
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
|
||||
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
base = devm_ioremap_resource(&pdev->dev, mem);
|
||||
|
@ -79,6 +79,7 @@
|
||||
/* control register 1 */
|
||||
#define SPRD_CTL1 0x001C
|
||||
#define SPRD_DMA_EN BIT(15)
|
||||
#define SPRD_LOOPBACK_EN BIT(14)
|
||||
#define RX_HW_FLOW_CTL_THLD BIT(6)
|
||||
#define RX_HW_FLOW_CTL_EN BIT(7)
|
||||
#define TX_HW_FLOW_CTL_EN BIT(8)
|
||||
@ -164,7 +165,14 @@ static unsigned int sprd_get_mctrl(struct uart_port *port)
|
||||
|
||||
static void sprd_set_mctrl(struct uart_port *port, unsigned int mctrl)
|
||||
{
|
||||
/* nothing to do */
|
||||
u32 val = serial_in(port, SPRD_CTL1);
|
||||
|
||||
if (mctrl & TIOCM_LOOP)
|
||||
val |= SPRD_LOOPBACK_EN;
|
||||
else
|
||||
val &= ~SPRD_LOOPBACK_EN;
|
||||
|
||||
serial_out(port, SPRD_CTL1, val);
|
||||
}
|
||||
|
||||
static void sprd_stop_rx(struct uart_port *port)
|
||||
@ -609,7 +617,7 @@ static inline void sprd_rx(struct uart_port *port)
|
||||
|
||||
if (lsr & (SPRD_LSR_BI | SPRD_LSR_PE |
|
||||
SPRD_LSR_FE | SPRD_LSR_OE))
|
||||
if (handle_lsr_errors(port, &lsr, &flag))
|
||||
if (handle_lsr_errors(port, &flag, &lsr))
|
||||
continue;
|
||||
if (uart_handle_sysrq_char(port, ch))
|
||||
continue;
|
||||
@ -975,7 +983,7 @@ static void sprd_console_write(struct console *co, const char *s,
|
||||
|
||||
static int __init sprd_console_setup(struct console *co, char *options)
|
||||
{
|
||||
struct uart_port *port;
|
||||
struct sprd_uart_port *sprd_uart_port;
|
||||
int baud = 115200;
|
||||
int bits = 8;
|
||||
int parity = 'n';
|
||||
@ -984,15 +992,17 @@ static int __init sprd_console_setup(struct console *co, char *options)
|
||||
if (co->index >= UART_NR_MAX || co->index < 0)
|
||||
co->index = 0;
|
||||
|
||||
port = &sprd_port[co->index]->port;
|
||||
if (port == NULL) {
|
||||
sprd_uart_port = sprd_port[co->index];
|
||||
if (!sprd_uart_port || !sprd_uart_port->port.membase) {
|
||||
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);
|
||||
return uart_set_options(&sprd_uart_port->port, co, baud,
|
||||
parity, bits, flow);
|
||||
}
|
||||
|
||||
static struct uart_driver sprd_uart_driver;
|
||||
@ -1006,6 +1016,13 @@ static struct console sprd_console = {
|
||||
.data = &sprd_uart_driver,
|
||||
};
|
||||
|
||||
static int __init sprd_serial_console_init(void)
|
||||
{
|
||||
register_console(&sprd_console);
|
||||
return 0;
|
||||
}
|
||||
console_initcall(sprd_serial_console_init);
|
||||
|
||||
#define SPRD_CONSOLE (&sprd_console)
|
||||
|
||||
/* Support for earlycon */
|
||||
@ -1094,6 +1111,16 @@ static int sprd_remove(struct platform_device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool sprd_uart_is_console(struct uart_port *uport)
|
||||
{
|
||||
struct console *cons = sprd_uart_driver.cons;
|
||||
|
||||
if (cons && cons->index >= 0 && cons->index == uport->line)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int sprd_clk_init(struct uart_port *uport)
|
||||
{
|
||||
struct clk *clk_uart, *clk_parent;
|
||||
@ -1120,10 +1147,17 @@ static int sprd_clk_init(struct uart_port *uport)
|
||||
|
||||
u->clk = devm_clk_get(uport->dev, "enable");
|
||||
if (IS_ERR(u->clk)) {
|
||||
if (PTR_ERR(u->clk) != -EPROBE_DEFER)
|
||||
dev_err(uport->dev, "uart%d can't get enable clock\n",
|
||||
uport->line);
|
||||
return PTR_ERR(u->clk);
|
||||
if (PTR_ERR(u->clk) == -EPROBE_DEFER)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
dev_warn(uport->dev, "uart%d can't get enable clock\n",
|
||||
uport->line);
|
||||
|
||||
/* To keep console alive even if the error occurred */
|
||||
if (!sprd_uart_is_console(uport))
|
||||
return PTR_ERR(u->clk);
|
||||
|
||||
u->clk = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -1173,10 +1207,8 @@ static int sprd_probe(struct platform_device *pdev)
|
||||
up->mapbase = res->start;
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
dev_err(&pdev->dev, "not provide irq resource: %d\n", irq);
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
}
|
||||
up->irq = irq;
|
||||
|
||||
/*
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/pm_wakeirq.h>
|
||||
@ -881,13 +882,13 @@ static void stm32_pm(struct uart_port *port, unsigned int state,
|
||||
|
||||
switch (state) {
|
||||
case UART_PM_STATE_ON:
|
||||
clk_prepare_enable(stm32port->clk);
|
||||
pm_runtime_get_sync(port->dev);
|
||||
break;
|
||||
case UART_PM_STATE_OFF:
|
||||
spin_lock_irqsave(&port->lock, flags);
|
||||
stm32_clr_bits(port, ofs->cr1, BIT(cfg->uart_enable_bit));
|
||||
spin_unlock_irqrestore(&port->lock, flags);
|
||||
clk_disable_unprepare(stm32port->clk);
|
||||
pm_runtime_put_sync(port->dev);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -927,11 +928,8 @@ static int stm32_init_port(struct stm32_port *stm32port,
|
||||
port->fifosize = stm32port->info->cfg.fifosize;
|
||||
|
||||
ret = platform_get_irq(pdev, 0);
|
||||
if (ret <= 0) {
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(&pdev->dev, "Can't get event IRQ: %d\n", ret);
|
||||
return ret ? ret : -ENODEV;
|
||||
}
|
||||
if (ret <= 0)
|
||||
return ret ? : -ENODEV;
|
||||
port->irq = ret;
|
||||
|
||||
port->rs485_config = stm32_config_rs485;
|
||||
@ -940,14 +938,8 @@ static int stm32_init_port(struct stm32_port *stm32port,
|
||||
|
||||
if (stm32port->info->cfg.has_wakeup) {
|
||||
stm32port->wakeirq = platform_get_irq(pdev, 1);
|
||||
if (stm32port->wakeirq <= 0 && stm32port->wakeirq != -ENXIO) {
|
||||
if (stm32port->wakeirq != -EPROBE_DEFER)
|
||||
dev_err(&pdev->dev,
|
||||
"Can't get event wake IRQ: %d\n",
|
||||
stm32port->wakeirq);
|
||||
return stm32port->wakeirq ? stm32port->wakeirq :
|
||||
-ENODEV;
|
||||
}
|
||||
if (stm32port->wakeirq <= 0 && stm32port->wakeirq != -ENXIO)
|
||||
return stm32port->wakeirq ? : -ENODEV;
|
||||
}
|
||||
|
||||
stm32port->fifoen = stm32port->info->cfg.has_fifo;
|
||||
@ -1185,6 +1177,11 @@ static int stm32_serial_probe(struct platform_device *pdev)
|
||||
|
||||
platform_set_drvdata(pdev, &stm32port->port);
|
||||
|
||||
pm_runtime_get_noresume(&pdev->dev);
|
||||
pm_runtime_set_active(&pdev->dev);
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
pm_runtime_put_sync(&pdev->dev);
|
||||
|
||||
return 0;
|
||||
|
||||
err_wirq:
|
||||
@ -1206,6 +1203,9 @@ static int stm32_serial_remove(struct platform_device *pdev)
|
||||
struct uart_port *port = platform_get_drvdata(pdev);
|
||||
struct stm32_port *stm32_port = to_stm32_port(port);
|
||||
struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
|
||||
int err;
|
||||
|
||||
pm_runtime_get_sync(&pdev->dev);
|
||||
|
||||
stm32_clr_bits(port, ofs->cr3, USART_CR3_DMAR);
|
||||
|
||||
@ -1234,7 +1234,12 @@ static int stm32_serial_remove(struct platform_device *pdev)
|
||||
|
||||
clk_disable_unprepare(stm32_port->clk);
|
||||
|
||||
return uart_remove_one_port(&stm32_usart_driver, port);
|
||||
err = uart_remove_one_port(&stm32_usart_driver, port);
|
||||
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
pm_runtime_put_noidle(&pdev->dev);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
@ -1337,8 +1342,8 @@ static struct uart_driver stm32_usart_driver = {
|
||||
.cons = STM32_SERIAL_CONSOLE,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static void stm32_serial_enable_wakeup(struct uart_port *port, bool enable)
|
||||
static void __maybe_unused stm32_serial_enable_wakeup(struct uart_port *port,
|
||||
bool enable)
|
||||
{
|
||||
struct stm32_port *stm32_port = to_stm32_port(port);
|
||||
struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
|
||||
@ -1362,7 +1367,7 @@ static void stm32_serial_enable_wakeup(struct uart_port *port, bool enable)
|
||||
}
|
||||
}
|
||||
|
||||
static int stm32_serial_suspend(struct device *dev)
|
||||
static int __maybe_unused stm32_serial_suspend(struct device *dev)
|
||||
{
|
||||
struct uart_port *port = dev_get_drvdata(dev);
|
||||
|
||||
@ -1373,21 +1378,46 @@ static int stm32_serial_suspend(struct device *dev)
|
||||
else
|
||||
stm32_serial_enable_wakeup(port, false);
|
||||
|
||||
pinctrl_pm_select_sleep_state(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stm32_serial_resume(struct device *dev)
|
||||
static int __maybe_unused stm32_serial_resume(struct device *dev)
|
||||
{
|
||||
struct uart_port *port = dev_get_drvdata(dev);
|
||||
|
||||
pinctrl_pm_select_default_state(dev);
|
||||
|
||||
if (device_may_wakeup(dev))
|
||||
stm32_serial_enable_wakeup(port, false);
|
||||
|
||||
return uart_resume_port(&stm32_usart_driver, port);
|
||||
}
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
static int __maybe_unused stm32_serial_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct uart_port *port = dev_get_drvdata(dev);
|
||||
struct stm32_port *stm32port = container_of(port,
|
||||
struct stm32_port, port);
|
||||
|
||||
clk_disable_unprepare(stm32port->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused stm32_serial_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct uart_port *port = dev_get_drvdata(dev);
|
||||
struct stm32_port *stm32port = container_of(port,
|
||||
struct stm32_port, port);
|
||||
|
||||
return clk_prepare_enable(stm32port->clk);
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops stm32_serial_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(stm32_serial_runtime_suspend,
|
||||
stm32_serial_runtime_resume, NULL)
|
||||
SET_SYSTEM_SLEEP_PM_OPS(stm32_serial_suspend, stm32_serial_resume)
|
||||
};
|
||||
|
||||
|
@ -1194,7 +1194,7 @@ static void cdns_uart_console_write(struct console *co, const char *s,
|
||||
unsigned int count)
|
||||
{
|
||||
struct uart_port *port = console_port;
|
||||
unsigned long flags;
|
||||
unsigned long flags = 0;
|
||||
unsigned int imr, ctrl;
|
||||
int locked = 1;
|
||||
|
||||
|
@ -37,5 +37,7 @@ struct gsm_netconfig {
|
||||
#define GSMIOC_ENABLE_NET _IOW('G', 2, struct gsm_netconfig)
|
||||
#define GSMIOC_DISABLE_NET _IO('G', 3)
|
||||
|
||||
/* get the base tty number for a configured gsmmux tty */
|
||||
#define GSMIOC_GETFIRST _IOR('G', 4, __u32)
|
||||
|
||||
#endif
|
||||
|
@ -161,9 +161,6 @@
|
||||
/* Blackfin bf5xx */
|
||||
#define PORT_BFIN 75
|
||||
|
||||
/* Micrel KS8695 */
|
||||
#define PORT_KS8695 76
|
||||
|
||||
/* Broadcom SB1250, etc. SOC */
|
||||
#define PORT_SB1250_DUART 77
|
||||
|
||||
@ -290,4 +287,10 @@
|
||||
/* SiFive UART */
|
||||
#define PORT_SIFIVE_V0 120
|
||||
|
||||
/* Sunix UART */
|
||||
#define PORT_SUNIX 121
|
||||
|
||||
/* Freescale Linflex UART */
|
||||
#define PORT_LINFLEXUART 121
|
||||
|
||||
#endif /* _UAPILINUX_SERIAL_CORE_H */
|
||||
|
Loading…
Reference in New Issue
Block a user