162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * st-asc.c: ST Asynchronous serial controller (ASC) driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2003-2013 STMicroelectronics (R&D) Limited 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/module.h> 962306a36Sopenharmony_ci#include <linux/serial.h> 1062306a36Sopenharmony_ci#include <linux/console.h> 1162306a36Sopenharmony_ci#include <linux/sysrq.h> 1262306a36Sopenharmony_ci#include <linux/pinctrl/consumer.h> 1362306a36Sopenharmony_ci#include <linux/platform_device.h> 1462306a36Sopenharmony_ci#include <linux/io.h> 1562306a36Sopenharmony_ci#include <linux/irq.h> 1662306a36Sopenharmony_ci#include <linux/tty.h> 1762306a36Sopenharmony_ci#include <linux/tty_flip.h> 1862306a36Sopenharmony_ci#include <linux/delay.h> 1962306a36Sopenharmony_ci#include <linux/spinlock.h> 2062306a36Sopenharmony_ci#include <linux/of.h> 2162306a36Sopenharmony_ci#include <linux/of_platform.h> 2262306a36Sopenharmony_ci#include <linux/serial_core.h> 2362306a36Sopenharmony_ci#include <linux/clk.h> 2462306a36Sopenharmony_ci#include <linux/gpio/consumer.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define DRIVER_NAME "st-asc" 2762306a36Sopenharmony_ci#define ASC_SERIAL_NAME "ttyAS" 2862306a36Sopenharmony_ci#define ASC_FIFO_SIZE 16 2962306a36Sopenharmony_ci#define ASC_MAX_PORTS 8 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci/* Pinctrl states */ 3262306a36Sopenharmony_ci#define DEFAULT 0 3362306a36Sopenharmony_ci#define NO_HW_FLOWCTRL 1 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistruct asc_port { 3662306a36Sopenharmony_ci struct uart_port port; 3762306a36Sopenharmony_ci struct gpio_desc *rts; 3862306a36Sopenharmony_ci struct clk *clk; 3962306a36Sopenharmony_ci struct pinctrl *pinctrl; 4062306a36Sopenharmony_ci struct pinctrl_state *states[2]; 4162306a36Sopenharmony_ci unsigned int hw_flow_control:1; 4262306a36Sopenharmony_ci unsigned int force_m1:1; 4362306a36Sopenharmony_ci}; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistatic struct asc_port asc_ports[ASC_MAX_PORTS]; 4662306a36Sopenharmony_cistatic struct uart_driver asc_uart_driver; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci/*---- UART Register definitions ------------------------------*/ 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci/* Register offsets */ 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci#define ASC_BAUDRATE 0x00 5362306a36Sopenharmony_ci#define ASC_TXBUF 0x04 5462306a36Sopenharmony_ci#define ASC_RXBUF 0x08 5562306a36Sopenharmony_ci#define ASC_CTL 0x0C 5662306a36Sopenharmony_ci#define ASC_INTEN 0x10 5762306a36Sopenharmony_ci#define ASC_STA 0x14 5862306a36Sopenharmony_ci#define ASC_GUARDTIME 0x18 5962306a36Sopenharmony_ci#define ASC_TIMEOUT 0x1C 6062306a36Sopenharmony_ci#define ASC_TXRESET 0x20 6162306a36Sopenharmony_ci#define ASC_RXRESET 0x24 6262306a36Sopenharmony_ci#define ASC_RETRIES 0x28 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci/* ASC_RXBUF */ 6562306a36Sopenharmony_ci#define ASC_RXBUF_PE 0x100 6662306a36Sopenharmony_ci#define ASC_RXBUF_FE 0x200 6762306a36Sopenharmony_ci/* 6862306a36Sopenharmony_ci * Some of status comes from higher bits of the character and some come from 6962306a36Sopenharmony_ci * the status register. Combining both of them in to single status using dummy 7062306a36Sopenharmony_ci * bits. 7162306a36Sopenharmony_ci */ 7262306a36Sopenharmony_ci#define ASC_RXBUF_DUMMY_RX 0x10000 7362306a36Sopenharmony_ci#define ASC_RXBUF_DUMMY_BE 0x20000 7462306a36Sopenharmony_ci#define ASC_RXBUF_DUMMY_OE 0x40000 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci/* ASC_CTL */ 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci#define ASC_CTL_MODE_MSK 0x0007 7962306a36Sopenharmony_ci#define ASC_CTL_MODE_8BIT 0x0001 8062306a36Sopenharmony_ci#define ASC_CTL_MODE_7BIT_PAR 0x0003 8162306a36Sopenharmony_ci#define ASC_CTL_MODE_9BIT 0x0004 8262306a36Sopenharmony_ci#define ASC_CTL_MODE_8BIT_WKUP 0x0005 8362306a36Sopenharmony_ci#define ASC_CTL_MODE_8BIT_PAR 0x0007 8462306a36Sopenharmony_ci#define ASC_CTL_STOP_MSK 0x0018 8562306a36Sopenharmony_ci#define ASC_CTL_STOP_HALFBIT 0x0000 8662306a36Sopenharmony_ci#define ASC_CTL_STOP_1BIT 0x0008 8762306a36Sopenharmony_ci#define ASC_CTL_STOP_1_HALFBIT 0x0010 8862306a36Sopenharmony_ci#define ASC_CTL_STOP_2BIT 0x0018 8962306a36Sopenharmony_ci#define ASC_CTL_PARITYODD 0x0020 9062306a36Sopenharmony_ci#define ASC_CTL_LOOPBACK 0x0040 9162306a36Sopenharmony_ci#define ASC_CTL_RUN 0x0080 9262306a36Sopenharmony_ci#define ASC_CTL_RXENABLE 0x0100 9362306a36Sopenharmony_ci#define ASC_CTL_SCENABLE 0x0200 9462306a36Sopenharmony_ci#define ASC_CTL_FIFOENABLE 0x0400 9562306a36Sopenharmony_ci#define ASC_CTL_CTSENABLE 0x0800 9662306a36Sopenharmony_ci#define ASC_CTL_BAUDMODE 0x1000 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci/* ASC_GUARDTIME */ 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci#define ASC_GUARDTIME_MSK 0x00FF 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci/* ASC_INTEN */ 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci#define ASC_INTEN_RBE 0x0001 10562306a36Sopenharmony_ci#define ASC_INTEN_TE 0x0002 10662306a36Sopenharmony_ci#define ASC_INTEN_THE 0x0004 10762306a36Sopenharmony_ci#define ASC_INTEN_PE 0x0008 10862306a36Sopenharmony_ci#define ASC_INTEN_FE 0x0010 10962306a36Sopenharmony_ci#define ASC_INTEN_OE 0x0020 11062306a36Sopenharmony_ci#define ASC_INTEN_TNE 0x0040 11162306a36Sopenharmony_ci#define ASC_INTEN_TOI 0x0080 11262306a36Sopenharmony_ci#define ASC_INTEN_RHF 0x0100 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci/* ASC_RETRIES */ 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci#define ASC_RETRIES_MSK 0x00FF 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci/* ASC_RXBUF */ 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci#define ASC_RXBUF_MSK 0x03FF 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci/* ASC_STA */ 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci#define ASC_STA_RBF 0x0001 12562306a36Sopenharmony_ci#define ASC_STA_TE 0x0002 12662306a36Sopenharmony_ci#define ASC_STA_THE 0x0004 12762306a36Sopenharmony_ci#define ASC_STA_PE 0x0008 12862306a36Sopenharmony_ci#define ASC_STA_FE 0x0010 12962306a36Sopenharmony_ci#define ASC_STA_OE 0x0020 13062306a36Sopenharmony_ci#define ASC_STA_TNE 0x0040 13162306a36Sopenharmony_ci#define ASC_STA_TOI 0x0080 13262306a36Sopenharmony_ci#define ASC_STA_RHF 0x0100 13362306a36Sopenharmony_ci#define ASC_STA_TF 0x0200 13462306a36Sopenharmony_ci#define ASC_STA_NKD 0x0400 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci/* ASC_TIMEOUT */ 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci#define ASC_TIMEOUT_MSK 0x00FF 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci/* ASC_TXBUF */ 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci#define ASC_TXBUF_MSK 0x01FF 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci/*---- Inline function definitions ---------------------------*/ 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_cistatic inline struct asc_port *to_asc_port(struct uart_port *port) 14762306a36Sopenharmony_ci{ 14862306a36Sopenharmony_ci return container_of(port, struct asc_port, port); 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_cistatic inline u32 asc_in(struct uart_port *port, u32 offset) 15262306a36Sopenharmony_ci{ 15362306a36Sopenharmony_ci#ifdef readl_relaxed 15462306a36Sopenharmony_ci return readl_relaxed(port->membase + offset); 15562306a36Sopenharmony_ci#else 15662306a36Sopenharmony_ci return readl(port->membase + offset); 15762306a36Sopenharmony_ci#endif 15862306a36Sopenharmony_ci} 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_cistatic inline void asc_out(struct uart_port *port, u32 offset, u32 value) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci#ifdef writel_relaxed 16362306a36Sopenharmony_ci writel_relaxed(value, port->membase + offset); 16462306a36Sopenharmony_ci#else 16562306a36Sopenharmony_ci writel(value, port->membase + offset); 16662306a36Sopenharmony_ci#endif 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci/* 17062306a36Sopenharmony_ci * Some simple utility functions to enable and disable interrupts. 17162306a36Sopenharmony_ci * Note that these need to be called with interrupts disabled. 17262306a36Sopenharmony_ci */ 17362306a36Sopenharmony_cistatic inline void asc_disable_tx_interrupts(struct uart_port *port) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci u32 intenable = asc_in(port, ASC_INTEN) & ~ASC_INTEN_THE; 17662306a36Sopenharmony_ci asc_out(port, ASC_INTEN, intenable); 17762306a36Sopenharmony_ci (void)asc_in(port, ASC_INTEN); /* Defeat bus write posting */ 17862306a36Sopenharmony_ci} 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_cistatic inline void asc_enable_tx_interrupts(struct uart_port *port) 18162306a36Sopenharmony_ci{ 18262306a36Sopenharmony_ci u32 intenable = asc_in(port, ASC_INTEN) | ASC_INTEN_THE; 18362306a36Sopenharmony_ci asc_out(port, ASC_INTEN, intenable); 18462306a36Sopenharmony_ci} 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_cistatic inline void asc_disable_rx_interrupts(struct uart_port *port) 18762306a36Sopenharmony_ci{ 18862306a36Sopenharmony_ci u32 intenable = asc_in(port, ASC_INTEN) & ~ASC_INTEN_RBE; 18962306a36Sopenharmony_ci asc_out(port, ASC_INTEN, intenable); 19062306a36Sopenharmony_ci (void)asc_in(port, ASC_INTEN); /* Defeat bus write posting */ 19162306a36Sopenharmony_ci} 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_cistatic inline void asc_enable_rx_interrupts(struct uart_port *port) 19462306a36Sopenharmony_ci{ 19562306a36Sopenharmony_ci u32 intenable = asc_in(port, ASC_INTEN) | ASC_INTEN_RBE; 19662306a36Sopenharmony_ci asc_out(port, ASC_INTEN, intenable); 19762306a36Sopenharmony_ci} 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_cistatic inline u32 asc_txfifo_is_empty(struct uart_port *port) 20062306a36Sopenharmony_ci{ 20162306a36Sopenharmony_ci return asc_in(port, ASC_STA) & ASC_STA_TE; 20262306a36Sopenharmony_ci} 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_cistatic inline u32 asc_txfifo_is_half_empty(struct uart_port *port) 20562306a36Sopenharmony_ci{ 20662306a36Sopenharmony_ci return asc_in(port, ASC_STA) & ASC_STA_THE; 20762306a36Sopenharmony_ci} 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_cistatic inline const char *asc_port_name(struct uart_port *port) 21062306a36Sopenharmony_ci{ 21162306a36Sopenharmony_ci return to_platform_device(port->dev)->name; 21262306a36Sopenharmony_ci} 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci/*----------------------------------------------------------------------*/ 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci/* 21762306a36Sopenharmony_ci * This section contains code to support the use of the ASC as a 21862306a36Sopenharmony_ci * generic serial port. 21962306a36Sopenharmony_ci */ 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_cistatic inline unsigned asc_hw_txroom(struct uart_port *port) 22262306a36Sopenharmony_ci{ 22362306a36Sopenharmony_ci u32 status = asc_in(port, ASC_STA); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci if (status & ASC_STA_THE) 22662306a36Sopenharmony_ci return port->fifosize / 2; 22762306a36Sopenharmony_ci else if (!(status & ASC_STA_TF)) 22862306a36Sopenharmony_ci return 1; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci return 0; 23162306a36Sopenharmony_ci} 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci/* 23462306a36Sopenharmony_ci * Start transmitting chars. 23562306a36Sopenharmony_ci * This is called from both interrupt and task level. 23662306a36Sopenharmony_ci * Either way interrupts are disabled. 23762306a36Sopenharmony_ci */ 23862306a36Sopenharmony_cistatic void asc_transmit_chars(struct uart_port *port) 23962306a36Sopenharmony_ci{ 24062306a36Sopenharmony_ci u8 ch; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci uart_port_tx_limited(port, ch, asc_hw_txroom(port), 24362306a36Sopenharmony_ci true, 24462306a36Sopenharmony_ci asc_out(port, ASC_TXBUF, ch), 24562306a36Sopenharmony_ci ({})); 24662306a36Sopenharmony_ci} 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_cistatic void asc_receive_chars(struct uart_port *port) 24962306a36Sopenharmony_ci{ 25062306a36Sopenharmony_ci struct tty_port *tport = &port->state->port; 25162306a36Sopenharmony_ci unsigned long status, mode; 25262306a36Sopenharmony_ci unsigned long c = 0; 25362306a36Sopenharmony_ci u8 flag; 25462306a36Sopenharmony_ci bool ignore_pe = false; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci /* 25762306a36Sopenharmony_ci * Datasheet states: If the MODE field selects an 8-bit frame then 25862306a36Sopenharmony_ci * this [parity error] bit is undefined. Software should ignore this 25962306a36Sopenharmony_ci * bit when reading 8-bit frames. 26062306a36Sopenharmony_ci */ 26162306a36Sopenharmony_ci mode = asc_in(port, ASC_CTL) & ASC_CTL_MODE_MSK; 26262306a36Sopenharmony_ci if (mode == ASC_CTL_MODE_8BIT || mode == ASC_CTL_MODE_8BIT_PAR) 26362306a36Sopenharmony_ci ignore_pe = true; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci if (irqd_is_wakeup_set(irq_get_irq_data(port->irq))) 26662306a36Sopenharmony_ci pm_wakeup_event(tport->tty->dev, 0); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci while ((status = asc_in(port, ASC_STA)) & ASC_STA_RBF) { 26962306a36Sopenharmony_ci c = asc_in(port, ASC_RXBUF) | ASC_RXBUF_DUMMY_RX; 27062306a36Sopenharmony_ci flag = TTY_NORMAL; 27162306a36Sopenharmony_ci port->icount.rx++; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci if (status & ASC_STA_OE || c & ASC_RXBUF_FE || 27462306a36Sopenharmony_ci (c & ASC_RXBUF_PE && !ignore_pe)) { 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci if (c & ASC_RXBUF_FE) { 27762306a36Sopenharmony_ci if (c == (ASC_RXBUF_FE | ASC_RXBUF_DUMMY_RX)) { 27862306a36Sopenharmony_ci port->icount.brk++; 27962306a36Sopenharmony_ci if (uart_handle_break(port)) 28062306a36Sopenharmony_ci continue; 28162306a36Sopenharmony_ci c |= ASC_RXBUF_DUMMY_BE; 28262306a36Sopenharmony_ci } else { 28362306a36Sopenharmony_ci port->icount.frame++; 28462306a36Sopenharmony_ci } 28562306a36Sopenharmony_ci } else if (c & ASC_RXBUF_PE) { 28662306a36Sopenharmony_ci port->icount.parity++; 28762306a36Sopenharmony_ci } 28862306a36Sopenharmony_ci /* 28962306a36Sopenharmony_ci * Reading any data from the RX FIFO clears the 29062306a36Sopenharmony_ci * overflow error condition. 29162306a36Sopenharmony_ci */ 29262306a36Sopenharmony_ci if (status & ASC_STA_OE) { 29362306a36Sopenharmony_ci port->icount.overrun++; 29462306a36Sopenharmony_ci c |= ASC_RXBUF_DUMMY_OE; 29562306a36Sopenharmony_ci } 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci c &= port->read_status_mask; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci if (c & ASC_RXBUF_DUMMY_BE) 30062306a36Sopenharmony_ci flag = TTY_BREAK; 30162306a36Sopenharmony_ci else if (c & ASC_RXBUF_PE) 30262306a36Sopenharmony_ci flag = TTY_PARITY; 30362306a36Sopenharmony_ci else if (c & ASC_RXBUF_FE) 30462306a36Sopenharmony_ci flag = TTY_FRAME; 30562306a36Sopenharmony_ci } 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci if (uart_handle_sysrq_char(port, c & 0xff)) 30862306a36Sopenharmony_ci continue; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci uart_insert_char(port, c, ASC_RXBUF_DUMMY_OE, c & 0xff, flag); 31162306a36Sopenharmony_ci } 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci /* Tell the rest of the system the news. New characters! */ 31462306a36Sopenharmony_ci tty_flip_buffer_push(tport); 31562306a36Sopenharmony_ci} 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_cistatic irqreturn_t asc_interrupt(int irq, void *ptr) 31862306a36Sopenharmony_ci{ 31962306a36Sopenharmony_ci struct uart_port *port = ptr; 32062306a36Sopenharmony_ci u32 status; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci spin_lock(&port->lock); 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci status = asc_in(port, ASC_STA); 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci if (status & ASC_STA_RBF) { 32762306a36Sopenharmony_ci /* Receive FIFO not empty */ 32862306a36Sopenharmony_ci asc_receive_chars(port); 32962306a36Sopenharmony_ci } 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci if ((status & ASC_STA_THE) && 33262306a36Sopenharmony_ci (asc_in(port, ASC_INTEN) & ASC_INTEN_THE)) { 33362306a36Sopenharmony_ci /* Transmitter FIFO at least half empty */ 33462306a36Sopenharmony_ci asc_transmit_chars(port); 33562306a36Sopenharmony_ci } 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci spin_unlock(&port->lock); 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci return IRQ_HANDLED; 34062306a36Sopenharmony_ci} 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci/*----------------------------------------------------------------------*/ 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci/* 34562306a36Sopenharmony_ci * UART Functions 34662306a36Sopenharmony_ci */ 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_cistatic unsigned int asc_tx_empty(struct uart_port *port) 34962306a36Sopenharmony_ci{ 35062306a36Sopenharmony_ci return asc_txfifo_is_empty(port) ? TIOCSER_TEMT : 0; 35162306a36Sopenharmony_ci} 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_cistatic void asc_set_mctrl(struct uart_port *port, unsigned int mctrl) 35462306a36Sopenharmony_ci{ 35562306a36Sopenharmony_ci struct asc_port *ascport = to_asc_port(port); 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci /* 35862306a36Sopenharmony_ci * This routine is used for seting signals of: DTR, DCD, CTS and RTS. 35962306a36Sopenharmony_ci * We use ASC's hardware for CTS/RTS when hardware flow-control is 36062306a36Sopenharmony_ci * enabled, however if the RTS line is required for another purpose, 36162306a36Sopenharmony_ci * commonly controlled using HUP from userspace, then we need to toggle 36262306a36Sopenharmony_ci * it manually, using GPIO. 36362306a36Sopenharmony_ci * 36462306a36Sopenharmony_ci * Some boards also have DTR and DCD implemented using PIO pins, code to 36562306a36Sopenharmony_ci * do this should be hooked in here. 36662306a36Sopenharmony_ci */ 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci if (!ascport->rts) 36962306a36Sopenharmony_ci return; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci /* If HW flow-control is enabled, we can't fiddle with the RTS line */ 37262306a36Sopenharmony_ci if (asc_in(port, ASC_CTL) & ASC_CTL_CTSENABLE) 37362306a36Sopenharmony_ci return; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci gpiod_set_value(ascport->rts, mctrl & TIOCM_RTS); 37662306a36Sopenharmony_ci} 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_cistatic unsigned int asc_get_mctrl(struct uart_port *port) 37962306a36Sopenharmony_ci{ 38062306a36Sopenharmony_ci /* 38162306a36Sopenharmony_ci * This routine is used for geting signals of: DTR, DCD, DSR, RI, 38262306a36Sopenharmony_ci * and CTS/RTS 38362306a36Sopenharmony_ci */ 38462306a36Sopenharmony_ci return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS; 38562306a36Sopenharmony_ci} 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci/* There are probably characters waiting to be transmitted. */ 38862306a36Sopenharmony_cistatic void asc_start_tx(struct uart_port *port) 38962306a36Sopenharmony_ci{ 39062306a36Sopenharmony_ci struct circ_buf *xmit = &port->state->xmit; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci if (!uart_circ_empty(xmit)) 39362306a36Sopenharmony_ci asc_enable_tx_interrupts(port); 39462306a36Sopenharmony_ci} 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci/* Transmit stop */ 39762306a36Sopenharmony_cistatic void asc_stop_tx(struct uart_port *port) 39862306a36Sopenharmony_ci{ 39962306a36Sopenharmony_ci asc_disable_tx_interrupts(port); 40062306a36Sopenharmony_ci} 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci/* Receive stop */ 40362306a36Sopenharmony_cistatic void asc_stop_rx(struct uart_port *port) 40462306a36Sopenharmony_ci{ 40562306a36Sopenharmony_ci asc_disable_rx_interrupts(port); 40662306a36Sopenharmony_ci} 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci/* Handle breaks - ignored by us */ 40962306a36Sopenharmony_cistatic void asc_break_ctl(struct uart_port *port, int break_state) 41062306a36Sopenharmony_ci{ 41162306a36Sopenharmony_ci /* Nothing here yet .. */ 41262306a36Sopenharmony_ci} 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci/* 41562306a36Sopenharmony_ci * Enable port for reception. 41662306a36Sopenharmony_ci */ 41762306a36Sopenharmony_cistatic int asc_startup(struct uart_port *port) 41862306a36Sopenharmony_ci{ 41962306a36Sopenharmony_ci if (request_irq(port->irq, asc_interrupt, 0, 42062306a36Sopenharmony_ci asc_port_name(port), port)) { 42162306a36Sopenharmony_ci dev_err(port->dev, "cannot allocate irq.\n"); 42262306a36Sopenharmony_ci return -ENODEV; 42362306a36Sopenharmony_ci } 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci asc_transmit_chars(port); 42662306a36Sopenharmony_ci asc_enable_rx_interrupts(port); 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci return 0; 42962306a36Sopenharmony_ci} 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_cistatic void asc_shutdown(struct uart_port *port) 43262306a36Sopenharmony_ci{ 43362306a36Sopenharmony_ci asc_disable_tx_interrupts(port); 43462306a36Sopenharmony_ci asc_disable_rx_interrupts(port); 43562306a36Sopenharmony_ci free_irq(port->irq, port); 43662306a36Sopenharmony_ci} 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_cistatic void asc_pm(struct uart_port *port, unsigned int state, 43962306a36Sopenharmony_ci unsigned int oldstate) 44062306a36Sopenharmony_ci{ 44162306a36Sopenharmony_ci struct asc_port *ascport = to_asc_port(port); 44262306a36Sopenharmony_ci unsigned long flags; 44362306a36Sopenharmony_ci u32 ctl; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci switch (state) { 44662306a36Sopenharmony_ci case UART_PM_STATE_ON: 44762306a36Sopenharmony_ci clk_prepare_enable(ascport->clk); 44862306a36Sopenharmony_ci break; 44962306a36Sopenharmony_ci case UART_PM_STATE_OFF: 45062306a36Sopenharmony_ci /* 45162306a36Sopenharmony_ci * Disable the ASC baud rate generator, which is as close as 45262306a36Sopenharmony_ci * we can come to turning it off. Note this is not called with 45362306a36Sopenharmony_ci * the port spinlock held. 45462306a36Sopenharmony_ci */ 45562306a36Sopenharmony_ci spin_lock_irqsave(&port->lock, flags); 45662306a36Sopenharmony_ci ctl = asc_in(port, ASC_CTL) & ~ASC_CTL_RUN; 45762306a36Sopenharmony_ci asc_out(port, ASC_CTL, ctl); 45862306a36Sopenharmony_ci spin_unlock_irqrestore(&port->lock, flags); 45962306a36Sopenharmony_ci clk_disable_unprepare(ascport->clk); 46062306a36Sopenharmony_ci break; 46162306a36Sopenharmony_ci } 46262306a36Sopenharmony_ci} 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_cistatic void asc_set_termios(struct uart_port *port, struct ktermios *termios, 46562306a36Sopenharmony_ci const struct ktermios *old) 46662306a36Sopenharmony_ci{ 46762306a36Sopenharmony_ci struct asc_port *ascport = to_asc_port(port); 46862306a36Sopenharmony_ci struct gpio_desc *gpiod; 46962306a36Sopenharmony_ci unsigned int baud; 47062306a36Sopenharmony_ci u32 ctrl_val; 47162306a36Sopenharmony_ci tcflag_t cflag; 47262306a36Sopenharmony_ci unsigned long flags; 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci /* Update termios to reflect hardware capabilities */ 47562306a36Sopenharmony_ci termios->c_cflag &= ~(CMSPAR | 47662306a36Sopenharmony_ci (ascport->hw_flow_control ? 0 : CRTSCTS)); 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci port->uartclk = clk_get_rate(ascport->clk); 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); 48162306a36Sopenharmony_ci cflag = termios->c_cflag; 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci spin_lock_irqsave(&port->lock, flags); 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci /* read control register */ 48662306a36Sopenharmony_ci ctrl_val = asc_in(port, ASC_CTL); 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci /* stop serial port and reset value */ 48962306a36Sopenharmony_ci asc_out(port, ASC_CTL, (ctrl_val & ~ASC_CTL_RUN)); 49062306a36Sopenharmony_ci ctrl_val = ASC_CTL_RXENABLE | ASC_CTL_FIFOENABLE; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci /* reset fifo rx & tx */ 49362306a36Sopenharmony_ci asc_out(port, ASC_TXRESET, 1); 49462306a36Sopenharmony_ci asc_out(port, ASC_RXRESET, 1); 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci /* set character length */ 49762306a36Sopenharmony_ci if ((cflag & CSIZE) == CS7) { 49862306a36Sopenharmony_ci ctrl_val |= ASC_CTL_MODE_7BIT_PAR; 49962306a36Sopenharmony_ci cflag |= PARENB; 50062306a36Sopenharmony_ci } else { 50162306a36Sopenharmony_ci ctrl_val |= (cflag & PARENB) ? ASC_CTL_MODE_8BIT_PAR : 50262306a36Sopenharmony_ci ASC_CTL_MODE_8BIT; 50362306a36Sopenharmony_ci cflag &= ~CSIZE; 50462306a36Sopenharmony_ci cflag |= CS8; 50562306a36Sopenharmony_ci } 50662306a36Sopenharmony_ci termios->c_cflag = cflag; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci /* set stop bit */ 50962306a36Sopenharmony_ci ctrl_val |= (cflag & CSTOPB) ? ASC_CTL_STOP_2BIT : ASC_CTL_STOP_1BIT; 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci /* odd parity */ 51262306a36Sopenharmony_ci if (cflag & PARODD) 51362306a36Sopenharmony_ci ctrl_val |= ASC_CTL_PARITYODD; 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci /* hardware flow control */ 51662306a36Sopenharmony_ci if ((cflag & CRTSCTS)) { 51762306a36Sopenharmony_ci ctrl_val |= ASC_CTL_CTSENABLE; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci /* If flow-control selected, stop handling RTS manually */ 52062306a36Sopenharmony_ci if (ascport->rts) { 52162306a36Sopenharmony_ci devm_gpiod_put(port->dev, ascport->rts); 52262306a36Sopenharmony_ci ascport->rts = NULL; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci pinctrl_select_state(ascport->pinctrl, 52562306a36Sopenharmony_ci ascport->states[DEFAULT]); 52662306a36Sopenharmony_ci } 52762306a36Sopenharmony_ci } else { 52862306a36Sopenharmony_ci /* If flow-control disabled, it's safe to handle RTS manually */ 52962306a36Sopenharmony_ci if (!ascport->rts && ascport->states[NO_HW_FLOWCTRL]) { 53062306a36Sopenharmony_ci pinctrl_select_state(ascport->pinctrl, 53162306a36Sopenharmony_ci ascport->states[NO_HW_FLOWCTRL]); 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci gpiod = devm_gpiod_get(port->dev, "rts", GPIOD_OUT_LOW); 53462306a36Sopenharmony_ci if (!IS_ERR(gpiod)) { 53562306a36Sopenharmony_ci gpiod_set_consumer_name(gpiod, 53662306a36Sopenharmony_ci port->dev->of_node->name); 53762306a36Sopenharmony_ci ascport->rts = gpiod; 53862306a36Sopenharmony_ci } 53962306a36Sopenharmony_ci } 54062306a36Sopenharmony_ci } 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci if ((baud < 19200) && !ascport->force_m1) { 54362306a36Sopenharmony_ci asc_out(port, ASC_BAUDRATE, (port->uartclk / (16 * baud))); 54462306a36Sopenharmony_ci } else { 54562306a36Sopenharmony_ci /* 54662306a36Sopenharmony_ci * MODE 1: recommended for high bit rates (above 19.2K) 54762306a36Sopenharmony_ci * 54862306a36Sopenharmony_ci * baudrate * 16 * 2^16 54962306a36Sopenharmony_ci * ASCBaudRate = ------------------------ 55062306a36Sopenharmony_ci * inputclock 55162306a36Sopenharmony_ci * 55262306a36Sopenharmony_ci * To keep maths inside 64bits, we divide inputclock by 16. 55362306a36Sopenharmony_ci */ 55462306a36Sopenharmony_ci u64 dividend = (u64)baud * (1 << 16); 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci do_div(dividend, port->uartclk / 16); 55762306a36Sopenharmony_ci asc_out(port, ASC_BAUDRATE, dividend); 55862306a36Sopenharmony_ci ctrl_val |= ASC_CTL_BAUDMODE; 55962306a36Sopenharmony_ci } 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci uart_update_timeout(port, cflag, baud); 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci ascport->port.read_status_mask = ASC_RXBUF_DUMMY_OE; 56462306a36Sopenharmony_ci if (termios->c_iflag & INPCK) 56562306a36Sopenharmony_ci ascport->port.read_status_mask |= ASC_RXBUF_FE | ASC_RXBUF_PE; 56662306a36Sopenharmony_ci if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK)) 56762306a36Sopenharmony_ci ascport->port.read_status_mask |= ASC_RXBUF_DUMMY_BE; 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci /* 57062306a36Sopenharmony_ci * Characters to ignore 57162306a36Sopenharmony_ci */ 57262306a36Sopenharmony_ci ascport->port.ignore_status_mask = 0; 57362306a36Sopenharmony_ci if (termios->c_iflag & IGNPAR) 57462306a36Sopenharmony_ci ascport->port.ignore_status_mask |= ASC_RXBUF_FE | ASC_RXBUF_PE; 57562306a36Sopenharmony_ci if (termios->c_iflag & IGNBRK) { 57662306a36Sopenharmony_ci ascport->port.ignore_status_mask |= ASC_RXBUF_DUMMY_BE; 57762306a36Sopenharmony_ci /* 57862306a36Sopenharmony_ci * If we're ignoring parity and break indicators, 57962306a36Sopenharmony_ci * ignore overruns too (for real raw support). 58062306a36Sopenharmony_ci */ 58162306a36Sopenharmony_ci if (termios->c_iflag & IGNPAR) 58262306a36Sopenharmony_ci ascport->port.ignore_status_mask |= ASC_RXBUF_DUMMY_OE; 58362306a36Sopenharmony_ci } 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci /* 58662306a36Sopenharmony_ci * Ignore all characters if CREAD is not set. 58762306a36Sopenharmony_ci */ 58862306a36Sopenharmony_ci if (!(termios->c_cflag & CREAD)) 58962306a36Sopenharmony_ci ascport->port.ignore_status_mask |= ASC_RXBUF_DUMMY_RX; 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci /* Set the timeout */ 59262306a36Sopenharmony_ci asc_out(port, ASC_TIMEOUT, 20); 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci /* write final value and enable port */ 59562306a36Sopenharmony_ci asc_out(port, ASC_CTL, (ctrl_val | ASC_CTL_RUN)); 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci spin_unlock_irqrestore(&port->lock, flags); 59862306a36Sopenharmony_ci} 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_cistatic const char *asc_type(struct uart_port *port) 60162306a36Sopenharmony_ci{ 60262306a36Sopenharmony_ci return (port->type == PORT_ASC) ? DRIVER_NAME : NULL; 60362306a36Sopenharmony_ci} 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_cistatic void asc_release_port(struct uart_port *port) 60662306a36Sopenharmony_ci{ 60762306a36Sopenharmony_ci} 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_cistatic int asc_request_port(struct uart_port *port) 61062306a36Sopenharmony_ci{ 61162306a36Sopenharmony_ci return 0; 61262306a36Sopenharmony_ci} 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci/* 61562306a36Sopenharmony_ci * Called when the port is opened, and UPF_BOOT_AUTOCONF flag is set 61662306a36Sopenharmony_ci * Set type field if successful 61762306a36Sopenharmony_ci */ 61862306a36Sopenharmony_cistatic void asc_config_port(struct uart_port *port, int flags) 61962306a36Sopenharmony_ci{ 62062306a36Sopenharmony_ci if ((flags & UART_CONFIG_TYPE)) 62162306a36Sopenharmony_ci port->type = PORT_ASC; 62262306a36Sopenharmony_ci} 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_cistatic int 62562306a36Sopenharmony_ciasc_verify_port(struct uart_port *port, struct serial_struct *ser) 62662306a36Sopenharmony_ci{ 62762306a36Sopenharmony_ci /* No user changeable parameters */ 62862306a36Sopenharmony_ci return -EINVAL; 62962306a36Sopenharmony_ci} 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci#ifdef CONFIG_CONSOLE_POLL 63262306a36Sopenharmony_ci/* 63362306a36Sopenharmony_ci * Console polling routines for writing and reading from the uart while 63462306a36Sopenharmony_ci * in an interrupt or debug context (i.e. kgdb). 63562306a36Sopenharmony_ci */ 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_cistatic int asc_get_poll_char(struct uart_port *port) 63862306a36Sopenharmony_ci{ 63962306a36Sopenharmony_ci if (!(asc_in(port, ASC_STA) & ASC_STA_RBF)) 64062306a36Sopenharmony_ci return NO_POLL_CHAR; 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci return asc_in(port, ASC_RXBUF); 64362306a36Sopenharmony_ci} 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_cistatic void asc_put_poll_char(struct uart_port *port, unsigned char c) 64662306a36Sopenharmony_ci{ 64762306a36Sopenharmony_ci while (!asc_txfifo_is_half_empty(port)) 64862306a36Sopenharmony_ci cpu_relax(); 64962306a36Sopenharmony_ci asc_out(port, ASC_TXBUF, c); 65062306a36Sopenharmony_ci} 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci#endif /* CONFIG_CONSOLE_POLL */ 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci/*---------------------------------------------------------------------*/ 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_cistatic const struct uart_ops asc_uart_ops = { 65762306a36Sopenharmony_ci .tx_empty = asc_tx_empty, 65862306a36Sopenharmony_ci .set_mctrl = asc_set_mctrl, 65962306a36Sopenharmony_ci .get_mctrl = asc_get_mctrl, 66062306a36Sopenharmony_ci .start_tx = asc_start_tx, 66162306a36Sopenharmony_ci .stop_tx = asc_stop_tx, 66262306a36Sopenharmony_ci .stop_rx = asc_stop_rx, 66362306a36Sopenharmony_ci .break_ctl = asc_break_ctl, 66462306a36Sopenharmony_ci .startup = asc_startup, 66562306a36Sopenharmony_ci .shutdown = asc_shutdown, 66662306a36Sopenharmony_ci .set_termios = asc_set_termios, 66762306a36Sopenharmony_ci .type = asc_type, 66862306a36Sopenharmony_ci .release_port = asc_release_port, 66962306a36Sopenharmony_ci .request_port = asc_request_port, 67062306a36Sopenharmony_ci .config_port = asc_config_port, 67162306a36Sopenharmony_ci .verify_port = asc_verify_port, 67262306a36Sopenharmony_ci .pm = asc_pm, 67362306a36Sopenharmony_ci#ifdef CONFIG_CONSOLE_POLL 67462306a36Sopenharmony_ci .poll_get_char = asc_get_poll_char, 67562306a36Sopenharmony_ci .poll_put_char = asc_put_poll_char, 67662306a36Sopenharmony_ci#endif /* CONFIG_CONSOLE_POLL */ 67762306a36Sopenharmony_ci}; 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_cistatic int asc_init_port(struct asc_port *ascport, 68062306a36Sopenharmony_ci struct platform_device *pdev) 68162306a36Sopenharmony_ci{ 68262306a36Sopenharmony_ci struct uart_port *port = &ascport->port; 68362306a36Sopenharmony_ci struct resource *res; 68462306a36Sopenharmony_ci int ret; 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci port->iotype = UPIO_MEM; 68762306a36Sopenharmony_ci port->flags = UPF_BOOT_AUTOCONF; 68862306a36Sopenharmony_ci port->ops = &asc_uart_ops; 68962306a36Sopenharmony_ci port->fifosize = ASC_FIFO_SIZE; 69062306a36Sopenharmony_ci port->dev = &pdev->dev; 69162306a36Sopenharmony_ci port->irq = platform_get_irq(pdev, 0); 69262306a36Sopenharmony_ci port->has_sysrq = IS_ENABLED(CONFIG_SERIAL_ST_ASC_CONSOLE); 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci port->membase = devm_platform_get_and_ioremap_resource(pdev, 0, &res); 69562306a36Sopenharmony_ci if (IS_ERR(port->membase)) 69662306a36Sopenharmony_ci return PTR_ERR(port->membase); 69762306a36Sopenharmony_ci port->mapbase = res->start; 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci spin_lock_init(&port->lock); 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci ascport->clk = devm_clk_get(&pdev->dev, NULL); 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci if (WARN_ON(IS_ERR(ascport->clk))) 70462306a36Sopenharmony_ci return -EINVAL; 70562306a36Sopenharmony_ci /* ensure that clk rate is correct by enabling the clk */ 70662306a36Sopenharmony_ci ret = clk_prepare_enable(ascport->clk); 70762306a36Sopenharmony_ci if (ret) 70862306a36Sopenharmony_ci return ret; 70962306a36Sopenharmony_ci ascport->port.uartclk = clk_get_rate(ascport->clk); 71062306a36Sopenharmony_ci WARN_ON(ascport->port.uartclk == 0); 71162306a36Sopenharmony_ci clk_disable_unprepare(ascport->clk); 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci ascport->pinctrl = devm_pinctrl_get(&pdev->dev); 71462306a36Sopenharmony_ci if (IS_ERR(ascport->pinctrl)) { 71562306a36Sopenharmony_ci ret = PTR_ERR(ascport->pinctrl); 71662306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to get Pinctrl: %d\n", ret); 71762306a36Sopenharmony_ci return ret; 71862306a36Sopenharmony_ci } 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci ascport->states[DEFAULT] = 72162306a36Sopenharmony_ci pinctrl_lookup_state(ascport->pinctrl, "default"); 72262306a36Sopenharmony_ci if (IS_ERR(ascport->states[DEFAULT])) { 72362306a36Sopenharmony_ci ret = PTR_ERR(ascport->states[DEFAULT]); 72462306a36Sopenharmony_ci dev_err(&pdev->dev, 72562306a36Sopenharmony_ci "Failed to look up Pinctrl state 'default': %d\n", ret); 72662306a36Sopenharmony_ci return ret; 72762306a36Sopenharmony_ci } 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci /* "no-hw-flowctrl" state is optional */ 73062306a36Sopenharmony_ci ascport->states[NO_HW_FLOWCTRL] = 73162306a36Sopenharmony_ci pinctrl_lookup_state(ascport->pinctrl, "no-hw-flowctrl"); 73262306a36Sopenharmony_ci if (IS_ERR(ascport->states[NO_HW_FLOWCTRL])) 73362306a36Sopenharmony_ci ascport->states[NO_HW_FLOWCTRL] = NULL; 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci return 0; 73662306a36Sopenharmony_ci} 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_cistatic struct asc_port *asc_of_get_asc_port(struct platform_device *pdev) 73962306a36Sopenharmony_ci{ 74062306a36Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 74162306a36Sopenharmony_ci int id; 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci if (!np) 74462306a36Sopenharmony_ci return NULL; 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci id = of_alias_get_id(np, "serial"); 74762306a36Sopenharmony_ci if (id < 0) 74862306a36Sopenharmony_ci id = of_alias_get_id(np, ASC_SERIAL_NAME); 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci if (id < 0) 75162306a36Sopenharmony_ci id = 0; 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci if (WARN_ON(id >= ASC_MAX_PORTS)) 75462306a36Sopenharmony_ci return NULL; 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci asc_ports[id].hw_flow_control = of_property_read_bool(np, 75762306a36Sopenharmony_ci "uart-has-rtscts"); 75862306a36Sopenharmony_ci asc_ports[id].force_m1 = of_property_read_bool(np, "st,force-m1"); 75962306a36Sopenharmony_ci asc_ports[id].port.line = id; 76062306a36Sopenharmony_ci asc_ports[id].rts = NULL; 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci return &asc_ports[id]; 76362306a36Sopenharmony_ci} 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci#ifdef CONFIG_OF 76662306a36Sopenharmony_cistatic const struct of_device_id asc_match[] = { 76762306a36Sopenharmony_ci { .compatible = "st,asc", }, 76862306a36Sopenharmony_ci {}, 76962306a36Sopenharmony_ci}; 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, asc_match); 77262306a36Sopenharmony_ci#endif 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_cistatic int asc_serial_probe(struct platform_device *pdev) 77562306a36Sopenharmony_ci{ 77662306a36Sopenharmony_ci int ret; 77762306a36Sopenharmony_ci struct asc_port *ascport; 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci ascport = asc_of_get_asc_port(pdev); 78062306a36Sopenharmony_ci if (!ascport) 78162306a36Sopenharmony_ci return -ENODEV; 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci ret = asc_init_port(ascport, pdev); 78462306a36Sopenharmony_ci if (ret) 78562306a36Sopenharmony_ci return ret; 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci ret = uart_add_one_port(&asc_uart_driver, &ascport->port); 78862306a36Sopenharmony_ci if (ret) 78962306a36Sopenharmony_ci return ret; 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci platform_set_drvdata(pdev, &ascport->port); 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci return 0; 79462306a36Sopenharmony_ci} 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_cistatic int asc_serial_remove(struct platform_device *pdev) 79762306a36Sopenharmony_ci{ 79862306a36Sopenharmony_ci struct uart_port *port = platform_get_drvdata(pdev); 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci uart_remove_one_port(&asc_uart_driver, port); 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci return 0; 80362306a36Sopenharmony_ci} 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 80662306a36Sopenharmony_cistatic int asc_serial_suspend(struct device *dev) 80762306a36Sopenharmony_ci{ 80862306a36Sopenharmony_ci struct uart_port *port = dev_get_drvdata(dev); 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci return uart_suspend_port(&asc_uart_driver, port); 81162306a36Sopenharmony_ci} 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_cistatic int asc_serial_resume(struct device *dev) 81462306a36Sopenharmony_ci{ 81562306a36Sopenharmony_ci struct uart_port *port = dev_get_drvdata(dev); 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci return uart_resume_port(&asc_uart_driver, port); 81862306a36Sopenharmony_ci} 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */ 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci/*----------------------------------------------------------------------*/ 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci#ifdef CONFIG_SERIAL_ST_ASC_CONSOLE 82562306a36Sopenharmony_cistatic void asc_console_putchar(struct uart_port *port, unsigned char ch) 82662306a36Sopenharmony_ci{ 82762306a36Sopenharmony_ci unsigned int timeout = 1000000; 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci /* Wait for upto 1 second in case flow control is stopping us. */ 83062306a36Sopenharmony_ci while (--timeout && !asc_txfifo_is_half_empty(port)) 83162306a36Sopenharmony_ci udelay(1); 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci asc_out(port, ASC_TXBUF, ch); 83462306a36Sopenharmony_ci} 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci/* 83762306a36Sopenharmony_ci * Print a string to the serial port trying not to disturb 83862306a36Sopenharmony_ci * any possible real use of the port... 83962306a36Sopenharmony_ci */ 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_cistatic void asc_console_write(struct console *co, const char *s, unsigned count) 84262306a36Sopenharmony_ci{ 84362306a36Sopenharmony_ci struct uart_port *port = &asc_ports[co->index].port; 84462306a36Sopenharmony_ci unsigned long flags; 84562306a36Sopenharmony_ci unsigned long timeout = 1000000; 84662306a36Sopenharmony_ci int locked = 1; 84762306a36Sopenharmony_ci u32 intenable; 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci if (port->sysrq) 85062306a36Sopenharmony_ci locked = 0; /* asc_interrupt has already claimed the lock */ 85162306a36Sopenharmony_ci else if (oops_in_progress) 85262306a36Sopenharmony_ci locked = spin_trylock_irqsave(&port->lock, flags); 85362306a36Sopenharmony_ci else 85462306a36Sopenharmony_ci spin_lock_irqsave(&port->lock, flags); 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci /* 85762306a36Sopenharmony_ci * Disable interrupts so we don't get the IRQ line bouncing 85862306a36Sopenharmony_ci * up and down while interrupts are disabled. 85962306a36Sopenharmony_ci */ 86062306a36Sopenharmony_ci intenable = asc_in(port, ASC_INTEN); 86162306a36Sopenharmony_ci asc_out(port, ASC_INTEN, 0); 86262306a36Sopenharmony_ci (void)asc_in(port, ASC_INTEN); /* Defeat bus write posting */ 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci uart_console_write(port, s, count, asc_console_putchar); 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci while (--timeout && !asc_txfifo_is_empty(port)) 86762306a36Sopenharmony_ci udelay(1); 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci asc_out(port, ASC_INTEN, intenable); 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci if (locked) 87262306a36Sopenharmony_ci spin_unlock_irqrestore(&port->lock, flags); 87362306a36Sopenharmony_ci} 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_cistatic int asc_console_setup(struct console *co, char *options) 87662306a36Sopenharmony_ci{ 87762306a36Sopenharmony_ci struct asc_port *ascport; 87862306a36Sopenharmony_ci int baud = 115200; 87962306a36Sopenharmony_ci int bits = 8; 88062306a36Sopenharmony_ci int parity = 'n'; 88162306a36Sopenharmony_ci int flow = 'n'; 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci if (co->index >= ASC_MAX_PORTS) 88462306a36Sopenharmony_ci return -ENODEV; 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci ascport = &asc_ports[co->index]; 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci /* 88962306a36Sopenharmony_ci * This driver does not support early console initialization 89062306a36Sopenharmony_ci * (use ARM early printk support instead), so we only expect 89162306a36Sopenharmony_ci * this to be called during the uart port registration when the 89262306a36Sopenharmony_ci * driver gets probed and the port should be mapped at that point. 89362306a36Sopenharmony_ci */ 89462306a36Sopenharmony_ci if (ascport->port.mapbase == 0 || ascport->port.membase == NULL) 89562306a36Sopenharmony_ci return -ENXIO; 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci if (options) 89862306a36Sopenharmony_ci uart_parse_options(options, &baud, &parity, &bits, &flow); 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci return uart_set_options(&ascport->port, co, baud, parity, bits, flow); 90162306a36Sopenharmony_ci} 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_cistatic struct console asc_console = { 90462306a36Sopenharmony_ci .name = ASC_SERIAL_NAME, 90562306a36Sopenharmony_ci .device = uart_console_device, 90662306a36Sopenharmony_ci .write = asc_console_write, 90762306a36Sopenharmony_ci .setup = asc_console_setup, 90862306a36Sopenharmony_ci .flags = CON_PRINTBUFFER, 90962306a36Sopenharmony_ci .index = -1, 91062306a36Sopenharmony_ci .data = &asc_uart_driver, 91162306a36Sopenharmony_ci}; 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci#define ASC_SERIAL_CONSOLE (&asc_console) 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci#else 91662306a36Sopenharmony_ci#define ASC_SERIAL_CONSOLE NULL 91762306a36Sopenharmony_ci#endif /* CONFIG_SERIAL_ST_ASC_CONSOLE */ 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_cistatic struct uart_driver asc_uart_driver = { 92062306a36Sopenharmony_ci .owner = THIS_MODULE, 92162306a36Sopenharmony_ci .driver_name = DRIVER_NAME, 92262306a36Sopenharmony_ci .dev_name = ASC_SERIAL_NAME, 92362306a36Sopenharmony_ci .major = 0, 92462306a36Sopenharmony_ci .minor = 0, 92562306a36Sopenharmony_ci .nr = ASC_MAX_PORTS, 92662306a36Sopenharmony_ci .cons = ASC_SERIAL_CONSOLE, 92762306a36Sopenharmony_ci}; 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_cistatic const struct dev_pm_ops asc_serial_pm_ops = { 93062306a36Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(asc_serial_suspend, asc_serial_resume) 93162306a36Sopenharmony_ci}; 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_cistatic struct platform_driver asc_serial_driver = { 93462306a36Sopenharmony_ci .probe = asc_serial_probe, 93562306a36Sopenharmony_ci .remove = asc_serial_remove, 93662306a36Sopenharmony_ci .driver = { 93762306a36Sopenharmony_ci .name = DRIVER_NAME, 93862306a36Sopenharmony_ci .pm = &asc_serial_pm_ops, 93962306a36Sopenharmony_ci .of_match_table = of_match_ptr(asc_match), 94062306a36Sopenharmony_ci }, 94162306a36Sopenharmony_ci}; 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_cistatic int __init asc_init(void) 94462306a36Sopenharmony_ci{ 94562306a36Sopenharmony_ci int ret; 94662306a36Sopenharmony_ci static const char banner[] __initconst = 94762306a36Sopenharmony_ci KERN_INFO "STMicroelectronics ASC driver initialized\n"; 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci printk(banner); 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci ret = uart_register_driver(&asc_uart_driver); 95262306a36Sopenharmony_ci if (ret) 95362306a36Sopenharmony_ci return ret; 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci ret = platform_driver_register(&asc_serial_driver); 95662306a36Sopenharmony_ci if (ret) 95762306a36Sopenharmony_ci uart_unregister_driver(&asc_uart_driver); 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_ci return ret; 96062306a36Sopenharmony_ci} 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_cistatic void __exit asc_exit(void) 96362306a36Sopenharmony_ci{ 96462306a36Sopenharmony_ci platform_driver_unregister(&asc_serial_driver); 96562306a36Sopenharmony_ci uart_unregister_driver(&asc_uart_driver); 96662306a36Sopenharmony_ci} 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_cimodule_init(asc_init); 96962306a36Sopenharmony_cimodule_exit(asc_exit); 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ciMODULE_ALIAS("platform:" DRIVER_NAME); 97262306a36Sopenharmony_ciMODULE_AUTHOR("STMicroelectronics (R&D) Limited"); 97362306a36Sopenharmony_ciMODULE_DESCRIPTION("STMicroelectronics ASC serial port driver"); 97462306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 975