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