From 69b1aaa4504159054728cebe8df43b82c41e8987 Mon Sep 17 00:00:00 2001 From: Martin Hundebøll Date: Wed, 10 Jul 2019 21:26:53 +0200 Subject: tty: n_gsm: remove obsolete mknod doc example MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The n_gsm driver handles registration of /dev/gsmttyX nodes, so there's no need to do mknod manually. Signed-off-by: Martin Hundebøll Link: https://lore.kernel.org/r/20190710192656.60381-1-martin@geanix.com Signed-off-by: Greg Kroah-Hartman --- Documentation/driver-api/serial/n_gsm.rst | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/Documentation/driver-api/serial/n_gsm.rst b/Documentation/driver-api/serial/n_gsm.rst index f3ad9fd26408..4f37198423f7 100644 --- a/Documentation/driver-api/serial/n_gsm.rst +++ b/Documentation/driver-api/serial/n_gsm.rst @@ -63,24 +63,14 @@ Major parts of the initialization program : 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. +4. use these devices as plain serial ports. for example, it's possible: - and to use gnokii to send / receive SMS on ttygsm1 - to use ppp to establish a datalink on ttygsm2 -6. first close all virtual ports before closing the physical port. +5. first close all virtual ports before closing the physical port. Note that after closing the physical port the modem is still in multiplexing mode. This may prevent a successful re-opening of the port later. To avoid -- cgit v1.2.3-59-g8ed1b From a64d19aa0ef62c7238e45d3f1cac90dd1915e560 Mon Sep 17 00:00:00 2001 From: Martin Hundebøll Date: Wed, 10 Jul 2019 21:26:54 +0200 Subject: tty: n_gsm: update doc example to use header for N_GSM0710 define MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is no reason to gues the line discipline number when it is available from tty.h Signed-off-by: Martin Hundebøll Link: https://lore.kernel.org/r/20190710192656.60381-2-martin@geanix.com Signed-off-by: Greg Kroah-Hartman --- Documentation/driver-api/serial/n_gsm.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/driver-api/serial/n_gsm.rst b/Documentation/driver-api/serial/n_gsm.rst index 4f37198423f7..0ba731ab00b2 100644 --- a/Documentation/driver-api/serial/n_gsm.rst +++ b/Documentation/driver-api/serial/n_gsm.rst @@ -23,7 +23,7 @@ Major parts of the initialization program : (a good starting point is util-linux-ng/sys-utils/ldattach.c):: #include - #define N_GSM0710 21 /* GSM 0710 Mux */ + #include #define DEFAULT_SPEED B115200 #define SERIAL_PORT /dev/ttyS0 -- cgit v1.2.3-59-g8ed1b From 43a9e710cbed16e1e811713b5ccae5293471287e Mon Sep 17 00:00:00 2001 From: Martin Hundebøll Date: Wed, 10 Jul 2019 21:26:55 +0200 Subject: tty: n_gsm: add helpers to convert mux-num to/from tty-base MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make it obvious how the gsm mux number relates to the virtual tty lines by using helper functions instead of shifting 6 bits. Signed-off-by: Martin Hundebøll Link: https://lore.kernel.org/r/20190710192656.60381-3-martin@geanix.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/n_gsm.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c index c4e16b31f9ab..a60be591f1fc 100644 --- a/drivers/tty/n_gsm.c +++ b/drivers/tty/n_gsm.c @@ -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++) @@ -2908,7 +2919,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; -- cgit v1.2.3-59-g8ed1b From 627a545c6bb0c7de09208e6546f5111290477261 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Tue, 23 Jul 2019 04:54:00 -0700 Subject: serial: 8250_omap: Fix idling for unloaded serdev drivers For many years omap variants have been setting the runtime PM autosuspend delay to -1 to prevent unsafe policy with lossy first character on wake-up. The user must specifically enable the timeout for UARTs if desired. We must not enable the workaround for serdev devices though. It leads into UARTs not idling if no serdev devices are loaded and there is no sysfs entry to configure the UART in that case. And this means that my PM may not work unless the serdev modules are loaded. We can detect a serdev device being configured based on a dts child node, and we can simply skip the workround in that case. And the serdev driver can idle the port during runtime when suitable if an out-of-band wake-up GPIO line exists for example. Let's also add some comments to the workaround while at it. Cc: Johan Hovold Signed-off-by: Tony Lindgren Link: https://lore.kernel.org/r/20190723115400.46432-1-tony@atomide.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_omap.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c index 3ef65cbd2478..c68e2b3a1634 100644 --- a/drivers/tty/serial/8250/8250_omap.c +++ b/drivers/tty/serial/8250/8250_omap.c @@ -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); -- cgit v1.2.3-59-g8ed1b From acf01e66566ff6c95354aa6e907fa83438d78b55 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Thu, 4 Jul 2019 16:57:47 -0700 Subject: tty: hvcs: Fix odd use of strlcpy Use the typical style of array, not the equivalent &array[0]. Signed-off-by: Joe Perches Link: https://lore.kernel.org/r/1be432798311378947ec4e6051cad1235b6eccbb.1562283944.git.joe@perches.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/hvc/hvcs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/tty/hvc/hvcs.c b/drivers/tty/hvc/hvcs.c index 5fb214e67d73..ee0604cd9c6b 100644 --- a/drivers/tty/hvc/hvcs.c +++ b/drivers/tty/hvc/hvcs.c @@ -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)); } /* -- cgit v1.2.3-59-g8ed1b From 0998a63b493b8591bea9e81144f8ed05e0a92cef Mon Sep 17 00:00:00 2001 From: Fuqian Huang Date: Mon, 15 Jul 2019 11:20:01 +0800 Subject: tty: serial: Remove call to memset after pci_alloc_consistent pci_alloc_consistent calls dma_alloc_coherent directly. In commit 518a2f1925c3 ("dma-mapping: zero memory returned from dma_alloc_*"), dma_alloc_coherent has already zeroed the memory. So memset is not needed. Signed-off-by: Fuqian Huang Link: https://lore.kernel.org/r/20190715032001.7212-1-huangfq.daxian@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/icom.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/tty/serial/icom.c b/drivers/tty/serial/icom.c index ad374f7c476d..624f3d541c68 100644 --- a/drivers/tty/serial/icom.c +++ b/drivers/tty/serial/icom.c @@ -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 */ -- cgit v1.2.3-59-g8ed1b From 95604220cec8eb5bcde9041625ae8570f1077da6 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 23 Jul 2019 16:03:14 +0100 Subject: tty/isicom: remove redundant assignment to variable word_count The variable word_count is being assigned a value that is never read before a return, the assignment is redundant and can be removed. Addresses-Coverity: ("Unused value") Signed-off-by: Colin Ian King Acked-by: Jiri Slaby Link: https://lore.kernel.org/r/20190723150314.14513-1-colin.king@canonical.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/isicom.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/tty/isicom.c b/drivers/tty/isicom.c index e04a43e89f6b..fc38f96475bf 100644 --- a/drivers/tty/isicom.c +++ b/drivers/tty/isicom.c @@ -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; -- cgit v1.2.3-59-g8ed1b From 76b4106c4b4843d4815c5d0bd3352f662ae586d8 Mon Sep 17 00:00:00 2001 From: Chuhong Yuan Date: Wed, 24 Jul 2019 21:17:58 +0800 Subject: serial: 8250: Use dev_get_drvdata where possible Instead of using to_pci_dev + pci_get_drvdata, use dev_get_drvdata to make code simpler. Signed-off-by: Chuhong Yuan Link: https://lore.kernel.org/r/20190724131758.1764-1-hslester96@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_exar.c | 3 +-- drivers/tty/serial/8250/8250_pci.c | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c index edd6dfe055bf..03b347afd46c 100644 --- a/drivers/tty/serial/8250/8250_exar.c +++ b/drivers/tty/serial/8250/8250_exar.c @@ -561,8 +561,7 @@ 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; for (i = 0; i < priv->nr; i++) diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index 7f740b37700b..b714d8d0e161 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -3859,8 +3859,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); -- cgit v1.2.3-59-g8ed1b From 18b1345e60ae8887d914e83f535962cd3bb1c1be Mon Sep 17 00:00:00 2001 From: Chuhong Yuan Date: Wed, 24 Jul 2019 21:18:25 +0800 Subject: tty: nozomi: Use dev_get_drvdata Instead of using to_pci_dev + pci_get_drvdata, use dev_get_drvdata to make code simpler. Signed-off-by: Chuhong Yuan Link: https://lore.kernel.org/r/20190724131825.1875-1-hslester96@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/nozomi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/tty/nozomi.c b/drivers/tty/nozomi.c index 3214e22e79f3..ed99948f3b7f 100644 --- a/drivers/tty/nozomi.c +++ b/drivers/tty/nozomi.c @@ -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); } -- cgit v1.2.3-59-g8ed1b From df60a8af8420cb440a2456e7b3ec02446f1e2e74 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Sun, 21 Jul 2019 18:01:35 +0300 Subject: serial: 8250_exar: Use struct_size() helper One of the more common cases of allocation size calculations is finding the size of a structure that has a zero-sized array at the end, along with memory for some number of elements for that array. Make use of the struct_size() helper instead of an open-coded version in order to avoid any potential type mistakes. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20190721150135.82065-1-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_exar.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c index 03b347afd46c..8d60e5481af0 100644 --- a/drivers/tty/serial/8250/8250_exar.c +++ b/drivers/tty/serial/8250/8250_exar.c @@ -478,9 +478,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; -- cgit v1.2.3-59-g8ed1b From 38eb523461dc89543070ef41512ea134bba07284 Mon Sep 17 00:00:00 2001 From: Fugang Duan Date: Wed, 17 Jul 2019 13:19:26 +0800 Subject: tty: serial: fsl_lpuart: remove the dev.coherent_dma_mask zero setting By default, .of_dma_configure() init dev.coherent_dma_mask to BIT(32) that match the eDMA address range. If re-init dev.coherent_dma_mask to zero, then streaming dma mapping will go swiotlb dma_map, if swiotlb is not initalized then it causes mapping failed. Signed-off-by: Fugang Duan Link: https://lore.kernel.org/r/20190717051930.15514-2-fugang.duan@nxp.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/fsl_lpuart.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index 92dad2b4ec36..b856d1dba49b 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -2320,8 +2320,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); -- cgit v1.2.3-59-g8ed1b From d8a0e92e1c1e6c665ca044dcf900b7b4243d4a0c Mon Sep 17 00:00:00 2001 From: Fugang Duan Date: Wed, 17 Jul 2019 13:19:27 +0800 Subject: tty: serial: fsl_lpuart: add earlycon for imx8qxp platform Add earlycon support for imx8qxp platform. Signed-off-by: Fugang Duan Link: https://lore.kernel.org/r/20190717051930.15514-3-fugang.duan@nxp.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/fsl_lpuart.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index b856d1dba49b..7bc304798f10 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -2288,6 +2288,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); -- cgit v1.2.3-59-g8ed1b From ca8d92f6d3ddc24140554c24dcbe0a43759f37d7 Mon Sep 17 00:00:00 2001 From: Fugang Duan Date: Wed, 17 Jul 2019 13:19:28 +0800 Subject: tty: serial: fsl_lpuart: use kzalloc() instead of kmalloc() Use kzalloc() instead of kmalloc() to get clean rx buffer that is useful for DMA mode debug to check the data moving validity. Signed-off-by: Fugang Duan Link: https://lore.kernel.org/r/20190717051930.15514-4-fugang.duan@nxp.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/fsl_lpuart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index 7bc304798f10..746681be3760 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -1097,7 +1097,7 @@ 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; -- cgit v1.2.3-59-g8ed1b From 638341d5dbd11f057a9b00176ade767b08a9e95f Mon Sep 17 00:00:00 2001 From: Fugang Duan Date: Wed, 17 Jul 2019 13:19:29 +0800 Subject: tty: serial: fsl_lpuart: remove sg_set_buf() for sport->rx_sgl Since .sg_init_one() already set sg entry page like below code. sg_init_one() sg_init_table(sg, 1); sg_set_buf(sg, buf, buflen); So it should not set sg entry page again, remove the redundant code. Signed-off-by: Fugang Duan Link: https://lore.kernel.org/r/20190717051930.15514-5-fugang.duan@nxp.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/fsl_lpuart.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index 746681be3760..ba87b58cce4a 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -1102,7 +1102,6 @@ static inline int lpuart_start_rx_dma(struct lpuart_port *sport) 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) { -- cgit v1.2.3-59-g8ed1b From f77ebb241ce00ec99ed4e42ff3ab38dd6d1ce6f5 Mon Sep 17 00:00:00 2001 From: Fugang Duan Date: Wed, 17 Jul 2019 13:19:30 +0800 Subject: tty: serial: fsl_lpuart: correct the FIFO depth size VF610/LS1021a/i.MX7ULP/i.MX8QXP reference manual describe the TXFIFOSIZE/RXFIFOSIZE field as below. 000b - FIFO/Buffer depth = 1 dataword. 001b - FIFO/Buffer depth = 4 datawords. 010b - FIFO/Buffer depth = 8 datawords. 011b - FIFO/Buffer depth = 16 datawords. 100b - FIFO/Buffer depth = 32 datawords. 101b - FIFO/Buffer depth = 64 datawords. 110b - FIFO/Buffer depth = 128 datawords. 111b - FIFO/Buffer depth = 256 datawords. (Reserved for VF610) So the FIFO depth should be: 0x1 << (val ? (val + 1) : 0) Signed-off-by: Fugang Duan Link: https://lore.kernel.org/r/20190717051930.15514-6-fugang.duan@nxp.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/fsl_lpuart.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index ba87b58cce4a..d15e65f88214 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -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 @@ -1380,13 +1381,12 @@ static int lpuart_startup(struct uart_port *port) /* 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->txfifo_size = UARTFIFO_DEPTH((temp >> UARTPFIFO_TXSIZE_OFF) & + UARTPFIFO_FIFOSIZE_MASK); sport->port.fifosize = sport->txfifo_size; - sport->rxfifo_size = 0x1 << (((temp >> UARTPFIFO_RXSIZE_OFF) & - UARTPFIFO_FIFOSIZE_MASK) + 1); + sport->rxfifo_size = UARTFIFO_DEPTH((temp >> UARTPFIFO_RXSIZE_OFF) & + UARTPFIFO_FIFOSIZE_MASK); spin_lock_irqsave(&sport->port.lock, flags); @@ -1431,13 +1431,12 @@ static int lpuart32_startup(struct uart_port *port) /* determine FIFO size */ temp = lpuart32_read(&sport->port, UARTFIFO); - sport->txfifo_size = 0x1 << (((temp >> UARTFIFO_TXSIZE_OFF) & - UARTFIFO_FIFOSIZE_MASK) - 1); - + sport->txfifo_size = UARTFIFO_DEPTH((temp >> UARTFIFO_TXSIZE_OFF) & + UARTFIFO_FIFOSIZE_MASK); sport->port.fifosize = sport->txfifo_size; - sport->rxfifo_size = 0x1 << (((temp >> UARTFIFO_RXSIZE_OFF) & - UARTFIFO_FIFOSIZE_MASK) - 1); + sport->rxfifo_size = UARTFIFO_DEPTH((temp >> UARTFIFO_RXSIZE_OFF) & + UARTFIFO_FIFOSIZE_MASK); spin_lock_irqsave(&sport->port.lock, flags); -- cgit v1.2.3-59-g8ed1b From ea5ab2e422de0ef0fc476fe40f0829abe72684cd Mon Sep 17 00:00:00 2001 From: Navid Emamdoost Date: Fri, 19 Jul 2019 12:48:45 -0500 Subject: 8250_lpss: check null return when calling pci_ioremap_bar pci_ioremap_bar may return null. This is eventually de-referenced at drivers/dma/dw/core.c:1154 and drivers/dma/dw/core.c:1168. A null check is needed to prevent null de-reference. I am adding the check and in case of failure. Thanks to Andy Shevchenko for the hint on the necessity of pci_iounmap when exiting. Signed-off-by: Navid Emamdoost Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20190719174848.24216-1-navid.emamdoost@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_lpss.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/8250/8250_lpss.c b/drivers/tty/serial/8250/8250_lpss.c index 53ca9ba6ab4b..d07e431110d9 100644 --- a/drivers/tty/serial/8250/8250_lpss.c +++ b/drivers/tty/serial/8250/8250_lpss.c @@ -169,10 +169,12 @@ static void qrk_serial_setup_dma(struct lpss8250 *lpss, struct uart_port *port) struct pci_dev *pdev = to_pci_dev(port->dev); int ret; + chip->pdata = &qrk_serial_dma_pdata; chip->dev = &pdev->dev; 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 +197,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) {} -- cgit v1.2.3-59-g8ed1b From 96b79ac705cccb9ff2485d59b83ea0b94b5329eb Mon Sep 17 00:00:00 2001 From: Erwan Le Ray Date: Thu, 13 Jun 2019 15:49:51 +0200 Subject: dt-bindings: serial: stm32: add wakeup option Add a note for enabling wakeup capabilities of usart Signed-off-by: Bich Hemon Signed-off-by: Erwan Le Ray Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/1560433800-12255-2-git-send-email-erwan.leray@st.com Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/serial/st,stm32-usart.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Documentation/devicetree/bindings/serial/st,stm32-usart.txt b/Documentation/devicetree/bindings/serial/st,stm32-usart.txt index a6b19485c9dc..8620f7fcbd50 100644 --- a/Documentation/devicetree/bindings/serial/st,stm32-usart.txt +++ b/Documentation/devicetree/bindings/serial/st,stm32-usart.txt @@ -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 { -- cgit v1.2.3-59-g8ed1b From 94616d9a9db0099f5251c60b831e03619c63523a Mon Sep 17 00:00:00 2001 From: Erwan Le Ray Date: Thu, 13 Jun 2019 15:49:53 +0200 Subject: serial: stm32: select pinctrl state in each suspend/resume function Select either pinctrl sleep state in suspend function or default state in resume function. Signed-off-by: Bich Hemon Signed-off-by: Erwan Le Ray Link: https://lore.kernel.org/r/1560433800-12255-4-git-send-email-erwan.leray@st.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/stm32-usart.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c index 24a2261f879a..7bb2bb9fffbc 100644 --- a/drivers/tty/serial/stm32-usart.c +++ b/drivers/tty/serial/stm32-usart.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -1373,6 +1374,8 @@ static int stm32_serial_suspend(struct device *dev) else stm32_serial_enable_wakeup(port, false); + pinctrl_pm_select_sleep_state(dev); + return 0; } @@ -1380,6 +1383,8 @@ static int 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); -- cgit v1.2.3-59-g8ed1b From fb6dcef62d52fe76f6b369e6af093dc4ad5db407 Mon Sep 17 00:00:00 2001 From: Erwan Le Ray Date: Thu, 13 Jun 2019 15:49:54 +0200 Subject: serial: stm32: add pm_runtime support Use pm_runtime for clock management. Signed-off-by: Bich Hemon Signed-off-by: Erwan Le Ray Link: https://lore.kernel.org/r/1560433800-12255-5-git-send-email-erwan.leray@st.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/stm32-usart.c | 41 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c index 7bb2bb9fffbc..6ddc6b08b29a 100644 --- a/drivers/tty/serial/stm32-usart.c +++ b/drivers/tty/serial/stm32-usart.c @@ -882,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; } } @@ -1186,6 +1186,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: @@ -1207,6 +1212,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); @@ -1235,7 +1243,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; } @@ -1392,7 +1405,29 @@ static int stm32_serial_resume(struct device *dev) } #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) }; -- cgit v1.2.3-59-g8ed1b From fe94347d6dc97e044db011010c55cb95367c23d0 Mon Sep 17 00:00:00 2001 From: Erwan Le Ray Date: Thu, 13 Jun 2019 15:49:55 +0200 Subject: serial: stm32: Use __maybe_unused instead of #if CONFIG_PM_SLEEP Use __maybe_unused for power management related functionsinstead of fixes: 270e5a74fe4c ("serial: stm32: add wakeup mechanism") Signed-off-by: Erwan Le Ray Link: https://lore.kernel.org/r/1560433800-12255-6-git-send-email-erwan.leray@st.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/stm32-usart.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c index 6ddc6b08b29a..45dbc42e15b9 100644 --- a/drivers/tty/serial/stm32-usart.c +++ b/drivers/tty/serial/stm32-usart.c @@ -1351,8 +1351,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; @@ -1376,7 +1376,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); @@ -1392,7 +1392,7 @@ static int stm32_serial_suspend(struct device *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); @@ -1403,7 +1403,6 @@ static int stm32_serial_resume(struct device *dev) return uart_resume_port(&stm32_usart_driver, port); } -#endif /* CONFIG_PM_SLEEP */ static int __maybe_unused stm32_serial_runtime_suspend(struct device *dev) { -- cgit v1.2.3-59-g8ed1b From fdc2de87124f5183a98ea7eced1f76dbdba22951 Mon Sep 17 00:00:00 2001 From: Je Yen Tam Date: Fri, 26 Jul 2019 15:40:12 +0800 Subject: serial/8250: Add support for NI-Serial PXI/PXIe+485 devices Add support for NI-Serial PXIe-RS232, PXI-RS485 and PXIe-RS485 devices. Signed-off-by: Je Yen Tam Link: https://lore.kernel.org/r/20190726074012.2590-1-je.yen.tam@ni.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_pci.c | 292 ++++++++++++++++++++++++++++++++++++- 1 file changed, 288 insertions(+), 4 deletions(-) diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index b714d8d0e161..d0be326a17e5 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -730,8 +730,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 +761,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) @@ -1786,6 +1897,15 @@ 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 /* Unknown vendors/cards - this should not be in linux/pci_ids.h */ @@ -2011,6 +2131,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, @@ -2740,6 +2941,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, @@ -3381,6 +3589,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 */ @@ -5063,6 +5320,33 @@ 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 }, /* * ADDI-DATA GmbH communication cards -- cgit v1.2.3-59-g8ed1b From 72d819612ae0b080ba141a6959cea138ddc1e330 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 26 Jul 2019 20:28:17 +0300 Subject: serial: 8250_pnp: Move to struct dev_pm_ops The established way to provide PM callbacks is through struct dev_pm_ops which is more generic. Convert driver to use it instead of legacy approach. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20190726172817.73253-1-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_pnp.c | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/drivers/tty/serial/8250/8250_pnp.c b/drivers/tty/serial/8250/8250_pnp.c index dfca33141fcc..de90d681b64c 100644 --- a/drivers/tty/serial/8250/8250_pnp.c +++ b/drivers/tty/serial/8250/8250_pnp.c @@ -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, }; -- cgit v1.2.3-59-g8ed1b From a25aee902e666e8cc41e33754f13c69a979dd67f Mon Sep 17 00:00:00 2001 From: Sergey Organov Date: Fri, 26 Jul 2019 21:52:39 +0300 Subject: serial: imx: set_termios(): do not enable autoRTS if RTS is unset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Don't let receiver hardware automatically control RTS output if it was requested to be inactive. To ensure this, set_termios() shouldn't set UCR2_CTSC bit if UCR2_CTS (=TIOCM_RTS) is cleared. Added corresponding check in imx_uart_rts_auto() to fix this. Acked-by: Uwe Kleine-König Reviewed-by: Sascha Hauer Tested-by: Sascha Hauer Signed-off-by: Sergey Organov Link: https://lore.kernel.org/r/1564167161-3972-2-git-send-email-sorganov@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/imx.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index 57d6e6ba556e..32f36d86a960 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -405,7 +405,12 @@ static void imx_uart_rts_inactive(struct imx_port *sport, u32 *ucr2) /* called with port.lock taken and irqs caller dependent */ static void imx_uart_rts_auto(struct imx_port *sport, u32 *ucr2) { - *ucr2 |= UCR2_CTSC; + /* + * 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; } /* called with port.lock taken and irqs off */ -- cgit v1.2.3-59-g8ed1b From 197540dc8301a296e8ae7aa062f9f61f047eea05 Mon Sep 17 00:00:00 2001 From: Sergey Organov Date: Fri, 26 Jul 2019 21:52:40 +0300 Subject: serial: imx: set_mctrl(): correctly restore autoRTS state imx_uart_set_mctrl() happened to set UCR2_CTSC bit whenever TIOCM_RTS was set, no matter if RTS/CTS handshake is enabled or not. Now fixed by turning handshake on only when CRTSCTS bit for the port is set. Reviewed-by: Sascha Hauer Tested-by: Sascha Hauer Signed-off-by: Sergey Organov Link: https://lore.kernel.org/r/1564167161-3972-3-git-send-email-sorganov@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/imx.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index 32f36d86a960..059ba354d17e 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -974,10 +974,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); } -- cgit v1.2.3-59-g8ed1b From b777b5de6aaa98df37c0fe1f7a33fa1c63d0e326 Mon Sep 17 00:00:00 2001 From: Sergey Organov Date: Fri, 26 Jul 2019 21:52:41 +0300 Subject: serial: imx: get rid of imx_uart_rts_auto() Called in only one place, for RS232, it only obscures things, as it doesn't go well with 2 similar named functions, imx_uart_rts_inactive() and imx_uart_rts_active(), that both are RS485-specific. Reviewed-by: Sascha Hauer Tested-by: Sascha Hauer Signed-off-by: Sergey Organov Link: https://lore.kernel.org/r/1564167161-3972-4-git-send-email-sorganov@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/imx.c | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index 059ba354d17e..d9a73c7683ea 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -402,17 +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) -{ - /* - * 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; -} - /* called with port.lock taken and irqs off */ static void imx_uart_start_rx(struct uart_port *port) { @@ -1604,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; -- cgit v1.2.3-59-g8ed1b From 656321793ff419eda148d91dd02b167d6da819e4 Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Mon, 29 Jul 2019 12:52:03 -0700 Subject: tty: serial: fsl_lpuart: fix framing error handling when using DMA When using DMA framing error get cleared properly. However, due to the additional read from the data register, an underflow in the receive FIFO buffer occurs (the FIFO pointer gets out of sync). Clear the FIFO in case an underflow has occurred. Also disable the receiver during this operation and when reading the data register to minimize potential interference. Signed-off-by: Stefan Agner Acked-by: Max Krummenacher Signed-off-by: Andrey Smirnov Cc: Stefan Agner Cc: Bhuvanchandra DV Cc: Chris Healy Cc: Cory Tusar Cc: Lucas Stach Cc: Greg Kroah-Hartman Cc: Jiri Slaby Cc: linux-imx@nxp.com Cc: linux-serial@vger.kernel.org Cc: linux-kernel@vger.kernel.org Link: https://lore.kernel.org/r/20190729195226.8862-2-andrew.smirnov@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/fsl_lpuart.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index d15e65f88214..0643fa368d35 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -983,6 +983,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); @@ -990,6 +997,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); } } -- cgit v1.2.3-59-g8ed1b From cc584ab860575e8d7da18aab3701369e1c9bdff8 Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Mon, 29 Jul 2019 12:52:04 -0700 Subject: tty: serial: fsl_lpuart: flush receive FIFO after overruns After overruns the FIFO pointers become misaligned. This typically shows by characters still being stuck in the FIFO despite the empty flag being asserted. After the first assertion of the overrun flag the empty flag still seems to indicate FIFO state correctly and all data can be read. However, after another overrun assertion the FIFO seems to be off by one such that the last received character is still in the FIFO (despite the empty flag being asserted). Flushing the receive FIFO reinitializes pointers. Hence it is recommended to flush the FIFO after overruns, see also: https://community.nxp.com/thread/321175 Hence, on assertion of the overrun flag read the remaining data from the FIFO and flush buffers. Signed-off-by: Stefan Agner Acked-by: Max Krummenacher Cc: Stefan Agner Cc: Bhuvanchandra DV Cc: Chris Healy Cc: Cory Tusar Cc: Lucas Stach Cc: Greg Kroah-Hartman Cc: Jiri Slaby Cc: linux-imx@nxp.com Cc: linux-serial@vger.kernel.org Cc: linux-kernel@vger.kernel.org Link: https://lore.kernel.org/r/20190729195226.8862-3-andrew.smirnov@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/fsl_lpuart.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index 0643fa368d35..317bbc1dd2b4 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -799,7 +799,7 @@ out: static irqreturn_t lpuart_rxint(int irq, void *dev_id) { 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; @@ -826,7 +826,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) @@ -853,6 +853,17 @@ 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); -- cgit v1.2.3-59-g8ed1b From 9bc19af9dacbb53f6993c81e993b773bf1cef8b4 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Mon, 29 Jul 2019 12:52:05 -0700 Subject: tty: serial: fsl_lpuart: Flush HW FIFOs in .flush_buffer Switching baud rate might cause bogus data to appear in HW FIFO. Add code to do a HW FIFO flush to .flush_buffer callback to avoid that. Signed-off-by: Fugang Duan Signed-off-by: Andrey Smirnov Cc: Stefan Agner Cc: Bhuvanchandra DV Cc: Chris Healy Cc: Cory Tusar Cc: Lucas Stach Cc: Greg Kroah-Hartman Cc: Jiri Slaby Cc: linux-imx@nxp.com Cc: linux-serial@vger.kernel.org Cc: linux-kernel@vger.kernel.org Link: https://lore.kernel.org/r/20190729195226.8862-4-andrew.smirnov@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/fsl_lpuart.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index 317bbc1dd2b4..12ddca608eba 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -518,9 +518,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) { @@ -530,6 +537,16 @@ 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); + } } #if defined(CONFIG_CONSOLE_POLL) @@ -754,12 +771,6 @@ static unsigned int lpuart32_tx_empty(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 irqreturn_t lpuart_txint(int irq, void *dev_id) { struct lpuart_port *sport = dev_id; -- cgit v1.2.3-59-g8ed1b From 3993ddc236a5af74e80ee97e0f58390980b885ec Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Mon, 29 Jul 2019 12:52:06 -0700 Subject: tty: serial: fsl_lpuart: Simplify RX/TX IRQ handlers It appears that lpuart_rxint, lpuart_txint and lpuart32_rxint were modelled after identical function found in UART driver for i.MX. However, while said functions are used as individual IRQ handlers in i.MX driver (in case of i.MX1), it is not the case for LPUART. Given that, there's no need for us to restrict the prototype of the handler to irqreturn_t foo(int, void *) and we can drop all of uneened boilerplate code by changing it void foo(struct lpuart_port *). Signed-off-by: Andrey Smirnov Cc: Stefan Agner Cc: Bhuvanchandra DV Cc: Chris Healy Cc: Cory Tusar Cc: Lucas Stach Cc: Greg Kroah-Hartman Cc: Jiri Slaby Cc: linux-imx@nxp.com Cc: linux-serial@vger.kernel.org Cc: linux-kernel@vger.kernel.org Link: https://lore.kernel.org/r/20190729195226.8862-5-andrew.smirnov@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/fsl_lpuart.c | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index 12ddca608eba..c60c5354a5b9 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -771,9 +771,8 @@ static unsigned int lpuart32_tx_empty(struct uart_port *port) return 0; } -static irqreturn_t lpuart_txint(int irq, void *dev_id) +static void lpuart_txint(struct lpuart_port *sport) { - struct lpuart_port *sport = dev_id; struct circ_buf *xmit = &sport->port.state->xmit; unsigned long flags; @@ -804,12 +803,10 @@ static irqreturn_t lpuart_txint(int irq, void *dev_id) out: 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, overrun = 0; struct tty_port *port = &sport->port.state->port; unsigned long flags; @@ -878,12 +875,10 @@ out: 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_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; @@ -942,7 +937,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) @@ -953,10 +947,10 @@ 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); + lpuart_rxint(sport); if (sts & UARTSR1_TDRE) - lpuart_txint(irq, dev_id); + lpuart_txint(sport); return IRQ_HANDLED; } @@ -971,10 +965,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); + lpuart_txint(sport); lpuart32_write(&sport->port, sts, UARTSTAT); return IRQ_HANDLED; -- cgit v1.2.3-59-g8ed1b From 834a974168b54464b2d8dcdbb07e80290e8b2c41 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Mon, 29 Jul 2019 12:52:07 -0700 Subject: tty: serial: fsl_lpuart: Fix bogus indentation Fix bogus indentation in rx_dma_timer_init(). Signed-off-by: Andrey Smirnov Cc: Stefan Agner Cc: Bhuvanchandra DV Cc: Chris Healy Cc: Cory Tusar Cc: Lucas Stach Cc: Greg Kroah-Hartman Cc: Jiri Slaby Cc: linux-imx@nxp.com Cc: linux-serial@vger.kernel.org Cc: linux-kernel@vger.kernel.org Link: https://lore.kernel.org/r/20190729195226.8862-6-andrew.smirnov@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/fsl_lpuart.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index c60c5354a5b9..57c5825f5de7 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -1409,9 +1409,9 @@ static void lpuart32_setup_watermark(struct lpuart_port *sport) 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); + 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) -- cgit v1.2.3-59-g8ed1b From 66127ec7e3248f0f1086830fc7e649f1fb0e3b09 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Mon, 29 Jul 2019 12:52:09 -0700 Subject: tty: serial: fsl_lpuart: Drop unnecessary uart_write_wakeup() Uart_write_wakeup() will already be called as a part of lpuart*_transmit_buffer() call, so there doesn't seem to be a reason to call it again right after. It also appears that second uart_write_wakeup() might potentially cause unwanted write wakeup when transmitting an x_char. See commit 5e42e9a30cda ("serial: imx: Fix x_char handling and tx flow control") where this problem was fixed in a very similarly structured i.MX UART driver. Signed-off-by: Andrey Smirnov Cc: Stefan Agner Cc: Bhuvanchandra DV Cc: Chris Healy Cc: Cory Tusar Cc: Lucas Stach Cc: Greg Kroah-Hartman Cc: Jiri Slaby Cc: linux-imx@nxp.com Cc: linux-serial@vger.kernel.org Cc: linux-kernel@vger.kernel.org Link: https://lore.kernel.org/r/20190729195226.8862-8-andrew.smirnov@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/fsl_lpuart.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index 57c5825f5de7..c35f81df0cff 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -798,9 +798,6 @@ static void lpuart_txint(struct lpuart_port *sport) else lpuart_transmit_buffer(sport); - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(&sport->port); - out: spin_unlock_irqrestore(&sport->port.lock, flags); } -- cgit v1.2.3-59-g8ed1b From 15dd287b28b959d6c16d2f7e49aab75a80749ec8 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Mon, 29 Jul 2019 12:52:10 -0700 Subject: tty: serial: fsl_lpuart: Fix issue in software flow control Although I haven't observed this bug in practice, it seems that the code for handling x_char of LPUART is pretty much identical to that of i.MX. So the fix found in commit 7e2fb5aa8d81 ("serial: imx: Fix issue in software flow control"): serial: imx: Fix issue in software flow control After send out x_char in UART driver, x_char needs to be cleared by UART driver itself, otherwise data in TXFIFO can no longer be sent out. Also tx counter needs to be increased to keep track of correct number of transmitted data. Signed-off-by: Jiada Wang Signed-off-by: Greg Kroah-Hartman should apply here as well. Signed-off-by: Andrey Smirnov Cc: Stefan Agner Cc: Bhuvanchandra DV Cc: Chris Healy Cc: Cory Tusar Cc: Lucas Stach Cc: Greg Kroah-Hartman Cc: Jiri Slaby Cc: linux-imx@nxp.com Cc: linux-serial@vger.kernel.org Cc: linux-kernel@vger.kernel.org Link: https://lore.kernel.org/r/20190729195226.8862-9-andrew.smirnov@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/fsl_lpuart.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index c35f81df0cff..8657addbfd1c 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -782,6 +782,8 @@ static void lpuart_txint(struct lpuart_port *sport) lpuart32_write(&sport->port, sport->port.x_char, UARTDATA); else writeb(sport->port.x_char, sport->port.membase + UARTDR); + sport->port.icount.tx++; + sport->port.x_char = 0; goto out; } -- cgit v1.2.3-59-g8ed1b From 93b9523a8e72e33388e1adeb4ca4ac3dbf614c82 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Mon, 29 Jul 2019 12:52:11 -0700 Subject: tty: serial: fls_lpuart: Split shared TX IRQ handler into two While sharing code for Tx interrupt handler between 8 and 32 bit variant of the peripheral saves a bit of code duplication it also adds quite a number of lpuart_is_32() checks which makes it harder to understand. Move shared bits back into corresponding lpuart*_transmit_buffer functions, split lpuart_txint into lpuart_txint and lpuart32_txint so we can drop all extra lpuart_is_32() check and make the code flow more linear. Signed-off-by: Andrey Smirnov Cc: Stefan Agner Cc: Bhuvanchandra DV Cc: Chris Healy Cc: Cory Tusar Cc: Lucas Stach Cc: Greg Kroah-Hartman Cc: Jiri Slaby Cc: linux-imx@nxp.com Cc: linux-serial@vger.kernel.org Cc: linux-kernel@vger.kernel.org Link: https://lore.kernel.org/r/20190729195226.8862-10-andrew.smirnov@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/fsl_lpuart.c | 61 +++++++++++++++++++++++------------------ 1 file changed, 35 insertions(+), 26 deletions(-) diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index 8657addbfd1c..ed6d3f836c93 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -663,6 +663,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 (uart_circ_empty(xmit) || uart_tx_stopped(&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); @@ -682,6 +694,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 (uart_circ_empty(xmit) || uart_tx_stopped(&sport->port)) { + lpuart32_stop_tx(&sport->port); + return; + } + txcnt = lpuart32_read(&sport->port, UARTWATER); txcnt = txcnt >> UARTWATER_TXCNT_OFF; txcnt &= UARTWATER_COUNT_MASK; @@ -773,34 +797,10 @@ static unsigned int lpuart32_tx_empty(struct uart_port *port) static void lpuart_txint(struct lpuart_port *sport) { - 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); - sport->port.icount.tx++; - sport->port.x_char = 0; - 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); - -out: + lpuart_transmit_buffer(sport); spin_unlock_irqrestore(&sport->port.lock, flags); } @@ -876,6 +876,15 @@ out: tty_flip_buffer_push(port); } +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) { unsigned int flg, ignored = 0; @@ -967,7 +976,7 @@ static irqreturn_t lpuart32_int(int irq, void *dev_id) lpuart32_rxint(sport); if ((sts & UARTSTAT_TDRE) && !sport->lpuart_dma_tx_use) - lpuart_txint(sport); + lpuart32_txint(sport); lpuart32_write(&sport->port, sts, UARTSTAT); return IRQ_HANDLED; -- cgit v1.2.3-59-g8ed1b From d26454ee3c4566f8698ade45766b54a56995d7ee Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Mon, 29 Jul 2019 12:52:12 -0700 Subject: tty: serial: fsl_lpuart: Drop no-op bit opearation The check for termios->c_cflag & CRTSCTS ensure that if we reach else branch, CRTSCTS in termios->c_cflag is already going to be cleard. Doing so explicitly there is not necessary. Drop it. Signed-off-by: Andrey Smirnov Cc: Stefan Agner Cc: Bhuvanchandra DV Cc: Chris Healy Cc: Cory Tusar Cc: Lucas Stach Cc: Greg Kroah-Hartman Cc: Jiri Slaby Cc: linux-imx@nxp.com Cc: linux-serial@vger.kernel.org Cc: linux-kernel@vger.kernel.org Link: https://lore.kernel.org/r/20190729195226.8862-11-andrew.smirnov@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/fsl_lpuart.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index ed6d3f836c93..b3cc44548b34 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -1650,12 +1650,10 @@ 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) { + if (termios->c_cflag & CRTSCTS) modem |= (UARTMODEM_RXRTSE | UARTMODEM_TXCTSE); - } else { - termios->c_cflag &= ~CRTSCTS; + else modem &= ~(UARTMODEM_RXRTSE | UARTMODEM_TXCTSE); - } if (termios->c_cflag & CSTOPB) termios->c_cflag &= ~CSTOPB; -- cgit v1.2.3-59-g8ed1b From bcfa46bfb9b7c6924ef9a61737cb2567281eac7b Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Mon, 29 Jul 2019 12:52:13 -0700 Subject: tty: serial: fsl_lpuart: Drop unnecessary extra parenthesis Drop unnecessary extra parenthesis in the driver. Signed-off-by: Andrey Smirnov Cc: Stefan Agner Cc: Bhuvanchandra DV Cc: Chris Healy Cc: Cory Tusar Cc: Lucas Stach Cc: Greg Kroah-Hartman Cc: Jiri Slaby Cc: linux-imx@nxp.com Cc: linux-serial@vger.kernel.org Cc: linux-kernel@vger.kernel.org Link: https://lore.kernel.org/r/20190729195226.8862-12-andrew.smirnov@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/fsl_lpuart.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index b3cc44548b34..79922f179b08 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -1443,7 +1443,7 @@ static int lpuart_startup(struct uart_port *port) lpuart_setup_watermark(sport); temp = readb(sport->port.membase + UARTCR2); - temp |= (UARTCR2_RIE | UARTCR2_TIE | UARTCR2_RE | UARTCR2_TE); + 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)) { @@ -1651,7 +1651,7 @@ lpuart_set_termios(struct uart_port *port, struct ktermios *termios, termios->c_cflag &= ~CRTSCTS; if (termios->c_cflag & CRTSCTS) - modem |= (UARTMODEM_RXRTSE | UARTMODEM_TXCTSE); + modem |= UARTMODEM_RXRTSE | UARTMODEM_TXCTSE; else modem &= ~(UARTMODEM_RXRTSE | UARTMODEM_TXCTSE); @@ -1662,7 +1662,7 @@ lpuart_set_termios(struct uart_port *port, struct ktermios *termios, 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) @@ -1701,7 +1701,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; @@ -1815,7 +1815,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; @@ -1868,7 +1868,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); @@ -1917,7 +1917,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; @@ -2084,7 +2084,7 @@ 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); @@ -2115,7 +2115,7 @@ 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); @@ -2572,7 +2572,7 @@ static int lpuart_resume(struct device *dev) } else { lpuart_setup_watermark(sport); temp = readb(sport->port.membase + UARTCR2); - temp |= (UARTCR2_RIE | UARTCR2_TIE | UARTCR2_RE | UARTCR2_TE); + temp |= UARTCR2_RIE | UARTCR2_TIE | UARTCR2_RE | UARTCR2_TE; writeb(temp, sport->port.membase + UARTCR2); } -- cgit v1.2.3-59-g8ed1b From 76e3f2ac4a4a8d5b1222427809430c9921176f1e Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Mon, 29 Jul 2019 12:52:14 -0700 Subject: tty: serial: fsl_lpuart: Clear CSTOPB unconditionally Clearing CSTOPB bit if it is set is functionally equivalent to jsut clearing it unconditionally. Drop unnecessary check. Signed-off-by: Andrey Smirnov Cc: Stefan Agner Cc: Bhuvanchandra DV Cc: Chris Healy Cc: Cory Tusar Cc: Lucas Stach Cc: Greg Kroah-Hartman Cc: Jiri Slaby Cc: linux-imx@nxp.com Cc: linux-serial@vger.kernel.org Cc: linux-kernel@vger.kernel.org Link: https://lore.kernel.org/r/20190729195226.8862-13-andrew.smirnov@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/fsl_lpuart.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index 79922f179b08..a0f38d7caa76 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -1655,8 +1655,7 @@ lpuart_set_termios(struct uart_port *port, struct ktermios *termios, 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) -- cgit v1.2.3-59-g8ed1b From 1da17d7cf8e2c4b60163d54300f72c02f510327c Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Mon, 29 Jul 2019 12:52:15 -0700 Subject: tty: serial: fsl_lpuart: Use appropriate lpuart32_* I/O funcs When dealing with 32-bit variant of LPUART IP block appropriate I/O helpers have to be used to properly deal with endianness differences. Change all of the offending code to do that. Fixes: a5fa2660d787 ("tty/serial/fsl_lpuart: Add CONSOLE_POLL support for lpuart32.") Signed-off-by: Andrey Smirnov Cc: Stefan Agner Cc: Bhuvanchandra DV Cc: Chris Healy Cc: Cory Tusar Cc: Lucas Stach Cc: Greg Kroah-Hartman Cc: Jiri Slaby Cc: linux-imx@nxp.com Cc: linux-serial@vger.kernel.org Cc: linux-kernel@vger.kernel.org Link: https://lore.kernel.org/r/20190729195226.8862-14-andrew.smirnov@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/fsl_lpuart.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index a0f38d7caa76..395df8c7e1d5 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -617,26 +617,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; @@ -644,18 +644,18 @@ 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)) + while (!(lpuart32_read(port, UARTSTAT) & UARTSTAT_TDRE)) barrier(); - writel(c, port->membase + UARTDATA); + 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 -- cgit v1.2.3-59-g8ed1b From 56dd627fb223d1225a7d22e7a797609b91699a81 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Mon, 29 Jul 2019 12:52:16 -0700 Subject: tty: serial: fsl_lpuart: Introduce lpuart_wait_bit_set() Busy polling on a bit in a register is used in multiple places in the driver. Move it into a shared function. Signed-off-by: Andrey Smirnov Cc: Stefan Agner Cc: Bhuvanchandra DV Cc: Chris Healy Cc: Cory Tusar Cc: Lucas Stach Cc: Greg Kroah-Hartman Cc: Jiri Slaby Cc: linux-imx@nxp.com Cc: linux-serial@vger.kernel.org Cc: linux-kernel@vger.kernel.org Link: https://lore.kernel.org/r/20190729195226.8862-15-andrew.smirnov@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/fsl_lpuart.c | 42 +++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index 395df8c7e1d5..eb748ef85da7 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -549,6 +549,20 @@ static void lpuart_flush_buffer(struct uart_port *port) } } +static void lpuart_wait_bit_set(struct uart_port *port, unsigned int offset, + u8 bit) +{ + while (!(readb(port->membase + offset) & bit)) + barrier(); +} + +static void lpuart32_wait_bit_set(struct uart_port *port, unsigned int offset, + u32 bit) +{ + while (!(lpuart32_read(port, offset) & bit)) + barrier(); +} + #if defined(CONFIG_CONSOLE_POLL) static int lpuart_poll_init(struct uart_port *port) @@ -592,9 +606,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); } @@ -644,9 +656,7 @@ static int lpuart32_poll_init(struct uart_port *port) static void lpuart32_poll_put_char(struct uart_port *port, unsigned char c) { - while (!(lpuart32_read(port, UARTSTAT) & UARTSTAT_TDRE)) - barrier(); - + lpuart32_wait_bit_set(port, UARTSTAT, UARTSTAT_TDRE); lpuart32_write(port, UARTDATA, c); } @@ -1722,8 +1732,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), @@ -1938,8 +1947,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), @@ -2054,17 +2062,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); } @@ -2090,8 +2094,7 @@ lpuart_console_write(struct console *co, const char *s, unsigned int count) 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); @@ -2121,8 +2124,7 @@ lpuart32_console_write(struct console *co, const char *s, unsigned int count) 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); -- cgit v1.2.3-59-g8ed1b From f2f5e04c75c11d7e97d2fd24c162de3994451289 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Mon, 29 Jul 2019 12:52:17 -0700 Subject: tty: serial: fsl_lpuart: Use cpu_relax() instead of barrier() Use cpu_relax() instead of barrier() in a tight polling loops to make them a bit more idiomatic. Should also improve things on ARM64 a bit since cpu_relax() will expand into "yield" instruction there. Signed-off-by: Andrey Smirnov Cc: Stefan Agner Cc: Bhuvanchandra DV Cc: Chris Healy Cc: Cory Tusar Cc: Lucas Stach Cc: Greg Kroah-Hartman Cc: Jiri Slaby Cc: linux-imx@nxp.com Cc: linux-serial@vger.kernel.org Cc: linux-kernel@vger.kernel.org Link: https://lore.kernel.org/r/20190729195226.8862-16-andrew.smirnov@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/fsl_lpuart.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index eb748ef85da7..58043e01a242 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -553,14 +553,14 @@ static void lpuart_wait_bit_set(struct uart_port *port, unsigned int offset, u8 bit) { while (!(readb(port->membase + offset) & bit)) - barrier(); + cpu_relax(); } static void lpuart32_wait_bit_set(struct uart_port *port, unsigned int offset, u32 bit) { while (!(lpuart32_read(port, offset) & bit)) - barrier(); + cpu_relax(); } #if defined(CONFIG_CONSOLE_POLL) -- cgit v1.2.3-59-g8ed1b From a90fa53282f33721cbedeaa7c86dc2a037246319 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Mon, 29 Jul 2019 12:52:18 -0700 Subject: tty: serial: fsl_lpuart: Introduce lpuart_stopped_or_empty() The check for uart_circ_empty(xmit) || uart_tx_stopped(&sport->port) appears in multiple places in the driver. Move it into a helper function. Signed-off-by: Andrey Smirnov Cc: Stefan Agner Cc: Bhuvanchandra DV Cc: Chris Healy Cc: Cory Tusar Cc: Lucas Stach Cc: Greg Kroah-Hartman Cc: Jiri Slaby Cc: linux-imx@nxp.com Cc: linux-serial@vger.kernel.org Cc: linux-kernel@vger.kernel.org Link: https://lore.kernel.org/r/20190729195226.8862-17-andrew.smirnov@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/fsl_lpuart.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index 58043e01a242..e59dd90fc34c 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -452,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; @@ -479,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); @@ -680,7 +685,7 @@ static inline void lpuart_transmit_buffer(struct lpuart_port *sport) return; } - if (uart_circ_empty(xmit) || uart_tx_stopped(&sport->port)) { + if (lpuart_stopped_or_empty(&sport->port)) { lpuart_stop_tx(&sport->port); return; } @@ -711,7 +716,7 @@ static inline void lpuart32_transmit_buffer(struct lpuart_port *sport) return; } - if (uart_circ_empty(xmit) || uart_tx_stopped(&sport->port)) { + if (lpuart_stopped_or_empty(&sport->port)) { lpuart32_stop_tx(&sport->port); return; } @@ -739,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) @@ -757,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); -- cgit v1.2.3-59-g8ed1b From 8a9b82422ff5b8d4a7dffacd82208b083363b56c Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Mon, 29 Jul 2019 12:52:19 -0700 Subject: tty: serial: fsl_lpuart: Drop unnecessary lpuart*_stop_tx() By the time lpuart_shutdown() calls lpuart_stop_tx() UARTCR2_TE and UARTCR2_TIE (which the latter will clear) are already cleared, so that function call should effectively be a no-op. Moreso, lpuart_stop_tx() is expected to be executed with port spinlock held, which the caller doesn't. Given all that, drop the call to lpuart_stop_tx() in lpuart_shutdown(). In case of lpuart32_shutdown()/lpuart32_stop_tx(), TIE won't even be set if lpuart_dma_tx_use is true. Drop it there as well. Signed-off-by: Andrey Smirnov Cc: Stefan Agner Cc: Bhuvanchandra DV Cc: Chris Healy Cc: Cory Tusar Cc: Lucas Stach Cc: Greg Kroah-Hartman Cc: Jiri Slaby Cc: linux-imx@nxp.com Cc: linux-serial@vger.kernel.org Cc: linux-kernel@vger.kernel.org Link: https://lore.kernel.org/r/20190729195226.8862-18-andrew.smirnov@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/fsl_lpuart.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index e59dd90fc34c..2597a877d639 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -1574,8 +1574,6 @@ static void lpuart_shutdown(struct uart_port *port) sport->dma_tx_in_progress = false; dmaengine_terminate_all(sport->dma_tx_chan); } - - lpuart_stop_tx(port); } } @@ -1607,8 +1605,6 @@ static void lpuart32_shutdown(struct uart_port *port) sport->dma_tx_in_progress = false; dmaengine_terminate_all(sport->dma_tx_chan); } - - lpuart32_stop_tx(port); } } -- cgit v1.2.3-59-g8ed1b From 769d55c523f7ba572d7e7aefa17106e5deff66e0 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Mon, 29 Jul 2019 12:52:20 -0700 Subject: tty: serial: fsl_lpuart: Introduce lpuart_dma_shutdown() Last steps of .shutdown() code are identical for lpuart and lpuart32 cases, so move it all into a standalone subroutine. Signed-off-by: Andrey Smirnov Cc: Stefan Agner Cc: Bhuvanchandra DV Cc: Chris Healy Cc: Cory Tusar Cc: Lucas Stach Cc: Greg Kroah-Hartman Cc: Jiri Slaby Cc: linux-imx@nxp.com Cc: linux-serial@vger.kernel.org Cc: linux-kernel@vger.kernel.org Link: https://lore.kernel.org/r/20190729195226.8862-19-andrew.smirnov@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/fsl_lpuart.c | 42 ++++++++++++++++++----------------------- 1 file changed, 18 insertions(+), 24 deletions(-) diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index 2597a877d639..8cf10d12947f 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -1547,6 +1547,22 @@ static int lpuart32_startup(struct uart_port *port) 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); @@ -1563,18 +1579,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_dma_shutdown(sport); } static void lpuart32_shutdown(struct uart_port *port) @@ -1594,18 +1599,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); - } - } + lpuart_dma_shutdown(sport); } static void -- cgit v1.2.3-59-g8ed1b From 1df217868178bde7f4405255416de9547d16c6e8 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Tue, 30 Jul 2019 11:15:44 -0700 Subject: tty: Remove dev_err() usage after platform_get_irq() We don't need dev_err() messages when platform_get_irq() fails now that platform_get_irq() prints an error message itself when something goes wrong. Let's remove these prints with a simple semantic patch. // @@ expression ret; struct platform_device *E; @@ ret = ( platform_get_irq(E, ...) | platform_get_irq_byname(E, ...) ); if ( \( ret < 0 \| ret <= 0 \) ) { ( -if (ret != -EPROBE_DEFER) -{ ... -dev_err(...); -... } | ... -dev_err(...); ) ... } // While we're here, remove braces on if statements that only have one statement (manually). Cc: Greg Kroah-Hartman Cc: Jiri Slaby Cc: Greg Kroah-Hartman Signed-off-by: Stephen Boyd Link: https://lore.kernel.org/r/20190730181557.90391-45-swboyd@chromium.org Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_bcm2835aux.c | 4 +--- drivers/tty/serial/8250/8250_lpc18xx.c | 4 +--- drivers/tty/serial/8250/8250_uniphier.c | 4 +--- drivers/tty/serial/amba-pl011.c | 5 +---- drivers/tty/serial/fsl_lpuart.c | 4 +--- drivers/tty/serial/lpc32xx_hs.c | 5 +---- drivers/tty/serial/mvebu-uart.c | 12 +++--------- drivers/tty/serial/owl-uart.c | 4 +--- drivers/tty/serial/qcom_geni_serial.c | 4 +--- drivers/tty/serial/rda-uart.c | 4 +--- drivers/tty/serial/sccnxp.c | 1 - drivers/tty/serial/serial-tegra.c | 4 +--- drivers/tty/serial/sifive.c | 4 +--- drivers/tty/serial/sprd_serial.c | 4 +--- drivers/tty/serial/stm32-usart.c | 17 ++++------------- 15 files changed, 19 insertions(+), 61 deletions(-) diff --git a/drivers/tty/serial/8250/8250_bcm2835aux.c b/drivers/tty/serial/8250/8250_bcm2835aux.c index bd53661103eb..8ce700c1a7fc 100644 --- a/drivers/tty/serial/8250/8250_bcm2835aux.c +++ b/drivers/tty/serial/8250/8250_bcm2835aux.c @@ -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 */ diff --git a/drivers/tty/serial/8250/8250_lpc18xx.c b/drivers/tty/serial/8250/8250_lpc18xx.c index eddf119374e1..570e25d6f37e 100644 --- a/drivers/tty/serial/8250/8250_lpc18xx.c +++ b/drivers/tty/serial/8250/8250_lpc18xx.c @@ -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) { diff --git a/drivers/tty/serial/8250/8250_uniphier.c b/drivers/tty/serial/8250/8250_uniphier.c index 164ba133437a..e0b73a5402db 100644 --- a/drivers/tty/serial/8250/8250_uniphier.c +++ b/drivers/tty/serial/8250/8250_uniphier.c @@ -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) diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index 5921a33b2a07..3a7d1a66f79c 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -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 diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index 8cf10d12947f..0e09e6dc5ccb 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -2385,10 +2385,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)) diff --git a/drivers/tty/serial/lpc32xx_hs.c b/drivers/tty/serial/lpc32xx_hs.c index f4e27d0ad947..7c67e3afbac7 100644 --- a/drivers/tty/serial/lpc32xx_hs.c +++ b/drivers/tty/serial/lpc32xx_hs.c @@ -687,11 +687,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; diff --git a/drivers/tty/serial/mvebu-uart.c b/drivers/tty/serial/mvebu-uart.c index 7e7b1559fa36..c12a12556339 100644 --- a/drivers/tty/serial/mvebu-uart.c +++ b/drivers/tty/serial/mvebu-uart.c @@ -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; } diff --git a/drivers/tty/serial/owl-uart.c b/drivers/tty/serial/owl-uart.c index 29a6dc6a8d23..03963af77b15 100644 --- a/drivers/tty/serial/owl-uart.c +++ b/drivers/tty/serial/owl-uart.c @@ -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); diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c index 35e5f9c5d5be..f879710e23f1 100644 --- a/drivers/tty/serial/qcom_geni_serial.c +++ b/drivers/tty/serial/qcom_geni_serial.c @@ -1291,10 +1291,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; diff --git a/drivers/tty/serial/rda-uart.c b/drivers/tty/serial/rda-uart.c index 284623eefaeb..c1b0d7662ef9 100644 --- a/drivers/tty/serial/rda-uart.c +++ b/drivers/tty/serial/rda-uart.c @@ -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); diff --git a/drivers/tty/serial/sccnxp.c b/drivers/tty/serial/sccnxp.c index 68a24a14f6b7..d2b77aae42ae 100644 --- a/drivers/tty/serial/sccnxp.c +++ b/drivers/tty/serial/sccnxp.c @@ -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; } diff --git a/drivers/tty/serial/serial-tegra.c b/drivers/tty/serial/serial-tegra.c index d5269aaaf9b2..76ffbc7826ae 100644 --- a/drivers/tty/serial/serial-tegra.c +++ b/drivers/tty/serial/serial-tegra.c @@ -1307,10 +1307,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); diff --git a/drivers/tty/serial/sifive.c b/drivers/tty/serial/sifive.c index be4687814353..d5f81b98e4d7 100644 --- a/drivers/tty/serial/sifive.c +++ b/drivers/tty/serial/sifive.c @@ -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); diff --git a/drivers/tty/serial/sprd_serial.c b/drivers/tty/serial/sprd_serial.c index 73d71a4e6c0c..284709f61831 100644 --- a/drivers/tty/serial/sprd_serial.c +++ b/drivers/tty/serial/sprd_serial.c @@ -1173,10 +1173,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; /* diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c index 45dbc42e15b9..df90747ee3a8 100644 --- a/drivers/tty/serial/stm32-usart.c +++ b/drivers/tty/serial/stm32-usart.c @@ -928,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; @@ -941,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; -- cgit v1.2.3-59-g8ed1b From 2bf593f101f3ca8f512b7fcd50952d7f682ca794 Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Wed, 31 Jul 2019 16:05:57 +1000 Subject: xilinx_uartps.c: suppress "may be used uninitialised" warning A powerpc allyesconfig build produces this warning: In file included from include/linux/radix-tree.h:16, from include/linux/idr.h:15, from include/linux/kernfs.h:13, from include/linux/sysfs.h:16, from include/linux/kobject.h:20, from include/linux/device.h:16, from include/linux/platform_device.h:13, from drivers/tty/serial/xilinx_uartps.c:16: drivers/tty/serial/xilinx_uartps.c: In function 'cdns_uart_console_write': include/linux/spinlock.h:288:3: warning: 'flags' may be used uninitialized in this function [-Wmaybe-uninitialized] _raw_spin_unlock_irqrestore(lock, flags); \ ^~~~~~~~~~~~~~~~~~~~~~~~~~~ drivers/tty/serial/xilinx_uartps.c:1197:16: note: 'flags' was declared here unsigned long flags; ^~~~~ It looks like gcc just can't track the relationship between "locked" and "flags", and it is obvious that "flags" won't be used when "locked" is zero, so the simplest thing is to initialise flags. Cc: Jiri Slaby Cc: Michal Simek Signed-off-by: Stephen Rothwell Link: https://lore.kernel.org/r/20190731160557.6a09c3e1@canb.auug.org.au Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/xilinx_uartps.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index f145946f659b..da4563aaaf5c 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c @@ -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; -- cgit v1.2.3-59-g8ed1b From 6aa57f16185cfd3619a2257a4d34072b7b296d97 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 4 Jul 2019 10:46:09 +0200 Subject: serial: sh-sci: use driver core functions, not sysfs ones. This is a driver, do not call "raw" sysfs functions, instead call driver core ones. Specifically convert the use of sysfs_create_file() and sysfs_remove_file() to use device_create_file() and device_remove_file() Cc: Jiri Slaby Cc: linux-serial@vger.kernel.org Link: https://lore.kernel.org/r/20190704084617.3602-4-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index d18c680aa64b..91af4cd1ee03 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -3152,14 +3152,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; } @@ -3347,19 +3343,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; } -- cgit v1.2.3-59-g8ed1b From 6be254c2113d18984bae002b3b3dfc133cf56ac5 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 31 Jul 2019 20:05:56 +0300 Subject: serial: 8250_exar: No need to autoconfigure Exar ports Since we have a separate driver there is no need to autoconfigure ports, we already know what they are. Drop autoconfiguration in 8250_port and move type detection to 8250_exar. Cc: Aaron Sierra Cc: Jan Kiszka Cc: Sudip Mukherjee Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20190731170558.52897-1-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_exar.c | 19 +++++++++++++++++-- drivers/tty/serial/8250/8250_port.c | 34 ---------------------------------- 2 files changed, 17 insertions(+), 36 deletions(-) diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c index 8d60e5481af0..94212d9bf73f 100644 --- a/drivers/tty/serial/8250/8250_exar.c +++ b/drivers/tty/serial/8250/8250_exar.c @@ -36,6 +36,7 @@ #define UART_EXAR_INT0 0x80 #define UART_EXAR_8XMODE 0x88 /* 8X sampling rate select */ +#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 */ @@ -133,12 +134,27 @@ static int default_setup(struct exar8250 *priv, struct pci_dev *pcidev, { 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; + } else { + port->port.type = PORT_XR17D15X; + } + return 0; } @@ -494,8 +510,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; diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index c1cec808571b..6729b82ed2e3 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -45,7 +45,6 @@ */ #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 @@ -1011,27 +1010,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 +1148,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. -- cgit v1.2.3-59-g8ed1b From ef4e281ecccd5bc824a52a4b1e8fbdff3614c852 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 31 Jul 2019 20:05:57 +0300 Subject: serial: 8250_exar: Extract PM routine from 8250_port There are Exar quirks in 8250_port which belong to 8250_exar module. Extract PM routine to the correct module and do not contaminate generic code with it. Cc: Aaron Sierra Cc: Jan Kiszka Cc: Sudip Mukherjee Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20190731170558.52897-2-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_exar.c | 14 ++++++++++++++ drivers/tty/serial/8250/8250_port.c | 16 ++-------------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c index 94212d9bf73f..1bf9adea2e61 100644 --- a/drivers/tty/serial/8250/8250_exar.c +++ b/drivers/tty/serial/8250/8250_exar.c @@ -36,6 +36,7 @@ #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 */ @@ -128,6 +129,17 @@ 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); +} + static int default_setup(struct exar8250 *priv, struct pci_dev *pcidev, int idx, unsigned int offset, struct uart_8250_port *port) @@ -155,6 +167,8 @@ static int default_setup(struct exar8250 *priv, struct pci_dev *pcidev, port->port.type = PORT_XR17D15X; } + port->port.pm = exar_pm; + return 0; } diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index 6729b82ed2e3..c3ba4c794fee 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -44,7 +44,6 @@ * These are definitions for the Exar XR17V35X and XR17(C|D)15X */ #define UART_EXAR_INT0 0x80 -#define UART_EXAR_SLEEP 0x8b /* Sleep mode */ /* Nuvoton NPCM timeout register */ #define UART_NPCM_TOR 7 @@ -708,19 +707,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) { @@ -737,7 +725,7 @@ static void serial8250_set_sleep(struct uart_8250_port *p, int sleep) serial_out(p, UART_LCR, lcr); } } -out: + serial8250_rpm_put(p); } -- cgit v1.2.3-59-g8ed1b From b2b4b8ed3c06e5fc3586234b2dd2bb802b941261 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 31 Jul 2019 20:05:58 +0300 Subject: serial: 8250_exar: Move custom divisor support out from 8250_port There are Exar custom divisor support in 8250_port which belongs to 8250_exar module. Move it out to the correct module and do not contaminate generic code with it. Cc: Aaron Sierra Cc: Jan Kiszka Cc: Sudip Mukherjee Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20190731170558.52897-3-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_exar.c | 28 ++++++++++++++++++++++++++++ drivers/tty/serial/8250/8250_port.c | 26 -------------------------- 2 files changed, 28 insertions(+), 26 deletions(-) diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c index 1bf9adea2e61..3a39368e6e47 100644 --- a/drivers/tty/serial/8250/8250_exar.c +++ b/drivers/tty/serial/8250/8250_exar.c @@ -140,6 +140,31 @@ static void exar_pm(struct uart_port *port, unsigned int state, unsigned int old 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 int default_setup(struct exar8250 *priv, struct pci_dev *pcidev, int idx, unsigned int offset, struct uart_8250_port *port) @@ -163,6 +188,9 @@ static int default_setup(struct exar8250 *priv, struct pci_dev *pcidev, 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; } diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index c3ba4c794fee..995a7e8b7839 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -2407,23 +2407,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) @@ -2451,8 +2434,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 @@ -2539,13 +2520,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); -- cgit v1.2.3-59-g8ed1b From 47b1747f705e90d8197b77207e19c0ec67c16958 Mon Sep 17 00:00:00 2001 From: Robert Middleton Date: Thu, 1 Aug 2019 10:56:40 -0400 Subject: serial: 8250_exar: Clear buffer before shutdown When closing and shutting down the exar serial port, if the chip has not finished sending all of the data in its buffer, the remaining bytes will be lost. Hold off on the shutdown until the bytes have all been sent. Signed-off-by: Robert Middleton Link: https://lore.kernel.org/r/20190801145640.26080-1-robert.middleton@rm5248.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_exar.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c index 3a39368e6e47..357e20a6566f 100644 --- a/drivers/tty/serial/8250/8250_exar.c +++ b/drivers/tty/serial/8250/8250_exar.c @@ -19,6 +19,7 @@ #include #include #include +#include #include @@ -515,6 +516,27 @@ static irqreturn_t exar_misc_handler(int irq, void *data) return IRQ_HANDLED; } +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; + msleep(1); + } while (!uart_circ_empty(xmit) && !tx_complete && i++ < 1000); + + serial8250_do_shutdown(port); +} + static int exar_pci_probe(struct pci_dev *pcidev, const struct pci_device_id *ent) { @@ -555,6 +577,7 @@ exar_pci_probe(struct pci_dev *pcidev, const struct pci_device_id *ent) 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; + uart.port.shutdown = exar_shutdown; rc = devm_request_irq(&pcidev->dev, uart.port.irq, exar_misc_handler, IRQF_SHARED, "exar_uart", priv); -- cgit v1.2.3-59-g8ed1b From ce734600545fc7da8ae2c5d192b255761a244fb3 Mon Sep 17 00:00:00 2001 From: Vivek Gautam Date: Thu, 1 Aug 2019 17:41:53 +0530 Subject: tty: serial: qcom_geni_serial: Update the oversampling rate For QUP IP versions 2.5 and above the oversampling rate is halved from 32 to 16. Update this rate after reading hardware version register, so that the clock divider value is correctly set to achieve required baud rate. Signed-off-by: Vivek Gautam Link: https://lore.kernel.org/r/20190801121153.10613-1-vivek.gautam@codeaurora.org Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/qcom_geni_serial.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c index f879710e23f1..c73c18393e9b 100644 --- a/drivers/tty/serial/qcom_geni_serial.c +++ b/drivers/tty/serial/qcom_geni_serial.c @@ -920,12 +920,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 +952,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; -- cgit v1.2.3-59-g8ed1b From 7027e62a7d0690141f56e5221af3825ead440ecb Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 31 Jul 2019 14:45:55 +0200 Subject: serial: sh-sci: Use DEVICE_ATTR_RW() for rx_fifo_trigger While commit b6b996b6cdeecf7e ("treewide: Use DEVICE_ATTR_RW") converted the rx_fifo_timeout attribute, it forgot to convert rx_fifo_trigger due to a slightly different function naming. Signed-off-by: Geert Uytterhoeven Reviewed-by: Ulrich Hecht Link: https://lore.kernel.org/r/20190731124555.14349-1-geert+renesas@glider.be Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 91af4cd1ee03..7a03622cdda9 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -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, -- cgit v1.2.3-59-g8ed1b From 72169e4234d4cd3cd177d3021efde82bbdfa43e8 Mon Sep 17 00:00:00 2001 From: Aaron Sierra Date: Thu, 1 Aug 2019 13:59:56 -0500 Subject: serial: 8250_exar: Absorb remaining 8250_port INT0 support Move INT0 clearing out of common, per-port serial8250_do_startup() into PCI device probe/resume. As described in commit 2c0ac5b48a35 ("serial: exar: Fix stuck MSIs"), the purpose of clearing INT0 is to prevent the PCI interrupt line from becoming stuck asserted, "which is fatal with edge-triggered MSIs". Like the clearing via interrupt handler that moved from common code in commit c7e1b4059075 ("tty: serial: exar: Relocate sleep wake-up handling"), this clearing at startup can be better handled at the PCI device level. Cc: Jan Kiszka Cc: Sudip Mukherjee Signed-off-by: Aaron Sierra Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20190801185956.3222-1-asierra@xes-inc.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_exar.c | 24 ++++++++++++++++-------- drivers/tty/serial/8250/8250_port.c | 9 --------- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c index 357e20a6566f..3e93bd2326c9 100644 --- a/drivers/tty/serial/8250/8250_exar.c +++ b/drivers/tty/serial/8250/8250_exar.c @@ -493,6 +493,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 @@ -504,14 +514,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; } @@ -584,6 +587,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) { @@ -642,6 +648,8 @@ static int __maybe_unused exar_resume(struct device *dev) 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]); diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index 995a7e8b7839..706645f89132 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -40,11 +40,6 @@ #include "8250.h" -/* - * These are definitions for the Exar XR17V35X and XR17(C|D)15X - */ -#define UART_EXAR_INT0 0x80 - /* Nuvoton NPCM timeout register */ #define UART_NPCM_TOR 7 #define UART_NPCM_TOIE BIT(7) /* Timeout Interrupt Enable */ @@ -2138,8 +2133,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; @@ -2297,8 +2290,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; -- cgit v1.2.3-59-g8ed1b From e8b2a6187544e79fbc4863fe5cc746de8bba07ff Mon Sep 17 00:00:00 2001 From: Frieder Schrempf Date: Fri, 2 Aug 2019 10:04:09 +0000 Subject: serial: mctrl_gpio: Avoid probe failures in case of missing gpiolib If CONFIG_GPIOLIB is not enabled, mctrl_gpio_init() and mctrl_gpio_init_noauto() will currently return an error pointer with -ENOSYS. As the mctrl GPIOs are usually optional, drivers need to check for this condition to allow continue probing. To avoid the need for this check in each driver, we return NULL instead, as all the mctrl_gpio_*() functions are skipped anyway. We also adapt mctrl_gpio_to_gpiod() to be in line with this change. Reviewed-by: Fabio Estevam Signed-off-by: Frieder Schrempf Reviewed-by: Uwe Kleine-Knig Link: https://lore.kernel.org/r/20190802100349.8659-1-frieder.schrempf@kontron.de Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial_mctrl_gpio.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/tty/serial/serial_mctrl_gpio.h b/drivers/tty/serial/serial_mctrl_gpio.h index b7d3cca48ede..1b2ff503b2c2 100644 --- a/drivers/tty/serial/serial_mctrl_gpio.h +++ b/drivers/tty/serial/serial_mctrl_gpio.h @@ -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 -- cgit v1.2.3-59-g8ed1b From e55a09732be9b4e13cf3b5d2b9bb41b3e60e5ea6 Mon Sep 17 00:00:00 2001 From: Frieder Schrempf Date: Fri, 2 Aug 2019 10:04:10 +0000 Subject: serial: sh-sci: Don't check for mctrl_gpio_init() returning -ENOSYS Now that the mctrl_gpio code returns NULL instead of ERR_PTR(-ENOSYS) if CONFIG_GPIOLIB is disabled, we can safely remove this check. Signed-off-by: Frieder Schrempf Acked-by: Uwe Kleine-Knig Link: https://lore.kernel.org/r/20190802100349.8659-3-frieder.schrempf@kontron.de Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 7a03622cdda9..7f565fcbf1ca 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -3281,7 +3281,7 @@ 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) { -- cgit v1.2.3-59-g8ed1b From 3d7514da039186729f823db789193002730151c7 Mon Sep 17 00:00:00 2001 From: Frieder Schrempf Date: Fri, 2 Aug 2019 10:04:11 +0000 Subject: serial: 8250: Don't check for mctrl_gpio_init() returning -ENOSYS Now that the mctrl_gpio code returns NULL instead of ERR_PTR(-ENOSYS) if CONFIG_GPIOLIB is disabled, we can safely remove this check. Signed-off-by: Frieder Schrempf Acked-by: Uwe Kleine-Knig Link: https://lore.kernel.org/r/20190802100349.8659-4-frieder.schrempf@kontron.de Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_core.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index df3bcc0b2d74..e682390ce0de 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -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; } -- cgit v1.2.3-59-g8ed1b From 44e60d527e59f90d75bfda8521a10dc6e2d000d4 Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Fri, 2 Aug 2019 21:08:17 +0800 Subject: tty: serial: qcom_geni_serial: use devm_platform_ioremap_resource() to simplify code Use devm_platform_ioremap_resource() to simplify the code a bit. This is detected by coccinelle. Reported-by: Hulk Robot Signed-off-by: YueHaibing Link: https://lore.kernel.org/r/20190802130817.16220-1-yuehaibing@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/qcom_geni_serial.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c index c73c18393e9b..14c6306bc462 100644 --- a/drivers/tty/serial/qcom_geni_serial.c +++ b/drivers/tty/serial/qcom_geni_serial.c @@ -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; -- cgit v1.2.3-59-g8ed1b From 653d00c8d0f14b5aec8804b8ab107d5ac70b4a12 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 5 Aug 2019 17:25:34 +0300 Subject: serial: 8250_exar: Consolidate callback assignments in default_setup() For better maintenance consolidate port callbacks in default_setup(). Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20190805142535.21948-1-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_exar.c | 43 ++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c index 3e93bd2326c9..873aa6b0c2f3 100644 --- a/drivers/tty/serial/8250/8250_exar.c +++ b/drivers/tty/serial/8250/8250_exar.c @@ -166,6 +166,26 @@ static void xr17v35x_set_divisor(struct uart_port *p, unsigned int baud, 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; + msleep(1); + } 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) @@ -197,6 +217,7 @@ static int default_setup(struct exar8250 *priv, struct pci_dev *pcidev, } port->port.pm = exar_pm; + port->port.shutdown = exar_shutdown; return 0; } @@ -519,27 +540,6 @@ static irqreturn_t exar_misc_handler(int irq, void *data) return IRQ_HANDLED; } -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; - msleep(1); - } while (!uart_circ_empty(xmit) && !tx_complete && i++ < 1000); - - serial8250_do_shutdown(port); -} - static int exar_pci_probe(struct pci_dev *pcidev, const struct pci_device_id *ent) { @@ -580,7 +580,6 @@ exar_pci_probe(struct pci_dev *pcidev, const struct pci_device_id *ent) 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; - uart.port.shutdown = exar_shutdown; rc = devm_request_irq(&pcidev->dev, uart.port.irq, exar_misc_handler, IRQF_SHARED, "exar_uart", priv); -- cgit v1.2.3-59-g8ed1b From 3f72879e005ecec835ec75f7d8455c04b29de045 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 5 Aug 2019 17:25:35 +0300 Subject: serial: 8250_exar: Replace msleep(1) with usleep_range() As explained in Documentation/timers/timers-howto.rst the small amount of milliseconds sometimes produces much longer delays. Replace msleep(1) with usleep_range(1000, 1100). Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20190805142535.21948-2-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_exar.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c index 873aa6b0c2f3..597eb9d16f21 100644 --- a/drivers/tty/serial/8250/8250_exar.c +++ b/drivers/tty/serial/8250/8250_exar.c @@ -180,7 +180,7 @@ static void exar_shutdown(struct uart_port *port) tx_complete = 1; else tx_complete = 0; - msleep(1); + usleep_range(1000, 1100); } while (!uart_circ_empty(xmit) && !tx_complete && i++ < 1000); serial8250_do_shutdown(port); -- cgit v1.2.3-59-g8ed1b From 5982199ca071cb4f11503ecd6f37aa5c6a8dea6e Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Mon, 5 Aug 2019 11:56:56 -0700 Subject: tty: serial: fsl_lpuart: Introduce lpuart_tx_dma_startup() Code configure DMA TX path in lpuart_startup(), lpuart32_startup() and lpuart_resume() is doing exactly the same thing, so move it into a standalone subroutine. Signed-off-by: Andrey Smirnov Cc: Stefan Agner Cc: Chris Healy Cc: Cory Tusar Cc: Lucas Stach Cc: Greg Kroah-Hartman Cc: Jiri Slaby Cc: linux-imx@nxp.com Cc: linux-serial@vger.kernel.org Cc: linux-kernel@vger.kernel.org Link: https://lore.kernel.org/r/20190805185701.22863-2-andrew.smirnov@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/fsl_lpuart.c | 53 ++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 30 deletions(-) diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index 0e09e6dc5ccb..9da4529ee223 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -1435,6 +1435,26 @@ static void rx_dma_timer_init(struct lpuart_port *sport) 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 int lpuart_startup(struct uart_port *port) { struct lpuart_port *sport = container_of(port, struct lpuart_port, port); @@ -1471,14 +1491,7 @@ static int lpuart_startup(struct uart_port *port) 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; - } + lpuart_tx_dma_startup(sport); spin_unlock_irqrestore(&sport->port.lock, flags); @@ -1521,14 +1534,7 @@ static int lpuart32_startup(struct uart_port *port) 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; - } + lpuart_tx_dma_startup(sport); if (sport->lpuart_dma_rx_use) { /* RXWATER must be 0 */ @@ -2577,20 +2583,7 @@ 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) { -- cgit v1.2.3-59-g8ed1b From fd60e8e4a701043831370f6f635de2738fec07c8 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Mon, 5 Aug 2019 11:56:57 -0700 Subject: tty: serial: fsl_lpuart: Introduce lpuart_rx_dma_startup() Code doing initial DMA RX configuration in lpuart_startup() and lpuart32_startup() is exactly the same, so move it into a standalone subroutine. Signed-off-by: Andrey Smirnov Cc: Stefan Agner Cc: Chris Healy Cc: Cory Tusar Cc: Lucas Stach Cc: Greg Kroah-Hartman Cc: Jiri Slaby Cc: linux-imx@nxp.com Cc: linux-serial@vger.kernel.org Cc: linux-kernel@vger.kernel.org Link: https://lore.kernel.org/r/20190805185701.22863-3-andrew.smirnov@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/fsl_lpuart.c | 40 +++++++++++++++++----------------------- 1 file changed, 17 insertions(+), 23 deletions(-) diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index 9da4529ee223..4b59801a0a0c 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -1455,6 +1455,21 @@ static void lpuart_tx_dma_startup(struct lpuart_port *sport) } } +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); + 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; + } +} + static int lpuart_startup(struct uart_port *port) { struct lpuart_port *sport = container_of(port, struct lpuart_port, port); @@ -1479,18 +1494,7 @@ static int lpuart_startup(struct uart_port *port) 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; - } - + lpuart_rx_dma_startup(sport); lpuart_tx_dma_startup(sport); spin_unlock_irqrestore(&sport->port.lock, flags); @@ -1522,18 +1526,8 @@ static int lpuart32_startup(struct uart_port *port) temp |= UARTCTRL_RE | UARTCTRL_TE | UARTCTRL_ILIE; lpuart32_write(&sport->port, temp, UARTCTRL); - 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; - } + lpuart_rx_dma_startup(sport); lpuart_tx_dma_startup(sport); if (sport->lpuart_dma_rx_use) { -- cgit v1.2.3-59-g8ed1b From 4ff69041eccf4edb412bbe7296458d3e1be10dd2 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Mon, 5 Aug 2019 11:56:58 -0700 Subject: tty: serial: fsl_lpuart: Introduce lpuart32_configure() Code doing final steps of TX/RX configuration in lpuart32_startup() and lpuart_resume() is identical, so move it into a standalone subroutine. Signed-off-by: Andrey Smirnov Cc: Stefan Agner Cc: Chris Healy Cc: Cory Tusar Cc: Lucas Stach Cc: Greg Kroah-Hartman Cc: Jiri Slaby Cc: linux-imx@nxp.com Cc: linux-serial@vger.kernel.org Cc: linux-kernel@vger.kernel.org Link: https://lore.kernel.org/r/20190805185701.22863-4-andrew.smirnov@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/fsl_lpuart.c | 48 ++++++++++++++++++----------------------- 1 file changed, 21 insertions(+), 27 deletions(-) diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index 4b59801a0a0c..1286daa1be79 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -1502,6 +1502,24 @@ static int lpuart_startup(struct uart_port *port) return 0; } +static void lpuart32_configure(struct lpuart_port *sport) +{ + unsigned long temp; + + 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); +} + static int lpuart32_startup(struct uart_port *port) { struct lpuart_port *sport = container_of(port, struct lpuart_port, port); @@ -1530,18 +1548,7 @@ static int lpuart32_startup(struct uart_port *port) lpuart_rx_dma_startup(sport); lpuart_tx_dma_startup(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); + lpuart32_configure(sport); spin_unlock_irqrestore(&sport->port.lock, flags); return 0; @@ -2579,21 +2586,8 @@ static int lpuart_resume(struct device *dev) 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); -- cgit v1.2.3-59-g8ed1b From 352bd55e5dce57176122693c80b1805e9db89b1e Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Mon, 5 Aug 2019 11:56:59 -0700 Subject: tty: serial: fsl_lpuart: Introduce lpuart*_setup_watermark_enable() Most users of lpuart*_setup_watermark() enable identical set of flags right after the call, so combine those two action into a subroutine and make use of it. Signed-off-by: Andrey Smirnov Cc: Stefan Agner Cc: Chris Healy Cc: Cory Tusar Cc: Lucas Stach Cc: Greg Kroah-Hartman Cc: Jiri Slaby Cc: linux-imx@nxp.com Cc: linux-serial@vger.kernel.org Cc: linux-kernel@vger.kernel.org Link: https://lore.kernel.org/r/20190805185701.22863-5-andrew.smirnov@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/fsl_lpuart.c | 50 +++++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index 1286daa1be79..fb9961edce3a 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -1403,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_TIE | UARTCR2_RE | UARTCR2_TE; + writeb(cr2, sport->port.membase + UARTCR2); +} + static void lpuart32_setup_watermark(struct lpuart_port *sport) { unsigned long val, ctrl; @@ -1428,6 +1439,17 @@ static void lpuart32_setup_watermark(struct lpuart_port *sport) lpuart32_write(&sport->port, ctrl_saved, UARTCTRL); } +static void lpuart32_setup_watermark_enable(struct lpuart_port *sport) +{ + 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); @@ -1488,11 +1510,7 @@ static int lpuart_startup(struct uart_port *port) 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); + lpuart_setup_watermark_enable(sport); lpuart_rx_dma_startup(sport); lpuart_tx_dma_startup(sport); @@ -1538,11 +1556,7 @@ static int lpuart32_startup(struct uart_port *port) spin_lock_irqsave(&sport->port.lock, flags); - lpuart32_setup_watermark(sport); - - temp = lpuart32_read(&sport->port, UARTCTRL); - temp |= UARTCTRL_RE | UARTCTRL_TE | UARTCTRL_ILIE; - lpuart32_write(&sport->port, temp, UARTCTRL); + lpuart32_setup_watermark_enable(sport); lpuart_rx_dma_startup(sport); @@ -2558,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) { -- cgit v1.2.3-59-g8ed1b From f7ec1721b38c82089b1bbd92f828cb615557cbc5 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Mon, 5 Aug 2019 11:57:00 -0700 Subject: tty: serial: fsl_lpuart: Don't enable TIE in .startup() or .resume() Enabling TIE in .startup() callback causes the driver to start (or at least try) to transmit data before .start_tx() is called. Which, while harmless (since TIE handler will immediately disable it), is a no-op and shouldn't really happen. Drop UARTCR2_TIE from list of bits set in lpuart_startup(). This change will also not enable TIE in .resume(), but it seems that, similart to .startup(), transmit interrupt shouldn't be enabled there either. Signed-off-by: Andrey Smirnov Cc: Stefan Agner Cc: Chris Healy Cc: Cory Tusar Cc: Lucas Stach Cc: Greg Kroah-Hartman Cc: Jiri Slaby Cc: linux-imx@nxp.com Cc: linux-serial@vger.kernel.org Cc: linux-kernel@vger.kernel.org Link: https://lore.kernel.org/r/20190805185701.22863-6-andrew.smirnov@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/fsl_lpuart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index fb9961edce3a..5c3cc1051aa8 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -1410,7 +1410,7 @@ static void lpuart_setup_watermark_enable(struct lpuart_port *sport) lpuart_setup_watermark(sport); cr2 = readb(sport->port.membase + UARTCR2); - cr2 |= UARTCR2_RIE | UARTCR2_TIE | UARTCR2_RE | UARTCR2_TE; + cr2 |= UARTCR2_RIE | UARTCR2_RE | UARTCR2_TE; writeb(cr2, sport->port.membase + UARTCR2); } -- cgit v1.2.3-59-g8ed1b From 6798e901ab237ae51a3d192d571ec1e462e962b5 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Mon, 5 Aug 2019 11:57:01 -0700 Subject: tty: serial: fsl_lpuart: Ignore TX/RX interrupts if DMA is enabled In a mixed DMA/IRQ use-case (e.g.: DMA for TX, IRQ for RX), interrupt handler might try to handle Rx/Tx condition it shouldn't. Change the code to only handle TX/RX event if corresponding path isn't being handled by DMA. Signed-off-by: Andrey Smirnov Cc: Stefan Agner Cc: Chris Healy Cc: Cory Tusar Cc: Lucas Stach Cc: Greg Kroah-Hartman Cc: Jiri Slaby Cc: linux-imx@nxp.com Cc: linux-serial@vger.kernel.org Cc: linux-kernel@vger.kernel.org Link: https://lore.kernel.org/r/20190805185701.22863-7-andrew.smirnov@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/fsl_lpuart.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index 5c3cc1051aa8..3e17bb8a0b16 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -967,10 +967,10 @@ static irqreturn_t lpuart_int(int irq, void *dev_id) sts = readb(sport->port.membase + UARTSR1); - if (sts & UARTSR1_RDRF) + if (sts & UARTSR1_RDRF && !sport->lpuart_dma_rx_use) lpuart_rxint(sport); - if (sts & UARTSR1_TDRE) + if (sts & UARTSR1_TDRE && !sport->lpuart_dma_tx_use) lpuart_txint(sport); return IRQ_HANDLED; -- cgit v1.2.3-59-g8ed1b From 4b967e63fdfbc234dc53dc085236009c85d5451f Mon Sep 17 00:00:00 2001 From: Rahul Tanwar Date: Wed, 7 Aug 2019 17:21:31 +0800 Subject: serial: lantiq: Add SMP support The existing driver can only support single core SoC. But new multicore platforms which reuse the same driver/IP need SMP support. This patch adds multicore support in the driver. Signed-off-by: Rahul Tanwar Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/7912786cccad60c72b20ea724af1def505ab22aa.1565160764.git.rahul.tanwar@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/lantiq.c | 47 ++++++++++++++++++++++++++++++--------------- 1 file changed, 32 insertions(+), 15 deletions(-) diff --git a/drivers/tty/serial/lantiq.c b/drivers/tty/serial/lantiq.c index 9de9f0f239a1..42e27b48e9cc 100644 --- a/drivers/tty/serial/lantiq.c +++ b/drivers/tty/serial/lantiq.c @@ -99,7 +99,6 @@ 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_uart_port { struct uart_port port; @@ -110,6 +109,7 @@ struct ltq_uart_port { unsigned int tx_irq; unsigned int rx_irq; unsigned int err_irq; + spinlock_t lock; /* exclusive access for multi core */ }; static inline void asc_update_bits(u32 clear, u32 set, void __iomem *reg) @@ -135,9 +135,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 +247,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 +261,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 +276,12 @@ 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; } @@ -307,11 +315,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,6 +341,8 @@ lqasc_startup(struct uart_port *port) asc_update_bits(0, ASCCON_M_8ASYNC | ASCCON_FEN | ASCCON_TOEN | ASCCON_ROEN, port->membase + LTQ_ASC_CON); + spin_unlock_irqrestore(<q_port->lock, flags); + retval = request_irq(ltq_port->tx_irq, lqasc_tx_int, 0, "asc_tx", port); if (retval) { @@ -367,15 +379,19 @@ static void lqasc_shutdown(struct uart_port *port) { struct ltq_uart_port *ltq_port = to_ltq_uart_port(port); + unsigned long flags; + free_irq(ltq_port->tx_irq, port); free_irq(ltq_port->rx_irq, port); free_irq(ltq_port->err_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 +406,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 +460,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 +488,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 +606,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 +622,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 @@ -766,6 +782,7 @@ lqasc_probe(struct platform_device *pdev) 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); -- cgit v1.2.3-59-g8ed1b From a8571fda4740c6fb6a22f9b03699b53d699a6118 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 6 Aug 2019 12:43:14 +0300 Subject: serial: 8250_dw: Use a unified new dev variable in remove The commit 2cb78eab2376 ("serial: 8250_dw: Use a unified new dev variable in probe") introduced a local dev variable in ->probe(). Do the same in ->remove() in order to prepare for sequential patches. Signed-off-by: Andy Shevchenko Reviewed-by: Kefeng Wang Link: https://lore.kernel.org/r/20190806094322.64987-1-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_dw.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index 284e8d052fc3..7b559f969f61 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -667,8 +667,9 @@ 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); @@ -680,8 +681,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; } -- cgit v1.2.3-59-g8ed1b From 62907e90cc7e81a74f79650321005e9c102b1ce4 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 6 Aug 2019 12:43:15 +0300 Subject: serial: 8250_dw: use pointer to uart local variable The use of pointer will simplify enabling runtime PM for the driver. Signed-off-by: Andy Shevchenko Reviewed-by: Heikki Krogerus Link: https://lore.kernel.org/r/20190806094322.64987-2-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_dw.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index 7b559f969f61..010d0a292e73 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -492,10 +492,10 @@ static void dw8250_setup_port(struct uart_port *p) 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; @@ -634,10 +634,10 @@ static int dw8250_probe(struct platform_device *pdev) if (p->fifosize) { data->dma.rxconf.src_maxburst = p->fifosize / 4; data->dma.txconf.dst_maxburst = p->fifosize / 4; - uart.dma = &data->dma; + up->dma = &data->dma; } - data->line = serial8250_register_8250_port(&uart); + data->line = serial8250_register_8250_port(up); if (data->line < 0) { err = data->line; goto err_reset; -- cgit v1.2.3-59-g8ed1b From 136e0ab99b22378e3ff7d54f799a3a329316e869 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 6 Aug 2019 12:43:16 +0300 Subject: serial: 8250_dw: split Synopsys DesignWare 8250 common functions We would like to use same functions in the couple of drivers for Synopsys DesignWare 8250 UART. Split them from 8250_dw into new brand library module which users will select explicitly. Signed-off-by: Andy Shevchenko Reviewed-by: Heikki Krogerus Link: https://lore.kernel.org/r/20190806094322.64987-3-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_dwlib.c | 126 +++++++++++++++++++++++++++++++++++ drivers/tty/serial/8250/8250_dwlib.h | 19 ++++++ drivers/tty/serial/8250/Kconfig | 3 + drivers/tty/serial/8250/Makefile | 1 + 4 files changed, 149 insertions(+) create mode 100644 drivers/tty/serial/8250/8250_dwlib.c create mode 100644 drivers/tty/serial/8250/8250_dwlib.h diff --git a/drivers/tty/serial/8250/8250_dwlib.c b/drivers/tty/serial/8250/8250_dwlib.c new file mode 100644 index 000000000000..6d6a78eead3e --- /dev/null +++ b/drivers/tty/serial/8250/8250_dwlib.c @@ -0,0 +1,126 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* Synopsys DesignWare 8250 library. */ + +#include +#include +#include +#include +#include +#include + +#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); diff --git a/drivers/tty/serial/8250/8250_dwlib.h b/drivers/tty/serial/8250/8250_dwlib.h new file mode 100644 index 000000000000..87a4db2a8aba --- /dev/null +++ b/drivers/tty/serial/8250/8250_dwlib.h @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* Synopsys DesignWare 8250 library header file. */ + +#include + +#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); diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig index 509f6a3bb9ff..d628258fb4cf 100644 --- a/drivers/tty/serial/8250/Kconfig +++ b/drivers/tty/serial/8250/Kconfig @@ -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 diff --git a/drivers/tty/serial/8250/Makefile b/drivers/tty/serial/8250/Makefile index 18751bc63a84..9b451d81588b 100644 --- a/drivers/tty/serial/8250/Makefile +++ b/drivers/tty/serial/8250/Makefile @@ -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 -- cgit v1.2.3-59-g8ed1b From 4d5675c3b10b7bfa56447c26c29930e35b6d41ee Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 6 Aug 2019 12:43:17 +0300 Subject: serial: 8250_dw: switch to use 8250_dwlib library Since we have a common library module for Synopsys DesignWare UART, let us use it. Signed-off-by: Andy Shevchenko Reviewed-by: Heikki Krogerus Link: https://lore.kernel.org/r/20190806094322.64987-4-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_dw.c | 162 +++++++------------------------------- drivers/tty/serial/8250/Kconfig | 1 + 2 files changed, 28 insertions(+), 135 deletions(-) diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index 010d0a292e73..1c72fdc2dd37 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -27,66 +27,36 @@ #include -#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,59 +376,12 @@ 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 = {}, *up = &uart; @@ -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; - up->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(up); - 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; } @@ -671,7 +563,7 @@ static int dw8250_remove(struct platform_device *pdev) pm_runtime_get_sync(dev); - serial8250_unregister_port(data->line); + serial8250_unregister_port(data->data.line); reset_control_assert(data->rst); @@ -692,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; } @@ -701,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; } diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig index d628258fb4cf..90abf97b378d 100644 --- a/drivers/tty/serial/8250/Kconfig +++ b/drivers/tty/serial/8250/Kconfig @@ -357,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. -- cgit v1.2.3-59-g8ed1b From bf414f5520ef856f6026bcd39942704d7004f7f0 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 6 Aug 2019 12:43:18 +0300 Subject: serial: 8250_lpss: switch to use 8250_dwlib library Since we have a common library module for Synopsys DesignWare UART, let us use it. Signed-off-by: Andy Shevchenko Reviewed-by: Heikki Krogerus Link: https://lore.kernel.org/r/20190806094322.64987-5-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_lpss.c | 22 +++++++++++++--------- drivers/tty/serial/8250/Kconfig | 1 + 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/drivers/tty/serial/8250/8250_lpss.c b/drivers/tty/serial/8250/8250_lpss.c index d07e431110d9..3bf23770c188 100644 --- a/drivers/tty/serial/8250/8250_lpss.c +++ b/drivers/tty/serial/8250/8250_lpss.c @@ -14,7 +14,7 @@ #include #include -#include "8250.h" +#include "8250_dwlib.h" #define PCI_DEVICE_ID_INTEL_QRK_UARTx 0x0936 @@ -48,21 +48,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; @@ -163,7 +167,7 @@ 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); @@ -247,7 +251,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; @@ -296,7 +300,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; @@ -320,7 +324,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; @@ -335,7 +339,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); diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig index 90abf97b378d..ff5d142bbd70 100644 --- a/drivers/tty/serial/8250/Kconfig +++ b/drivers/tty/serial/8250/Kconfig @@ -444,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 -- cgit v1.2.3-59-g8ed1b From b4d0aac23e355933e963492bd71f98a36819137a Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 6 Aug 2019 12:43:19 +0300 Subject: serial: 8250_lpss: add fractional divisor support For Synopsys DesignWare 8250 uart which version >= 4.00a, there's a valid divisor latch fraction register. Now the preparation is done, it's easy to add the feature support. This patch firstly tries to get the fractional divisor width during probe, then setups specific get_divisor() and set_divisor() hook. Among other changes the FIFO size is now retrieved from the hardware. Signed-off-by: Andy Shevchenko Reviewed-by: Heikki Krogerus Link: https://lore.kernel.org/r/20190806094322.64987-6-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_lpss.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/tty/serial/8250/8250_lpss.c b/drivers/tty/serial/8250/8250_lpss.c index 3bf23770c188..0c6aa990db3d 100644 --- a/drivers/tty/serial/8250/8250_lpss.c +++ b/drivers/tty/serial/8250/8250_lpss.c @@ -113,7 +113,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); @@ -139,10 +138,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; @@ -316,6 +311,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; -- cgit v1.2.3-59-g8ed1b From f6bbb9f531c601135a75ba96e17196c1f745c4ed Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 6 Aug 2019 12:43:20 +0300 Subject: serial: 8250_lpss: register DMA IRQ and pool with instance ID It is really useful not only for debugging to have an DMA IRQ line and pool being mapped to the corresponding IP by using its instance ID. Provide PCI device and function as instance ID for Intel Quark UART DMA. Signed-off-by: Andy Shevchenko Reviewed-by: Heikki Krogerus Link: https://lore.kernel.org/r/20190806094322.64987-7-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_lpss.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/tty/serial/8250/8250_lpss.c b/drivers/tty/serial/8250/8250_lpss.c index 0c6aa990db3d..2bb30e688433 100644 --- a/drivers/tty/serial/8250/8250_lpss.c +++ b/drivers/tty/serial/8250/8250_lpss.c @@ -170,6 +170,7 @@ static void qrk_serial_setup_dma(struct lpss8250 *lpss, struct uart_port *port) 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); if (!chip->regs) -- cgit v1.2.3-59-g8ed1b From d53aa935b7d17ba198ebe96f31c7879a66428e08 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 6 Aug 2019 12:43:21 +0300 Subject: serial: 8250_lpss: Get rid of custom LPSS_DEVICE() macro Since PCI core provides a generic PCI_DEVICE_DATA() macro, replace LPSS_DEVICE() with former one. No functional change intended. Signed-off-by: Andy Shevchenko Reviewed-by: Heikki Krogerus Link: https://lore.kernel.org/r/20190806094322.64987-8-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_lpss.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/drivers/tty/serial/8250/8250_lpss.c b/drivers/tty/serial/8250/8250_lpss.c index 2bb30e688433..cfe0ab443250 100644 --- a/drivers/tty/serial/8250/8250_lpss.c +++ b/drivers/tty/serial/8250/8250_lpss.c @@ -356,17 +356,15 @@ 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, 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); -- cgit v1.2.3-59-g8ed1b From 4f912b898dc2bc76ed593ac78077085cc81a3523 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 6 Aug 2019 12:43:22 +0300 Subject: serial: 8250_lpss: Enable HS UART on Elkhart Lake Intel Elkhart Lake may use High Speed UART from OSE IP block. This is different to what we have in main LPSS, though compatible with older version of it, which is handled by this driver. Enable OSE HS UART on Intel Elkhart Lake by adding PCI IDs. Signed-off-by: Andy Shevchenko Reviewed-by: Heikki Krogerus Link: https://lore.kernel.org/r/20190806094322.64987-9-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_lpss.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/tty/serial/8250/8250_lpss.c b/drivers/tty/serial/8250/8250_lpss.c index cfe0ab443250..5f72ef3ea574 100644 --- a/drivers/tty/serial/8250/8250_lpss.c +++ b/drivers/tty/serial/8250/8250_lpss.c @@ -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 @@ -349,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, @@ -358,6 +370,12 @@ static const struct lpss8250_board qrk_board = { static const struct pci_device_id pci_ids[] = { { 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) }, -- cgit v1.2.3-59-g8ed1b From 0de2580fdb77014a7bf1c415c8333f39bf43725b Mon Sep 17 00:00:00 2001 From: Rahul Tanwar Date: Thu, 8 Aug 2019 18:02:06 +0800 Subject: serial: lantiq: Use proper DT compatible string Use explicit string instead of a macro for devicetree compatible string. This series of patches is to add support for multiple SoCs which reuse the same serial controller IP. The following patches will add another compatible string to support new Lightning Mountain(LGM) SoC. So it makes sense to have the compatible strings explicitly mentioned instead of a fixed macro. Suggested-by: Andy Shevchenko Signed-off-by: Rahul Tanwar Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/57e2b69e9fbd93328a477b4c7dd2dcc78784ecb1.1565257887.git.rahul.tanwar@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/lantiq.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/lantiq.c b/drivers/tty/serial/lantiq.c index 42e27b48e9cc..660d21db57dc 100644 --- a/drivers/tty/serial/lantiq.c +++ b/drivers/tty/serial/lantiq.c @@ -693,7 +693,7 @@ 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); static struct uart_driver lqasc_reg = { .owner = THIS_MODULE, @@ -792,7 +792,7 @@ lqasc_probe(struct platform_device *pdev) } static const struct of_device_id ltq_asc_match[] = { - { .compatible = DRVNAME }, + { .compatible = "lantiq,asc" }, {}, }; -- cgit v1.2.3-59-g8ed1b From 14208b3890ca369ed55332f6c05ff51cff5789cc Mon Sep 17 00:00:00 2001 From: Rahul Tanwar Date: Thu, 8 Aug 2019 18:02:07 +0800 Subject: serial: lantiq: Make IRQ & ISR assignment dynamic This driver/IP is reused across multiple SoCs. Older SoCs supported three separate IRQs for tx, rx & err interrupts. Newer Lightning Mountain SoC supports single IRQ for all of tx/rx/err interrupts. This patch modifies the driver design to support dynamic assignment of IRQ resources & ISRs based on devicetree node compatible entries. Suggested-by: Andy Shevchenko Signed-off-by: Rahul Tanwar Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/b166a0593bee191fcd77b5bdf8fedc6f6330a371.1565257887.git.rahul.tanwar@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/lantiq.c | 141 ++++++++++++++++++++++++++++++-------------- 1 file changed, 96 insertions(+), 45 deletions(-) diff --git a/drivers/tty/serial/lantiq.c b/drivers/tty/serial/lantiq.c index 660d21db57dc..b129531dfb9a 100644 --- a/drivers/tty/serial/lantiq.c +++ b/drivers/tty/serial/lantiq.c @@ -100,6 +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; +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; /* clock used to derive divider */ @@ -110,6 +116,8 @@ struct ltq_uart_port { unsigned int rx_irq; unsigned int err_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) @@ -343,35 +351,12 @@ lqasc_startup(struct uart_port *port) spin_unlock_irqrestore(<q_port->lock, flags); - 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"); + 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; } @@ -381,9 +366,7 @@ lqasc_shutdown(struct uart_port *port) struct ltq_uart_port *ltq_port = to_ltq_uart_port(port); unsigned long flags; - free_irq(ltq_port->tx_irq, port); - free_irq(ltq_port->rx_irq, port); - free_irq(ltq_port->err_irq, port); + ltq_port->soc->free_irq(port); spin_lock_irqsave(<q_port->lock, flags); __raw_writel(0, port->membase + LTQ_ASC_CON); @@ -705,24 +688,98 @@ 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 __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) { @@ -743,13 +800,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; @@ -758,7 +808,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)) @@ -778,10 +827,6 @@ 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); @@ -791,8 +836,14 @@ 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 of_device_id ltq_asc_match[] = { - { .compatible = "lantiq,asc" }, + { .compatible = "lantiq,asc", .data = &soc_data_lantiq }, {}, }; -- cgit v1.2.3-59-g8ed1b From b832776bbc908925cc0d749491cd5988aaf96206 Mon Sep 17 00:00:00 2001 From: Rahul Tanwar Date: Thu, 8 Aug 2019 18:02:08 +0800 Subject: serial: lantiq: Add support for Lightning Mountain SoC This patch adds IRQ & ISR support in the driver for Lightning Mountain SoC. Signed-off-by: Rahul Tanwar Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/0df20f6e4bbf9de09c85a5c92c92e642f62f441f.1565257887.git.rahul.tanwar@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/lantiq.c | 71 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/drivers/tty/serial/lantiq.c b/drivers/tty/serial/lantiq.c index b129531dfb9a..fcbea43dc334 100644 --- a/drivers/tty/serial/lantiq.c +++ b/drivers/tty/serial/lantiq.c @@ -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 @@ -115,6 +116,7 @@ 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; @@ -293,6 +295,31 @@ lqasc_rx_int(int irq, void *_port) 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; +} + static unsigned int lqasc_tx_empty(struct uart_port *port) { @@ -677,6 +704,7 @@ lqasc_serial_early_console_setup(struct earlycon_device *device, return 0; } 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, @@ -751,6 +779,42 @@ static void free_irq_lantiq(struct uart_port *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) { @@ -842,8 +906,15 @@ static const struct ltq_soc_data soc_data_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 = "lantiq,asc", .data = &soc_data_lantiq }, + { .compatible = "intel,lgm-asc", .data = &soc_data_intel }, {}, }; -- cgit v1.2.3-59-g8ed1b From bd0d9d159988d1ebb97ea244ae4dfa8365ed7d85 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 9 Aug 2019 22:27:30 +0200 Subject: serial: remove ks8695 driver The platform is getting removed, so there are no more users of this driver. Signed-off-by: Arnd Bergmann Link: https://lore.kernel.org/r/20190809202749.742267-3-arnd@arndb.de Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/Kconfig | 17 - drivers/tty/serial/Makefile | 1 - drivers/tty/serial/serial_ks8695.c | 698 ------------------------------------- include/uapi/linux/serial_core.h | 3 - 4 files changed, 719 deletions(-) delete mode 100644 drivers/tty/serial/serial_ks8695.c diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 3083dbae35f7..7041107ea78d 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -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 diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index 15a0fccadf7e..7f744136489e 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -59,7 +59,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 diff --git a/drivers/tty/serial/serial_ks8695.c b/drivers/tty/serial/serial_ks8695.c deleted file mode 100644 index b461d791188c..000000000000 --- a/drivers/tty/serial/serial_ks8695.c +++ /dev/null @@ -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 -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include - -#if defined(CONFIG_SERIAL_KS8695_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) -#define SUPPORT_SYSRQ -#endif - -#include - - -#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"); diff --git a/include/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h index 3cc3af1c2ee1..e8dc1787c3c6 100644 --- a/include/uapi/linux/serial_core.h +++ b/include/uapi/linux/serial_core.h @@ -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 -- cgit v1.2.3-59-g8ed1b From 8515dbc1f51b4f728908cdb993c849c83743aba7 Mon Sep 17 00:00:00 2001 From: Kai-Heng Feng Date: Sat, 10 Aug 2019 03:01:29 +0800 Subject: serial: 8250_pci: Add support for Sunix serial boards Add support to Sunix serial boards with up to 16 ports. Sunix board need its own setup callback instead of using Timedia's, to properly support more than 4 ports. Cc: Morris Ku Cc: Debbie Liu Signed-off-by: Kai-Heng Feng Link: https://lore.kernel.org/r/20190809190130.30773-1-kai.heng.feng@canonical.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_pci.c | 93 ++++++++++++++++++++++++++++++------- drivers/tty/serial/8250/8250_port.c | 8 ++++ include/uapi/linux/serial_core.h | 3 ++ 3 files changed, 87 insertions(+), 17 deletions(-) diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index d0be326a17e5..c0d10a35bf70 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -1803,6 +1803,30 @@ 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); +} + #define PCI_VENDOR_ID_SBSMODULARIO 0x124B #define PCI_SUBVENDOR_ID_SBSMODULARIO 0x124B #define PCI_DEVICE_ID_OCTPRO 0x0001 @@ -2490,21 +2514,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 @@ -2965,6 +2982,11 @@ enum pci_board_num_t { 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, }; /* @@ -3751,6 +3773,31 @@ 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, + }, }; static const struct pci_device_id blacklist[] = { @@ -4788,17 +4835,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 diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index 706645f89132..8407166610ce 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -301,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 */ diff --git a/include/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h index e8dc1787c3c6..bd0d9d176a66 100644 --- a/include/uapi/linux/serial_core.h +++ b/include/uapi/linux/serial_core.h @@ -287,4 +287,7 @@ /* SiFive UART */ #define PORT_SIFIVE_V0 120 +/* Sunix UART */ +#define PORT_SUNIX 121 + #endif /* _UAPILINUX_SERIAL_CORE_H */ -- cgit v1.2.3-59-g8ed1b From c6c94eecb19bdc881518c5762474da6ec9f68c81 Mon Sep 17 00:00:00 2001 From: Kai-Heng Feng Date: Sat, 10 Aug 2019 03:01:30 +0800 Subject: parport: parport_serial: Add support for Sunix Multi I/O boards Sunix Multi I/O boards are different to Timedia's. This patch adds proper support for Sunix MIO boards with 1 parallel and up to 4 serial ports. Cc: Morris Ku Cc: Debbie Liu Signed-off-by: Kai-Heng Feng Link: https://lore.kernel.org/r/20190809190130.30773-2-kai.heng.feng@canonical.com Signed-off-by: Greg Kroah-Hartman --- drivers/parport/parport_serial.c | 44 +++++++++++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 12 deletions(-) diff --git a/drivers/parport/parport_serial.c b/drivers/parport/parport_serial.c index 461fd8a24278..60d5d985113c 100644 --- a/drivers/parport/parport_serial.c +++ b/drivers/parport/parport_serial.c @@ -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, }, }; -- cgit v1.2.3-59-g8ed1b From 8d41ab87630bdd249996a7ec1f138a23bcd2f5ff Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 9 Aug 2019 18:40:42 +0100 Subject: tty/serial: atmel: remove redundant assignment to ret Variable ret is initialized to a value that is never read and it is re-assigned later. The initialization is redundant and can be removed. Addresses-Coverity: ("Unused value") Signed-off-by: Colin Ian King Link: https://lore.kernel.org/r/20190809174042.6276-1-colin.king@canonical.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/atmel_serial.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index 0b4f36905321..19a85d6fe3d2 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -2888,7 +2888,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)); -- cgit v1.2.3-59-g8ed1b From 09864c1cdf5c537bd01bff45181406e422ea988c Mon Sep 17 00:00:00 2001 From: Stefan-gabriel Mirea Date: Fri, 9 Aug 2019 11:29:16 +0000 Subject: tty: serial: Add linflexuart driver for S32V234 Introduce support for LINFlex driver, based on: - the version of Freescale LPUART driver after commit b3e3bf2ef2c7 ("Merge 4.0-rc7 into tty-next"); - commit abf1e0a98083 ("tty: serial: fsl_lpuart: lock port on console write"). In this basic version, the driver can be tested using initramfs and relies on the clocks and pin muxing set up by U-Boot. Remarks concerning the earlycon support: - LinFlexD does not allow character transmissions in the INIT mode (see section 47.4.2.1 in the reference manual[1]). Therefore, a mutual exclusion between the first linflex_setup_watermark/linflex_set_termios executions and linflex_earlycon_putchar was employed and the characters normally sent to earlycon during initialization are kept in a buffer and sent afterwards. - Empirically, character transmission is also forbidden within the last 1-2 ms before entering the INIT mode, so we use an explicit timeout (PREINIT_DELAY) between linflex_earlycon_putchar and the first call to linflex_setup_watermark. - U-Boot currently uses the UART FIFO mode, while this driver makes the transition to the buffer mode. Therefore, the earlycon putchar function matches the U-Boot behavior before initializations and the Linux behavior after. [1] https://www.nxp.com/webapp/Download?colCode=S32V234RM Signed-off-by: Stoica Cosmin-Stefan Signed-off-by: Adrian.Nitu Signed-off-by: Larisa Grigore Signed-off-by: Ana Nedelcu Signed-off-by: Mihaela Martinas Signed-off-by: Matthew Nunez [stefan-gabriel.mirea@nxp.com: Reduced for upstreaming and implemented earlycon support] Signed-off-by: Stefan-Gabriel Mirea Link: https://lore.kernel.org/r/20190809112853.15846-6-stefan-gabriel.mirea@nxp.com Signed-off-by: Greg Kroah-Hartman --- Documentation/admin-guide/kernel-parameters.txt | 6 + drivers/tty/serial/Kconfig | 15 + drivers/tty/serial/Makefile | 1 + drivers/tty/serial/fsl_linflexuart.c | 942 ++++++++++++++++++++++++ include/uapi/linux/serial_core.h | 3 + 5 files changed, 967 insertions(+) create mode 100644 drivers/tty/serial/fsl_linflexuart.c diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index 47d981a86e2f..9ee66d61986b 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -1090,6 +1090,12 @@ the framebuffer, pass the 'ram' option so that it is mapped with the correct attributes. + linflex, + 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 diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 7041107ea78d..db524e2c6db3 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -1416,6 +1416,21 @@ config SERIAL_FSL_LPUART_CONSOLE If you have enabled the lpuart serial port on the Freescale SoCs, you can make it the console by answering Y to this option. +config SERIAL_FSL_LINFLEXUART + tristate "Freescale linflexuart serial port support" + 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 diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index 7f744136489e..490d74533fbd 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -80,6 +80,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 diff --git a/drivers/tty/serial/fsl_linflexuart.c b/drivers/tty/serial/fsl_linflexuart.c new file mode 100644 index 000000000000..26b9601a0952 --- /dev/null +++ b/drivers/tty/serial/fsl_linflexuart.c @@ -0,0 +1,942 @@ +// 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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,s32-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 spinlock_t 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,s32-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, + .owner = THIS_MODULE, + .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); + +#ifdef CONFIG_SERIAL_FSL_LINFLEXUART_CONSOLE + spin_lock_init(&init_lock); +#endif + + 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"); diff --git a/include/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h index bd0d9d176a66..0f4f87a6fd54 100644 --- a/include/uapi/linux/serial_core.h +++ b/include/uapi/linux/serial_core.h @@ -290,4 +290,7 @@ /* Sunix UART */ #define PORT_SUNIX 121 +/* Freescale Linflex UART */ +#define PORT_LINFLEXUART 121 + #endif /* _UAPILINUX_SERIAL_CORE_H */ -- cgit v1.2.3-59-g8ed1b From d2ace81bf902a9f11d52e59e5d232d2255a0e353 Mon Sep 17 00:00:00 2001 From: Razvan Stefanescu Date: Tue, 13 Aug 2019 10:40:25 +0300 Subject: tty/serial: atmel: reschedule TX after RX was started When half-duplex RS485 communication is used, after RX is started, TX tasklet still needs to be scheduled tasklet. This avoids console freezing when more data is to be transmitted, if the serial communication is not closed. Fixes: 69646d7a3689 ("tty/serial: atmel: RS485 HD w/DMA: enable RX after TX is stopped") Signed-off-by: Razvan Stefanescu Cc: stable Link: https://lore.kernel.org/r/20190813074025.16218-1-razvan.stefanescu@microchip.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/atmel_serial.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index 19a85d6fe3d2..9a54c9e6d36e 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -1400,7 +1400,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); -- cgit v1.2.3-59-g8ed1b From 6300b140c29fdcd45e84fb551196a6e38dadddcc Mon Sep 17 00:00:00 2001 From: Andreas Abel Date: Mon, 12 Aug 2019 16:58:10 +0530 Subject: serial: tegra: add internal loopback functionality Add the internal loopback functionality that can be enabled with TIOCM_LOOP. Signed-off-by: Andreas Abel Signed-off-by: Krishna Yarlagadda Acked-by: Thierry Reding Link: https://lore.kernel.org/r/1565609303-27000-2-git-send-email-kyarlagadda@nvidia.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial-tegra.c | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/drivers/tty/serial/serial-tegra.c b/drivers/tty/serial/serial-tegra.c index 76ffbc7826ae..83cc8dca58d4 100644 --- a/drivers/tty/serial/serial-tegra.c +++ b/drivers/tty/serial/serial-tegra.c @@ -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 */ @@ -192,16 +192,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) -- cgit v1.2.3-59-g8ed1b From 18a4c404d2b4de47da519955ddda0b153c03f12d Mon Sep 17 00:00:00 2001 From: Ahung Cheng Date: Mon, 12 Aug 2019 16:58:13 +0530 Subject: serial: tegra: protect IER against LCR.DLAB The IER and DLH registers occupy the same address space, selected by the LCR.DLAB bit. Hence, add port lock to protect IER when LCR.DLAB bit is set. Signed-off-by: Ahung Cheng Signed-off-by: Krishna Yarlagadda Acked-by: Thierry Reding Link: https://lore.kernel.org/r/1565609303-27000-5-git-send-email-kyarlagadda@nvidia.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial-tegra.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/tty/serial/serial-tegra.c b/drivers/tty/serial/serial-tegra.c index 83cc8dca58d4..82a8d5130cbc 100644 --- a/drivers/tty/serial/serial-tegra.c +++ b/drivers/tty/serial/serial-tegra.c @@ -294,6 +294,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) @@ -313,6 +314,7 @@ static int tegra_set_baudrate(struct tegra_uart_port *tup, unsigned int baud) 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); @@ -325,6 +327,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; -- cgit v1.2.3-59-g8ed1b From a16c4c5a9cb6368a08c457b6b2dc0be25958dfc0 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 14 Aug 2019 11:29:24 +0200 Subject: serial: sh-sci: Don't check for mctrl_gpio_to_gpiod() returning error Since commit 1d267ea6539f2663 ("serial: mctrl-gpio: simplify init routine"), mctrl_gpio_init() returns failure if the assignment to any member of the gpio array results in an error pointer. Since commit c359522194593815 ("serial: mctrl_gpio: Avoid probe failures in case of missing gpiolib"), mctrl_gpio_to_gpiod() returns NULL in the !CONFIG_GPIOLIB case. Hence there is no longer a need to check for mctrl_gpio_to_gpiod() returning an error value. A simple NULL check is sufficient. This follows the spirit of commit 445df7ff3fd1a0a9 ("serial: mctrl-gpio: drop usages of IS_ERR_OR_NULL") in the mctrl-gpio core. Signed-off-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/20190814092924.13857-4-geert+renesas@glider.be Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 7f565fcbf1ca..4e754a4850e6 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -2099,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; @@ -3285,10 +3285,8 @@ static int sci_probe_single(struct platform_device *dev, 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; } -- cgit v1.2.3-59-g8ed1b From 6cbdf5c659ab6e8f081efde1ec60e0f8f8f7d8a4 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 14 Aug 2019 11:29:23 +0200 Subject: serial: mxs-auart: Don't check for mctrl_gpio_to_gpiod() returning error Since commit 1d267ea6539f2663 ("serial: mctrl-gpio: simplify init routine"), mctrl_gpio_init() returns failure if the assignment to any member of the gpio array results in an error pointer. Since commit c359522194593815 ("serial: mctrl_gpio: Avoid probe failures in case of missing gpiolib"), mctrl_gpio_to_gpiod() returns NULL in the !CONFIG_GPIOLIB case. Hence there is no longer a need to check for mctrl_gpio_to_gpiod() returning an error value. A simple NULL check is sufficient. This follows the spirit of commit 445df7ff3fd1a0a9 ("serial: mctrl-gpio: drop usages of IS_ERR_OR_NULL") in the mctrl-gpio core. Signed-off-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/20190814092924.13857-3-geert+renesas@glider.be Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/mxs-auart.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c index 4c188f4079b3..e34525970682 100644 --- a/drivers/tty/serial/mxs-auart.c +++ b/drivers/tty/serial/mxs-auart.c @@ -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) -- cgit v1.2.3-59-g8ed1b From 37ba760b6bb7402d8a8cce178063be78e3da474d Mon Sep 17 00:00:00 2001 From: Baolin Wang Date: Wed, 21 Aug 2019 20:39:09 +0800 Subject: serial: sprd: Add loopback function support Add loopback function support for Spreadtrum serial controller. Signed-off-by: Baolin Wang Link: https://lore.kernel.org/r/1275cd9968f1ceb5ac049cc23f1e508025cd552f.1566375260.git.baolin.wang@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sprd_serial.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/sprd_serial.c b/drivers/tty/serial/sprd_serial.c index 284709f61831..de077d7b5f00 100644 --- a/drivers/tty/serial/sprd_serial.c +++ b/drivers/tty/serial/sprd_serial.c @@ -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) -- cgit v1.2.3-59-g8ed1b From d193db7fb10d3ae8322e5f65e4c39946156e24c2 Mon Sep 17 00:00:00 2001 From: Kai-Heng Feng Date: Sat, 17 Aug 2019 00:51:24 +0800 Subject: serial: 8250_pci: Merge 8250_moxa to 8250_pci Moxa serial boards only need a special setup function, we can use generic 8250 framework for other parts. So let's merge 8250_moxa to 8250_pci. Signed-off-by: Kai-Heng Feng Link: https://lore.kernel.org/r/20190816165124.16942-1-kai.heng.feng@canonical.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_moxa.c | 155 ------------------------------------ drivers/tty/serial/8250/8250_pci.c | 113 ++++++++++++++++++++++---- drivers/tty/serial/8250/Kconfig | 10 --- drivers/tty/serial/8250/Makefile | 1 - 4 files changed, 99 insertions(+), 180 deletions(-) delete mode 100644 drivers/tty/serial/8250/8250_moxa.c diff --git a/drivers/tty/serial/8250/8250_moxa.c b/drivers/tty/serial/8250/8250_moxa.c deleted file mode 100644 index 1ee4cd94d4fa..000000000000 --- a/drivers/tty/serial/8250/8250_moxa.c +++ /dev/null @@ -1,155 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * 8250_moxa.c - MOXA Smartio/Industio MUE multiport serial driver. - * - * Author: Mathieu OTHACEHE - */ - -#include -#include - -#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"); diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index c0d10a35bf70..76d58bddeb06 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -1827,6 +1827,22 @@ pci_sunix_setup(struct serial_private *priv, 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 @@ -1931,6 +1947,18 @@ pci_sunix_setup(struct serial_private *priv, #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 @@ -2781,6 +2809,16 @@ 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, + }, /* * Default "match everything" terminator entry @@ -2987,6 +3025,9 @@ enum pci_board_num_t { pbn_sunix_pci_4s, pbn_sunix_pci_8s, pbn_sunix_pci_16s, + pbn_moxa8250_2p, + pbn_moxa8250_4p, + pbn_moxa8250_8p, }; /* @@ -3798,6 +3839,24 @@ static struct pciserial_board pci_boards[] = { .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[] = { @@ -3811,20 +3870,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), }, @@ -5407,6 +5452,46 @@ static const struct pci_device_id serial_pci_tbl[] = { 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 */ diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig index ff5d142bbd70..7ef60f8b6e2c 100644 --- a/drivers/tty/serial/8250/Kconfig +++ b/drivers/tty/serial/8250/Kconfig @@ -468,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 diff --git a/drivers/tty/serial/8250/Makefile b/drivers/tty/serial/8250/Makefile index 9b451d81588b..08c1d8117506 100644 --- a/drivers/tty/serial/8250/Makefile +++ b/drivers/tty/serial/8250/Makefile @@ -35,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 -- cgit v1.2.3-59-g8ed1b From a7b121b4b8b0bcc14fc1c2a81d34096109a65dd6 Mon Sep 17 00:00:00 2001 From: Martin Hundebøll Date: Mon, 12 Aug 2019 23:12:43 +0200 Subject: tty: n_gsm: add ioctl to map serial device to mux'ed tty MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Guessing the first tty for a gsm0710 multiplexed serial device is not currently possible, which makes it racy to use with multiple modems. Add a way to map the physical serial tty to its related mux devices using an ioctl. Signed-off-by: Martin Hundebøll Link: https://lore.kernel.org/r/20190812211243.98686-1-martin@geanix.com Signed-off-by: Greg Kroah-Hartman --- Documentation/driver-api/serial/n_gsm.rst | 11 +++++++++-- drivers/tty/n_gsm.c | 4 ++++ include/uapi/linux/gsmmux.h | 2 ++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/Documentation/driver-api/serial/n_gsm.rst b/Documentation/driver-api/serial/n_gsm.rst index 0ba731ab00b2..286e7ff4d2d9 100644 --- a/Documentation/driver-api/serial/n_gsm.rst +++ b/Documentation/driver-api/serial/n_gsm.rst @@ -18,10 +18,13 @@ 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 + #include #include #include #define DEFAULT_SPEED B115200 @@ -30,6 +33,7 @@ Major parts of the initialization program : 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,19 +62,22 @@ 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. use these devices as plain serial ports. +5. use these devices as plain serial ports. for example, it's possible: - and to use gnokii to send / receive SMS on ttygsm1 - to use ppp to establish a datalink on ttygsm2 -5. first close all virtual ports before closing the physical port. +6. first close all virtual ports before closing the physical port. Note that after closing the physical port the modem is still in multiplexing mode. This may prevent a successful re-opening of the port later. To avoid diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c index a60be591f1fc..d30525946892 100644 --- a/drivers/tty/n_gsm.c +++ b/drivers/tty/n_gsm.c @@ -2613,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: @@ -2624,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); } diff --git a/include/uapi/linux/gsmmux.h b/include/uapi/linux/gsmmux.h index 101d3c469acb..cb8693b39cb7 100644 --- a/include/uapi/linux/gsmmux.h +++ b/include/uapi/linux/gsmmux.h @@ -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 -- cgit v1.2.3-59-g8ed1b From c140e97f803f66c0d20a891aba848c1b77880830 Mon Sep 17 00:00:00 2001 From: Mao Wenan Date: Tue, 20 Aug 2019 20:40:15 +0800 Subject: tty: serial: add dependence for CONFIG_SERIAL_FSL_LINFLEXUART When CONFIG_SERIAL_FSL_LINFLEXUART=y and CONFIG_PRINTK is not set, one compilation error is found as below: drivers/tty/serial/fsl_linflexuart.c: In function linflex_earlycon_putchar: drivers/tty/serial/fsl_linflexuart.c:608:31: error: CONFIG_LOG_BUF_SHIFT undeclared (first use in this function); did you mean CONFIG_ISA_BUS_API? if (earlycon_buf.len >= 1 << CONFIG_LOG_BUF_SHIFT) ^~~~~~~~~~~~~~~~~~~~ CONFIG_ISA_BUS_API This because CONFIG_LOG_BUF_SHIFT is depended on CONFIG_PRINTK, fix this by adding dependence for CONFIG_SERIAL_FSL_LINFLEXUART. Fixes: b953815b819b ("tty: serial: Add linflexuart driver for S32V234") Signed-off-by: Mao Wenan Link: https://lore.kernel.org/r/20190820124015.28409-1-maowenan@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index db524e2c6db3..f1e9a43e46e7 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -1418,6 +1418,7 @@ config SERIAL_FSL_LPUART_CONSOLE 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. -- cgit v1.2.3-59-g8ed1b From 4ad8e34d1f47fe46a05bb9fb17a7b5704beed446 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 14 Aug 2019 17:07:59 +0300 Subject: serial: mctrl_gpio: Use gpiod flags directly Description of the modem line control GPIOs contain a boolean type to set direction of the line. Since GPIO library provides an enumerator type of flags, we may utilize it and allow a bit more flexibility on the choice of the type of the line parameters. It also removes an additional layer of value conversion. Signed-off-by: Andy Shevchenko Reviewed-by: Stefan Roese Reviewed-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/20190814140759.17486-1-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial_mctrl_gpio.c | 36 +++++++++++++++++----------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/drivers/tty/serial/serial_mctrl_gpio.c b/drivers/tty/serial/serial_mctrl_gpio.c index 2b400189be91..d9074303c88e 100644 --- a/drivers/tty/serial/serial_mctrl_gpio.c +++ b/drivers/tty/serial/serial_mctrl_gpio.c @@ -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]); -- cgit v1.2.3-59-g8ed1b From 68e26a8d224f022cf7ec6ebb319a88ad29eb46a4 Mon Sep 17 00:00:00 2001 From: "Ji-Ze Hong (Peter Hong)" Date: Fri, 16 Aug 2019 13:27:29 +0800 Subject: serial: 8250_pci: Add F81504A series Support Fintek F81504A/508A/512A is PCIE to 4/8/12 UARTs device. It's support IO/MMIO/PCIE conf to access all functions. The old F81504/508/512 is only support IO. Signed-off-by: Ji-Ze Hong (Peter Hong) Link: https://lore.kernel.org/r/1565933249-23076-1-git-send-email-hpeter+linux_kernel@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_pci.c | 121 +++++++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index 76d58bddeb06..83e21b65d57b 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -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 { @@ -1707,6 +1712,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) @@ -2819,6 +2895,30 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { .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 @@ -3014,6 +3114,9 @@ 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, @@ -3773,6 +3876,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, @@ -5719,6 +5837,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 }, -- cgit v1.2.3-59-g8ed1b From 8428413b1d14fc880b805c98f571169e7f1ac369 Mon Sep 17 00:00:00 2001 From: Ralf Ramsauer Date: Mon, 12 Aug 2019 13:21:52 +0200 Subject: serial: 8250_pci: Implement MSI(-X) support There may be setups, where legacy interrupts are not available. This is the caese, e.g., when Linux runs as guest (aka. non-root cell) of the partitioning hypervisor Jailhouse. There, only MSI(-X) interrupts are available for guests. But the 8250_pci driver currently only supports legacy ints. So let's enable MSI(-X) interrupts. Nevertheless, this needs to handled with care: while many 8250 devices actually claim to support MSI(-X) interrupts it should not be enabled be default. I had at least one device in my hands with broken MSI implementation. So better introduce a whitelist with devices that are known to support MSI(-X) interrupts. I tested all devices mentioned in the patch. Signed-off-by: Ralf Ramsauer Link: https://lore.kernel.org/r/20190812112152.693622-1-ralf.ramsauer@oth-regensburg.de Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_pci.c | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index 83e21b65d57b..6adbadd6a56a 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -58,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); @@ -4155,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++) { -- cgit v1.2.3-59-g8ed1b From 06e9b2fe7f1279bea649002cc40cce54a383f052 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Tue, 27 Aug 2019 11:46:14 +0000 Subject: tty: serial: linflexuart: Use DEFINE_SPINLOCK() for spinlock spinlock can be initialized automatically with DEFINE_SPINLOCK() rather than explicitly calling spin_lock_init(). Signed-off-by: Wei Yongjun Link: https://lore.kernel.org/r/20190827114614.102037-1-weiyongjun1@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/fsl_linflexuart.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/tty/serial/fsl_linflexuart.c b/drivers/tty/serial/fsl_linflexuart.c index 26b9601a0952..8aea7822b731 100644 --- a/drivers/tty/serial/fsl_linflexuart.c +++ b/drivers/tty/serial/fsl_linflexuart.c @@ -136,7 +136,7 @@ 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 spinlock_t init_lock; +static DEFINE_SPINLOCK(init_lock); static bool during_init; static struct { @@ -922,10 +922,6 @@ static int __init linflex_serial_init(void) if (ret) uart_unregister_driver(&linflex_reg); -#ifdef CONFIG_SERIAL_FSL_LINFLEXUART_CONSOLE - spin_lock_init(&init_lock); -#endif - return ret; } -- cgit v1.2.3-59-g8ed1b From 8016c3da0cc263f257e802fae36482eaad2d04fa Mon Sep 17 00:00:00 2001 From: Jan Kundrát Date: Wed, 28 Aug 2019 19:56:26 +0200 Subject: tty: max310x: fix off-by-one buffer access when storing overrun MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A recent change split the insertion loop into two parts. The first part accessed bytes 0, 1, ... (rxlen - 2), and the second part by mistake took offset `rxlen` instead of the correct `rxlen - 1`. So one byte was not stored, and the final access wrote past the end of the rx_buf. Fixes: 9c12d739d69b (tty: max310x: Split uart characters insertion loop) Signed-off-by: Jan Kundrát Reviewed-by: Serge Semin Link: https://lore.kernel.org/r/13ea227620aaad8a7231d42ed03a8508297d4eb3.1567027079.git.jan.kundrat@cesnet.cz Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/max310x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c index e6c48a99bd85..0e0c2740ec7e 100644 --- a/drivers/tty/serial/max310x.c +++ b/drivers/tty/serial/max310x.c @@ -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)) { -- cgit v1.2.3-59-g8ed1b From 2eda5345e4ef61b35101b4fef58417a41d8d53fd Mon Sep 17 00:00:00 2001 From: Sergey Organov Date: Wed, 28 Aug 2019 21:37:51 +0300 Subject: serial: imx: get rid of unbounded busy-waiting loop imx_set_termios(): remove busy-waiting "drain Tx FIFO" loop. Worse yet, it was potentially unbounded wait due to RTS/CTS (hardware) handshake. Let user space ensure draining is done before termios change, if draining is needed in the first place. Signed-off-by: Sergey Organov Link: https://lore.kernel.org/r/1567017475-11919-2-git-send-email-sorganov@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/imx.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index d9a73c7683ea..47b6156f5b2c 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -1644,7 +1644,7 @@ imx_uart_set_termios(struct uart_port *port, struct ktermios *termios, uart_update_timeout(port, termios->c_cflag, baud); /* - * disable interrupts and drain transmitter + * disable interrupts */ old_ucr1 = imx_uart_readl(sport, UCR1); imx_uart_writel(sport, @@ -1652,9 +1652,6 @@ imx_uart_set_termios(struct uart_port *port, struct ktermios *termios, 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); -- cgit v1.2.3-59-g8ed1b From 88c38044c12b09ad8b4b82b61133e66e879325ba Mon Sep 17 00:00:00 2001 From: Sergey Organov Date: Wed, 28 Aug 2019 21:37:52 +0300 Subject: serial: imx: do not stop Rx/Tx on termios change imx_set_termios(): stopping receiver and transmitter does harm when something that doesn't touch transmission format/rate changes, such as RTS/CTS handshake. OTOH, it does no good on baud rate or format change, as synchronization on upper-level protocols is still required to do it right. Therefore, just stop doing it. Signed-off-by: Sergey Organov Link: https://lore.kernel.org/r/1567017475-11919-3-git-send-email-sorganov@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/imx.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index 47b6156f5b2c..fa723a9a9c8c 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -1652,9 +1652,6 @@ imx_uart_set_termios(struct uart_port *port, struct ktermios *termios, UCR1); imx_uart_writel(sport, old_ucr2 & ~UCR2_ATEN, UCR2); - /* 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) -- cgit v1.2.3-59-g8ed1b From 85f30fbf32d1e8a55c5c563aaf8a35488c6d9745 Mon Sep 17 00:00:00 2001 From: Sergey Organov Date: Wed, 28 Aug 2019 21:37:53 +0300 Subject: serial: imx: do not disable individual irqs during termios change imx_set_termios(): disabling individual interrupt requests in UART for duration of the routine is pointless. Get rid of it. Signed-off-by: Sergey Organov Link: https://lore.kernel.org/r/1567017475-11919-4-git-send-email-sorganov@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/imx.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index fa723a9a9c8c..cc3783c0a7ed 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -1541,7 +1541,7 @@ 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; @@ -1643,15 +1643,6 @@ imx_uart_set_termios(struct uart_port *port, struct ktermios *termios, */ uart_update_timeout(port, termios->c_cflag, baud); - /* - * disable interrupts - */ - 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); - /* custom-baudrate handling */ div = sport->port.uartclk / (baud * 16); if (baud == 38400 && quot != div) @@ -1686,8 +1677,6 @@ imx_uart_set_termios(struct uart_port *port, struct ktermios *termios, 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)) -- cgit v1.2.3-59-g8ed1b From d47bcb4a6cf0be5f3b390fe3fc12416034f86ae2 Mon Sep 17 00:00:00 2001 From: Sergey Organov Date: Wed, 28 Aug 2019 21:37:54 +0300 Subject: serial: imx: fix data breakage on termios change imx_set_termios(): avoid writing baud rate divider registers when the values to be written are the same as current. Any writing seems 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. Signed-off-by: Sergey Organov Link: https://lore.kernel.org/r/1567017475-11919-5-git-send-email-sorganov@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/imx.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index cc3783c0a7ed..e89045a085fb 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -1545,7 +1545,7 @@ imx_uart_set_termios(struct uart_port *port, struct ktermios *termios, 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; /* @@ -1670,8 +1670,21 @@ 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, -- cgit v1.2.3-59-g8ed1b From c514a6f848b5bd991c908a395ba4e49a307a4ca7 Mon Sep 17 00:00:00 2001 From: Sergey Organov Date: Wed, 28 Aug 2019 21:37:55 +0300 Subject: serial: imx: use Tx ready rather than Tx empty irq This should help to avoid unnecessary gaps in transmission while adding little overhead due to low default Tx threshold level (2 bytes). Signed-off-by: Sergey Organov Link: https://lore.kernel.org/r/1567017475-11919-6-git-send-email-sorganov@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/imx.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index e89045a085fb..87c58f9f6390 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -439,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 && @@ -517,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); @@ -679,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) { @@ -688,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; } @@ -874,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; @@ -1474,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); @@ -1778,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; @@ -1938,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); @@ -2294,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) { -- cgit v1.2.3-59-g8ed1b From d2d8d4c049db46d7100b508764acad1e4f9547d3 Mon Sep 17 00:00:00 2001 From: Richard Genoud Date: Mon, 26 Aug 2019 09:17:52 +0200 Subject: tty/serial: atmel: remove unneeded atmel_get_lines_status function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit 18dfef9c7f87 ("serial: atmel: convert to irq handling provided mctrl-gpio"), the GPIOs interrupts are handled by mctrl_gpio_irq_handle(). So, atmel_get_lines_status() can be completely killed and replaced by : atmel_uart_readl(port, ATMEL_US_CSR); Suggested-by: Uwe Kleine-König Acked-by: Uwe Kleine-König Signed-off-by: Richard Genoud Link: https://lore.kernel.org/r/20190826071752.30396-1-richard.genoud@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/atmel_serial.c | 48 ++------------------------------------- 1 file changed, 2 insertions(+), 46 deletions(-) diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index 9a54c9e6d36e..a8dc8af83f39 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -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) @@ -1453,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) @@ -2002,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 -- cgit v1.2.3-59-g8ed1b From 99038fe75afaef59e20417d593a9d618d3ea14e6 Mon Sep 17 00:00:00 2001 From: Chunyan Zhang Date: Mon, 26 Aug 2019 15:29:27 +0800 Subject: serial: sprd: check the right port and membase When calling sprd_console_setup(), sprd_uart_port probably is NULL, we should check that first instead of checking its items directly. Also we should check membase to avoid accessing uart device before its initialization finished. Signed-off-by: Chunyan Zhang Signed-off-by: Chunyan Zhang Reviewed-by: Baolin Wang Tested-by: Baolin Wang Link: https://lore.kernel.org/r/20190826072929.7696-2-zhang.lyra@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sprd_serial.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/tty/serial/sprd_serial.c b/drivers/tty/serial/sprd_serial.c index de077d7b5f00..759ba2d0345e 100644 --- a/drivers/tty/serial/sprd_serial.c +++ b/drivers/tty/serial/sprd_serial.c @@ -983,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'; @@ -992,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; -- cgit v1.2.3-59-g8ed1b From e85c9d6786e5afc93c3a29582cf42ee4b00b1e08 Mon Sep 17 00:00:00 2001 From: Chunyan Zhang Date: Mon, 26 Aug 2019 15:29:28 +0800 Subject: serial: sprd: add console_initcall in sprd's uart driver Use console_initcall to save the console index we selected on the command line to sprd_console before probe finished. Thus we can make different processes to the uart devices during initialization according to whether it is used for console. Signed-off-by: Chunyan Zhang Signed-off-by: Chunyan Zhang Reviewed-by: Baolin Wang Tested-by: Baolin Wang Link: https://lore.kernel.org/r/20190826072929.7696-3-zhang.lyra@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sprd_serial.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/tty/serial/sprd_serial.c b/drivers/tty/serial/sprd_serial.c index 759ba2d0345e..6b9000f7adca 100644 --- a/drivers/tty/serial/sprd_serial.c +++ b/drivers/tty/serial/sprd_serial.c @@ -1016,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 */ -- cgit v1.2.3-59-g8ed1b From 418319026ca3e7b7b8964fed8dbe7ba464c1d95a Mon Sep 17 00:00:00 2001 From: Chunyan Zhang Date: Mon, 26 Aug 2019 15:29:29 +0800 Subject: serial: sprd: keep console alive even if missing the 'enable' clock The sprd serial console can work with only 26M fixed clock, but the probe() is returning fail if the clock "enable" is not configured in device tree. This patch will fix the problem to let the uart device which is used for console can be initialized even missing "enable" clock configured in devicetree. We should make sure the debug function as available as we can. Signed-off-by: Chunyan Zhang Signed-off-by: Chunyan Zhang Reviewed-by: Baolin Wang Tested-by: Baolin Wang Link: https://lore.kernel.org/r/20190826072929.7696-4-zhang.lyra@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sprd_serial.c | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/drivers/tty/serial/sprd_serial.c b/drivers/tty/serial/sprd_serial.c index 6b9000f7adca..f8db5c8e4e39 100644 --- a/drivers/tty/serial/sprd_serial.c +++ b/drivers/tty/serial/sprd_serial.c @@ -1111,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; @@ -1137,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; -- cgit v1.2.3-59-g8ed1b From 7030082a7415d18e3befdf1f9ec05b3d5de98de4 Mon Sep 17 00:00:00 2001 From: Martin Hundebøll Date: Thu, 22 Aug 2019 23:56:01 +0200 Subject: tty: n_gsm: avoid recursive locking with async port hangup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When tearing down the n_gsm ldisc while one or more of its child ports are open, a lock dep warning occurs: [ 56.254258] ====================================================== [ 56.260447] WARNING: possible circular locking dependency detected [ 56.266641] 5.2.0-00118-g1fd58e20e5b0 #30 Not tainted [ 56.271701] ------------------------------------------------------ [ 56.277890] cmux/271 is trying to acquire lock: [ 56.282436] 8215283a (&tty->legacy_mutex){+.+.}, at: __tty_hangup.part.0+0x58/0x27c [ 56.290128] [ 56.290128] but task is already holding lock: [ 56.295970] e9e2b842 (&gsm->mutex){+.+.}, at: gsm_cleanup_mux+0x9c/0x15c [ 56.302699] [ 56.302699] which lock already depends on the new lock. [ 56.302699] [ 56.310884] [ 56.310884] the existing dependency chain (in reverse order) is: [ 56.318372] [ 56.318372] -> #2 (&gsm->mutex){+.+.}: [ 56.323624] mutex_lock_nested+0x1c/0x24 [ 56.328079] gsm_cleanup_mux+0x9c/0x15c [ 56.332448] gsmld_ioctl+0x418/0x4e8 [ 56.336554] tty_ioctl+0x96c/0xcb0 [ 56.340492] do_vfs_ioctl+0x41c/0xa5c [ 56.344685] ksys_ioctl+0x34/0x60 [ 56.348535] ret_fast_syscall+0x0/0x28 [ 56.352815] 0xbe97cc04 [ 56.355791] [ 56.355791] -> #1 (&tty->ldisc_sem){++++}: [ 56.361388] tty_ldisc_lock+0x50/0x74 [ 56.365581] tty_init_dev+0x88/0x1c4 [ 56.369687] tty_open+0x1c8/0x430 [ 56.373536] chrdev_open+0xa8/0x19c [ 56.377560] do_dentry_open+0x118/0x3c4 [ 56.381928] path_openat+0x2fc/0x1190 [ 56.386123] do_filp_open+0x68/0xd4 [ 56.390146] do_sys_open+0x164/0x220 [ 56.394257] kernel_init_freeable+0x328/0x3e4 [ 56.399146] kernel_init+0x8/0x110 [ 56.403078] ret_from_fork+0x14/0x20 [ 56.407183] 0x0 [ 56.409548] [ 56.409548] -> #0 (&tty->legacy_mutex){+.+.}: [ 56.415402] __mutex_lock+0x64/0x90c [ 56.419508] mutex_lock_nested+0x1c/0x24 [ 56.423961] __tty_hangup.part.0+0x58/0x27c [ 56.428676] gsm_cleanup_mux+0xe8/0x15c [ 56.433043] gsmld_close+0x48/0x90 [ 56.436979] tty_ldisc_kill+0x2c/0x6c [ 56.441173] tty_ldisc_release+0x88/0x194 [ 56.445715] tty_release_struct+0x14/0x44 [ 56.450254] tty_release+0x36c/0x43c [ 56.454365] __fput+0x94/0x1e8 Avoid the warning by doing the port hangup asynchronously. Signed-off-by: Martin Hundebøll Link: https://lore.kernel.org/r/20190822215601.9028-1-martin@geanix.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/n_gsm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c index d30525946892..36a3eb4ad4c5 100644 --- a/drivers/tty/n_gsm.c +++ b/drivers/tty/n_gsm.c @@ -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); -- cgit v1.2.3-59-g8ed1b From 2bd3661ea0eb2056852cbc58c5d96bb4df2f164f Mon Sep 17 00:00:00 2001 From: Stefan-gabriel Mirea Date: Fri, 23 Aug 2019 19:11:35 +0000 Subject: serial: fsl_linflexuart: Update compatible string The "fsl,s32-linflexuart" compatible string is too generic. Make it SoC specific. Signed-off-by: Stefan-Gabriel Mirea Link: https://lore.kernel.org/r/20190823191115.18490-4-stefan-gabriel.mirea@nxp.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/fsl_linflexuart.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/fsl_linflexuart.c b/drivers/tty/serial/fsl_linflexuart.c index 8aea7822b731..9ed97614a941 100644 --- a/drivers/tty/serial/fsl_linflexuart.c +++ b/drivers/tty/serial/fsl_linflexuart.c @@ -127,7 +127,7 @@ static const struct of_device_id linflex_dt_ids[] = { { - .compatible = "fsl,s32-linflexuart", + .compatible = "fsl,s32v234-linflexuart", }, { /* sentinel */ } }; @@ -801,7 +801,7 @@ static int __init linflex_early_console_setup(struct earlycon_device *device, return 0; } -OF_EARLYCON_DECLARE(linflex, "fsl,s32-linflexuart", +OF_EARLYCON_DECLARE(linflex, "fsl,s32v234-linflexuart", linflex_early_console_setup); #define LINFLEX_CONSOLE (&linflex_console) -- cgit v1.2.3-59-g8ed1b From 0e16feab6cce2b91d2996d4bc4eff01ece577c4a Mon Sep 17 00:00:00 2001 From: Stoica Cosmin-Stefan Date: Fri, 23 Aug 2019 19:11:40 +0000 Subject: dt-bindings: serial: Document Freescale LINFlexD UART Add documentation for the serial communication interface module (LINFlexD), found in two instances on S32V234. Signed-off-by: Stoica Cosmin-Stefan Signed-off-by: Larisa Grigore Signed-off-by: Stefan-Gabriel Mirea Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20190823191115.18490-7-stefan-gabriel.mirea@nxp.com Signed-off-by: Greg Kroah-Hartman --- .../bindings/serial/fsl,s32-linflexuart.txt | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 Documentation/devicetree/bindings/serial/fsl,s32-linflexuart.txt diff --git a/Documentation/devicetree/bindings/serial/fsl,s32-linflexuart.txt b/Documentation/devicetree/bindings/serial/fsl,s32-linflexuart.txt new file mode 100644 index 000000000000..f1bbe0826be5 --- /dev/null +++ b/Documentation/devicetree/bindings/serial/fsl,s32-linflexuart.txt @@ -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 -- cgit v1.2.3-59-g8ed1b From 8966110c56459d3d0cf3bded77a92988063b0842 Mon Sep 17 00:00:00 2001 From: kbuild test robot Date: Sun, 25 Aug 2019 22:28:37 +0800 Subject: tty: serial: fix platform_no_drv_owner.cocci warnings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit drivers/tty/serial/fsl_linflexuart.c:907:3-8: No need to set .owner here. The core will do it. Remove .owner field if calls are used which set it automatically Generated by: scripts/coccinelle/api/platform_no_drv_owner.cocci Fixes: b953815b819b ("tty: serial: Add linflexuart driver for S32V234") CC: Stefan-gabriel Mirea Signed-off-by: kbuild test robot Reviewed-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20190825142837.zt3hpa22c7iofg3v@48261080c7f1 Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/fsl_linflexuart.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/tty/serial/fsl_linflexuart.c b/drivers/tty/serial/fsl_linflexuart.c index 9ed97614a941..68d74f2b5106 100644 --- a/drivers/tty/serial/fsl_linflexuart.c +++ b/drivers/tty/serial/fsl_linflexuart.c @@ -904,7 +904,6 @@ static struct platform_driver linflex_driver = { .remove = linflex_remove, .driver = { .name = DRIVER_NAME, - .owner = THIS_MODULE, .of_match_table = linflex_dt_ids, .pm = &linflex_pm_ops, }, -- cgit v1.2.3-59-g8ed1b From 5a8c296f5362fffc9da5da10eef01cf0b9b4ac7f Mon Sep 17 00:00:00 2001 From: Christoph Vogtländer Date: Wed, 4 Sep 2019 14:11:41 +0200 Subject: serial: max310x: Properly set flags in AutoCTS mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 391f93f2ec9f ("serial: core: Rework hw-assisted flow control support") has changed the way the AutoCTS mode is handled. According to that change, serial drivers which enable H/W AutoCTS mode must set UPSTAT_AUTORTS, UPSTAT_AUTOCTS and UPSTAT_AUTOXOFF to prevent the serial core from inadvertently disabling RX or TX. This patch adds proper handling of UPSTAT_AUTORTS, UPSTAT_AUTOCTS and UPSTAT_AUTOXOFF flags. Signed-off-by: Christoph Vogtländer Link: https://lore.kernel.org/r/20190904121141.4570-1-c.vogtlaender@sigma-surface-science.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/max310x.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c index 0e0c2740ec7e..aed19c688beb 100644 --- a/drivers/tty/serial/max310x.c +++ b/drivers/tty/serial/max310x.c @@ -955,15 +955,23 @@ 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) + + 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); /* Get baud rate generator configuration */ -- cgit v1.2.3-59-g8ed1b From 7d4f881ff1fb327e96fc36a58c4f0b6482ea453d Mon Sep 17 00:00:00 2001 From: Christoph Vogtländer Date: Wed, 4 Sep 2019 14:17:46 +0200 Subject: serial: max310x: turn off transmitter before activating AutoCTS or auto transmitter flow control MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As documented in the data-sheet, the transmitter must be disabled before activating AutoCTS or auto transmitter flow control. Accordingly, the transmitter must be enabled after AutoCTS or auto transmitter flow control gets deactivated. Signed-off-by: Christoph Vogtländer Link: https://lore.kernel.org/r/20190904121746.4641-1-c.vogtlaender@sigma-surface-science.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/max310x.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c index aed19c688beb..8434bd5a8ec7 100644 --- a/drivers/tty/serial/max310x.c +++ b/drivers/tty/serial/max310x.c @@ -956,6 +956,15 @@ static void max310x_set_termios(struct uart_port *port, max310x_port_write(port, MAX310X_XON1_REG, termios->c_cc[VSTART]); max310x_port_write(port, MAX310X_XOFF1_REG, termios->c_cc[VSTOP]); + /* 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) { @@ -974,6 +983,15 @@ static void max310x_set_termios(struct uart_port *port, } 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, -- cgit v1.2.3-59-g8ed1b From a162261703910dc0b2627157c98b7b5eba33ab34 Mon Sep 17 00:00:00 2001 From: Pragnesh Patel Date: Wed, 4 Sep 2019 15:49:11 +0530 Subject: dt-bindings: serial: Convert riscv,sifive-serial to json-schema Convert the riscv,sifive-serial binding to DT schema using json-schema. Signed-off-by: Pragnesh Patel Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/1567592383-8920-1-git-send-email-pragnesh.patel@sifive.com Signed-off-by: Greg Kroah-Hartman --- .../devicetree/bindings/serial/sifive-serial.txt | 33 ------------ .../devicetree/bindings/serial/sifive-serial.yaml | 62 ++++++++++++++++++++++ 2 files changed, 62 insertions(+), 33 deletions(-) delete mode 100644 Documentation/devicetree/bindings/serial/sifive-serial.txt create mode 100644 Documentation/devicetree/bindings/serial/sifive-serial.yaml diff --git a/Documentation/devicetree/bindings/serial/sifive-serial.txt b/Documentation/devicetree/bindings/serial/sifive-serial.txt deleted file mode 100644 index c86b1e524159..000000000000 --- a/Documentation/devicetree/bindings/serial/sifive-serial.txt +++ /dev/null @@ -1,33 +0,0 @@ -SiFive asynchronous serial interface (UART) - -Required properties: - -- compatible: should be something similar to - "sifive,-uart" for the UART as integrated - on a particular chip, and "sifive,uart" 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>; -}; diff --git a/Documentation/devicetree/bindings/serial/sifive-serial.yaml b/Documentation/devicetree/bindings/serial/sifive-serial.yaml new file mode 100644 index 000000000000..e8d3aeda1202 --- /dev/null +++ b/Documentation/devicetree/bindings/serial/sifive-serial.yaml @@ -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 + - Paul Walmsley + - Palmer Dabbelt + +allOf: + - $ref: /schemas/serial.yaml# + +properties: + compatible: + items: + - const: sifive,fu540-c000-uart + - const: sifive,uart0 + + description: + Should be something similar to "sifive,-uart" + for the UART as integrated on a particular chip, + and "sifive,uart" 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 + serial@10010000 { + compatible = "sifive,fu540-c000-uart", "sifive,uart0"; + interrupt-parent = <&plic0>; + interrupts = <80>; + reg = <0x0 0x10010000 0x0 0x1000>; + clocks = <&prci PRCI_CLK_TLCLK>; + }; + +... -- cgit v1.2.3-59-g8ed1b From 9c801e313195addaf11c16e155f50789d6ebfd19 Mon Sep 17 00:00:00 2001 From: Chunyan Zhang Date: Thu, 5 Sep 2019 15:41:51 +0800 Subject: serial: sprd: correct the wrong sequence of arguments The sequence of arguments which was passed to handle_lsr_errors() didn't match the parameters defined in that function, &lsr was passed to flag and &flag was passed to lsr, this patch fixed that. Fixes: b7396a38fb28 ("tty/serial: Add Spreadtrum sc9836-uart driver support") Signed-off-by: Chunyan Zhang Signed-off-by: Chunyan Zhang Cc: stable Link: https://lore.kernel.org/r/20190905074151.5268-1-zhang.lyra@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sprd_serial.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/sprd_serial.c b/drivers/tty/serial/sprd_serial.c index f8db5c8e4e39..771d11196523 100644 --- a/drivers/tty/serial/sprd_serial.c +++ b/drivers/tty/serial/sprd_serial.c @@ -617,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; -- cgit v1.2.3-59-g8ed1b From 33ae787b74fc0e1dfe6bb998c7e4d1c2369a8776 Mon Sep 17 00:00:00 2001 From: Shardar Shariff Md Date: Wed, 4 Sep 2019 10:12:56 +0530 Subject: serial: tegra: add support to ignore read Add support to ignore read characters if CREAD flag is not set. Signed-off-by: Shardar Shariff Md Signed-off-by: Krishna Yarlagadda Link: https://lore.kernel.org/r/1567572187-29820-2-git-send-email-kyarlagadda@nvidia.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial-tegra.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/tty/serial/serial-tegra.c b/drivers/tty/serial/serial-tegra.c index 82a8d5130cbc..9c15c87fa9e5 100644 --- a/drivers/tty/serial/serial-tegra.c +++ b/drivers/tty/serial/serial-tegra.c @@ -547,6 +547,9 @@ static void tegra_uart_handle_rx_pio(struct tegra_uart_port *tup, 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); } @@ -565,6 +568,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, @@ -1193,6 +1200,11 @@ 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; + spin_unlock_irqrestore(&u->lock, flags); } -- cgit v1.2.3-59-g8ed1b From 494f79bd2365703e4093efa0ecf4b139d83aba97 Mon Sep 17 00:00:00 2001 From: Ahung Cheng Date: Wed, 4 Sep 2019 10:12:57 +0530 Subject: serial: tegra: avoid reg access when clk disabled This avoids two race conditions from the UART shutdown sequence both leading to 'Machine check error in AXI2APB' and kernel oops. One was that the clock was disabled before the DMA was terminated making it possible for the DMA callbacks to be called after the clock was disabled. These callbacks could write to the UART registers causing timeout. The second was that the clock was disabled before the UART was completely flagged as closed. This is done after the shutdown is called and a new write could be started after the clock was disabled. tegra_uart_start_pio_tx could be called causing timeout. Given that the baud rate is reset at the end of shutdown sequence, this fix is to examine the baud rate to avoid register access from both race conditions. Besides, terminate the DMA before disabling the clock. Signed-off-by: Ahung Cheng Signed-off-by: Shardar Mohammed Signed-off-by: Krishna Yarlagadda Link: https://lore.kernel.org/r/1567572187-29820-3-git-send-email-kyarlagadda@nvidia.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial-tegra.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/drivers/tty/serial/serial-tegra.c b/drivers/tty/serial/serial-tegra.c index 9c15c87fa9e5..29bf7b774f42 100644 --- a/drivers/tty/serial/serial-tegra.c +++ b/drivers/tty/serial/serial-tegra.c @@ -126,6 +126,8 @@ struct tegra_uart_port { 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) @@ -461,6 +463,9 @@ 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) @@ -832,6 +837,12 @@ 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; + + tegra_uart_dma_channel_free(tup, true); + tegra_uart_dma_channel_free(tup, false); + clk_disable_unprepare(tup->uart_clk); } @@ -1069,12 +1080,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); } -- cgit v1.2.3-59-g8ed1b From b9c2470fb150a3cc82a3ee8072da88cb2a73e213 Mon Sep 17 00:00:00 2001 From: Shardar Shariff Md Date: Wed, 4 Sep 2019 10:12:58 +0530 Subject: serial: tegra: flush the RX fifo on frame error FIFO reset/flush code implemented now does not follow programming guidelines. RTS line has to be turned off while flushing FIFOs to avoid new transfers. Also check LSR bits UART_LSR_TEMT and UART_LSR_DR to confirm FIFOs are flushed. Signed-off-by: Shardar Shariff Md Signed-off-by: Krishna Yarlagadda Link: https://lore.kernel.org/r/1567572187-29820-4-git-send-email-kyarlagadda@nvidia.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial-tegra.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/tty/serial/serial-tegra.c b/drivers/tty/serial/serial-tegra.c index 29bf7b774f42..4cd6d5f84171 100644 --- a/drivers/tty/serial/serial-tegra.c +++ b/drivers/tty/serial/serial-tegra.c @@ -266,6 +266,10 @@ static void tegra_uart_wait_sym_time(struct tegra_uart_port *tup, 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); @@ -289,6 +293,16 @@ 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 int tegra_set_baudrate(struct tegra_uart_port *tup, unsigned int baud) -- cgit v1.2.3-59-g8ed1b From cb79f504bb261460bd54b8a5577100f21982b20a Mon Sep 17 00:00:00 2001 From: Krishna Yarlagadda Date: Wed, 4 Sep 2019 10:12:59 +0530 Subject: serial: tegra: report error to upper tty layer Report overrun/parity/frame/break errors to top tty layer. Add support to ignore break character if IGNBRK is set. Signed-off-by: Shardar Shariff Md Signed-off-by: Krishna Yarlagadda Link: https://lore.kernel.org/r/1567572187-29820-5-git-send-email-kyarlagadda@nvidia.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial-tegra.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/drivers/tty/serial/serial-tegra.c b/drivers/tty/serial/serial-tegra.c index 4cd6d5f84171..c3f99131f86d 100644 --- a/drivers/tty/serial/serial-tegra.c +++ b/drivers/tty/serial/serial-tegra.c @@ -373,13 +373,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; } @@ -561,6 +569,9 @@ 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++; @@ -1223,6 +1234,8 @@ static void tegra_uart_set_termios(struct uart_port *u, /* 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); } -- cgit v1.2.3-59-g8ed1b From c9fd37f926fc57c2788504da429521227ab5a024 Mon Sep 17 00:00:00 2001 From: Krishna Yarlagadda Date: Wed, 4 Sep 2019 10:13:00 +0530 Subject: dt-binding: serial: tegra: add new chips Add new compatible string for Tegra186. It differs from earlier chips as it has FIFO mode enable check and 8 byte DMA buffer. Add new compatible string for Tegra194. Tegra194 has different error tolerance levels for baud rate compared to older chips. Signed-off-by: Krishna Yarlagadda Link: https://lore.kernel.org/r/1567572187-29820-6-git-send-email-kyarlagadda@nvidia.com Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/serial/nvidia,tegra20-hsuart.txt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/serial/nvidia,tegra20-hsuart.txt b/Documentation/devicetree/bindings/serial/nvidia,tegra20-hsuart.txt index d7edf732eb7f..dab31d44c4cd 100644 --- a/Documentation/devicetree/bindings/serial/nvidia,tegra20-hsuart.txt +++ b/Documentation/devicetree/bindings/serial/nvidia,tegra20-hsuart.txt @@ -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. -- cgit v1.2.3-59-g8ed1b From 222dcdff3405a31803aecd3bf66f62d46b8bda98 Mon Sep 17 00:00:00 2001 From: Krishna Yarlagadda Date: Wed, 4 Sep 2019 10:13:01 +0530 Subject: serial: tegra: check for FIFO mode enabled status Chips prior to Tegra186 needed delay of 3 UART clock cycles to avoid data loss. This issue is fixed in Tegra186 and a new flag is added to check if FIFO mode is enabled. chip data updated to check if this flag is available for a chip. Tegra186 has new compatible to enable this flag. Signed-off-by: Shardar Shariff Md Signed-off-by: Krishna Yarlagadda Link: https://lore.kernel.org/r/1567572187-29820-7-git-send-email-kyarlagadda@nvidia.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial-tegra.c | 52 ++++++++++++++++++++++++++++++++++----- 1 file changed, 46 insertions(+), 6 deletions(-) diff --git a/drivers/tty/serial/serial-tegra.c b/drivers/tty/serial/serial-tegra.c index c3f99131f86d..69af621ff7c0 100644 --- a/drivers/tty/serial/serial-tegra.c +++ b/drivers/tty/serial/serial-tegra.c @@ -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,7 @@ 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; }; struct tegra_uart_port { @@ -263,6 +266,21 @@ 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; @@ -282,6 +300,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 */ @@ -917,12 +937,20 @@ static int tegra_uart_hw_init(struct tegra_uart_port *tup) /* 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 @@ -1293,12 +1321,21 @@ 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, }; 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, +}; + +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, }; static const struct of_device_id tegra_uart_of_match[] = { @@ -1308,6 +1345,9 @@ 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, }, { }, }; -- cgit v1.2.3-59-g8ed1b From 53d0a062cb771d62cd205d9e2845fe26c9989142 Mon Sep 17 00:00:00 2001 From: Krishna Yarlagadda Date: Wed, 4 Sep 2019 10:13:02 +0530 Subject: serial: tegra: set maximum num of uart ports to 8 Set maximum number of UART ports to 8 as older chips have 5 ports and Tergra186 and later chips will have 8 ports. Add this info to chip data. Read device tree compatible of this driver and register uart driver with max ports of matching chip data. Signed-off-by: Shardar Shariff Md Signed-off-by: Krishna Yarlagadda Link: https://lore.kernel.org/r/1567572187-29820-8-git-send-email-kyarlagadda@nvidia.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial-tegra.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/serial-tegra.c b/drivers/tty/serial/serial-tegra.c index 69af621ff7c0..8422516a2afd 100644 --- a/drivers/tty/serial/serial-tegra.c +++ b/drivers/tty/serial/serial-tegra.c @@ -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 @@ -87,6 +87,7 @@ struct tegra_uart_chip_data { bool allow_txfifo_reset_fifo_mode; bool support_clk_src_div; bool fifo_mode_enable_status; + int uart_max_port; }; struct tegra_uart_port { @@ -1322,6 +1323,7 @@ static struct tegra_uart_chip_data tegra20_uart_chip_data = { .allow_txfifo_reset_fifo_mode = true, .support_clk_src_div = false, .fifo_mode_enable_status = false, + .uart_max_port = 5, }; static struct tegra_uart_chip_data tegra30_uart_chip_data = { @@ -1329,6 +1331,7 @@ static struct tegra_uart_chip_data tegra30_uart_chip_data = { .allow_txfifo_reset_fifo_mode = false, .support_clk_src_div = true, .fifo_mode_enable_status = false, + .uart_max_port = 5, }; static struct tegra_uart_chip_data tegra186_uart_chip_data = { @@ -1336,6 +1339,7 @@ static struct tegra_uart_chip_data tegra186_uart_chip_data = { .allow_txfifo_reset_fifo_mode = false, .support_clk_src_div = true, .fifo_mode_enable_status = true, + .uart_max_port = 8, }; static const struct of_device_id tegra_uart_of_match[] = { @@ -1468,11 +1472,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; } -- cgit v1.2.3-59-g8ed1b From 7799a3aa81279637031abad19e56e4bbf1481d4e Mon Sep 17 00:00:00 2001 From: Krishna Yarlagadda Date: Wed, 4 Sep 2019 10:13:03 +0530 Subject: serial: tegra: add support to use 8 bytes trigger Add support to use 8 bytes trigger for Tegra186 SOC. Signed-off-by: Shardar Shariff Md Signed-off-by: Krishna Yarlagadda Link: https://lore.kernel.org/r/1567572187-29820-9-git-send-email-kyarlagadda@nvidia.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial-tegra.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/serial-tegra.c b/drivers/tty/serial/serial-tegra.c index 8422516a2afd..02f85353d720 100644 --- a/drivers/tty/serial/serial-tegra.c +++ b/drivers/tty/serial/serial-tegra.c @@ -88,6 +88,7 @@ struct tegra_uart_chip_data { bool support_clk_src_div; bool fifo_mode_enable_status; int uart_max_port; + int max_dma_burst_bytes; }; struct tegra_uart_port { @@ -931,7 +932,12 @@ 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->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); @@ -1045,7 +1051,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; @@ -1324,6 +1330,7 @@ static struct tegra_uart_chip_data tegra20_uart_chip_data = { .support_clk_src_div = false, .fifo_mode_enable_status = false, .uart_max_port = 5, + .max_dma_burst_bytes = 4, }; static struct tegra_uart_chip_data tegra30_uart_chip_data = { @@ -1332,6 +1339,7 @@ static struct tegra_uart_chip_data tegra30_uart_chip_data = { .support_clk_src_div = true, .fifo_mode_enable_status = false, .uart_max_port = 5, + .max_dma_burst_bytes = 4, }; static struct tegra_uart_chip_data tegra186_uart_chip_data = { @@ -1340,6 +1348,7 @@ static struct tegra_uart_chip_data tegra186_uart_chip_data = { .support_clk_src_div = true, .fifo_mode_enable_status = true, .uart_max_port = 8, + .max_dma_burst_bytes = 8, }; static const struct of_device_id tegra_uart_of_match[] = { -- cgit v1.2.3-59-g8ed1b From d90fd87bab5ee8fd6a7fea409c2cc2eb667d7b59 Mon Sep 17 00:00:00 2001 From: Krishna Yarlagadda Date: Wed, 4 Sep 2019 10:13:04 +0530 Subject: serial: tegra: DT for Adjusted baud rates Tegra186 chip has a hardware issue resulting in frame errors when tolerance level for baud rate is negative. Provided entries to adjust baud rate to be within acceptable range and work with devices that can send negative baud rate. Also report error when baud rate set is out of tolerance range of controller updated in device tree. Signed-off-by: Krishna Yarlagadda Link: https://lore.kernel.org/r/1567572187-29820-10-git-send-email-kyarlagadda@nvidia.com Signed-off-by: Greg Kroah-Hartman --- .../bindings/serial/nvidia,tegra20-hsuart.txt | 32 ++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/Documentation/devicetree/bindings/serial/nvidia,tegra20-hsuart.txt b/Documentation/devicetree/bindings/serial/nvidia,tegra20-hsuart.txt index dab31d44c4cd..f709304036c2 100644 --- a/Documentation/devicetree/bindings/serial/nvidia,tegra20-hsuart.txt +++ b/Documentation/devicetree/bindings/serial/nvidia,tegra20-hsuart.txt @@ -24,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. + + 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: @@ -38,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 */ }; -- cgit v1.2.3-59-g8ed1b From f04a3cc8d4550463e0c15be59d91177a5def1ca5 Mon Sep 17 00:00:00 2001 From: Krishna Yarlagadda Date: Wed, 4 Sep 2019 10:13:05 +0530 Subject: serial: tegra: add support to adjust baud rate Add support to adjust baud rates to fall under supported tolerance range through DT. Tegra186 chip has a hardware issue resulting in frame errors when tolerance level for baud rate is negative. Provided entries to adjust baud rate to be within acceptable range and work with devices that can send negative baud rate. Also report error when baud rate set is out of tolerance range of controller updated in device tree. Signed-off-by: Shardar Shariff Md Signed-off-by: Krishna Yarlagadda Link: https://lore.kernel.org/r/1567572187-29820-11-git-send-email-kyarlagadda@nvidia.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial-tegra.c | 68 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/drivers/tty/serial/serial-tegra.c b/drivers/tty/serial/serial-tegra.c index 02f85353d720..f970ed7b182d 100644 --- a/drivers/tty/serial/serial-tegra.c +++ b/drivers/tty/serial/serial-tegra.c @@ -91,6 +91,12 @@ struct tegra_uart_chip_data { int max_dma_burst_bytes; }; +struct tegra_baud_tolerance { + u32 lower_range_baud; + u32 upper_range_baud; + s32 tolerance; +}; + struct tegra_uart_port { struct uart_port uport; const struct tegra_uart_chip_data *cdata; @@ -127,6 +133,8 @@ 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; }; static void tegra_uart_start_next_tx(struct tegra_uart_port *tup); @@ -327,6 +335,21 @@ static void tegra_uart_fifo_reset(struct tegra_uart_port *tup, u8 fcr_bits) 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_set_baudrate(struct tegra_uart_port *tup, unsigned int baud) { unsigned long rate; @@ -340,6 +363,9 @@ static int tegra_set_baudrate(struct tegra_uart_port *tup, unsigned int baud) if (tup->cdata->support_clk_src_div) { rate = baud * 16; + 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, @@ -1311,6 +1337,12 @@ 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) { @@ -1321,6 +1353,42 @@ static int tegra_uart_parse_dt(struct platform_device *pdev, tup->enable_modem_interrupt = of_property_read_bool(np, "nvidia,enable-modem-interrupt"); + 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; } -- cgit v1.2.3-59-g8ed1b From d781ec21bae6ff8f9e07682e8947a654484611f5 Mon Sep 17 00:00:00 2001 From: Krishna Yarlagadda Date: Wed, 4 Sep 2019 10:13:06 +0530 Subject: serial: tegra: report clk rate errors Standard UART controllers support +/-4% baud rate error tolerance. Tegra186 only supports 0% to +4% error tolerance whereas other Tegra chips support standard +/-4% rate. Add chip data for knowing error tolerance level for each soc. Creating new compatible for Tegra194 chip as it supports baud rate error tolerance of -2 to +2%, different from older chips. Signed-off-by: Shardar Shariff Md Signed-off-by: Krishna Yarlagadda Link: https://lore.kernel.org/r/1567572187-29820-12-git-send-email-kyarlagadda@nvidia.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial-tegra.c | 59 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 57 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/serial-tegra.c b/drivers/tty/serial/serial-tegra.c index f970ed7b182d..d0fd41719e70 100644 --- a/drivers/tty/serial/serial-tegra.c +++ b/drivers/tty/serial/serial-tegra.c @@ -89,6 +89,8 @@ struct tegra_uart_chip_data { 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 { @@ -135,6 +137,8 @@ struct tegra_uart_port { unsigned int rx_bytes_requested; struct tegra_baud_tolerance *baud_tolerance; int n_adjustable_baud_rates; + int required_rate; + int configured_rate; }; static void tegra_uart_start_next_tx(struct tegra_uart_port *tup); @@ -350,6 +354,22 @@ static long tegra_get_tolerance_rate(struct tegra_uart_port *tup, 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) { unsigned long rate; @@ -363,6 +383,8 @@ 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); @@ -372,7 +394,11 @@ static int tegra_set_baudrate(struct tegra_uart_port *tup, unsigned int baud) "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); @@ -991,7 +1017,11 @@ static int tegra_uart_hw_init(struct tegra_uart_port *tup) * enqueued */ tup->lcr_shadow = TEGRA_UART_DEFAULT_LSR; - tegra_set_baudrate(tup, TEGRA_UART_DEFAULT_BAUD); + ret = tegra_set_baudrate(tup, TEGRA_UART_DEFAULT_BAUD); + if (ret < 0) { + dev_err(tup->uport.dev, "Failed to set baud rate\n"); + return ret; + } tup->fcr_shadow |= UART_FCR_DMA_SELECT; tegra_uart_write(tup, tup->fcr_shadow, UART_FCR); @@ -1190,6 +1220,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); @@ -1262,7 +1293,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); @@ -1399,6 +1434,8 @@ static struct tegra_uart_chip_data tegra20_uart_chip_data = { .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 = { @@ -1408,6 +1445,8 @@ static struct tegra_uart_chip_data tegra30_uart_chip_data = { .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 = { @@ -1417,6 +1456,19 @@ static struct tegra_uart_chip_data tegra186_uart_chip_data = { .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[] = { @@ -1429,6 +1481,9 @@ static const struct of_device_id tegra_uart_of_match[] = { }, { .compatible = "nvidia,tegra186-hsuart", .data = &tegra186_uart_chip_data, + }, { + .compatible = "nvidia,tegra194-hsuart", + .data = &tegra194_uart_chip_data, }, { }, }; -- cgit v1.2.3-59-g8ed1b From 1dce2df3ee06e4f10fd9b8919a0f2e90e0ac3188 Mon Sep 17 00:00:00 2001 From: Krishna Yarlagadda Date: Wed, 4 Sep 2019 10:13:07 +0530 Subject: serial: tegra: Add PIO mode support Add PIO mode support in receive and transmit path with RX interrupt trigger of 16 bytes for Tegra194 and older chips. Signed-off-by: Shardar Shariff Md Signed-off-by: Krishna Yarlagadda Link: https://lore.kernel.org/r/1567572187-29820-13-git-send-email-kyarlagadda@nvidia.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial-tegra.c | 117 ++++++++++++++++++++++++++++---------- 1 file changed, 86 insertions(+), 31 deletions(-) diff --git a/drivers/tty/serial/serial-tegra.c b/drivers/tty/serial/serial-tegra.c index d0fd41719e70..2f599515c133 100644 --- a/drivers/tty/serial/serial-tegra.c +++ b/drivers/tty/serial/serial-tegra.c @@ -139,6 +139,8 @@ struct tegra_uart_port { 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); @@ -567,7 +569,7 @@ static void tegra_uart_start_next_tx(struct tegra_uart_port *tup) 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)); @@ -800,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; @@ -813,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; @@ -841,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; @@ -851,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; @@ -869,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; @@ -886,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) @@ -939,8 +960,10 @@ static void tegra_uart_hw_deinit(struct tegra_uart_port *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); + 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); } @@ -985,10 +1008,14 @@ static int tegra_uart_hw_init(struct tegra_uart_port *tup) */ tup->fcr_shadow = UART_FCR_ENABLE_FIFO; - 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; + 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); @@ -1016,19 +1043,23 @@ static int tegra_uart_hw_init(struct tegra_uart_port *tup) * (115200, N, 8, 1) so that the receive DMA buffer may be * enqueued */ - tup->lcr_shadow = TEGRA_UART_DEFAULT_LSR; ret = tegra_set_baudrate(tup, TEGRA_UART_DEFAULT_BAUD); if (ret < 0) { dev_err(tup->uport.dev, "Failed to set baud rate\n"); return ret; } - tup->fcr_shadow |= UART_FCR_DMA_SELECT; - tegra_uart_write(tup, tup->fcr_shadow, UART_FCR); + 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; + 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; @@ -1050,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; } @@ -1145,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); @@ -1172,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; } @@ -1378,7 +1422,6 @@ static int tegra_uart_parse_dt(struct platform_device *pdev, int count; int n_entries; - port = of_alias_get_id(np, "serial"); if (port < 0) { dev_err(&pdev->dev, "failed to get alias id, errno %d\n", port); @@ -1388,6 +1431,18 @@ 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; -- cgit v1.2.3-59-g8ed1b