1249997Swkoszek/*- 2249997Swkoszek * Copyright (c) 2005 M. Warner Losh 3249997Swkoszek * Copyright (c) 2005 Olivier Houchard 4249997Swkoszek * Copyright (c) 2012 Thomas Skibo 5249997Swkoszek * All rights reserved. 6249997Swkoszek * 7249997Swkoszek * Redistribution and use in source and binary forms, with or without 8249997Swkoszek * modification, are permitted provided that the following conditions 9249997Swkoszek * are met: 10249997Swkoszek * 11249997Swkoszek * 1. Redistributions of source code must retain the above copyright 12249997Swkoszek * notice, this list of conditions and the following disclaimer. 13249997Swkoszek * 2. Redistributions in binary form must reproduce the above copyright 14249997Swkoszek * notice, this list of conditions and the following disclaimer in the 15249997Swkoszek * documentation and/or other materials provided with the distribution. 16249997Swkoszek * 17249997Swkoszek * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18249997Swkoszek * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19249997Swkoszek * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20249997Swkoszek * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 21249997Swkoszek * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22249997Swkoszek * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23249997Swkoszek * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24249997Swkoszek * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25249997Swkoszek * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26249997Swkoszek * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27249997Swkoszek * SUCH DAMAGE. 28249997Swkoszek */ 29249997Swkoszek 30249997Swkoszek/* A driver for the Cadence AMBA UART as used by the Xilinx Zynq-7000. 31249997Swkoszek * 32249997Swkoszek * Reference: Zynq-7000 All Programmable SoC Technical Reference Manual. 33249997Swkoszek * (v1.4) November 16, 2012. Xilinx doc UG585. UART is covered in Ch. 19 34249997Swkoszek * and register definitions are in appendix B.33. 35249997Swkoszek */ 36249997Swkoszek 37249997Swkoszek 38249997Swkoszek#include <sys/cdefs.h> 39249997Swkoszek__FBSDID("$FreeBSD$"); 40249997Swkoszek 41249997Swkoszek#include "opt_global.h" 42249997Swkoszek 43249997Swkoszek#include <sys/param.h> 44249997Swkoszek#include <sys/systm.h> 45249997Swkoszek#include <sys/bus.h> 46249997Swkoszek#include <sys/conf.h> 47249997Swkoszek#include <sys/cons.h> 48249997Swkoszek#include <sys/tty.h> 49249997Swkoszek#include <machine/bus.h> 50249997Swkoszek 51249997Swkoszek#include <dev/uart/uart.h> 52249997Swkoszek#include <dev/uart/uart_cpu.h> 53283327Sian#include <dev/uart/uart_cpu_fdt.h> 54249997Swkoszek#include <dev/uart/uart_bus.h> 55249997Swkoszek 56249997Swkoszek#include "uart_if.h" 57249997Swkoszek 58249997Swkoszek#define UART_FIFO_SIZE 64 59249997Swkoszek 60249997Swkoszek#define RD4(bas, reg) \ 61249997Swkoszek bus_space_read_4((bas)->bst, (bas)->bsh, uart_regofs((bas), (reg))) 62249997Swkoszek#define WR4(bas, reg, value) \ 63249997Swkoszek bus_space_write_4((bas)->bst, (bas)->bsh, uart_regofs((bas), (reg)), \ 64249997Swkoszek (value)) 65249997Swkoszek 66249997Swkoszek/* Register definitions for Cadence UART Controller. 67249997Swkoszek */ 68249997Swkoszek#define CDNC_UART_CTRL_REG 0x00 /* Control Register. */ 69249997Swkoszek#define CDNC_UART_CTRL_REG_STOPBRK (1<<8) 70249997Swkoszek#define CDNC_UART_CTRL_REG_STARTBRK (1<<7) 71249997Swkoszek#define CDNC_UART_CTRL_REG_TORST (1<<6) 72249997Swkoszek#define CDNC_UART_CTRL_REG_TX_DIS (1<<5) 73249997Swkoszek#define CDNC_UART_CTRL_REG_TX_EN (1<<4) 74249997Swkoszek#define CDNC_UART_CTRL_REG_RX_DIS (1<<3) 75249997Swkoszek#define CDNC_UART_CTRL_REG_RX_EN (1<<2) 76249997Swkoszek#define CDNC_UART_CTRL_REG_TXRST (1<<1) 77249997Swkoszek#define CDNC_UART_CTRL_REG_RXRST (1<<0) 78249997Swkoszek 79249997Swkoszek#define CDNC_UART_MODE_REG 0x04 /* Mode Register. */ 80249997Swkoszek#define CDNC_UART_MODE_REG_CHMOD_R_LOOP (3<<8) /* [9:8] - channel mode */ 81249997Swkoszek#define CDNC_UART_MODE_REG_CHMOD_L_LOOP (2<<8) 82249997Swkoszek#define CDNC_UART_MODE_REG_CHMOD_AUTECHO (1<<8) 83249997Swkoszek#define CDNC_UART_MODE_REG_STOP2 (2<<6) /* [7:6] - stop bits */ 84249997Swkoszek#define CDNC_UART_MODE_REG_PAR_NONE (4<<3) /* [5:3] - parity type */ 85249997Swkoszek#define CDNC_UART_MODE_REG_PAR_MARK (3<<3) 86249997Swkoszek#define CDNC_UART_MODE_REG_PAR_SPACE (2<<3) 87249997Swkoszek#define CDNC_UART_MODE_REG_PAR_ODD (1<<3) 88249997Swkoszek#define CDNC_UART_MODE_REG_PAR_EVEN (0<<3) 89249997Swkoszek#define CDNC_UART_MODE_REG_6BIT (3<<1) /* [2:1] - character len */ 90249997Swkoszek#define CDNC_UART_MODE_REG_7BIT (2<<1) 91249997Swkoszek#define CDNC_UART_MODE_REG_8BIT (0<<1) 92249997Swkoszek#define CDNC_UART_MODE_REG_CLKSEL (1<<0) 93249997Swkoszek 94249997Swkoszek#define CDNC_UART_IEN_REG 0x08 /* Interrupt registers. */ 95249997Swkoszek#define CDNC_UART_IDIS_REG 0x0C 96249997Swkoszek#define CDNC_UART_IMASK_REG 0x10 97249997Swkoszek#define CDNC_UART_ISTAT_REG 0x14 98249997Swkoszek#define CDNC_UART_INT_TXOVR (1<<12) 99249997Swkoszek#define CDNC_UART_INT_TXNRLYFUL (1<<11) /* tx "nearly" full */ 100249997Swkoszek#define CDNC_UART_INT_TXTRIG (1<<10) 101249997Swkoszek#define CDNC_UART_INT_DMSI (1<<9) /* delta modem status */ 102249997Swkoszek#define CDNC_UART_INT_RXTMOUT (1<<8) 103249997Swkoszek#define CDNC_UART_INT_PARITY (1<<7) 104249997Swkoszek#define CDNC_UART_INT_FRAMING (1<<6) 105249997Swkoszek#define CDNC_UART_INT_RXOVR (1<<5) 106249997Swkoszek#define CDNC_UART_INT_TXFULL (1<<4) 107249997Swkoszek#define CDNC_UART_INT_TXEMPTY (1<<3) 108249997Swkoszek#define CDNC_UART_INT_RXFULL (1<<2) 109249997Swkoszek#define CDNC_UART_INT_RXEMPTY (1<<1) 110249997Swkoszek#define CDNC_UART_INT_RXTRIG (1<<0) 111249997Swkoszek#define CDNC_UART_INT_ALL 0x1FFF 112249997Swkoszek 113249997Swkoszek#define CDNC_UART_BAUDGEN_REG 0x18 114249997Swkoszek#define CDNC_UART_RX_TIMEO_REG 0x1C 115249997Swkoszek#define CDNC_UART_RX_WATER_REG 0x20 116249997Swkoszek 117249997Swkoszek#define CDNC_UART_MODEM_CTRL_REG 0x24 118249997Swkoszek#define CDNC_UART_MODEM_CTRL_REG_FCM (1<<5) /* automatic flow control */ 119249997Swkoszek#define CDNC_UART_MODEM_CTRL_REG_RTS (1<<1) 120249997Swkoszek#define CDNC_UART_MODEM_CTRL_REG_DTR (1<<0) 121249997Swkoszek 122249997Swkoszek#define CDNC_UART_MODEM_STAT_REG 0x28 123249997Swkoszek#define CDNC_UART_MODEM_STAT_REG_FCMS (1<<8) /* flow control mode (rw) */ 124249997Swkoszek#define CDNC_UART_MODEM_STAT_REG_DCD (1<<7) 125249997Swkoszek#define CDNC_UART_MODEM_STAT_REG_RI (1<<6) 126249997Swkoszek#define CDNC_UART_MODEM_STAT_REG_DSR (1<<5) 127249997Swkoszek#define CDNC_UART_MODEM_STAT_REG_CTS (1<<4) 128249997Swkoszek#define CDNC_UART_MODEM_STAT_REG_DDCD (1<<3) /* change in DCD (w1tc) */ 129249997Swkoszek#define CDNC_UART_MODEM_STAT_REG_TERI (1<<2) /* trail edge ring (w1tc) */ 130249997Swkoszek#define CDNC_UART_MODEM_STAT_REG_DDSR (1<<1) /* change in DSR (w1tc) */ 131249997Swkoszek#define CDNC_UART_MODEM_STAT_REG_DCTS (1<<0) /* change in CTS (w1tc) */ 132249997Swkoszek 133249997Swkoszek#define CDNC_UART_CHAN_STAT_REG 0x2C /* Channel status register. */ 134249997Swkoszek#define CDNC_UART_CHAN_STAT_REG_TXNRLYFUL (1<<14) /* tx "nearly" full */ 135249997Swkoszek#define CDNC_UART_CHAN_STAT_REG_TXTRIG (1<<13) 136249997Swkoszek#define CDNC_UART_CHAN_STAT_REG_FDELT (1<<12) 137249997Swkoszek#define CDNC_UART_CHAN_STAT_REG_TXACTIVE (1<<11) 138249997Swkoszek#define CDNC_UART_CHAN_STAT_REG_RXACTIVE (1<<10) 139249997Swkoszek#define CDNC_UART_CHAN_STAT_REG_TXFULL (1<<4) 140249997Swkoszek#define CDNC_UART_CHAN_STAT_REG_TXEMPTY (1<<3) 141249997Swkoszek#define CDNC_UART_CHAN_STAT_REG_RXEMPTY (1<<1) 142249997Swkoszek#define CDNC_UART_CHAN_STAT_REG_RXTRIG (1<<0) 143249997Swkoszek 144249997Swkoszek#define CDNC_UART_FIFO 0x30 /* Data FIFO (tx and rx) */ 145249997Swkoszek#define CDNC_UART_BAUDDIV_REG 0x34 146249997Swkoszek#define CDNC_UART_FLOWDEL_REG 0x38 147249997Swkoszek#define CDNC_UART_TX_WATER_REG 0x44 148249997Swkoszek 149249997Swkoszek 150249997Swkoszek/* 151249997Swkoszek * Low-level UART interface. 152249997Swkoszek */ 153249997Swkoszekstatic int cdnc_uart_probe(struct uart_bas *bas); 154249997Swkoszekstatic void cdnc_uart_init(struct uart_bas *bas, int, int, int, int); 155249997Swkoszekstatic void cdnc_uart_term(struct uart_bas *bas); 156249997Swkoszekstatic void cdnc_uart_putc(struct uart_bas *bas, int); 157249997Swkoszekstatic int cdnc_uart_rxready(struct uart_bas *bas); 158249997Swkoszekstatic int cdnc_uart_getc(struct uart_bas *bas, struct mtx *mtx); 159249997Swkoszek 160249997Swkoszekextern SLIST_HEAD(uart_devinfo_list, uart_devinfo) uart_sysdevs; 161249997Swkoszek 162249997Swkoszekstatic struct uart_ops cdnc_uart_ops = { 163249997Swkoszek .probe = cdnc_uart_probe, 164249997Swkoszek .init = cdnc_uart_init, 165249997Swkoszek .term = cdnc_uart_term, 166249997Swkoszek .putc = cdnc_uart_putc, 167249997Swkoszek .rxready = cdnc_uart_rxready, 168249997Swkoszek .getc = cdnc_uart_getc, 169249997Swkoszek}; 170249997Swkoszek 171249997Swkoszek#define SIGCHG(c, i, s, d) \ 172249997Swkoszek if (c) { \ 173249997Swkoszek i |= (i & s) ? s : s | d; \ 174249997Swkoszek } else { \ 175249997Swkoszek i = (i & s) ? (i & ~s) | d : i; \ 176249997Swkoszek } 177249997Swkoszek 178249997Swkoszekstatic int 179249997Swkoszekcdnc_uart_probe(struct uart_bas *bas) 180249997Swkoszek{ 181249997Swkoszek 182249997Swkoszek return (0); 183249997Swkoszek} 184249997Swkoszek 185249997Swkoszekstatic int 186249997Swkoszekcdnc_uart_set_baud(struct uart_bas *bas, int baudrate) 187249997Swkoszek{ 188249997Swkoszek uint32_t baudgen, bauddiv; 189249997Swkoszek uint32_t best_bauddiv, best_baudgen, best_error; 190249997Swkoszek uint32_t baud_out, err; 191249997Swkoszek 192249997Swkoszek best_bauddiv = 0; 193249997Swkoszek best_baudgen = 0; 194249997Swkoszek best_error = ~0; 195249997Swkoszek 196249997Swkoszek /* Try all possible bauddiv values and pick best match. */ 197249997Swkoszek for (bauddiv = 4; bauddiv <= 255; bauddiv++) { 198249997Swkoszek baudgen = (bas->rclk + (baudrate * (bauddiv + 1)) / 2) / 199249997Swkoszek (baudrate * (bauddiv + 1)); 200249997Swkoszek if (baudgen < 1 || baudgen > 0xffff) 201249997Swkoszek continue; 202249997Swkoszek 203249997Swkoszek baud_out = bas->rclk / (baudgen * (bauddiv + 1)); 204249997Swkoszek err = baud_out > baudrate ? 205249997Swkoszek baud_out - baudrate : baudrate - baud_out; 206249997Swkoszek 207249997Swkoszek if (err < best_error) { 208249997Swkoszek best_error = err; 209249997Swkoszek best_bauddiv = bauddiv; 210249997Swkoszek best_baudgen = baudgen; 211249997Swkoszek } 212249997Swkoszek } 213249997Swkoszek 214249997Swkoszek if (best_bauddiv > 0) { 215249997Swkoszek WR4(bas, CDNC_UART_BAUDDIV_REG, best_bauddiv); 216249997Swkoszek WR4(bas, CDNC_UART_BAUDGEN_REG, best_baudgen); 217249997Swkoszek return (0); 218249997Swkoszek } else 219249997Swkoszek return (-1); /* out of range */ 220249997Swkoszek} 221249997Swkoszek 222249997Swkoszekstatic int 223249997Swkoszekcdnc_uart_set_params(struct uart_bas *bas, int baudrate, int databits, 224249997Swkoszek int stopbits, int parity) 225249997Swkoszek{ 226249997Swkoszek uint32_t mode_reg_value = 0; 227249997Swkoszek 228249997Swkoszek switch (databits) { 229249997Swkoszek case 6: 230249997Swkoszek mode_reg_value |= CDNC_UART_MODE_REG_6BIT; 231249997Swkoszek break; 232249997Swkoszek case 7: 233249997Swkoszek mode_reg_value |= CDNC_UART_MODE_REG_7BIT; 234249997Swkoszek break; 235249997Swkoszek case 8: 236249997Swkoszek default: 237249997Swkoszek mode_reg_value |= CDNC_UART_MODE_REG_8BIT; 238249997Swkoszek break; 239249997Swkoszek } 240249997Swkoszek 241249997Swkoszek if (stopbits == 2) 242249997Swkoszek mode_reg_value |= CDNC_UART_MODE_REG_STOP2; 243249997Swkoszek 244249997Swkoszek switch (parity) { 245249997Swkoszek case UART_PARITY_MARK: 246249997Swkoszek mode_reg_value |= CDNC_UART_MODE_REG_PAR_MARK; 247249997Swkoszek break; 248249997Swkoszek case UART_PARITY_SPACE: 249249997Swkoszek mode_reg_value |= CDNC_UART_MODE_REG_PAR_SPACE; 250249997Swkoszek break; 251249997Swkoszek case UART_PARITY_ODD: 252249997Swkoszek mode_reg_value |= CDNC_UART_MODE_REG_PAR_ODD; 253249997Swkoszek break; 254249997Swkoszek case UART_PARITY_EVEN: 255249997Swkoszek mode_reg_value |= CDNC_UART_MODE_REG_PAR_EVEN; 256249997Swkoszek break; 257249997Swkoszek case UART_PARITY_NONE: 258249997Swkoszek default: 259249997Swkoszek mode_reg_value |= CDNC_UART_MODE_REG_PAR_NONE; 260249997Swkoszek break; 261249997Swkoszek } 262249997Swkoszek 263249997Swkoszek WR4(bas, CDNC_UART_MODE_REG, mode_reg_value); 264249997Swkoszek 265249997Swkoszek if (baudrate > 0 && cdnc_uart_set_baud(bas, baudrate) < 0) 266249997Swkoszek return (EINVAL); 267249997Swkoszek 268249997Swkoszek return(0); 269249997Swkoszek} 270249997Swkoszek 271249997Swkoszekstatic void 272249997Swkoszekcdnc_uart_hw_init(struct uart_bas *bas) 273249997Swkoszek{ 274249997Swkoszek 275249997Swkoszek /* Reset RX and TX. */ 276249997Swkoszek WR4(bas, CDNC_UART_CTRL_REG, 277249997Swkoszek CDNC_UART_CTRL_REG_RXRST | CDNC_UART_CTRL_REG_TXRST); 278249997Swkoszek 279249997Swkoszek /* Interrupts all off. */ 280249997Swkoszek WR4(bas, CDNC_UART_IDIS_REG, CDNC_UART_INT_ALL); 281249997Swkoszek WR4(bas, CDNC_UART_ISTAT_REG, CDNC_UART_INT_ALL); 282249997Swkoszek 283249997Swkoszek /* Clear delta bits. */ 284249997Swkoszek WR4(bas, CDNC_UART_MODEM_STAT_REG, 285249997Swkoszek CDNC_UART_MODEM_STAT_REG_DDCD | CDNC_UART_MODEM_STAT_REG_TERI | 286249997Swkoszek CDNC_UART_MODEM_STAT_REG_DDSR | CDNC_UART_MODEM_STAT_REG_DCTS); 287249997Swkoszek 288249997Swkoszek /* RX FIFO water level, stale timeout */ 289249997Swkoszek WR4(bas, CDNC_UART_RX_WATER_REG, UART_FIFO_SIZE/2); 290249997Swkoszek WR4(bas, CDNC_UART_RX_TIMEO_REG, 10); 291249997Swkoszek 292249997Swkoszek /* TX FIFO water level (not used.) */ 293249997Swkoszek WR4(bas, CDNC_UART_TX_WATER_REG, UART_FIFO_SIZE/2); 294249997Swkoszek 295249997Swkoszek /* Bring RX and TX online. */ 296249997Swkoszek WR4(bas, CDNC_UART_CTRL_REG, 297249997Swkoszek CDNC_UART_CTRL_REG_RX_EN | CDNC_UART_CTRL_REG_TX_EN | 298249997Swkoszek CDNC_UART_CTRL_REG_TORST | CDNC_UART_CTRL_REG_STOPBRK); 299249997Swkoszek 300249997Swkoszek /* Set DTR and RTS. */ 301249997Swkoszek WR4(bas, CDNC_UART_MODEM_CTRL_REG, CDNC_UART_MODEM_CTRL_REG_DTR | 302249997Swkoszek CDNC_UART_MODEM_CTRL_REG_RTS); 303249997Swkoszek} 304249997Swkoszek 305249997Swkoszek/* 306249997Swkoszek * Initialize this device for use as a console. 307249997Swkoszek */ 308249997Swkoszekstatic void 309249997Swkoszekcdnc_uart_init(struct uart_bas *bas, int baudrate, int databits, int stopbits, 310249997Swkoszek int parity) 311249997Swkoszek{ 312249997Swkoszek 313249997Swkoszek /* Initialize hardware. */ 314249997Swkoszek cdnc_uart_hw_init(bas); 315249997Swkoszek 316249997Swkoszek /* Set baudrate, parameters. */ 317249997Swkoszek (void)cdnc_uart_set_params(bas, baudrate, databits, stopbits, parity); 318249997Swkoszek} 319249997Swkoszek 320249997Swkoszek/* 321249997Swkoszek * Free resources now that we're no longer the console. This appears to 322249997Swkoszek * be never called, and I'm unsure quite what to do if I am called. 323249997Swkoszek */ 324249997Swkoszekstatic void 325249997Swkoszekcdnc_uart_term(struct uart_bas *bas) 326249997Swkoszek{ 327249997Swkoszek 328249997Swkoszek /* XXX */ 329249997Swkoszek} 330249997Swkoszek 331249997Swkoszek/* 332249997Swkoszek * Put a character of console output (so we do it here polling rather than 333249997Swkoszek * interrutp driven). 334249997Swkoszek */ 335249997Swkoszekstatic void 336249997Swkoszekcdnc_uart_putc(struct uart_bas *bas, int c) 337249997Swkoszek{ 338249997Swkoszek 339249997Swkoszek /* Wait for room. */ 340249997Swkoszek while ((RD4(bas,CDNC_UART_CHAN_STAT_REG) & 341249997Swkoszek CDNC_UART_CHAN_STAT_REG_TXFULL) != 0) 342249997Swkoszek ; 343249997Swkoszek 344249997Swkoszek WR4(bas, CDNC_UART_FIFO, c); 345249997Swkoszek 346249997Swkoszek while ((RD4(bas,CDNC_UART_CHAN_STAT_REG) & 347249997Swkoszek CDNC_UART_CHAN_STAT_REG_TXEMPTY) == 0) 348249997Swkoszek ; 349249997Swkoszek} 350249997Swkoszek 351249997Swkoszek/* 352249997Swkoszek * Check for a character available. 353249997Swkoszek */ 354249997Swkoszekstatic int 355249997Swkoszekcdnc_uart_rxready(struct uart_bas *bas) 356249997Swkoszek{ 357249997Swkoszek 358249997Swkoszek return ((RD4(bas, CDNC_UART_CHAN_STAT_REG) & 359249997Swkoszek CDNC_UART_CHAN_STAT_REG_RXEMPTY) == 0); 360249997Swkoszek} 361249997Swkoszek 362249997Swkoszek/* 363249997Swkoszek * Block waiting for a character. 364249997Swkoszek */ 365249997Swkoszekstatic int 366249997Swkoszekcdnc_uart_getc(struct uart_bas *bas, struct mtx *mtx) 367249997Swkoszek{ 368249997Swkoszek int c; 369249997Swkoszek 370249997Swkoszek uart_lock(mtx); 371249997Swkoszek 372249997Swkoszek while ((RD4(bas, CDNC_UART_CHAN_STAT_REG) & 373249997Swkoszek CDNC_UART_CHAN_STAT_REG_RXEMPTY) != 0) { 374249997Swkoszek uart_unlock(mtx); 375249997Swkoszek DELAY(4); 376249997Swkoszek uart_lock(mtx); 377249997Swkoszek } 378249997Swkoszek 379249997Swkoszek c = RD4(bas, CDNC_UART_FIFO); 380249997Swkoszek 381249997Swkoszek uart_unlock(mtx); 382249997Swkoszek 383249997Swkoszek c &= 0xff; 384249997Swkoszek return (c); 385249997Swkoszek} 386249997Swkoszek 387249997Swkoszek/*****************************************************************************/ 388249997Swkoszek/* 389249997Swkoszek * High-level UART interface. 390249997Swkoszek */ 391249997Swkoszek 392249997Swkoszekstatic int cdnc_uart_bus_probe(struct uart_softc *sc); 393249997Swkoszekstatic int cdnc_uart_bus_attach(struct uart_softc *sc); 394249997Swkoszekstatic int cdnc_uart_bus_flush(struct uart_softc *, int); 395249997Swkoszekstatic int cdnc_uart_bus_getsig(struct uart_softc *); 396249997Swkoszekstatic int cdnc_uart_bus_ioctl(struct uart_softc *, int, intptr_t); 397249997Swkoszekstatic int cdnc_uart_bus_ipend(struct uart_softc *); 398249997Swkoszekstatic int cdnc_uart_bus_param(struct uart_softc *, int, int, int, int); 399249997Swkoszekstatic int cdnc_uart_bus_receive(struct uart_softc *); 400249997Swkoszekstatic int cdnc_uart_bus_setsig(struct uart_softc *, int); 401249997Swkoszekstatic int cdnc_uart_bus_transmit(struct uart_softc *); 402262649Simpstatic void cdnc_uart_bus_grab(struct uart_softc *); 403262649Simpstatic void cdnc_uart_bus_ungrab(struct uart_softc *); 404249997Swkoszek 405249997Swkoszekstatic kobj_method_t cdnc_uart_bus_methods[] = { 406249997Swkoszek KOBJMETHOD(uart_probe, cdnc_uart_bus_probe), 407249997Swkoszek KOBJMETHOD(uart_attach, cdnc_uart_bus_attach), 408249997Swkoszek KOBJMETHOD(uart_flush, cdnc_uart_bus_flush), 409249997Swkoszek KOBJMETHOD(uart_getsig, cdnc_uart_bus_getsig), 410249997Swkoszek KOBJMETHOD(uart_ioctl, cdnc_uart_bus_ioctl), 411249997Swkoszek KOBJMETHOD(uart_ipend, cdnc_uart_bus_ipend), 412249997Swkoszek KOBJMETHOD(uart_param, cdnc_uart_bus_param), 413249997Swkoszek KOBJMETHOD(uart_receive, cdnc_uart_bus_receive), 414249997Swkoszek KOBJMETHOD(uart_setsig, cdnc_uart_bus_setsig), 415249997Swkoszek KOBJMETHOD(uart_transmit, cdnc_uart_bus_transmit), 416262649Simp KOBJMETHOD(uart_grab, cdnc_uart_bus_grab), 417262649Simp KOBJMETHOD(uart_ungrab, cdnc_uart_bus_ungrab), 418249997Swkoszek 419249997Swkoszek KOBJMETHOD_END 420249997Swkoszek}; 421249997Swkoszek 422249997Swkoszekint 423249997Swkoszekcdnc_uart_bus_probe(struct uart_softc *sc) 424249997Swkoszek{ 425249997Swkoszek 426249997Swkoszek sc->sc_txfifosz = UART_FIFO_SIZE; 427249997Swkoszek sc->sc_rxfifosz = UART_FIFO_SIZE; 428249997Swkoszek sc->sc_hwiflow = 0; 429249997Swkoszek sc->sc_hwoflow = 0; 430249997Swkoszek 431249997Swkoszek device_set_desc(sc->sc_dev, "Cadence UART"); 432249997Swkoszek 433249997Swkoszek return (0); 434249997Swkoszek} 435249997Swkoszek 436249997Swkoszekstatic int 437249997Swkoszekcdnc_uart_bus_attach(struct uart_softc *sc) 438249997Swkoszek{ 439249997Swkoszek struct uart_bas *bas = &sc->sc_bas; 440249997Swkoszek struct uart_devinfo *di; 441249997Swkoszek 442249997Swkoszek if (sc->sc_sysdev != NULL) { 443249997Swkoszek di = sc->sc_sysdev; 444249997Swkoszek (void)cdnc_uart_set_params(bas, di->baudrate, di->databits, 445249997Swkoszek di->stopbits, di->parity); 446249997Swkoszek } else 447249997Swkoszek cdnc_uart_hw_init(bas); 448249997Swkoszek 449249997Swkoszek (void)cdnc_uart_bus_getsig(sc); 450249997Swkoszek 451249997Swkoszek /* Enable interrupts. */ 452249997Swkoszek WR4(bas, CDNC_UART_IEN_REG, 453249997Swkoszek CDNC_UART_INT_RXTRIG | CDNC_UART_INT_RXTMOUT | 454249997Swkoszek CDNC_UART_INT_TXOVR | CDNC_UART_INT_RXOVR | 455249997Swkoszek CDNC_UART_INT_DMSI); 456249997Swkoszek 457249997Swkoszek return (0); 458249997Swkoszek} 459249997Swkoszek 460249997Swkoszekstatic int 461249997Swkoszekcdnc_uart_bus_transmit(struct uart_softc *sc) 462249997Swkoszek{ 463249997Swkoszek int i; 464249997Swkoszek struct uart_bas *bas = &sc->sc_bas; 465249997Swkoszek 466249997Swkoszek uart_lock(sc->sc_hwmtx); 467249997Swkoszek 468249997Swkoszek /* Clear sticky TXEMPTY status bit. */ 469249997Swkoszek WR4(bas, CDNC_UART_ISTAT_REG, CDNC_UART_INT_TXEMPTY); 470249997Swkoszek 471249997Swkoszek for (i = 0; i < sc->sc_txdatasz; i++) 472249997Swkoszek WR4(bas, CDNC_UART_FIFO, sc->sc_txbuf[i]); 473249997Swkoszek 474249997Swkoszek /* Enable TX empty interrupt. */ 475249997Swkoszek WR4(bas, CDNC_UART_IEN_REG, CDNC_UART_INT_TXEMPTY); 476249997Swkoszek sc->sc_txbusy = 1; 477249997Swkoszek 478249997Swkoszek uart_unlock(sc->sc_hwmtx); 479249997Swkoszek 480249997Swkoszek return (0); 481249997Swkoszek} 482249997Swkoszek 483249997Swkoszekstatic int 484249997Swkoszekcdnc_uart_bus_setsig(struct uart_softc *sc, int sig) 485249997Swkoszek{ 486249997Swkoszek struct uart_bas *bas = &sc->sc_bas; 487249997Swkoszek uint32_t new, old, modem_ctrl; 488249997Swkoszek 489249997Swkoszek do { 490249997Swkoszek old = sc->sc_hwsig; 491249997Swkoszek new = old; 492249997Swkoszek if (sig & SER_DDTR) { 493249997Swkoszek SIGCHG(sig & SER_DTR, new, SER_DTR, SER_DDTR); 494249997Swkoszek } 495249997Swkoszek if (sig & SER_DRTS) { 496249997Swkoszek SIGCHG(sig & SER_RTS, new, SER_RTS, SER_DRTS); 497249997Swkoszek } 498249997Swkoszek } while (!atomic_cmpset_32(&sc->sc_hwsig, old, new)); 499249997Swkoszek uart_lock(sc->sc_hwmtx); 500249997Swkoszek modem_ctrl = RD4(bas, CDNC_UART_MODEM_CTRL_REG) & 501249997Swkoszek ~(CDNC_UART_MODEM_CTRL_REG_DTR | CDNC_UART_MODEM_CTRL_REG_RTS); 502249997Swkoszek if ((new & SER_DTR) != 0) 503249997Swkoszek modem_ctrl |= CDNC_UART_MODEM_CTRL_REG_DTR; 504249997Swkoszek if ((new & SER_RTS) != 0) 505249997Swkoszek modem_ctrl |= CDNC_UART_MODEM_CTRL_REG_RTS; 506249997Swkoszek WR4(bas, CDNC_UART_MODEM_CTRL_REG, modem_ctrl); 507249997Swkoszek 508249997Swkoszek uart_unlock(sc->sc_hwmtx); 509249997Swkoszek return (0); 510249997Swkoszek} 511249997Swkoszek 512249997Swkoszekstatic int 513249997Swkoszekcdnc_uart_bus_receive(struct uart_softc *sc) 514249997Swkoszek{ 515249997Swkoszek struct uart_bas *bas = &sc->sc_bas; 516249997Swkoszek uint32_t status; 517249997Swkoszek int c, c_status = 0; 518249997Swkoszek 519249997Swkoszek uart_lock(sc->sc_hwmtx); 520249997Swkoszek 521249997Swkoszek /* Check for parity or framing errors and clear the status bits. */ 522249997Swkoszek status = RD4(bas, CDNC_UART_ISTAT_REG); 523249997Swkoszek if ((status & (CDNC_UART_INT_FRAMING | CDNC_UART_INT_PARITY)) != 0) { 524249997Swkoszek WR4(bas, CDNC_UART_ISTAT_REG, 525249997Swkoszek status & (CDNC_UART_INT_FRAMING | CDNC_UART_INT_PARITY)); 526249997Swkoszek if ((status & CDNC_UART_INT_PARITY) != 0) 527249997Swkoszek c_status |= UART_STAT_PARERR; 528249997Swkoszek if ((status & CDNC_UART_INT_FRAMING) != 0) 529249997Swkoszek c_status |= UART_STAT_FRAMERR; 530249997Swkoszek } 531249997Swkoszek 532249997Swkoszek while ((RD4(bas, CDNC_UART_CHAN_STAT_REG) & 533249997Swkoszek CDNC_UART_CHAN_STAT_REG_RXEMPTY) == 0) { 534249997Swkoszek c = RD4(bas, CDNC_UART_FIFO) & 0xff; 535249997Swkoszek#ifdef KDB 536249997Swkoszek /* Detect break and drop into debugger. */ 537249997Swkoszek if (c == 0 && (c_status & UART_STAT_FRAMERR) != 0 && 538249997Swkoszek sc->sc_sysdev != NULL && 539249997Swkoszek sc->sc_sysdev->type == UART_DEV_CONSOLE) { 540249997Swkoszek kdb_break(); 541249997Swkoszek WR4(bas, CDNC_UART_ISTAT_REG, CDNC_UART_INT_FRAMING); 542249997Swkoszek } 543249997Swkoszek#endif 544249997Swkoszek uart_rx_put(sc, c | c_status); 545249997Swkoszek } 546249997Swkoszek 547249997Swkoszek uart_unlock(sc->sc_hwmtx); 548249997Swkoszek 549249997Swkoszek return (0); 550249997Swkoszek} 551249997Swkoszek 552249997Swkoszekstatic int 553249997Swkoszekcdnc_uart_bus_param(struct uart_softc *sc, int baudrate, int databits, 554249997Swkoszek int stopbits, int parity) 555249997Swkoszek{ 556249997Swkoszek 557249997Swkoszek return (cdnc_uart_set_params(&sc->sc_bas, baudrate, 558249997Swkoszek databits, stopbits, parity)); 559249997Swkoszek} 560249997Swkoszek 561249997Swkoszekstatic int 562249997Swkoszekcdnc_uart_bus_ipend(struct uart_softc *sc) 563249997Swkoszek{ 564249997Swkoszek int ipend = 0; 565249997Swkoszek struct uart_bas *bas = &sc->sc_bas; 566249997Swkoszek uint32_t istatus; 567249997Swkoszek 568249997Swkoszek uart_lock(sc->sc_hwmtx); 569249997Swkoszek 570249997Swkoszek istatus = RD4(bas, CDNC_UART_ISTAT_REG); 571249997Swkoszek 572249997Swkoszek /* Clear interrupt bits. */ 573249997Swkoszek WR4(bas, CDNC_UART_ISTAT_REG, istatus & 574249997Swkoszek (CDNC_UART_INT_RXTRIG | CDNC_UART_INT_RXTMOUT | 575249997Swkoszek CDNC_UART_INT_TXOVR | CDNC_UART_INT_RXOVR | 576249997Swkoszek CDNC_UART_INT_TXEMPTY | CDNC_UART_INT_DMSI)); 577249997Swkoszek 578249997Swkoszek /* Receive data. */ 579249997Swkoszek if ((istatus & (CDNC_UART_INT_RXTRIG | CDNC_UART_INT_RXTMOUT)) != 0) 580249997Swkoszek ipend |= SER_INT_RXREADY; 581249997Swkoszek 582249997Swkoszek /* Transmit fifo empty. */ 583249997Swkoszek if (sc->sc_txbusy && (istatus & CDNC_UART_INT_TXEMPTY) != 0) { 584249997Swkoszek /* disable txempty interrupt. */ 585249997Swkoszek WR4(bas, CDNC_UART_IDIS_REG, CDNC_UART_INT_TXEMPTY); 586249997Swkoszek ipend |= SER_INT_TXIDLE; 587249997Swkoszek } 588249997Swkoszek 589249997Swkoszek /* TX Overflow. */ 590249997Swkoszek if ((istatus & CDNC_UART_INT_TXOVR) != 0) 591249997Swkoszek ipend |= SER_INT_OVERRUN; 592249997Swkoszek 593249997Swkoszek /* RX Overflow. */ 594249997Swkoszek if ((istatus & CDNC_UART_INT_RXOVR) != 0) 595249997Swkoszek ipend |= SER_INT_OVERRUN; 596249997Swkoszek 597249997Swkoszek /* Modem signal change. */ 598249997Swkoszek if ((istatus & CDNC_UART_INT_DMSI) != 0) { 599249997Swkoszek WR4(bas, CDNC_UART_MODEM_STAT_REG, 600249997Swkoszek CDNC_UART_MODEM_STAT_REG_DDCD | 601249997Swkoszek CDNC_UART_MODEM_STAT_REG_TERI | 602249997Swkoszek CDNC_UART_MODEM_STAT_REG_DDSR | 603249997Swkoszek CDNC_UART_MODEM_STAT_REG_DCTS); 604249997Swkoszek ipend |= SER_INT_SIGCHG; 605249997Swkoszek } 606249997Swkoszek 607249997Swkoszek uart_unlock(sc->sc_hwmtx); 608249997Swkoszek return (ipend); 609249997Swkoszek} 610249997Swkoszek 611249997Swkoszekstatic int 612249997Swkoszekcdnc_uart_bus_flush(struct uart_softc *sc, int what) 613249997Swkoszek{ 614249997Swkoszek 615249997Swkoszek return (0); 616249997Swkoszek} 617249997Swkoszek 618249997Swkoszekstatic int 619249997Swkoszekcdnc_uart_bus_getsig(struct uart_softc *sc) 620249997Swkoszek{ 621249997Swkoszek struct uart_bas *bas = &sc->sc_bas; 622249997Swkoszek uint32_t new, old, sig; 623249997Swkoszek uint8_t modem_status; 624249997Swkoszek 625249997Swkoszek do { 626249997Swkoszek old = sc->sc_hwsig; 627249997Swkoszek sig = old; 628249997Swkoszek uart_lock(sc->sc_hwmtx); 629249997Swkoszek modem_status = RD4(bas, CDNC_UART_MODEM_STAT_REG); 630249997Swkoszek uart_unlock(sc->sc_hwmtx); 631249997Swkoszek SIGCHG(modem_status & CDNC_UART_MODEM_STAT_REG_DSR, 632249997Swkoszek sig, SER_DSR, SER_DDSR); 633249997Swkoszek SIGCHG(modem_status & CDNC_UART_MODEM_STAT_REG_CTS, 634249997Swkoszek sig, SER_CTS, SER_DCTS); 635249997Swkoszek SIGCHG(modem_status & CDNC_UART_MODEM_STAT_REG_DCD, 636249997Swkoszek sig, SER_DCD, SER_DDCD); 637249997Swkoszek SIGCHG(modem_status & CDNC_UART_MODEM_STAT_REG_RI, 638249997Swkoszek sig, SER_RI, SER_DRI); 639249997Swkoszek new = sig & ~SER_MASK_DELTA; 640249997Swkoszek } while (!atomic_cmpset_32(&sc->sc_hwsig, old, new)); 641249997Swkoszek return (sig); 642249997Swkoszek} 643249997Swkoszek 644249997Swkoszekstatic int 645249997Swkoszekcdnc_uart_bus_ioctl(struct uart_softc *sc, int request, intptr_t data) 646249997Swkoszek{ 647249997Swkoszek struct uart_bas *bas = &sc->sc_bas; 648249997Swkoszek uint32_t uart_ctrl, modem_ctrl; 649249997Swkoszek int error = 0; 650249997Swkoszek 651249997Swkoszek uart_lock(sc->sc_hwmtx); 652249997Swkoszek 653249997Swkoszek switch (request) { 654249997Swkoszek case UART_IOCTL_BREAK: 655249997Swkoszek uart_ctrl = RD4(bas, CDNC_UART_CTRL_REG); 656249997Swkoszek if (data) { 657249997Swkoszek uart_ctrl |= CDNC_UART_CTRL_REG_STARTBRK; 658249997Swkoszek uart_ctrl &= ~CDNC_UART_CTRL_REG_STOPBRK; 659249997Swkoszek } else { 660249997Swkoszek uart_ctrl |= CDNC_UART_CTRL_REG_STOPBRK; 661249997Swkoszek uart_ctrl &= ~CDNC_UART_CTRL_REG_STARTBRK; 662249997Swkoszek } 663249997Swkoszek WR4(bas, CDNC_UART_CTRL_REG, uart_ctrl); 664249997Swkoszek break; 665249997Swkoszek case UART_IOCTL_IFLOW: 666249997Swkoszek modem_ctrl = RD4(bas, CDNC_UART_MODEM_CTRL_REG); 667249997Swkoszek if (data) 668249997Swkoszek modem_ctrl |= CDNC_UART_MODEM_CTRL_REG_RTS; 669249997Swkoszek else 670249997Swkoszek modem_ctrl &= ~CDNC_UART_MODEM_CTRL_REG_RTS; 671249997Swkoszek WR4(bas, CDNC_UART_MODEM_CTRL_REG, modem_ctrl); 672249997Swkoszek break; 673249997Swkoszek default: 674249997Swkoszek error = EINVAL; 675249997Swkoszek break; 676249997Swkoszek } 677249997Swkoszek 678249997Swkoszek uart_unlock(sc->sc_hwmtx); 679249997Swkoszek 680249997Swkoszek return (error); 681249997Swkoszek} 682249997Swkoszek 683262649Simpstatic void 684262649Simpcdnc_uart_bus_grab(struct uart_softc *sc) 685262649Simp{ 686262649Simp 687262649Simp /* Enable interrupts. */ 688262649Simp WR4(&sc->sc_bas, CDNC_UART_IEN_REG, 689262649Simp CDNC_UART_INT_TXOVR | CDNC_UART_INT_RXOVR | 690262649Simp CDNC_UART_INT_DMSI); 691262649Simp} 692262649Simp 693262649Simpstatic void 694262649Simpcdnc_uart_bus_ungrab(struct uart_softc *sc) 695262649Simp{ 696262649Simp 697262649Simp /* Enable interrupts. */ 698262649Simp WR4(&sc->sc_bas, CDNC_UART_IEN_REG, 699262649Simp CDNC_UART_INT_RXTRIG | CDNC_UART_INT_RXTMOUT | 700262649Simp CDNC_UART_INT_TXOVR | CDNC_UART_INT_RXOVR | 701262649Simp CDNC_UART_INT_DMSI); 702262649Simp} 703262649Simp 704283327Sianstatic struct uart_class uart_cdnc_class = { 705249997Swkoszek "cdnc_uart", 706249997Swkoszek cdnc_uart_bus_methods, 707249997Swkoszek sizeof(struct uart_softc), 708249997Swkoszek .uc_ops = &cdnc_uart_ops, 709249997Swkoszek .uc_range = 8 710249997Swkoszek}; 711283327Sian 712283327Sianstatic struct ofw_compat_data compat_data[] = { 713283327Sian {"cadence,uart", (uintptr_t)&uart_cdnc_class}, 714283327Sian {NULL, (uintptr_t)NULL}, 715283327Sian}; 716283327SianUART_FDT_CLASS_AND_DEVICE(compat_data); 717