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