From 5930cb3511dfb4392de1180c892b353316ef29ec Mon Sep 17 00:00:00 2001 From: Baruch Siach Date: Thu, 18 Dec 2014 21:45:24 +0200 Subject: serial: driver for Conexant Digicolor USART Signed-off-by: Baruch Siach Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/Makefile | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/tty/serial/Makefile') diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index 9a548acf5fdc..770a19bb7fcb 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -92,6 +92,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_CONEXANT_DIGICOLOR) += digicolor-usart.o obj-$(CONFIG_SERIAL_MEN_Z135) += men_z135_uart.o # GPIOLIB helpers for modem control lines -- cgit v1.2.3 From 874e52086f9f1b9f9bdfbf98cca8506b110b63ba Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 12 Jan 2015 15:17:22 +0200 Subject: x86, mrst: remove Moorestown specific serial drivers Intel Moorestown platform support was removed few years ago. This is a follow up which removes Moorestown specific code for the serial devices. It includes mrst_max3110 and earlyprintk bits. This was used on SFI (Medfield, Clovertrail) based platforms as well, though new ones use normal serial interface for the console service. Signed-off-by: Andy Shevchenko Acked-by: David Cohen Signed-off-by: Greg Kroah-Hartman --- arch/x86/include/asm/intel-mid.h | 3 - arch/x86/kernel/early_printk.c | 5 - arch/x86/platform/intel-mid/device_libs/Makefile | 2 - .../intel-mid/device_libs/platform_max3111.c | 35 - .../platform/intel-mid/early_printk_intel_mid.c | 220 +---- drivers/tty/serial/Kconfig | 10 - drivers/tty/serial/Makefile | 1 - drivers/tty/serial/mrst_max3110.c | 909 --------------------- drivers/tty/serial/mrst_max3110.h | 61 -- 9 files changed, 4 insertions(+), 1242 deletions(-) delete mode 100644 arch/x86/platform/intel-mid/device_libs/platform_max3111.c delete mode 100644 drivers/tty/serial/mrst_max3110.c delete mode 100644 drivers/tty/serial/mrst_max3110.h (limited to 'drivers/tty/serial/Makefile') diff --git a/arch/x86/include/asm/intel-mid.h b/arch/x86/include/asm/intel-mid.h index e34e097b6f9d..705d35708a50 100644 --- a/arch/x86/include/asm/intel-mid.h +++ b/arch/x86/include/asm/intel-mid.h @@ -136,9 +136,6 @@ extern enum intel_mid_timer_options intel_mid_timer_options; #define SFI_MTMR_MAX_NUM 8 #define SFI_MRTC_MAX 8 -extern struct console early_mrst_console; -extern void mrst_early_console_init(void); - extern struct console early_hsu_console; extern void hsu_early_console_init(const char *); diff --git a/arch/x86/kernel/early_printk.c b/arch/x86/kernel/early_printk.c index 01d1c187c9f9..de814dfb8aee 100644 --- a/arch/x86/kernel/early_printk.c +++ b/arch/x86/kernel/early_printk.c @@ -226,11 +226,6 @@ static int __init setup_early_printk(char *buf) early_console_register(&xenboot_console, keep); #endif #ifdef CONFIG_EARLY_PRINTK_INTEL_MID - if (!strncmp(buf, "mrst", 4)) { - mrst_early_console_init(); - early_console_register(&early_mrst_console, keep); - } - if (!strncmp(buf, "hsu", 3)) { hsu_early_console_init(buf + 3); early_console_register(&early_hsu_console, keep); diff --git a/arch/x86/platform/intel-mid/device_libs/Makefile b/arch/x86/platform/intel-mid/device_libs/Makefile index af9307f2cc28..91ec9f8704bf 100644 --- a/arch/x86/platform/intel-mid/device_libs/Makefile +++ b/arch/x86/platform/intel-mid/device_libs/Makefile @@ -16,8 +16,6 @@ obj-$(subst m,y,$(CONFIG_INPUT_MPU3050)) += platform_mpu3050.o obj-$(subst m,y,$(CONFIG_INPUT_BMA150)) += platform_bma023.o obj-$(subst m,y,$(CONFIG_GPIO_PCA953X)) += platform_tca6416.o obj-$(subst m,y,$(CONFIG_DRM_MEDFIELD)) += platform_tc35876x.o -# SPI Devices -obj-$(subst m,y,$(CONFIG_SERIAL_MRST_MAX3110)) += platform_max3111.o # MISC Devices obj-$(subst m,y,$(CONFIG_KEYBOARD_GPIO)) += platform_gpio_keys.o obj-$(subst m,y,$(CONFIG_INTEL_MID_WATCHDOG)) += platform_wdt.o diff --git a/arch/x86/platform/intel-mid/device_libs/platform_max3111.c b/arch/x86/platform/intel-mid/device_libs/platform_max3111.c deleted file mode 100644 index afd1df94e0e5..000000000000 --- a/arch/x86/platform/intel-mid/device_libs/platform_max3111.c +++ /dev/null @@ -1,35 +0,0 @@ -/* - * platform_max3111.c: max3111 platform data initilization file - * - * (C) Copyright 2013 Intel Corporation - * Author: Sathyanarayanan Kuppuswamy - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; version 2 - * of the License. - */ - -#include -#include -#include - -static void __init *max3111_platform_data(void *info) -{ - struct spi_board_info *spi_info = info; - int intr = get_gpio_by_name("max3111_int"); - - spi_info->mode = SPI_MODE_0; - if (intr == -1) - return NULL; - spi_info->irq = intr + INTEL_MID_IRQ_OFFSET; - return NULL; -} - -static const struct devs_id max3111_dev_id __initconst = { - .name = "spi_max3111", - .type = SFI_DEV_TYPE_SPI, - .get_platform_data = &max3111_platform_data, -}; - -sfi_device(max3111_dev_id); diff --git a/arch/x86/platform/intel-mid/early_printk_intel_mid.c b/arch/x86/platform/intel-mid/early_printk_intel_mid.c index e0bd082a80e0..4e720829ab90 100644 --- a/arch/x86/platform/intel-mid/early_printk_intel_mid.c +++ b/arch/x86/platform/intel-mid/early_printk_intel_mid.c @@ -10,15 +10,13 @@ */ /* - * This file implements two early consoles named mrst and hsu. - * mrst is based on Maxim3110 spi-uart device, it exists in both - * Moorestown and Medfield platforms, while hsu is based on a High - * Speed UART device which only exists in the Medfield platform + * This file implements early console named hsu. + * hsu is based on a High Speed UART device which only exists in the Medfield + * platform */ #include #include -#include #include #include #include @@ -28,216 +26,6 @@ #include #include -#define MRST_SPI_TIMEOUT 0x200000 -#define MRST_REGBASE_SPI0 0xff128000 -#define MRST_REGBASE_SPI1 0xff128400 -#define MRST_CLK_SPI0_REG 0xff11d86c - -/* Bit fields in CTRLR0 */ -#define SPI_DFS_OFFSET 0 - -#define SPI_FRF_OFFSET 4 -#define SPI_FRF_SPI 0x0 -#define SPI_FRF_SSP 0x1 -#define SPI_FRF_MICROWIRE 0x2 -#define SPI_FRF_RESV 0x3 - -#define SPI_MODE_OFFSET 6 -#define SPI_SCPH_OFFSET 6 -#define SPI_SCOL_OFFSET 7 -#define SPI_TMOD_OFFSET 8 -#define SPI_TMOD_TR 0x0 /* xmit & recv */ -#define SPI_TMOD_TO 0x1 /* xmit only */ -#define SPI_TMOD_RO 0x2 /* recv only */ -#define SPI_TMOD_EPROMREAD 0x3 /* eeprom read mode */ - -#define SPI_SLVOE_OFFSET 10 -#define SPI_SRL_OFFSET 11 -#define SPI_CFS_OFFSET 12 - -/* Bit fields in SR, 7 bits */ -#define SR_MASK 0x7f /* cover 7 bits */ -#define SR_BUSY (1 << 0) -#define SR_TF_NOT_FULL (1 << 1) -#define SR_TF_EMPT (1 << 2) -#define SR_RF_NOT_EMPT (1 << 3) -#define SR_RF_FULL (1 << 4) -#define SR_TX_ERR (1 << 5) -#define SR_DCOL (1 << 6) - -struct dw_spi_reg { - u32 ctrl0; - u32 ctrl1; - u32 ssienr; - u32 mwcr; - u32 ser; - u32 baudr; - u32 txfltr; - u32 rxfltr; - u32 txflr; - u32 rxflr; - u32 sr; - u32 imr; - u32 isr; - u32 risr; - u32 txoicr; - u32 rxoicr; - u32 rxuicr; - u32 msticr; - u32 icr; - u32 dmacr; - u32 dmatdlr; - u32 dmardlr; - u32 idr; - u32 version; - - /* Currently operates as 32 bits, though only the low 16 bits matter */ - u32 dr; -} __packed; - -#define dw_readl(dw, name) __raw_readl(&(dw)->name) -#define dw_writel(dw, name, val) __raw_writel((val), &(dw)->name) - -/* Default use SPI0 register for mrst, we will detect Penwell and use SPI1 */ -static unsigned long mrst_spi_paddr = MRST_REGBASE_SPI0; - -static u32 *pclk_spi0; -/* Always contains an accessible address, start with 0 */ -static struct dw_spi_reg *pspi; - -static struct kmsg_dumper dw_dumper; -static int dumper_registered; - -static void dw_kmsg_dump(struct kmsg_dumper *dumper, - enum kmsg_dump_reason reason) -{ - static char line[1024]; - size_t len; - - /* When run to this, we'd better re-init the HW */ - mrst_early_console_init(); - - while (kmsg_dump_get_line(dumper, true, line, sizeof(line), &len)) - early_mrst_console.write(&early_mrst_console, line, len); -} - -/* Set the ratio rate to 115200, 8n1, IRQ disabled */ -static void max3110_write_config(void) -{ - u16 config; - - config = 0xc001; - dw_writel(pspi, dr, config); -} - -/* Translate char to a eligible word and send to max3110 */ -static void max3110_write_data(char c) -{ - u16 data; - - data = 0x8000 | c; - dw_writel(pspi, dr, data); -} - -void mrst_early_console_init(void) -{ - u32 ctrlr0 = 0; - u32 spi0_cdiv; - u32 freq; /* Freqency info only need be searched once */ - - /* Base clk is 100 MHz, the actual clk = 100M / (clk_divider + 1) */ - pclk_spi0 = (void *)set_fixmap_offset_nocache(FIX_EARLYCON_MEM_BASE, - MRST_CLK_SPI0_REG); - spi0_cdiv = ((*pclk_spi0) & 0xe00) >> 9; - freq = 100000000 / (spi0_cdiv + 1); - - if (intel_mid_identify_cpu() == INTEL_MID_CPU_CHIP_PENWELL) - mrst_spi_paddr = MRST_REGBASE_SPI1; - - pspi = (void *)set_fixmap_offset_nocache(FIX_EARLYCON_MEM_BASE, - mrst_spi_paddr); - - /* Disable SPI controller */ - dw_writel(pspi, ssienr, 0); - - /* Set control param, 8 bits, transmit only mode */ - ctrlr0 = dw_readl(pspi, ctrl0); - - ctrlr0 &= 0xfcc0; - ctrlr0 |= 0xf | (SPI_FRF_SPI << SPI_FRF_OFFSET) - | (SPI_TMOD_TO << SPI_TMOD_OFFSET); - dw_writel(pspi, ctrl0, ctrlr0); - - /* - * Change the spi0 clk to comply with 115200 bps, use 100000 to - * calculate the clk dividor to make the clock a little slower - * than real baud rate. - */ - dw_writel(pspi, baudr, freq/100000); - - /* Disable all INT for early phase */ - dw_writel(pspi, imr, 0x0); - - /* Set the cs to spi-uart */ - dw_writel(pspi, ser, 0x2); - - /* Enable the HW, the last step for HW init */ - dw_writel(pspi, ssienr, 0x1); - - /* Set the default configuration */ - max3110_write_config(); - - /* Register the kmsg dumper */ - if (!dumper_registered) { - dw_dumper.dump = dw_kmsg_dump; - kmsg_dump_register(&dw_dumper); - dumper_registered = 1; - } -} - -/* Slave select should be called in the read/write function */ -static void early_mrst_spi_putc(char c) -{ - unsigned int timeout; - u32 sr; - - timeout = MRST_SPI_TIMEOUT; - /* Early putc needs to make sure the TX FIFO is not full */ - while (--timeout) { - sr = dw_readl(pspi, sr); - if (!(sr & SR_TF_NOT_FULL)) - cpu_relax(); - else - break; - } - - if (!timeout) - pr_warn("MRST earlycon: timed out\n"); - else - max3110_write_data(c); -} - -/* Early SPI only uses polling mode */ -static void early_mrst_spi_write(struct console *con, const char *str, - unsigned n) -{ - int i; - - for (i = 0; i < n && *str; i++) { - if (*str == '\n') - early_mrst_spi_putc('\r'); - early_mrst_spi_putc(*str); - str++; - } -} - -struct console early_mrst_console = { - .name = "earlymrst", - .write = early_mrst_spi_write, - .flags = CON_PRINTBUFFER, - .index = -1, -}; - /* * Following is the early console based on Medfield HSU (High * Speed UART) device. @@ -259,7 +47,7 @@ void hsu_early_console_init(const char *s) port = clamp_val(port, 0, 2); paddr = HSU_PORT_BASE + port * 0x80; - phsu = (void *)set_fixmap_offset_nocache(FIX_EARLYCON_MEM_BASE, paddr); + phsu = (void __iomem *)set_fixmap_offset_nocache(FIX_EARLYCON_MEM_BASE, paddr); /* Disable FIFO */ writeb(0x0, phsu + UART_FCR); diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 96ec6cfd74e8..35677f75a1d8 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -482,16 +482,6 @@ config SERIAL_SA1100_CONSOLE your boot loader (lilo or loadlin) about how to pass options to the kernel at boot time.) -config SERIAL_MRST_MAX3110 - tristate "SPI UART driver for Max3110" - depends on SPI_DW_PCI - select SERIAL_CORE - select SERIAL_CORE_CONSOLE - help - This is the UART protocol driver for the MAX3110 device on - the Intel Moorestown platform. On other systems use the max3100 - driver. - config SERIAL_MFD_HSU tristate "Medfield High Speed UART support" depends on PCI diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index 770a19bb7fcb..a1c1085ef75c 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -77,7 +77,6 @@ obj-$(CONFIG_SERIAL_TIMBERDALE) += timbuart.o obj-$(CONFIG_SERIAL_GRLIB_GAISLER_APBUART) += apbuart.o obj-$(CONFIG_SERIAL_ALTERA_JTAGUART) += altera_jtaguart.o obj-$(CONFIG_SERIAL_VT8500) += vt8500_serial.o -obj-$(CONFIG_SERIAL_MRST_MAX3110) += mrst_max3110.o obj-$(CONFIG_SERIAL_MFD_HSU) += mfd.o obj-$(CONFIG_SERIAL_IFX6X60) += ifx6x60.o obj-$(CONFIG_SERIAL_PCH_UART) += pch_uart.o diff --git a/drivers/tty/serial/mrst_max3110.c b/drivers/tty/serial/mrst_max3110.c deleted file mode 100644 index 77239d5e620d..000000000000 --- a/drivers/tty/serial/mrst_max3110.c +++ /dev/null @@ -1,909 +0,0 @@ -/* - * mrst_max3110.c - spi uart protocol driver for Maxim 3110 - * - * Copyright (c) 2008-2010, Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - */ - -/* - * Note: - * 1. From Max3110 spec, the Rx FIFO has 8 words, while the Tx FIFO only has - * 1 word. If SPI master controller doesn't support sclk frequency change, - * then the char need be sent out one by one with some delay - * - * 2. Currently only RX available interrupt is used, no need for waiting TXE - * interrupt for a low speed UART device - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#ifdef CONFIG_MAGIC_SYSRQ -#define SUPPORT_SYSRQ -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "mrst_max3110.h" - -#define UART_TX_NEEDED 1 -#define CON_TX_NEEDED 2 -#define BIT_IRQ_PENDING 3 - -struct uart_max3110 { - struct uart_port port; - struct spi_device *spi; - char name[SPI_NAME_SIZE]; - - wait_queue_head_t wq; - struct task_struct *main_thread; - struct task_struct *read_thread; - struct mutex thread_mutex; - struct mutex io_mutex; - - u32 baud; - u16 cur_conf; - u8 clock; - u8 parity, word_7bits; - u16 irq; - - unsigned long uart_flags; - - /* console related */ - struct circ_buf con_xmit; -}; - -/* global data structure, may need be removed */ -static struct uart_max3110 *pmax; - -static int receive_chars(struct uart_max3110 *max, - unsigned short *str, int len); -static int max3110_read_multi(struct uart_max3110 *max); -static void max3110_con_receive(struct uart_max3110 *max); - -static int max3110_write_then_read(struct uart_max3110 *max, - const void *txbuf, void *rxbuf, unsigned len, int always_fast) -{ - struct spi_device *spi = max->spi; - struct spi_message message; - struct spi_transfer x; - int ret; - - mutex_lock(&max->io_mutex); - spi_message_init(&message); - memset(&x, 0, sizeof x); - x.len = len; - x.tx_buf = txbuf; - x.rx_buf = rxbuf; - spi_message_add_tail(&x, &message); - - if (always_fast) - x.speed_hz = spi->max_speed_hz; - else if (max->baud) - x.speed_hz = max->baud; - - /* Do the i/o */ - ret = spi_sync(spi, &message); - mutex_unlock(&max->io_mutex); - return ret; -} - -/* Write a 16b word to the device */ -static int max3110_out(struct uart_max3110 *max, const u16 out) -{ - void *buf; - u16 *obuf, *ibuf; - int ret; - - buf = kzalloc(8, GFP_KERNEL | GFP_DMA); - if (!buf) - return -ENOMEM; - - obuf = buf; - ibuf = buf + 4; - *obuf = out; - ret = max3110_write_then_read(max, obuf, ibuf, 2, 1); - if (ret) { - pr_warn("%s: get err msg %d when sending 0x%x\n", - __func__, ret, out); - goto exit; - } - - receive_chars(max, ibuf, 1); - -exit: - kfree(buf); - return ret; -} - -/* - * This is usually used to read data from SPIC RX FIFO, which doesn't - * need any delay like flushing character out. - * - * Return how many valide bytes are read back - */ -static int max3110_read_multi(struct uart_max3110 *max) -{ - void *buf; - u16 *obuf, *ibuf; - int ret, blen; - - blen = M3110_RX_FIFO_DEPTH * sizeof(u16); - buf = kzalloc(blen * 2, GFP_KERNEL | GFP_DMA); - if (!buf) - return 0; - - /* tx/rx always have the same length */ - obuf = buf; - ibuf = buf + blen; - - if (max3110_write_then_read(max, obuf, ibuf, blen, 1)) { - kfree(buf); - return 0; - } - - ret = receive_chars(max, ibuf, M3110_RX_FIFO_DEPTH); - - kfree(buf); - return ret; -} - -static void serial_m3110_con_putchar(struct uart_port *port, int ch) -{ - struct uart_max3110 *max = - container_of(port, struct uart_max3110, port); - struct circ_buf *xmit = &max->con_xmit; - - if (uart_circ_chars_free(xmit)) { - xmit->buf[xmit->head] = (char)ch; - xmit->head = (xmit->head + 1) & (PAGE_SIZE - 1); - } -} - -/* - * Print a string to the serial port trying not to disturb - * any possible real use of the port... - * - * The console_lock must be held when we get here. - */ -static void serial_m3110_con_write(struct console *co, - const char *s, unsigned int count) -{ - if (!pmax) - return; - - uart_console_write(&pmax->port, s, count, serial_m3110_con_putchar); - - if (!test_and_set_bit(CON_TX_NEEDED, &pmax->uart_flags)) - wake_up(&pmax->wq); -} - -static int __init -serial_m3110_con_setup(struct console *co, char *options) -{ - struct uart_max3110 *max = pmax; - int baud = 115200; - int bits = 8; - int parity = 'n'; - int flow = 'n'; - - pr_info("setting up console\n"); - - if (co->index == -1) - co->index = 0; - - if (!max) { - pr_err("pmax is NULL, return\n"); - return -ENODEV; - } - - if (options) - uart_parse_options(options, &baud, &parity, &bits, &flow); - - return uart_set_options(&max->port, co, baud, parity, bits, flow); -} - -static struct tty_driver *serial_m3110_con_device(struct console *co, - int *index) -{ - struct uart_driver *p = co->data; - *index = co->index; - return p->tty_driver; -} - -static struct uart_driver serial_m3110_reg; -static struct console serial_m3110_console = { - .name = "ttyS", - .write = serial_m3110_con_write, - .device = serial_m3110_con_device, - .setup = serial_m3110_con_setup, - .flags = CON_PRINTBUFFER, - .index = -1, - .data = &serial_m3110_reg, -}; - -static unsigned int serial_m3110_tx_empty(struct uart_port *port) -{ - return 1; -} - -static void serial_m3110_stop_tx(struct uart_port *port) -{ - return; -} - -/* stop_rx will be called in spin_lock env */ -static void serial_m3110_stop_rx(struct uart_port *port) -{ - return; -} - -#define WORDS_PER_XFER 128 -static void send_circ_buf(struct uart_max3110 *max, - struct circ_buf *xmit) -{ - void *buf; - u16 *obuf, *ibuf; - int i, len, blen, dma_size, left, ret = 0; - - - dma_size = WORDS_PER_XFER * sizeof(u16) * 2; - buf = kzalloc(dma_size, GFP_KERNEL | GFP_DMA); - if (!buf) - return; - obuf = buf; - ibuf = buf + dma_size/2; - - while (!uart_circ_empty(xmit)) { - left = uart_circ_chars_pending(xmit); - while (left) { - len = min(left, WORDS_PER_XFER); - blen = len * sizeof(u16); - memset(ibuf, 0, blen); - - for (i = 0; i < len; i++) { - obuf[i] = (u8)xmit->buf[xmit->tail] | WD_TAG; - xmit->tail = (xmit->tail + 1) & - (UART_XMIT_SIZE - 1); - } - - /* Fail to send msg to console is not very critical */ - - ret = max3110_write_then_read(max, obuf, ibuf, blen, 0); - if (ret) - pr_warn("%s: get err msg %d\n", __func__, ret); - - receive_chars(max, ibuf, len); - - max->port.icount.tx += len; - left -= len; - } - } - - kfree(buf); -} - -static void transmit_char(struct uart_max3110 *max) -{ - struct uart_port *port = &max->port; - struct circ_buf *xmit = &port->state->xmit; - - if (uart_circ_empty(xmit) || uart_tx_stopped(port)) - return; - - send_circ_buf(max, xmit); - - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(port); - - if (uart_circ_empty(xmit)) - serial_m3110_stop_tx(port); -} - -/* - * This will be called by uart_write() and tty_write, can't - * go to sleep - */ -static void serial_m3110_start_tx(struct uart_port *port) -{ - struct uart_max3110 *max = - container_of(port, struct uart_max3110, port); - - if (!test_and_set_bit(UART_TX_NEEDED, &max->uart_flags)) - wake_up(&max->wq); -} - -static int -receive_chars(struct uart_max3110 *max, unsigned short *str, int len) -{ - struct uart_port *port = &max->port; - struct tty_port *tport; - char buf[M3110_RX_FIFO_DEPTH]; - int r, w, usable; - - /* If uart is not opened, just return */ - if (!port->state) - return 0; - - tport = &port->state->port; - - for (r = 0, w = 0; r < len; r++) { - if (str[r] & MAX3110_BREAK && - uart_handle_break(port)) - continue; - - if (str[r] & MAX3110_READ_DATA_AVAILABLE) { - if (uart_handle_sysrq_char(port, str[r] & 0xff)) - continue; - - buf[w++] = str[r] & 0xff; - } - } - - if (!w) - return 0; - - for (r = 0; w; r += usable, w -= usable) { - usable = tty_buffer_request_room(tport, w); - if (usable) { - tty_insert_flip_string(tport, buf + r, usable); - port->icount.rx += usable; - } - } - tty_flip_buffer_push(tport); - - return r; -} - -/* - * This routine will be used in read_thread or RX IRQ handling, - * it will first do one round buffer read(8 words), if there is some - * valid RX data, will try to read 5 more rounds till all data - * is read out. - * - * Use stack space as data buffer to save some system load, and chose - * 504 Btyes as a threadhold to do a bulk push to upper tty layer when - * receiving bulk data, a much bigger buffer may cause stack overflow - */ -static void max3110_con_receive(struct uart_max3110 *max) -{ - int loop = 1, num; - - do { - num = max3110_read_multi(max); - - if (num) { - loop = 5; - } - } while (--loop); -} - -static int max3110_main_thread(void *_max) -{ - struct uart_max3110 *max = _max; - wait_queue_head_t *wq = &max->wq; - int ret = 0; - struct circ_buf *xmit = &max->con_xmit; - - pr_info("start main thread\n"); - - do { - wait_event_interruptible(*wq, - max->uart_flags || kthread_should_stop()); - - mutex_lock(&max->thread_mutex); - - if (test_and_clear_bit(BIT_IRQ_PENDING, &max->uart_flags)) - max3110_con_receive(max); - - /* first handle console output */ - if (test_and_clear_bit(CON_TX_NEEDED, &max->uart_flags)) - send_circ_buf(max, xmit); - - /* handle uart output */ - if (test_and_clear_bit(UART_TX_NEEDED, &max->uart_flags)) - transmit_char(max); - - mutex_unlock(&max->thread_mutex); - - } while (!kthread_should_stop()); - - return ret; -} - -static irqreturn_t serial_m3110_irq(int irq, void *dev_id) -{ - struct uart_max3110 *max = dev_id; - - /* max3110's irq is a falling edge, not level triggered, - * so no need to disable the irq */ - - if (!test_and_set_bit(BIT_IRQ_PENDING, &max->uart_flags)) - wake_up(&max->wq); - - return IRQ_HANDLED; -} - -/* if don't use RX IRQ, then need a thread to polling read */ -static int max3110_read_thread(void *_max) -{ - struct uart_max3110 *max = _max; - - pr_info("start read thread\n"); - do { - /* - * If can't acquire the mutex, it means the main thread - * is running which will also perform the rx job - */ - if (mutex_trylock(&max->thread_mutex)) { - max3110_con_receive(max); - mutex_unlock(&max->thread_mutex); - } - - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(HZ / 20); - } while (!kthread_should_stop()); - - return 0; -} - -static int serial_m3110_startup(struct uart_port *port) -{ - struct uart_max3110 *max = - container_of(port, struct uart_max3110, port); - u16 config = 0; - int ret = 0; - - if (port->line != 0) { - pr_err("uart port startup failed\n"); - return -1; - } - - /* Disable all IRQ and config it to 115200, 8n1 */ - config = WC_TAG | WC_FIFO_ENABLE - | WC_1_STOPBITS - | WC_8BIT_WORD - | WC_BAUD_DR2; - - /* as we use thread to handle tx/rx, need set low latency */ - port->state->port.low_latency = 1; - - if (max->irq) { - /* Enable RX IRQ only */ - config |= WC_RXA_IRQ_ENABLE; - } else { - /* If IRQ is disabled, start a read thread for input data */ - max->read_thread = - kthread_run(max3110_read_thread, max, "max3110_read"); - if (IS_ERR(max->read_thread)) { - ret = PTR_ERR(max->read_thread); - max->read_thread = NULL; - pr_err("Can't create read thread!\n"); - return ret; - } - } - - ret = max3110_out(max, config); - if (ret) { - if (max->read_thread) - kthread_stop(max->read_thread); - max->read_thread = NULL; - return ret; - } - - max->cur_conf = config; - return 0; -} - -static void serial_m3110_shutdown(struct uart_port *port) -{ - struct uart_max3110 *max = - container_of(port, struct uart_max3110, port); - u16 config; - - if (max->read_thread) { - kthread_stop(max->read_thread); - max->read_thread = NULL; - } - - /* Disable interrupts from this port */ - config = WC_TAG | WC_SW_SHDI; - max3110_out(max, config); -} - -static void serial_m3110_release_port(struct uart_port *port) -{ -} - -static int serial_m3110_request_port(struct uart_port *port) -{ - return 0; -} - -static void serial_m3110_config_port(struct uart_port *port, int flags) -{ - port->type = PORT_MAX3100; -} - -static int -serial_m3110_verify_port(struct uart_port *port, struct serial_struct *ser) -{ - /* we don't want the core code to modify any port params */ - return -EINVAL; -} - - -static const char *serial_m3110_type(struct uart_port *port) -{ - struct uart_max3110 *max = - container_of(port, struct uart_max3110, port); - return max->name; -} - -static void -serial_m3110_set_termios(struct uart_port *port, struct ktermios *termios, - struct ktermios *old) -{ - struct uart_max3110 *max = - container_of(port, struct uart_max3110, port); - unsigned char cval; - unsigned int baud, parity = 0; - int clk_div = -1; - u16 new_conf = max->cur_conf; - - switch (termios->c_cflag & CSIZE) { - case CS7: - cval = UART_LCR_WLEN7; - new_conf |= WC_7BIT_WORD; - break; - default: - /* We only support CS7 & CS8 */ - termios->c_cflag &= ~CSIZE; - termios->c_cflag |= CS8; - case CS8: - cval = UART_LCR_WLEN8; - new_conf |= WC_8BIT_WORD; - break; - } - - baud = uart_get_baud_rate(port, termios, old, 0, 230400); - - /* First calc the div for 1.8MHZ clock case */ - switch (baud) { - case 300: - clk_div = WC_BAUD_DR384; - break; - case 600: - clk_div = WC_BAUD_DR192; - break; - case 1200: - clk_div = WC_BAUD_DR96; - break; - case 2400: - clk_div = WC_BAUD_DR48; - break; - case 4800: - clk_div = WC_BAUD_DR24; - break; - case 9600: - clk_div = WC_BAUD_DR12; - break; - case 19200: - clk_div = WC_BAUD_DR6; - break; - case 38400: - clk_div = WC_BAUD_DR3; - break; - case 57600: - clk_div = WC_BAUD_DR2; - break; - case 115200: - clk_div = WC_BAUD_DR1; - break; - case 230400: - if (max->clock & MAX3110_HIGH_CLK) - break; - default: - /* Pick the previous baud rate */ - baud = max->baud; - clk_div = max->cur_conf & WC_BAUD_DIV_MASK; - tty_termios_encode_baud_rate(termios, baud, baud); - } - - if (max->clock & MAX3110_HIGH_CLK) { - clk_div += 1; - /* High clk version max3110 doesn't support B300 */ - if (baud == 300) { - baud = 600; - clk_div = WC_BAUD_DR384; - } - if (baud == 230400) - clk_div = WC_BAUD_DR1; - tty_termios_encode_baud_rate(termios, baud, baud); - } - - new_conf = (new_conf & ~WC_BAUD_DIV_MASK) | clk_div; - - if (unlikely(termios->c_cflag & CMSPAR)) - termios->c_cflag &= ~CMSPAR; - - if (termios->c_cflag & CSTOPB) - new_conf |= WC_2_STOPBITS; - else - new_conf &= ~WC_2_STOPBITS; - - if (termios->c_cflag & PARENB) { - new_conf |= WC_PARITY_ENABLE; - parity |= UART_LCR_PARITY; - } else - new_conf &= ~WC_PARITY_ENABLE; - - if (!(termios->c_cflag & PARODD)) - parity |= UART_LCR_EPAR; - max->parity = parity; - - uart_update_timeout(port, termios->c_cflag, baud); - - new_conf |= WC_TAG; - if (new_conf != max->cur_conf) { - if (!max3110_out(max, new_conf)) { - max->cur_conf = new_conf; - max->baud = baud; - } - } -} - -/* Don't handle hw handshaking */ -static unsigned int serial_m3110_get_mctrl(struct uart_port *port) -{ - return TIOCM_DSR | TIOCM_CAR | TIOCM_DSR; -} - -static void serial_m3110_set_mctrl(struct uart_port *port, unsigned int mctrl) -{ -} - -static void serial_m3110_break_ctl(struct uart_port *port, int break_state) -{ -} - -static void serial_m3110_pm(struct uart_port *port, unsigned int state, - unsigned int oldstate) -{ -} - -static struct uart_ops serial_m3110_ops = { - .tx_empty = serial_m3110_tx_empty, - .set_mctrl = serial_m3110_set_mctrl, - .get_mctrl = serial_m3110_get_mctrl, - .stop_tx = serial_m3110_stop_tx, - .start_tx = serial_m3110_start_tx, - .stop_rx = serial_m3110_stop_rx, - .break_ctl = serial_m3110_break_ctl, - .startup = serial_m3110_startup, - .shutdown = serial_m3110_shutdown, - .set_termios = serial_m3110_set_termios, - .pm = serial_m3110_pm, - .type = serial_m3110_type, - .release_port = serial_m3110_release_port, - .request_port = serial_m3110_request_port, - .config_port = serial_m3110_config_port, - .verify_port = serial_m3110_verify_port, -}; - -static struct uart_driver serial_m3110_reg = { - .owner = THIS_MODULE, - .driver_name = "MRST serial", - .dev_name = "ttyS", - .major = TTY_MAJOR, - .minor = 64, - .nr = 1, - .cons = &serial_m3110_console, -}; - -#ifdef CONFIG_PM_SLEEP -static int serial_m3110_suspend(struct device *dev) -{ - struct spi_device *spi = to_spi_device(dev); - struct uart_max3110 *max = spi_get_drvdata(spi); - - if (max->irq > 0) - disable_irq(max->irq); - uart_suspend_port(&serial_m3110_reg, &max->port); - max3110_out(max, max->cur_conf | WC_SW_SHDI); - return 0; -} - -static int serial_m3110_resume(struct device *dev) -{ - struct spi_device *spi = to_spi_device(dev); - struct uart_max3110 *max = spi_get_drvdata(spi); - - max3110_out(max, max->cur_conf); - uart_resume_port(&serial_m3110_reg, &max->port); - if (max->irq > 0) - enable_irq(max->irq); - return 0; -} - -static SIMPLE_DEV_PM_OPS(serial_m3110_pm_ops, serial_m3110_suspend, - serial_m3110_resume); -#define SERIAL_M3110_PM_OPS (&serial_m3110_pm_ops) - -#else -#define SERIAL_M3110_PM_OPS NULL -#endif - -static int serial_m3110_probe(struct spi_device *spi) -{ - struct uart_max3110 *max; - void *buffer; - u16 res; - int ret = 0; - - max = kzalloc(sizeof(*max), GFP_KERNEL); - if (!max) - return -ENOMEM; - - /* Set spi info */ - spi->bits_per_word = 16; - max->clock = MAX3110_HIGH_CLK; - - spi_setup(spi); - - max->port.type = PORT_MAX3100; - max->port.fifosize = 2; /* Only have 16b buffer */ - max->port.ops = &serial_m3110_ops; - max->port.line = 0; - max->port.dev = &spi->dev; - max->port.uartclk = 115200; - - max->spi = spi; - strcpy(max->name, spi->modalias); - max->irq = (u16)spi->irq; - - mutex_init(&max->thread_mutex); - mutex_init(&max->io_mutex); - - max->word_7bits = 0; - max->parity = 0; - max->baud = 0; - - max->cur_conf = 0; - max->uart_flags = 0; - - /* Check if reading configuration register returns something sane */ - - res = RC_TAG; - ret = max3110_write_then_read(max, (u8 *)&res, (u8 *)&res, 2, 0); - if (ret < 0 || res == 0 || res == 0xffff) { - dev_dbg(&spi->dev, "MAX3111 deemed not present (conf reg %04x)", - res); - ret = -ENODEV; - goto err_get_page; - } - - buffer = (void *)__get_free_page(GFP_KERNEL); - if (!buffer) { - ret = -ENOMEM; - goto err_get_page; - } - max->con_xmit.buf = buffer; - max->con_xmit.head = 0; - max->con_xmit.tail = 0; - - init_waitqueue_head(&max->wq); - - max->main_thread = kthread_run(max3110_main_thread, - max, "max3110_main"); - if (IS_ERR(max->main_thread)) { - ret = PTR_ERR(max->main_thread); - goto err_kthread; - } - - if (max->irq) { - ret = request_irq(max->irq, serial_m3110_irq, - IRQ_TYPE_EDGE_FALLING, "max3110", max); - if (ret) { - max->irq = 0; - dev_warn(&spi->dev, - "unable to allocate IRQ, will use polling method\n"); - } - } - - spi_set_drvdata(spi, max); - pmax = max; - - /* Give membase a psudo value to pass serial_core's check */ - max->port.membase = (unsigned char __iomem *)0xff110000; - uart_add_one_port(&serial_m3110_reg, &max->port); - - return 0; - -err_kthread: - free_page((unsigned long)buffer); -err_get_page: - kfree(max); - return ret; -} - -static int serial_m3110_remove(struct spi_device *dev) -{ - struct uart_max3110 *max = spi_get_drvdata(dev); - - if (!max) - return 0; - - uart_remove_one_port(&serial_m3110_reg, &max->port); - - free_page((unsigned long)max->con_xmit.buf); - - if (max->irq) - free_irq(max->irq, max); - - if (max->main_thread) - kthread_stop(max->main_thread); - - kfree(max); - return 0; -} - -static struct spi_driver uart_max3110_driver = { - .driver = { - .name = "spi_max3111", - .owner = THIS_MODULE, - .pm = SERIAL_M3110_PM_OPS, - }, - .probe = serial_m3110_probe, - .remove = serial_m3110_remove, -}; - -static int __init serial_m3110_init(void) -{ - int ret = 0; - - ret = uart_register_driver(&serial_m3110_reg); - if (ret) - return ret; - - ret = spi_register_driver(&uart_max3110_driver); - if (ret) - uart_unregister_driver(&serial_m3110_reg); - - return ret; -} - -static void __exit serial_m3110_exit(void) -{ - spi_unregister_driver(&uart_max3110_driver); - uart_unregister_driver(&serial_m3110_reg); -} - -module_init(serial_m3110_init); -module_exit(serial_m3110_exit); - -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("spi:max3110-uart"); diff --git a/drivers/tty/serial/mrst_max3110.h b/drivers/tty/serial/mrst_max3110.h deleted file mode 100644 index 35af0739513b..000000000000 --- a/drivers/tty/serial/mrst_max3110.h +++ /dev/null @@ -1,61 +0,0 @@ -#ifndef _MRST_MAX3110_H -#define _MRST_MAX3110_H - -#define MAX3110_HIGH_CLK 0x1 /* 3.6864 MHZ */ -#define MAX3110_LOW_CLK 0x0 /* 1.8432 MHZ */ - -/* status bits for all 4 MAX3110 operate modes */ -#define MAX3110_READ_DATA_AVAILABLE (1 << 15) -#define MAX3110_WRITE_BUF_EMPTY (1 << 14) -#define MAX3110_BREAK (1 << 10) - -#define WC_TAG (3 << 14) -#define RC_TAG (1 << 14) -#define WD_TAG (2 << 14) -#define RD_TAG (0 << 14) - -/* bits def for write configuration */ -#define WC_FIFO_ENABLE_MASK (1 << 13) -#define WC_FIFO_ENABLE (0 << 13) - -#define WC_SW_SHDI (1 << 12) - -#define WC_IRQ_MASK (0xF << 8) -#define WC_TXE_IRQ_ENABLE (1 << 11) /* TX empty irq */ -#define WC_RXA_IRQ_ENABLE (1 << 10) /* RX available irq */ -#define WC_PAR_HIGH_IRQ_ENABLE (1 << 9) -#define WC_REC_ACT_IRQ_ENABLE (1 << 8) - -#define WC_IRDA_ENABLE (1 << 7) - -#define WC_STOPBITS_MASK (1 << 6) -#define WC_2_STOPBITS (1 << 6) -#define WC_1_STOPBITS (0 << 6) - -#define WC_PARITY_ENABLE_MASK (1 << 5) -#define WC_PARITY_ENABLE (1 << 5) - -#define WC_WORDLEN_MASK (1 << 4) -#define WC_7BIT_WORD (1 << 4) -#define WC_8BIT_WORD (0 << 4) - -#define WC_BAUD_DIV_MASK (0xF) -#define WC_BAUD_DR1 (0x0) -#define WC_BAUD_DR2 (0x1) -#define WC_BAUD_DR4 (0x2) -#define WC_BAUD_DR8 (0x3) -#define WC_BAUD_DR16 (0x4) -#define WC_BAUD_DR32 (0x5) -#define WC_BAUD_DR64 (0x6) -#define WC_BAUD_DR128 (0x7) -#define WC_BAUD_DR3 (0x8) -#define WC_BAUD_DR6 (0x9) -#define WC_BAUD_DR12 (0xA) -#define WC_BAUD_DR24 (0xB) -#define WC_BAUD_DR48 (0xC) -#define WC_BAUD_DR96 (0xD) -#define WC_BAUD_DR192 (0xE) -#define WC_BAUD_DR384 (0xF) - -#define M3110_RX_FIFO_DEPTH 8 -#endif -- cgit v1.2.3 From b7396a38fb28db4ebbbf35da1057eb5206b4ad6c Mon Sep 17 00:00:00 2001 From: Chunyan Zhang Date: Wed, 28 Jan 2015 19:08:44 +0800 Subject: tty/serial: Add Spreadtrum sc9836-uart driver support Add a full sc9836-uart driver for SC9836 SoC which is based on the spreadtrum sharkl64 platform. This driver also support earlycon. Originally-by: Lanqing Liu Signed-off-by: Orson Zhai Signed-off-by: Chunyan Zhang Acked-by: Arnd Bergmann Reviewed-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/Kconfig | 18 + drivers/tty/serial/Makefile | 1 + drivers/tty/serial/sprd_serial.c | 793 +++++++++++++++++++++++++++++++++++++++ include/uapi/linux/serial_core.h | 3 + 4 files changed, 815 insertions(+) create mode 100644 drivers/tty/serial/sprd_serial.c (limited to 'drivers/tty/serial/Makefile') diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index c63317e87c33..ddcc0a4c659c 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -1583,6 +1583,24 @@ config SERIAL_MEN_Z135 This driver can also be build as a module. If so, the module will be called men_z135_uart.ko +config SERIAL_SPRD + tristate "Support for Spreadtrum serial" + depends on ARCH_SPRD + select SERIAL_CORE + help + This enables the driver for the Spreadtrum's serial. + +config SERIAL_SPRD_CONSOLE + bool "Spreadtrum UART console support" + depends on SERIAL_SPRD=y + select SERIAL_CORE_CONSOLE + select SERIAL_EARLYCON + help + Support for early debug console using Spreadtrum's serial. This enables + the console before standard serial driver is probed. This is enabled + with "earlycon" on the kernel command line. The console is + enabled when early_param is processed. + endmenu config SERIAL_MCTRL_GPIO diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index a1c1085ef75c..431879003ccd 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -93,6 +93,7 @@ obj-$(CONFIG_SERIAL_RP2) += rp2.o obj-$(CONFIG_SERIAL_FSL_LPUART) += fsl_lpuart.o obj-$(CONFIG_SERIAL_CONEXANT_DIGICOLOR) += digicolor-usart.o obj-$(CONFIG_SERIAL_MEN_Z135) += men_z135_uart.o +obj-$(CONFIG_SERIAL_SPRD) += sprd_serial.o # GPIOLIB helpers for modem control lines obj-$(CONFIG_SERIAL_MCTRL_GPIO) += serial_mctrl_gpio.o diff --git a/drivers/tty/serial/sprd_serial.c b/drivers/tty/serial/sprd_serial.c new file mode 100644 index 000000000000..594b63331ef4 --- /dev/null +++ b/drivers/tty/serial/sprd_serial.c @@ -0,0 +1,793 @@ +/* + * Copyright (C) 2012-2015 Spreadtrum Communications Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#if defined(CONFIG_SERIAL_SPRD_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* device name */ +#define UART_NR_MAX 8 +#define SPRD_TTY_NAME "ttyS" +#define SPRD_FIFO_SIZE 128 +#define SPRD_DEF_RATE 26000000 +#define SPRD_BAUD_IO_LIMIT 3000000 +#define SPRD_TIMEOUT 256 + +/* the offset of serial registers and BITs for them */ +/* data registers */ +#define SPRD_TXD 0x0000 +#define SPRD_RXD 0x0004 + +/* line status register and its BITs */ +#define SPRD_LSR 0x0008 +#define SPRD_LSR_OE BIT(4) +#define SPRD_LSR_FE BIT(3) +#define SPRD_LSR_PE BIT(2) +#define SPRD_LSR_BI BIT(7) +#define SPRD_LSR_TX_OVER BIT(15) + +/* data number in TX and RX fifo */ +#define SPRD_STS1 0x000C + +/* interrupt enable register and its BITs */ +#define SPRD_IEN 0x0010 +#define SPRD_IEN_RX_FULL BIT(0) +#define SPRD_IEN_TX_EMPTY BIT(1) +#define SPRD_IEN_BREAK_DETECT BIT(7) +#define SPRD_IEN_TIMEOUT BIT(13) + +/* interrupt clear register */ +#define SPRD_ICLR 0x0014 + +/* line control register */ +#define SPRD_LCR 0x0018 +#define SPRD_LCR_STOP_1BIT 0x10 +#define SPRD_LCR_STOP_2BIT 0x30 +#define SPRD_LCR_DATA_LEN (BIT(2) | BIT(3)) +#define SPRD_LCR_DATA_LEN5 0x0 +#define SPRD_LCR_DATA_LEN6 0x4 +#define SPRD_LCR_DATA_LEN7 0x8 +#define SPRD_LCR_DATA_LEN8 0xc +#define SPRD_LCR_PARITY (BIT(0) | BIT(1)) +#define SPRD_LCR_PARITY_EN 0x2 +#define SPRD_LCR_EVEN_PAR 0x0 +#define SPRD_LCR_ODD_PAR 0x1 + +/* control register 1 */ +#define SPRD_CTL1 0x001C +#define RX_HW_FLOW_CTL_THLD BIT(6) +#define RX_HW_FLOW_CTL_EN BIT(7) +#define TX_HW_FLOW_CTL_EN BIT(8) +#define RX_TOUT_THLD_DEF 0x3E00 +#define RX_HFC_THLD_DEF 0x40 + +/* fifo threshold register */ +#define SPRD_CTL2 0x0020 +#define THLD_TX_EMPTY 0x40 +#define THLD_RX_FULL 0x40 + +/* config baud rate register */ +#define SPRD_CLKD0 0x0024 +#define SPRD_CLKD1 0x0028 + +/* interrupt mask status register */ +#define SPRD_IMSR 0x002C +#define SPRD_IMSR_RX_FIFO_FULL BIT(0) +#define SPRD_IMSR_TX_FIFO_EMPTY BIT(1) +#define SPRD_IMSR_BREAK_DETECT BIT(7) +#define SPRD_IMSR_TIMEOUT BIT(13) + +struct reg_backup { + u32 ien; + u32 ctrl0; + u32 ctrl1; + u32 ctrl2; + u32 clkd0; + u32 clkd1; + u32 dspwait; +}; + +struct sprd_uart_port { + struct uart_port port; + struct reg_backup reg_bak; + char name[16]; +}; + +static struct sprd_uart_port *sprd_port[UART_NR_MAX]; +static int sprd_ports_num; + +static inline unsigned int serial_in(struct uart_port *port, int offset) +{ + return readl_relaxed(port->membase + offset); +} + +static inline void serial_out(struct uart_port *port, int offset, int value) +{ + writel_relaxed(value, port->membase + offset); +} + +static unsigned int sprd_tx_empty(struct uart_port *port) +{ + if (serial_in(port, SPRD_STS1) & 0xff00) + return 0; + else + return TIOCSER_TEMT; +} + +static unsigned int sprd_get_mctrl(struct uart_port *port) +{ + return TIOCM_DSR | TIOCM_CTS; +} + +static void sprd_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + /* nothing to do */ +} + +static void sprd_stop_tx(struct uart_port *port) +{ + unsigned int ien, iclr; + + iclr = serial_in(port, SPRD_ICLR); + ien = serial_in(port, SPRD_IEN); + + iclr |= SPRD_IEN_TX_EMPTY; + ien &= ~SPRD_IEN_TX_EMPTY; + + serial_out(port, SPRD_ICLR, iclr); + serial_out(port, SPRD_IEN, ien); +} + +static void sprd_start_tx(struct uart_port *port) +{ + unsigned int ien; + + ien = serial_in(port, SPRD_IEN); + if (!(ien & SPRD_IEN_TX_EMPTY)) { + ien |= SPRD_IEN_TX_EMPTY; + serial_out(port, SPRD_IEN, ien); + } +} + +static void sprd_stop_rx(struct uart_port *port) +{ + unsigned int ien, iclr; + + iclr = serial_in(port, SPRD_ICLR); + ien = serial_in(port, SPRD_IEN); + + ien &= ~(SPRD_IEN_RX_FULL | SPRD_IEN_BREAK_DETECT); + iclr |= SPRD_IEN_RX_FULL | SPRD_IEN_BREAK_DETECT; + + serial_out(port, SPRD_IEN, ien); + serial_out(port, SPRD_ICLR, iclr); +} + +/* The Sprd serial does not support this function. */ +static void sprd_break_ctl(struct uart_port *port, int break_state) +{ + /* nothing to do */ +} + +static int handle_lsr_errors(struct uart_port *port, + unsigned int *flag, + unsigned int *lsr) +{ + int ret = 0; + + /* statistics */ + if (*lsr & SPRD_LSR_BI) { + *lsr &= ~(SPRD_LSR_FE | SPRD_LSR_PE); + port->icount.brk++; + ret = uart_handle_break(port); + if (ret) + return ret; + } else if (*lsr & SPRD_LSR_PE) + port->icount.parity++; + else if (*lsr & SPRD_LSR_FE) + port->icount.frame++; + if (*lsr & SPRD_LSR_OE) + port->icount.overrun++; + + /* mask off conditions which should be ignored */ + *lsr &= port->read_status_mask; + if (*lsr & SPRD_LSR_BI) + *flag = TTY_BREAK; + else if (*lsr & SPRD_LSR_PE) + *flag = TTY_PARITY; + else if (*lsr & SPRD_LSR_FE) + *flag = TTY_FRAME; + + return ret; +} + +static inline void sprd_rx(struct uart_port *port) +{ + struct tty_port *tty = &port->state->port; + unsigned int ch, flag, lsr, max_count = SPRD_TIMEOUT; + + while ((serial_in(port, SPRD_STS1) & 0x00ff) && max_count--) { + lsr = serial_in(port, SPRD_LSR); + ch = serial_in(port, SPRD_RXD); + flag = TTY_NORMAL; + port->icount.rx++; + + if (lsr & (SPRD_LSR_BI | SPRD_LSR_PE | + SPRD_LSR_FE | SPRD_LSR_OE)) + if (handle_lsr_errors(port, &lsr, &flag)) + continue; + if (uart_handle_sysrq_char(port, ch)) + continue; + + uart_insert_char(port, lsr, SPRD_LSR_OE, ch, flag); + } + + tty_flip_buffer_push(tty); +} + +static inline void sprd_tx(struct uart_port *port) +{ + struct circ_buf *xmit = &port->state->xmit; + int count; + + if (port->x_char) { + serial_out(port, SPRD_TXD, port->x_char); + port->icount.tx++; + port->x_char = 0; + return; + } + + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + sprd_stop_tx(port); + return; + } + + count = THLD_TX_EMPTY; + do { + serial_out(port, SPRD_TXD, xmit->buf[xmit->tail]); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + port->icount.tx++; + if (uart_circ_empty(xmit)) + break; + } while (--count > 0); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + + if (uart_circ_empty(xmit)) + sprd_stop_tx(port); +} + +/* this handles the interrupt from one port */ +static irqreturn_t sprd_handle_irq(int irq, void *dev_id) +{ + struct uart_port *port = dev_id; + unsigned int ims; + + spin_lock(&port->lock); + + ims = serial_in(port, SPRD_IMSR); + + if (!ims) + return IRQ_NONE; + + serial_out(port, SPRD_ICLR, ~0); + + if (ims & (SPRD_IMSR_RX_FIFO_FULL | + SPRD_IMSR_BREAK_DETECT | SPRD_IMSR_TIMEOUT)) + sprd_rx(port); + + if (ims & SPRD_IMSR_TX_FIFO_EMPTY) + sprd_tx(port); + + spin_unlock(&port->lock); + + return IRQ_HANDLED; +} + +static int sprd_startup(struct uart_port *port) +{ + int ret = 0; + unsigned int ien, fc; + unsigned int timeout; + struct sprd_uart_port *sp; + unsigned long flags; + + serial_out(port, SPRD_CTL2, ((THLD_TX_EMPTY << 8) | THLD_RX_FULL)); + + /* clear rx fifo */ + timeout = SPRD_TIMEOUT; + while (timeout-- && serial_in(port, SPRD_STS1) & 0x00ff) + serial_in(port, SPRD_RXD); + + /* clear tx fifo */ + timeout = SPRD_TIMEOUT; + while (timeout-- && serial_in(port, SPRD_STS1) & 0xff00) + cpu_relax(); + + /* clear interrupt */ + serial_out(port, SPRD_IEN, 0); + serial_out(port, SPRD_ICLR, ~0); + + /* allocate irq */ + sp = container_of(port, struct sprd_uart_port, port); + snprintf(sp->name, sizeof(sp->name), "sprd_serial%d", port->line); + ret = devm_request_irq(port->dev, port->irq, sprd_handle_irq, + IRQF_SHARED, sp->name, port); + if (ret) { + dev_err(port->dev, "fail to request serial irq %d, ret=%d\n", + port->irq, ret); + return ret; + } + fc = serial_in(port, SPRD_CTL1); + fc |= RX_TOUT_THLD_DEF | RX_HFC_THLD_DEF; + serial_out(port, SPRD_CTL1, fc); + + /* enable interrupt */ + spin_lock_irqsave(&port->lock, flags); + ien = serial_in(port, SPRD_IEN); + ien |= SPRD_IEN_RX_FULL | SPRD_IEN_BREAK_DETECT | SPRD_IEN_TIMEOUT; + serial_out(port, SPRD_IEN, ien); + spin_unlock_irqrestore(&port->lock, flags); + + return 0; +} + +static void sprd_shutdown(struct uart_port *port) +{ + serial_out(port, SPRD_IEN, 0); + serial_out(port, SPRD_ICLR, ~0); + devm_free_irq(port->dev, port->irq, port); +} + +static void sprd_set_termios(struct uart_port *port, + struct ktermios *termios, + struct ktermios *old) +{ + unsigned int baud, quot; + unsigned int lcr = 0, fc; + unsigned long flags; + + /* ask the core to calculate the divisor for us */ + baud = uart_get_baud_rate(port, termios, old, 0, SPRD_BAUD_IO_LIMIT); + + quot = (unsigned int)((port->uartclk + baud / 2) / baud); + + /* set data length */ + switch (termios->c_cflag & CSIZE) { + case CS5: + lcr |= SPRD_LCR_DATA_LEN5; + break; + case CS6: + lcr |= SPRD_LCR_DATA_LEN6; + break; + case CS7: + lcr |= SPRD_LCR_DATA_LEN7; + break; + case CS8: + default: + lcr |= SPRD_LCR_DATA_LEN8; + break; + } + + /* calculate stop bits */ + lcr &= ~(SPRD_LCR_STOP_1BIT | SPRD_LCR_STOP_2BIT); + if (termios->c_cflag & CSTOPB) + lcr |= SPRD_LCR_STOP_2BIT; + else + lcr |= SPRD_LCR_STOP_1BIT; + + /* calculate parity */ + lcr &= ~SPRD_LCR_PARITY; + termios->c_cflag &= ~CMSPAR; /* no support mark/space */ + if (termios->c_cflag & PARENB) { + lcr |= SPRD_LCR_PARITY_EN; + if (termios->c_cflag & PARODD) + lcr |= SPRD_LCR_ODD_PAR; + else + lcr |= SPRD_LCR_EVEN_PAR; + } + + spin_lock_irqsave(&port->lock, flags); + + /* update the per-port timeout */ + uart_update_timeout(port, termios->c_cflag, baud); + + port->read_status_mask = SPRD_LSR_OE; + if (termios->c_iflag & INPCK) + port->read_status_mask |= SPRD_LSR_FE | SPRD_LSR_PE; + if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK)) + port->read_status_mask |= SPRD_LSR_BI; + + /* characters to ignore */ + port->ignore_status_mask = 0; + if (termios->c_iflag & IGNPAR) + port->ignore_status_mask |= SPRD_LSR_PE | SPRD_LSR_FE; + if (termios->c_iflag & IGNBRK) { + port->ignore_status_mask |= SPRD_LSR_BI; + /* + * If we're ignoring parity and break indicators, + * ignore overruns too (for real raw support). + */ + if (termios->c_iflag & IGNPAR) + port->ignore_status_mask |= SPRD_LSR_OE; + } + + /* flow control */ + fc = serial_in(port, SPRD_CTL1); + fc &= ~(RX_HW_FLOW_CTL_THLD | RX_HW_FLOW_CTL_EN | TX_HW_FLOW_CTL_EN); + if (termios->c_cflag & CRTSCTS) { + fc |= RX_HW_FLOW_CTL_THLD; + fc |= RX_HW_FLOW_CTL_EN; + fc |= TX_HW_FLOW_CTL_EN; + } + + /* clock divider bit0~bit15 */ + serial_out(port, SPRD_CLKD0, quot & 0xffff); + + /* clock divider bit16~bit20 */ + serial_out(port, SPRD_CLKD1, (quot & 0x1f0000) >> 16); + serial_out(port, SPRD_LCR, lcr); + fc |= RX_TOUT_THLD_DEF | RX_HFC_THLD_DEF; + serial_out(port, SPRD_CTL1, fc); + + spin_unlock_irqrestore(&port->lock, flags); + + /* Don't rewrite B0 */ + if (tty_termios_baud_rate(termios)) + tty_termios_encode_baud_rate(termios, baud, baud); +} + +static const char *sprd_type(struct uart_port *port) +{ + return "SPX"; +} + +static void sprd_release_port(struct uart_port *port) +{ + /* nothing to do */ +} + +static int sprd_request_port(struct uart_port *port) +{ + return 0; +} + +static void sprd_config_port(struct uart_port *port, int flags) +{ + if (flags & UART_CONFIG_TYPE) + port->type = PORT_SPRD; +} + +static int sprd_verify_port(struct uart_port *port, + struct serial_struct *ser) +{ + if (ser->type != PORT_SPRD) + return -EINVAL; + if (port->irq != ser->irq) + return -EINVAL; + return 0; +} + +static struct uart_ops serial_sprd_ops = { + .tx_empty = sprd_tx_empty, + .get_mctrl = sprd_get_mctrl, + .set_mctrl = sprd_set_mctrl, + .stop_tx = sprd_stop_tx, + .start_tx = sprd_start_tx, + .stop_rx = sprd_stop_rx, + .break_ctl = sprd_break_ctl, + .startup = sprd_startup, + .shutdown = sprd_shutdown, + .set_termios = sprd_set_termios, + .type = sprd_type, + .release_port = sprd_release_port, + .request_port = sprd_request_port, + .config_port = sprd_config_port, + .verify_port = sprd_verify_port, +}; + +#ifdef CONFIG_SERIAL_SPRD_CONSOLE +static inline void wait_for_xmitr(struct uart_port *port) +{ + unsigned int status, tmout = 10000; + + /* wait up to 10ms for the character(s) to be sent */ + do { + status = serial_in(port, SPRD_STS1); + if (--tmout == 0) + break; + udelay(1); + } while (status & 0xff00); +} + +static void sprd_console_putchar(struct uart_port *port, int ch) +{ + wait_for_xmitr(port); + serial_out(port, SPRD_TXD, ch); +} + +static void sprd_console_write(struct console *co, const char *s, + unsigned int count) +{ + struct uart_port *port = &sprd_port[co->index]->port; + int locked = 1; + unsigned long flags; + + if (port->sysrq) + locked = 0; + else if (oops_in_progress) + locked = spin_trylock_irqsave(&port->lock, flags); + else + spin_lock_irqsave(&port->lock, flags); + + uart_console_write(port, s, count, sprd_console_putchar); + + /* wait for transmitter to become empty */ + wait_for_xmitr(port); + + if (locked) + spin_unlock_irqrestore(&port->lock, flags); +} + +static int __init sprd_console_setup(struct console *co, char *options) +{ + struct uart_port *port; + int baud = 115200; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + if (co->index >= UART_NR_MAX || co->index < 0) + co->index = 0; + + port = &sprd_port[co->index]->port; + if (port == NULL) { + pr_info("serial port %d not yet initialized\n", co->index); + return -ENODEV; + } + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + return uart_set_options(port, co, baud, parity, bits, flow); +} + +static struct uart_driver sprd_uart_driver; +static struct console sprd_console = { + .name = SPRD_TTY_NAME, + .write = sprd_console_write, + .device = uart_console_device, + .setup = sprd_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &sprd_uart_driver, +}; + +#define SPRD_CONSOLE (&sprd_console) + +/* Support for earlycon */ +static void sprd_putc(struct uart_port *port, int c) +{ + unsigned int timeout = SPRD_TIMEOUT; + + while (timeout-- && + !(readl(port->membase + SPRD_LSR) & SPRD_LSR_TX_OVER)) + cpu_relax(); + + writeb(c, port->membase + SPRD_TXD); +} + +static void sprd_early_write(struct console *con, const char *s, + unsigned n) +{ + struct earlycon_device *dev = con->data; + + uart_console_write(&dev->port, s, n, sprd_putc); +} + +static int __init sprd_early_console_setup( + struct earlycon_device *device, + const char *opt) +{ + if (!device->port.membase) + return -ENODEV; + + device->con->write = sprd_early_write; + return 0; +} + +EARLYCON_DECLARE(sprd_serial, sprd_early_console_setup); +OF_EARLYCON_DECLARE(sprd_serial, "sprd,sc9836-uart", + sprd_early_console_setup); + +#else /* !CONFIG_SERIAL_SPRD_CONSOLE */ +#define SPRD_CONSOLE NULL +#endif + +static struct uart_driver sprd_uart_driver = { + .owner = THIS_MODULE, + .driver_name = "sprd_serial", + .dev_name = SPRD_TTY_NAME, + .major = 0, + .minor = 0, + .nr = UART_NR_MAX, + .cons = SPRD_CONSOLE, +}; + +static int sprd_probe_dt_alias(int index, struct device *dev) +{ + struct device_node *np; + int ret = index; + + if (!IS_ENABLED(CONFIG_OF)) + return ret; + + np = dev->of_node; + if (!np) + return ret; + + ret = of_alias_get_id(np, "serial"); + if (IS_ERR_VALUE(ret)) + ret = index; + else if (ret >= ARRAY_SIZE(sprd_port) || sprd_port[ret] != NULL) { + dev_warn(dev, "requested serial port %d not available.\n", ret); + ret = index; + } + + return ret; +} + +static int sprd_remove(struct platform_device *dev) +{ + struct sprd_uart_port *sup = platform_get_drvdata(dev); + + if (sup) { + uart_remove_one_port(&sprd_uart_driver, &sup->port); + sprd_port[sup->port.line] = NULL; + sprd_ports_num--; + } + + if (!sprd_ports_num) + uart_unregister_driver(&sprd_uart_driver); + + return 0; +} + +static int sprd_probe(struct platform_device *pdev) +{ + struct resource *res; + struct uart_port *up; + struct clk *clk; + int irq; + int index; + int ret; + + for (index = 0; index < ARRAY_SIZE(sprd_port); index++) + if (sprd_port[index] == NULL) + break; + + if (index == ARRAY_SIZE(sprd_port)) + return -EBUSY; + + index = sprd_probe_dt_alias(index, &pdev->dev); + + sprd_port[index] = devm_kzalloc(&pdev->dev, + sizeof(*sprd_port[index]), GFP_KERNEL); + if (!sprd_port[index]) + return -ENOMEM; + + up = &sprd_port[index]->port; + up->dev = &pdev->dev; + up->line = index; + up->type = PORT_SPRD; + up->iotype = SERIAL_IO_PORT; + up->uartclk = SPRD_DEF_RATE; + up->fifosize = SPRD_FIFO_SIZE; + up->ops = &serial_sprd_ops; + up->flags = UPF_BOOT_AUTOCONF; + + clk = devm_clk_get(&pdev->dev, NULL); + if (!IS_ERR(clk)) + up->uartclk = clk_get_rate(clk); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "not provide mem resource\n"); + return -ENODEV; + } + up->mapbase = res->start; + up->membase = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(up->membase)) + return PTR_ERR(up->membase); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "not provide irq resource\n"); + return -ENODEV; + } + up->irq = irq; + + if (!sprd_ports_num) { + ret = uart_register_driver(&sprd_uart_driver); + if (ret < 0) { + pr_err("Failed to register SPRD-UART driver\n"); + return ret; + } + } + sprd_ports_num++; + + ret = uart_add_one_port(&sprd_uart_driver, up); + if (ret) { + sprd_port[index] = NULL; + sprd_remove(pdev); + } + + platform_set_drvdata(pdev, up); + + return ret; +} + +static int sprd_suspend(struct device *dev) +{ + struct sprd_uart_port *sup = dev_get_drvdata(dev); + + uart_suspend_port(&sprd_uart_driver, &sup->port); + + return 0; +} + +static int sprd_resume(struct device *dev) +{ + struct sprd_uart_port *sup = dev_get_drvdata(dev); + + uart_resume_port(&sprd_uart_driver, &sup->port); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(sprd_pm_ops, sprd_suspend, sprd_resume); + +static const struct of_device_id serial_ids[] = { + {.compatible = "sprd,sc9836-uart",}, + {} +}; + +static struct platform_driver sprd_platform_driver = { + .probe = sprd_probe, + .remove = sprd_remove, + .driver = { + .name = "sprd_serial", + .of_match_table = of_match_ptr(serial_ids), + .pm = &sprd_pm_ops, + }, +}; + +module_platform_driver(sprd_platform_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Spreadtrum SoC serial driver series"); diff --git a/include/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h index ee628c489e47..55da91e763bb 100644 --- a/include/uapi/linux/serial_core.h +++ b/include/uapi/linux/serial_core.h @@ -252,4 +252,7 @@ /* Conexant Digicolor */ #define PORT_DIGICOLOR 110 +/* SPRD SERIAL */ +#define PORT_SPRD 111 + #endif /* _UAPILINUX_SERIAL_CORE_H */ -- cgit v1.2.3 From 692132b5b1c5ce97076915d4aed0c61513e18b03 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Mon, 2 Feb 2015 23:19:20 +0100 Subject: serial: driver for ETRAX FS UART This is the last missing piece to get a kernel booting to a prompt in qemu-cris. Signed-off-by: Niklas Cassel Acked-by: Jesper Nilsson Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/Kconfig | 10 + drivers/tty/serial/Makefile | 1 + drivers/tty/serial/etraxfs-uart.c | 996 ++++++++++++++++++++++++++++++++++++++ include/uapi/linux/serial_core.h | 3 + 4 files changed, 1010 insertions(+) create mode 100644 drivers/tty/serial/etraxfs-uart.c (limited to 'drivers/tty/serial/Makefile') diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index ddcc0a4c659c..5d916c7a216b 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -1085,6 +1085,16 @@ config SERIAL_VT8500_CONSOLE depends on SERIAL_VT8500=y select SERIAL_CORE_CONSOLE +config SERIAL_ETRAXFS + bool "ETRAX FS serial port support" + depends on ETRAX_ARCH_V32 && OF + select SERIAL_CORE + +config SERIAL_ETRAXFS_CONSOLE + bool "ETRAX FS serial console support" + depends on SERIAL_ETRAXFS + select SERIAL_CORE_CONSOLE + config SERIAL_NETX tristate "NetX serial port support" depends on ARCH_NETX diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index 431879003ccd..599be4b05a26 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -51,6 +51,7 @@ obj-$(CONFIG_SERIAL_MPSC) += mpsc.o obj-$(CONFIG_SERIAL_MESON) += meson_uart.o obj-$(CONFIG_SERIAL_SB1250_DUART) += sb1250-duart.o obj-$(CONFIG_ETRAX_SERIAL) += crisv10.o +obj-$(CONFIG_SERIAL_ETRAXFS) += etraxfs-uart.o obj-$(CONFIG_SERIAL_SCCNXP) += sccnxp.o obj-$(CONFIG_SERIAL_SC16IS7XX) += sc16is7xx.o obj-$(CONFIG_SERIAL_JSM) += jsm/ diff --git a/drivers/tty/serial/etraxfs-uart.c b/drivers/tty/serial/etraxfs-uart.c new file mode 100644 index 000000000000..a57301a6fe42 --- /dev/null +++ b/drivers/tty/serial/etraxfs-uart.c @@ -0,0 +1,996 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "etraxfs-uart" +#define UART_NR CONFIG_ETRAX_SERIAL_PORTS + +#define MODIFY_REG(instance, reg, var) \ + do { \ + if (REG_RD_INT(ser, instance, reg) != \ + REG_TYPE_CONV(int, reg_ser_##reg, var)) \ + REG_WR(ser, instance, reg, var); \ + } while (0) + +struct uart_cris_port { + struct uart_port port; + + int initialized; + int irq; + + void __iomem *regi_ser; + + struct gpio_desc *dtr_pin; + struct gpio_desc *dsr_pin; + struct gpio_desc *ri_pin; + struct gpio_desc *cd_pin; + + int write_ongoing; +}; + +static struct uart_driver etraxfs_uart_driver; +static struct uart_port *console_port; +static int console_baud = 115200; +static struct uart_cris_port *etraxfs_uart_ports[UART_NR]; + +static void cris_serial_port_init(struct uart_port *port, int line); +static void etraxfs_uart_stop_rx(struct uart_port *port); +static inline void etraxfs_uart_start_tx_bottom(struct uart_port *port); + +#ifdef CONFIG_SERIAL_ETRAXFS_CONSOLE +static void +cris_console_write(struct console *co, const char *s, unsigned int count) +{ + struct uart_cris_port *up; + int i; + reg_ser_r_stat_din stat; + reg_ser_rw_tr_dma_en tr_dma_en, old; + + up = etraxfs_uart_ports[co->index]; + + if (!up) + return; + + /* Switch to manual mode. */ + tr_dma_en = old = REG_RD(ser, up->regi_ser, rw_tr_dma_en); + if (tr_dma_en.en == regk_ser_yes) { + tr_dma_en.en = regk_ser_no; + REG_WR(ser, up->regi_ser, rw_tr_dma_en, tr_dma_en); + } + + /* Send data. */ + for (i = 0; i < count; i++) { + /* LF -> CRLF */ + if (s[i] == '\n') { + do { + stat = REG_RD(ser, up->regi_ser, r_stat_din); + } while (!stat.tr_rdy); + REG_WR_INT(ser, up->regi_ser, rw_dout, '\r'); + } + /* Wait until transmitter is ready and send. */ + do { + stat = REG_RD(ser, up->regi_ser, r_stat_din); + } while (!stat.tr_rdy); + REG_WR_INT(ser, up->regi_ser, rw_dout, s[i]); + } + + /* Restore mode. */ + if (tr_dma_en.en != old.en) + REG_WR(ser, up->regi_ser, rw_tr_dma_en, old); +} + +static int __init +cris_console_setup(struct console *co, char *options) +{ + struct uart_port *port; + int baud = 115200; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + if (co->index < 0 || co->index >= UART_NR) + co->index = 0; + port = &etraxfs_uart_ports[co->index]->port; + console_port = port; + + co->flags |= CON_CONSDEV; + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + console_baud = baud; + cris_serial_port_init(port, co->index); + uart_set_options(port, co, baud, parity, bits, flow); + + return 0; +} + +static struct tty_driver *cris_console_device(struct console *co, int *index) +{ + struct uart_driver *p = co->data; + *index = co->index; + return p->tty_driver; +} + +static struct console cris_console = { + .name = "ttyS", + .write = cris_console_write, + .device = cris_console_device, + .setup = cris_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &etraxfs_uart_driver, +}; +#endif /* CONFIG_SERIAL_ETRAXFS_CONSOLE */ + +static struct uart_driver etraxfs_uart_driver = { + .owner = THIS_MODULE, + .driver_name = "serial", + .dev_name = "ttyS", + .major = TTY_MAJOR, + .minor = 64, + .nr = UART_NR, +#ifdef CONFIG_SERIAL_ETRAXFS_CONSOLE + .cons = &cris_console, +#endif /* CONFIG_SERIAL_ETRAXFS_CONSOLE */ +}; + +static inline int crisv32_serial_get_rts(struct uart_cris_port *up) +{ + void __iomem *regi_ser = up->regi_ser; + /* + * Return what the user has controlled rts to or + * what the pin is? (if auto_rts is used it differs during tx) + */ + reg_ser_r_stat_din rstat = REG_RD(ser, regi_ser, r_stat_din); + + return !(rstat.rts_n == regk_ser_active); +} + +/* + * A set = 0 means 3.3V on the pin, bitvalue: 0=active, 1=inactive + * 0=0V , 1=3.3V + */ +static inline void crisv32_serial_set_rts(struct uart_cris_port *up, + int set, int force) +{ + void __iomem *regi_ser = up->regi_ser; + + unsigned long flags; + reg_ser_rw_rec_ctrl rec_ctrl; + + local_irq_save(flags); + rec_ctrl = REG_RD(ser, regi_ser, rw_rec_ctrl); + + if (set) + rec_ctrl.rts_n = regk_ser_active; + else + rec_ctrl.rts_n = regk_ser_inactive; + REG_WR(ser, regi_ser, rw_rec_ctrl, rec_ctrl); + local_irq_restore(flags); +} + +static inline int crisv32_serial_get_cts(struct uart_cris_port *up) +{ + void __iomem *regi_ser = up->regi_ser; + reg_ser_r_stat_din rstat = REG_RD(ser, regi_ser, r_stat_din); + + return (rstat.cts_n == regk_ser_active); +} + +/* + * Send a single character for XON/XOFF purposes. We do it in this separate + * function instead of the alternative support port.x_char, in the ...start_tx + * function, so we don't mix up this case with possibly enabling transmission + * of queued-up data (in case that's disabled after *receiving* an XOFF or + * negative CTS). This function is used for both DMA and non-DMA case; see HW + * docs specifically blessing sending characters manually when DMA for + * transmission is enabled and running. We may be asked to transmit despite + * the transmitter being disabled by a ..._stop_tx call so we need to enable + * it temporarily but restore the state afterwards. + */ +static void etraxfs_uart_send_xchar(struct uart_port *port, char ch) +{ + struct uart_cris_port *up = (struct uart_cris_port *)port; + reg_ser_rw_dout dout = { .data = ch }; + reg_ser_rw_ack_intr ack_intr = { .tr_rdy = regk_ser_yes }; + reg_ser_r_stat_din rstat; + reg_ser_rw_tr_ctrl prev_tr_ctrl, tr_ctrl; + void __iomem *regi_ser = up->regi_ser; + unsigned long flags; + + /* + * Wait for tr_rdy in case a character is already being output. Make + * sure we have integrity between the register reads and the writes + * below, but don't busy-wait with interrupts off and the port lock + * taken. + */ + spin_lock_irqsave(&port->lock, flags); + do { + spin_unlock_irqrestore(&port->lock, flags); + spin_lock_irqsave(&port->lock, flags); + prev_tr_ctrl = tr_ctrl = REG_RD(ser, regi_ser, rw_tr_ctrl); + rstat = REG_RD(ser, regi_ser, r_stat_din); + } while (!rstat.tr_rdy); + + /* + * Ack an interrupt if one was just issued for the previous character + * that was output. This is required for non-DMA as the interrupt is + * used as the only indicator that the transmitter is ready and it + * isn't while this x_char is being transmitted. + */ + REG_WR(ser, regi_ser, rw_ack_intr, ack_intr); + + /* Enable the transmitter in case it was disabled. */ + tr_ctrl.stop = 0; + REG_WR(ser, regi_ser, rw_tr_ctrl, tr_ctrl); + + /* + * Finally, send the blessed character; nothing should stop it now, + * except for an xoff-detected state, which we'll handle below. + */ + REG_WR(ser, regi_ser, rw_dout, dout); + up->port.icount.tx++; + + /* There might be an xoff state to clear. */ + rstat = REG_RD(ser, up->regi_ser, r_stat_din); + + /* + * Clear any xoff state that *may* have been there to + * inhibit transmission of the character. + */ + if (rstat.xoff_detect) { + reg_ser_rw_xoff_clr xoff_clr = { .clr = 1 }; + reg_ser_rw_tr_dma_en tr_dma_en; + + REG_WR(ser, regi_ser, rw_xoff_clr, xoff_clr); + tr_dma_en = REG_RD(ser, regi_ser, rw_tr_dma_en); + + /* + * If we had an xoff state but cleared it, instead sneak in a + * disabled state for the transmitter, after the character we + * sent. Thus we keep the port disabled, just as if the xoff + * state was still in effect (or actually, as if stop_tx had + * been called, as we stop DMA too). + */ + prev_tr_ctrl.stop = 1; + + tr_dma_en.en = 0; + REG_WR(ser, regi_ser, rw_tr_dma_en, tr_dma_en); + } + + /* Restore "previous" enabled/disabled state of the transmitter. */ + REG_WR(ser, regi_ser, rw_tr_ctrl, prev_tr_ctrl); + + spin_unlock_irqrestore(&port->lock, flags); +} + +/* + * Do not spin_lock_irqsave or disable interrupts by other means here; it's + * already done by the caller. + */ +static void etraxfs_uart_start_tx(struct uart_port *port) +{ + struct uart_cris_port *up = (struct uart_cris_port *)port; + + /* we have already done below if a write is ongoing */ + if (up->write_ongoing) + return; + + /* Signal that write is ongoing */ + up->write_ongoing = 1; + + etraxfs_uart_start_tx_bottom(port); +} + +static inline void etraxfs_uart_start_tx_bottom(struct uart_port *port) +{ + struct uart_cris_port *up = (struct uart_cris_port *)port; + void __iomem *regi_ser = up->regi_ser; + reg_ser_rw_tr_ctrl tr_ctrl; + reg_ser_rw_intr_mask intr_mask; + + tr_ctrl = REG_RD(ser, regi_ser, rw_tr_ctrl); + tr_ctrl.stop = regk_ser_no; + REG_WR(ser, regi_ser, rw_tr_ctrl, tr_ctrl); + intr_mask = REG_RD(ser, regi_ser, rw_intr_mask); + intr_mask.tr_rdy = regk_ser_yes; + REG_WR(ser, regi_ser, rw_intr_mask, intr_mask); +} + +/* + * This function handles both the DMA and non-DMA case by ordering the + * transmitter to stop of after the current character. We don't need to wait + * for any such character to be completely transmitted; we do that where it + * matters, like in etraxfs_uart_set_termios. Don't busy-wait here; see + * Documentation/serial/driver: this function is called within + * spin_lock_irq{,save} and thus separate ones would be disastrous (when SMP). + * There's no documented need to set the txd pin to any particular value; + * break setting is controlled solely by etraxfs_uart_break_ctl. + */ +static void etraxfs_uart_stop_tx(struct uart_port *port) +{ + struct uart_cris_port *up = (struct uart_cris_port *)port; + void __iomem *regi_ser = up->regi_ser; + reg_ser_rw_tr_ctrl tr_ctrl; + reg_ser_rw_intr_mask intr_mask; + reg_ser_rw_tr_dma_en tr_dma_en = {0}; + reg_ser_rw_xoff_clr xoff_clr = {0}; + + /* + * For the non-DMA case, we'd get a tr_rdy interrupt that we're not + * interested in as we're not transmitting any characters. For the + * DMA case, that interrupt is already turned off, but no reason to + * waste code on conditionals here. + */ + intr_mask = REG_RD(ser, regi_ser, rw_intr_mask); + intr_mask.tr_rdy = regk_ser_no; + REG_WR(ser, regi_ser, rw_intr_mask, intr_mask); + + tr_ctrl = REG_RD(ser, regi_ser, rw_tr_ctrl); + tr_ctrl.stop = 1; + REG_WR(ser, regi_ser, rw_tr_ctrl, tr_ctrl); + + /* + * Always clear possible hardware xoff-detected state here, no need to + * unnecessary consider mctrl settings and when they change. We clear + * it here rather than in start_tx: both functions are called as the + * effect of XOFF processing, but start_tx is also called when upper + * levels tell the driver that there are more characters to send, so + * avoid adding code there. + */ + xoff_clr.clr = 1; + REG_WR(ser, regi_ser, rw_xoff_clr, xoff_clr); + + /* + * Disable transmitter DMA, so that if we're in XON/XOFF, we can send + * those single characters without also giving go-ahead for queued up + * DMA data. + */ + tr_dma_en.en = 0; + REG_WR(ser, regi_ser, rw_tr_dma_en, tr_dma_en); + + /* + * Make sure that write_ongoing is reset when stopping tx. + */ + up->write_ongoing = 0; +} + +static void etraxfs_uart_stop_rx(struct uart_port *port) +{ + struct uart_cris_port *up = (struct uart_cris_port *)port; + void __iomem *regi_ser = up->regi_ser; + reg_ser_rw_rec_ctrl rec_ctrl = REG_RD(ser, regi_ser, rw_rec_ctrl); + + rec_ctrl.en = regk_ser_no; + REG_WR(ser, regi_ser, rw_rec_ctrl, rec_ctrl); +} + +static void etraxfs_uart_enable_ms(struct uart_port *port) +{ +} + +static void check_modem_status(struct uart_cris_port *up) +{ +} + +static unsigned int etraxfs_uart_tx_empty(struct uart_port *port) +{ + struct uart_cris_port *up = (struct uart_cris_port *)port; + unsigned long flags; + unsigned int ret; + reg_ser_r_stat_din rstat = {0}; + + spin_lock_irqsave(&up->port.lock, flags); + + rstat = REG_RD(ser, up->regi_ser, r_stat_din); + ret = rstat.tr_empty ? TIOCSER_TEMT : 0; + + spin_unlock_irqrestore(&up->port.lock, flags); + return ret; +} +static unsigned int etraxfs_uart_get_mctrl(struct uart_port *port) +{ + struct uart_cris_port *up = (struct uart_cris_port *)port; + unsigned int ret; + + ret = 0; + if (crisv32_serial_get_rts(up)) + ret |= TIOCM_RTS; + /* DTR is active low */ + if (up->dtr_pin && !gpiod_get_raw_value(up->dtr_pin)) + ret |= TIOCM_DTR; + /* CD is active low */ + if (up->cd_pin && !gpiod_get_raw_value(up->cd_pin)) + ret |= TIOCM_CD; + /* RI is active low */ + if (up->ri_pin && !gpiod_get_raw_value(up->ri_pin)) + ret |= TIOCM_RI; + /* DSR is active low */ + if (up->dsr_pin && !gpiod_get_raw_value(up->dsr_pin)) + ret |= TIOCM_DSR; + if (crisv32_serial_get_cts(up)) + ret |= TIOCM_CTS; + return ret; +} + +static void etraxfs_uart_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + struct uart_cris_port *up = (struct uart_cris_port *)port; + + crisv32_serial_set_rts(up, mctrl & TIOCM_RTS ? 1 : 0, 0); + /* DTR is active low */ + if (up->dtr_pin) + gpiod_set_raw_value(up->dtr_pin, mctrl & TIOCM_DTR ? 0 : 1); + /* RI is active low */ + if (up->ri_pin) + gpiod_set_raw_value(up->ri_pin, mctrl & TIOCM_RNG ? 0 : 1); + /* CD is active low */ + if (up->cd_pin) + gpiod_set_raw_value(up->cd_pin, mctrl & TIOCM_CD ? 0 : 1); +} + +static void etraxfs_uart_break_ctl(struct uart_port *port, int break_state) +{ + struct uart_cris_port *up = (struct uart_cris_port *)port; + unsigned long flags; + reg_ser_rw_tr_ctrl tr_ctrl; + reg_ser_rw_tr_dma_en tr_dma_en; + reg_ser_rw_intr_mask intr_mask; + + spin_lock_irqsave(&up->port.lock, flags); + tr_ctrl = REG_RD(ser, up->regi_ser, rw_tr_ctrl); + tr_dma_en = REG_RD(ser, up->regi_ser, rw_tr_dma_en); + intr_mask = REG_RD(ser, up->regi_ser, rw_intr_mask); + + if (break_state != 0) { /* Send break */ + /* + * We need to disable DMA (if used) or tr_rdy interrupts if no + * DMA. No need to make this conditional on use of DMA; + * disabling will be a no-op for the other mode. + */ + intr_mask.tr_rdy = regk_ser_no; + tr_dma_en.en = 0; + + /* + * Stop transmission and set the txd pin to 0 after the + * current character. The txd setting will take effect after + * any current transmission has completed. + */ + tr_ctrl.stop = 1; + tr_ctrl.txd = 0; + } else { + /* Re-enable the serial interrupt. */ + intr_mask.tr_rdy = regk_ser_yes; + + tr_ctrl.stop = 0; + tr_ctrl.txd = 1; + } + REG_WR(ser, up->regi_ser, rw_tr_ctrl, tr_ctrl); + REG_WR(ser, up->regi_ser, rw_tr_dma_en, tr_dma_en); + REG_WR(ser, up->regi_ser, rw_intr_mask, intr_mask); + + spin_unlock_irqrestore(&up->port.lock, flags); +} + +static void +transmit_chars_no_dma(struct uart_cris_port *up) +{ + int max_count; + struct circ_buf *xmit = &up->port.state->xmit; + + void __iomem *regi_ser = up->regi_ser; + reg_ser_r_stat_din rstat; + reg_ser_rw_ack_intr ack_intr = { .tr_rdy = regk_ser_yes }; + + if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) { + /* No more to send, so disable the interrupt. */ + reg_ser_rw_intr_mask intr_mask; + + intr_mask = REG_RD(ser, regi_ser, rw_intr_mask); + intr_mask.tr_rdy = 0; + intr_mask.tr_empty = 0; + REG_WR(ser, regi_ser, rw_intr_mask, intr_mask); + up->write_ongoing = 0; + return; + } + + /* If the serport is fast, we send up to max_count bytes before + exiting the loop. */ + max_count = 64; + do { + reg_ser_rw_dout dout = { .data = xmit->buf[xmit->tail] }; + + REG_WR(ser, regi_ser, rw_dout, dout); + REG_WR(ser, regi_ser, rw_ack_intr, ack_intr); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE-1); + up->port.icount.tx++; + if (xmit->head == xmit->tail) + break; + rstat = REG_RD(ser, regi_ser, r_stat_din); + } while ((--max_count > 0) && rstat.tr_rdy); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&up->port); +} + +static void receive_chars_no_dma(struct uart_cris_port *up) +{ + reg_ser_rs_stat_din stat_din; + reg_ser_r_stat_din rstat; + struct tty_port *port; + struct uart_icount *icount; + int max_count = 16; + char flag; + reg_ser_rw_ack_intr ack_intr = { 0 }; + + rstat = REG_RD(ser, up->regi_ser, r_stat_din); + icount = &up->port.icount; + port = &up->port.state->port; + + do { + stat_din = REG_RD(ser, up->regi_ser, rs_stat_din); + + flag = TTY_NORMAL; + ack_intr.dav = 1; + REG_WR(ser, up->regi_ser, rw_ack_intr, ack_intr); + icount->rx++; + + if (stat_din.framing_err | stat_din.par_err | stat_din.orun) { + if (stat_din.data == 0x00 && + stat_din.framing_err) { + /* Most likely a break. */ + flag = TTY_BREAK; + icount->brk++; + } else if (stat_din.par_err) { + flag = TTY_PARITY; + icount->parity++; + } else if (stat_din.orun) { + flag = TTY_OVERRUN; + icount->overrun++; + } else if (stat_din.framing_err) { + flag = TTY_FRAME; + icount->frame++; + } + } + + /* + * If this becomes important, we probably *could* handle this + * gracefully by keeping track of the unhandled character. + */ + if (!tty_insert_flip_char(port, stat_din.data, flag)) + panic("%s: No tty buffer space", __func__); + rstat = REG_RD(ser, up->regi_ser, r_stat_din); + } while (rstat.dav && (max_count-- > 0)); + spin_unlock(&up->port.lock); + tty_flip_buffer_push(port); + spin_lock(&up->port.lock); +} + +static irqreturn_t +ser_interrupt(int irq, void *dev_id) +{ + struct uart_cris_port *up = (struct uart_cris_port *)dev_id; + void __iomem *regi_ser; + int handled = 0; + + spin_lock(&up->port.lock); + + regi_ser = up->regi_ser; + + if (regi_ser) { + reg_ser_r_masked_intr masked_intr; + + masked_intr = REG_RD(ser, regi_ser, r_masked_intr); + /* + * Check what interrupts are active before taking + * actions. If DMA is used the interrupt shouldn't + * be enabled. + */ + if (masked_intr.dav) { + receive_chars_no_dma(up); + handled = 1; + } + check_modem_status(up); + + if (masked_intr.tr_rdy) { + transmit_chars_no_dma(up); + handled = 1; + } + } + spin_unlock(&up->port.lock); + return IRQ_RETVAL(handled); +} + +#ifdef CONFIG_CONSOLE_POLL +static int etraxfs_uart_get_poll_char(struct uart_port *port) +{ + reg_ser_rs_stat_din stat; + reg_ser_rw_ack_intr ack_intr = { 0 }; + struct uart_cris_port *up = (struct uart_cris_port *)port; + + do { + stat = REG_RD(ser, up->regi_ser, rs_stat_din); + } while (!stat.dav); + + /* Ack the data_avail interrupt. */ + ack_intr.dav = 1; + REG_WR(ser, up->regi_ser, rw_ack_intr, ack_intr); + + return stat.data; +} + +static void etraxfs_uart_put_poll_char(struct uart_port *port, + unsigned char c) +{ + reg_ser_r_stat_din stat; + struct uart_cris_port *up = (struct uart_cris_port *)port; + + do { + stat = REG_RD(ser, up->regi_ser, r_stat_din); + } while (!stat.tr_rdy); + REG_WR_INT(ser, up->regi_ser, rw_dout, c); +} +#endif /* CONFIG_CONSOLE_POLL */ + +static int etraxfs_uart_startup(struct uart_port *port) +{ + struct uart_cris_port *up = (struct uart_cris_port *)port; + unsigned long flags; + reg_ser_rw_intr_mask ser_intr_mask = {0}; + + ser_intr_mask.dav = regk_ser_yes; + + if (request_irq(etraxfs_uart_ports[port->line]->irq, ser_interrupt, + 0, DRV_NAME, etraxfs_uart_ports[port->line])) + panic("irq ser%d", port->line); + + spin_lock_irqsave(&up->port.lock, flags); + + REG_WR(ser, up->regi_ser, rw_intr_mask, ser_intr_mask); + + etraxfs_uart_set_mctrl(&up->port, up->port.mctrl); + + spin_unlock_irqrestore(&up->port.lock, flags); + + return 0; +} + +static void etraxfs_uart_shutdown(struct uart_port *port) +{ + struct uart_cris_port *up = (struct uart_cris_port *)port; + unsigned long flags; + + spin_lock_irqsave(&up->port.lock, flags); + + etraxfs_uart_stop_tx(port); + etraxfs_uart_stop_rx(port); + + free_irq(etraxfs_uart_ports[port->line]->irq, + etraxfs_uart_ports[port->line]); + + etraxfs_uart_set_mctrl(&up->port, up->port.mctrl); + + spin_unlock_irqrestore(&up->port.lock, flags); + +} + +static void +etraxfs_uart_set_termios(struct uart_port *port, struct ktermios *termios, + struct ktermios *old) +{ + struct uart_cris_port *up = (struct uart_cris_port *)port; + unsigned long flags; + reg_ser_rw_xoff xoff; + reg_ser_rw_xoff_clr xoff_clr = {0}; + reg_ser_rw_tr_ctrl tx_ctrl = {0}; + reg_ser_rw_tr_dma_en tx_dma_en = {0}; + reg_ser_rw_rec_ctrl rx_ctrl = {0}; + reg_ser_rw_tr_baud_div tx_baud_div = {0}; + reg_ser_rw_rec_baud_div rx_baud_div = {0}; + int baud; + + if (old && + termios->c_cflag == old->c_cflag && + termios->c_iflag == old->c_iflag) + return; + + /* Tx: 8 bit, no/even parity, 1 stop bit, no cts. */ + tx_ctrl.base_freq = regk_ser_f29_493; + tx_ctrl.en = 0; + tx_ctrl.stop = 0; + tx_ctrl.auto_rts = regk_ser_no; + tx_ctrl.txd = 1; + tx_ctrl.auto_cts = 0; + /* Rx: 8 bit, no/even parity. */ + rx_ctrl.dma_err = regk_ser_stop; + rx_ctrl.sampling = regk_ser_majority; + rx_ctrl.timeout = 1; + + rx_ctrl.rts_n = regk_ser_inactive; + + /* Common for tx and rx: 8N1. */ + tx_ctrl.data_bits = regk_ser_bits8; + rx_ctrl.data_bits = regk_ser_bits8; + tx_ctrl.par = regk_ser_even; + rx_ctrl.par = regk_ser_even; + tx_ctrl.par_en = regk_ser_no; + rx_ctrl.par_en = regk_ser_no; + + tx_ctrl.stop_bits = regk_ser_bits1; + + /* + * Change baud-rate and write it to the hardware. + * + * baud_clock = base_freq / (divisor*8) + * divisor = base_freq / (baud_clock * 8) + * base_freq is either: + * off, ext, 29.493MHz, 32.000 MHz, 32.768 MHz or 100 MHz + * 20.493MHz is used for standard baudrates + */ + + /* + * For the console port we keep the original baudrate here. Not very + * beautiful. + */ + if ((port != console_port) || old) + baud = uart_get_baud_rate(port, termios, old, 0, + port->uartclk / 8); + else + baud = console_baud; + + tx_baud_div.div = 29493000 / (8 * baud); + /* Rx uses same as tx. */ + rx_baud_div.div = tx_baud_div.div; + rx_ctrl.base_freq = tx_ctrl.base_freq; + + if ((termios->c_cflag & CSIZE) == CS7) { + /* Set 7 bit mode. */ + tx_ctrl.data_bits = regk_ser_bits7; + rx_ctrl.data_bits = regk_ser_bits7; + } + + if (termios->c_cflag & CSTOPB) { + /* Set 2 stop bit mode. */ + tx_ctrl.stop_bits = regk_ser_bits2; + } + + if (termios->c_cflag & PARENB) { + /* Enable parity. */ + tx_ctrl.par_en = regk_ser_yes; + rx_ctrl.par_en = regk_ser_yes; + } + + if (termios->c_cflag & CMSPAR) { + if (termios->c_cflag & PARODD) { + /* Set mark parity if PARODD and CMSPAR. */ + tx_ctrl.par = regk_ser_mark; + rx_ctrl.par = regk_ser_mark; + } else { + tx_ctrl.par = regk_ser_space; + rx_ctrl.par = regk_ser_space; + } + } else { + if (termios->c_cflag & PARODD) { + /* Set odd parity. */ + tx_ctrl.par = regk_ser_odd; + rx_ctrl.par = regk_ser_odd; + } + } + + if (termios->c_cflag & CRTSCTS) { + /* Enable automatic CTS handling. */ + tx_ctrl.auto_cts = regk_ser_yes; + } + + /* Make sure the tx and rx are enabled. */ + tx_ctrl.en = regk_ser_yes; + rx_ctrl.en = regk_ser_yes; + + spin_lock_irqsave(&port->lock, flags); + + tx_dma_en.en = 0; + REG_WR(ser, up->regi_ser, rw_tr_dma_en, tx_dma_en); + + /* Actually write the control regs (if modified) to the hardware. */ + uart_update_timeout(port, termios->c_cflag, port->uartclk/8); + MODIFY_REG(up->regi_ser, rw_rec_baud_div, rx_baud_div); + MODIFY_REG(up->regi_ser, rw_rec_ctrl, rx_ctrl); + + MODIFY_REG(up->regi_ser, rw_tr_baud_div, tx_baud_div); + MODIFY_REG(up->regi_ser, rw_tr_ctrl, tx_ctrl); + + tx_dma_en.en = 0; + REG_WR(ser, up->regi_ser, rw_tr_dma_en, tx_dma_en); + + xoff = REG_RD(ser, up->regi_ser, rw_xoff); + + if (up->port.state && up->port.state->port.tty && + (up->port.state->port.tty->termios.c_iflag & IXON)) { + xoff.chr = STOP_CHAR(up->port.state->port.tty); + xoff.automatic = regk_ser_yes; + } else + xoff.automatic = regk_ser_no; + + MODIFY_REG(up->regi_ser, rw_xoff, xoff); + + /* + * Make sure we don't start in an automatically shut-off state due to + * a previous early exit. + */ + xoff_clr.clr = 1; + REG_WR(ser, up->regi_ser, rw_xoff_clr, xoff_clr); + + etraxfs_uart_set_mctrl(&up->port, up->port.mctrl); + spin_unlock_irqrestore(&up->port.lock, flags); +} + +static const char * +etraxfs_uart_type(struct uart_port *port) +{ + return "CRISv32"; +} + +static void etraxfs_uart_release_port(struct uart_port *port) +{ +} + +static int etraxfs_uart_request_port(struct uart_port *port) +{ + return 0; +} + +static void etraxfs_uart_config_port(struct uart_port *port, int flags) +{ + struct uart_cris_port *up = (struct uart_cris_port *)port; + + up->port.type = PORT_CRIS; +} + +static const struct uart_ops etraxfs_uart_pops = { + .tx_empty = etraxfs_uart_tx_empty, + .set_mctrl = etraxfs_uart_set_mctrl, + .get_mctrl = etraxfs_uart_get_mctrl, + .stop_tx = etraxfs_uart_stop_tx, + .start_tx = etraxfs_uart_start_tx, + .send_xchar = etraxfs_uart_send_xchar, + .stop_rx = etraxfs_uart_stop_rx, + .enable_ms = etraxfs_uart_enable_ms, + .break_ctl = etraxfs_uart_break_ctl, + .startup = etraxfs_uart_startup, + .shutdown = etraxfs_uart_shutdown, + .set_termios = etraxfs_uart_set_termios, + .type = etraxfs_uart_type, + .release_port = etraxfs_uart_release_port, + .request_port = etraxfs_uart_request_port, + .config_port = etraxfs_uart_config_port, +#ifdef CONFIG_CONSOLE_POLL + .poll_get_char = etraxfs_uart_get_poll_char, + .poll_put_char = etraxfs_uart_put_poll_char, +#endif +}; + +static void cris_serial_port_init(struct uart_port *port, int line) +{ + struct uart_cris_port *up = (struct uart_cris_port *)port; + + if (up->initialized) + return; + up->initialized = 1; + port->line = line; + spin_lock_init(&port->lock); + port->ops = &etraxfs_uart_pops; + port->irq = up->irq; + port->iobase = (unsigned long) up->regi_ser; + port->uartclk = 29493000; + + /* + * We can't fit any more than 255 here (unsigned char), though + * actually UART_XMIT_SIZE characters could be pending output. + * At time of this writing, the definition of "fifosize" is here the + * amount of characters that can be pending output after a start_tx call + * until tx_empty returns 1: see serial_core.c:uart_wait_until_sent. + * This matters for timeout calculations unfortunately, but keeping + * larger amounts at the DMA wouldn't win much so let's just play nice. + */ + port->fifosize = 255; + port->flags = UPF_BOOT_AUTOCONF; +} + +static int etraxfs_uart_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct uart_cris_port *up; + int dev_id; + + if (!np) + return -ENODEV; + + dev_id = of_alias_get_id(np, "serial"); + if (dev_id < 0) + dev_id = 0; + + if (dev_id >= UART_NR) + return -EINVAL; + + if (etraxfs_uart_ports[dev_id]) + return -EBUSY; + + up = devm_kzalloc(&pdev->dev, sizeof(struct uart_cris_port), + GFP_KERNEL); + if (!up) + return -ENOMEM; + + up->irq = irq_of_parse_and_map(np, 0); + up->regi_ser = of_iomap(np, 0); + up->dtr_pin = devm_gpiod_get_optional(&pdev->dev, "dtr"); + up->dsr_pin = devm_gpiod_get_optional(&pdev->dev, "dsr"); + up->ri_pin = devm_gpiod_get_optional(&pdev->dev, "ri"); + up->cd_pin = devm_gpiod_get_optional(&pdev->dev, "cd"); + up->port.dev = &pdev->dev; + cris_serial_port_init(&up->port, dev_id); + + etraxfs_uart_ports[dev_id] = up; + platform_set_drvdata(pdev, &up->port); + uart_add_one_port(&etraxfs_uart_driver, &up->port); + + return 0; +} + +static int etraxfs_uart_remove(struct platform_device *pdev) +{ + struct uart_port *port; + + port = platform_get_drvdata(pdev); + uart_remove_one_port(&etraxfs_uart_driver, port); + etraxfs_uart_ports[pdev->id] = NULL; + + return 0; +} + +static const struct of_device_id etraxfs_uart_dt_ids[] = { + { .compatible = "axis,etraxfs-uart" }, + { /* sentinel */ } +}; + +MODULE_DEVICE_TABLE(of, etraxfs_uart_dt_ids); + +static struct platform_driver etraxfs_uart_platform_driver = { + .driver = { + .name = DRV_NAME, + .of_match_table = of_match_ptr(etraxfs_uart_dt_ids), + }, + .probe = etraxfs_uart_probe, + .remove = etraxfs_uart_remove, +}; + +static int __init etraxfs_uart_init(void) +{ + int ret; + + ret = uart_register_driver(&etraxfs_uart_driver); + if (ret) + return ret; + + ret = platform_driver_register(&etraxfs_uart_platform_driver); + if (ret) + uart_unregister_driver(&etraxfs_uart_driver); + + return ret; +} + +static void __exit etraxfs_uart_exit(void) +{ + platform_driver_unregister(&etraxfs_uart_platform_driver); + uart_unregister_driver(&etraxfs_uart_driver); +} + +module_init(etraxfs_uart_init); +module_exit(etraxfs_uart_exit); diff --git a/include/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h index 55da91e763bb..b2122813f18a 100644 --- a/include/uapi/linux/serial_core.h +++ b/include/uapi/linux/serial_core.h @@ -255,4 +255,7 @@ /* SPRD SERIAL */ #define PORT_SPRD 111 +/* Cris v10 / v32 SoC */ +#define PORT_CRIS 112 + #endif /* _UAPILINUX_SERIAL_CORE_H */ -- cgit v1.2.3