1178173Simp/* $NetBSD: uart.c,v 1.2 2007/03/23 20:05:47 dogcow Exp $ */ 2178173Simp 3178173Simp/*- 4178173Simp * Copyright (c) 2007 Ruslan Ermilov and Vsevolod Lobko. 5178173Simp * Copyright (c) 2007 Oleksandr Tymoshenko. 6178173Simp * All rights reserved. 7178173Simp * 8178173Simp * Redistribution and use in source and binary forms, with or 9178173Simp * without modification, are permitted provided that the following 10178173Simp * conditions are met: 11178173Simp * 1. Redistributions of source code must retain the above copyright 12178173Simp * notice, this list of conditions and the following disclaimer. 13178173Simp * 2. Redistributions in binary form must reproduce the above 14178173Simp * copyright notice, this list of conditions and the following 15178173Simp * disclaimer in the documentation and/or other materials provided 16178173Simp * with the distribution. 17178173Simp * 3. The names of the authors may not be used to endorse or promote 18178173Simp * products derived from this software without specific prior 19178173Simp * written permission. 20178173Simp * 21178173Simp * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY 22178173Simp * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 23178173Simp * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 24178173Simp * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS 25178173Simp * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 26178173Simp * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 27178173Simp * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 28178173Simp * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 29178173Simp * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 30178173Simp * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 31178173Simp * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 32178173Simp * OF SUCH DAMAGE. 33178173Simp */ 34178173Simp 35178173Simp#include <sys/cdefs.h> 36178173Simp__FBSDID("$FreeBSD$"); 37178173Simp 38178173Simp#include <sys/param.h> 39178173Simp#include <sys/systm.h> 40178173Simp#include <sys/bus.h> 41178173Simp#include <sys/conf.h> 42178173Simp#include <machine/bus.h> 43178173Simp 44178173Simp#include <dev/uart/uart.h> 45178173Simp#include <dev/uart/uart_cpu.h> 46178173Simp#include <dev/uart/uart_bus.h> 47178173Simp 48182901Sgonzo#include <mips/adm5120/uart_dev_adm5120.h> 49178173Simp 50178173Simp#include "uart_if.h" 51178173Simp 52178173Simp/* 53178173Simp * Low-level UART interface. 54178173Simp */ 55178173Simpstatic int adm5120_uart_probe(struct uart_bas *bas); 56178173Simpstatic void adm5120_uart_init(struct uart_bas *bas, int, int, int, int); 57178173Simpstatic void adm5120_uart_term(struct uart_bas *bas); 58178173Simpstatic void adm5120_uart_putc(struct uart_bas *bas, int); 59178173Simpstatic int adm5120_uart_rxready(struct uart_bas *bas); 60178173Simpstatic int adm5120_uart_getc(struct uart_bas *bas, struct mtx *); 61178173Simp 62178173Simpstatic struct uart_ops uart_adm5120_uart_ops = { 63178173Simp .probe = adm5120_uart_probe, 64178173Simp .init = adm5120_uart_init, 65178173Simp .term = adm5120_uart_term, 66178173Simp .putc = adm5120_uart_putc, 67178173Simp .rxready = adm5120_uart_rxready, 68178173Simp .getc = adm5120_uart_getc, 69178173Simp}; 70178173Simp 71178173Simpstatic int 72178173Simpadm5120_uart_probe(struct uart_bas *bas) 73178173Simp{ 74178173Simp 75178173Simp return (0); 76178173Simp} 77178173Simp 78178173Simpstatic void 79178173Simpadm5120_uart_init(struct uart_bas *bas, int baudrate, int databits, 80178173Simp int stopbits, int parity) 81178173Simp{ 82178173Simp 83178173Simp /* TODO: Set parameters for uart, meanwhile stick with 115200N1 */ 84178173Simp} 85178173Simp 86178173Simpstatic void 87178173Simpadm5120_uart_term(struct uart_bas *bas) 88178173Simp{ 89178173Simp 90178173Simp} 91178173Simp 92178173Simpstatic void 93178173Simpadm5120_uart_putc(struct uart_bas *bas, int c) 94178173Simp{ 95178173Simp char chr; 96178173Simp chr = c; 97178173Simp while (uart_getreg(bas, UART_FR_REG) & UART_FR_TX_FIFO_FULL) 98178173Simp ; 99178173Simp uart_setreg(bas, UART_DR_REG, c); 100178173Simp while (uart_getreg(bas, UART_FR_REG) & UART_FR_BUSY) 101178173Simp ; 102178173Simp uart_barrier(bas); 103178173Simp} 104178173Simp 105178173Simpstatic int 106178173Simpadm5120_uart_rxready(struct uart_bas *bas) 107178173Simp{ 108178173Simp if (uart_getreg(bas, UART_FR_REG) & UART_FR_RX_FIFO_EMPTY) 109178173Simp return (0); 110178173Simp 111178173Simp return (1); 112178173Simp} 113178173Simp 114178173Simpstatic int 115178173Simpadm5120_uart_getc(struct uart_bas *bas, struct mtx *hwmtx) 116178173Simp{ 117178173Simp int c; 118178173Simp 119178173Simp uart_lock(hwmtx); 120178173Simp 121178173Simp while (uart_getreg(bas, UART_FR_REG) & UART_FR_RX_FIFO_EMPTY) { 122178173Simp uart_unlock(hwmtx); 123178173Simp DELAY(10); 124178173Simp uart_lock(hwmtx); 125178173Simp } 126178173Simp 127178173Simp c = uart_getreg(bas, UART_DR_REG); 128178173Simp 129178173Simp uart_unlock(hwmtx); 130178173Simp 131178173Simp return (c); 132178173Simp} 133178173Simp 134178173Simp/* 135178173Simp * High-level UART interface. 136178173Simp */ 137178173Simpstruct adm5120_uart_softc { 138178173Simp struct uart_softc base; 139178173Simp}; 140178173Simp 141178173Simpstatic int adm5120_uart_bus_attach(struct uart_softc *); 142178173Simpstatic int adm5120_uart_bus_detach(struct uart_softc *); 143178173Simpstatic int adm5120_uart_bus_flush(struct uart_softc *, int); 144178173Simpstatic int adm5120_uart_bus_getsig(struct uart_softc *); 145178173Simpstatic int adm5120_uart_bus_ioctl(struct uart_softc *, int, intptr_t); 146178173Simpstatic int adm5120_uart_bus_ipend(struct uart_softc *); 147178173Simpstatic int adm5120_uart_bus_param(struct uart_softc *, int, int, int, int); 148178173Simpstatic int adm5120_uart_bus_probe(struct uart_softc *); 149178173Simpstatic int adm5120_uart_bus_receive(struct uart_softc *); 150178173Simpstatic int adm5120_uart_bus_setsig(struct uart_softc *, int); 151178173Simpstatic int adm5120_uart_bus_transmit(struct uart_softc *); 152262649Simpstatic void adm5120_uart_bus_grab(struct uart_softc *); 153262649Simpstatic void adm5120_uart_bus_ungrab(struct uart_softc *); 154178173Simp 155178173Simpstatic kobj_method_t adm5120_uart_methods[] = { 156178173Simp KOBJMETHOD(uart_attach, adm5120_uart_bus_attach), 157178173Simp KOBJMETHOD(uart_detach, adm5120_uart_bus_detach), 158178173Simp KOBJMETHOD(uart_flush, adm5120_uart_bus_flush), 159178173Simp KOBJMETHOD(uart_getsig, adm5120_uart_bus_getsig), 160178173Simp KOBJMETHOD(uart_ioctl, adm5120_uart_bus_ioctl), 161178173Simp KOBJMETHOD(uart_ipend, adm5120_uart_bus_ipend), 162178173Simp KOBJMETHOD(uart_param, adm5120_uart_bus_param), 163178173Simp KOBJMETHOD(uart_probe, adm5120_uart_bus_probe), 164178173Simp KOBJMETHOD(uart_receive, adm5120_uart_bus_receive), 165178173Simp KOBJMETHOD(uart_setsig, adm5120_uart_bus_setsig), 166178173Simp KOBJMETHOD(uart_transmit, adm5120_uart_bus_transmit), 167262649Simp KOBJMETHOD(uart_grab, adm5120_uart_bus_grab), 168262649Simp KOBJMETHOD(uart_ungrab, adm5120_uart_bus_ungrab), 169178173Simp { 0, 0 } 170178173Simp}; 171178173Simp 172178173Simpstruct uart_class uart_adm5120_uart_class = { 173178173Simp "adm5120", 174178173Simp adm5120_uart_methods, 175178173Simp sizeof(struct adm5120_uart_softc), 176178173Simp .uc_ops = &uart_adm5120_uart_ops, 177178173Simp .uc_range = 1, /* use hinted range */ 178178173Simp .uc_rclk = 62500000 179178173Simp}; 180178173Simp 181178173Simp#define SIGCHG(c, i, s, d) \ 182178173Simp if (c) { \ 183178173Simp i |= (i & s) ? s : s | d; \ 184178173Simp } else { \ 185178173Simp i = (i & s) ? (i & ~s) | d : i; \ 186178173Simp } 187178173Simp 188178173Simp/* 189178173Simp * Disable TX interrupt. uart should be locked 190178173Simp */ 191178173Simpstatic __inline void 192178173Simpadm5120_uart_disable_txintr(struct uart_softc *sc) 193178173Simp{ 194178173Simp uint8_t cr; 195178173Simp 196178173Simp cr = uart_getreg(&sc->sc_bas, UART_CR_REG); 197178173Simp cr &= ~UART_CR_TX_INT_EN; 198178173Simp uart_setreg(&sc->sc_bas, UART_CR_REG, cr); 199178173Simp} 200178173Simp 201178173Simp/* 202178173Simp * Enable TX interrupt. uart should be locked 203178173Simp */ 204178173Simpstatic __inline void 205178173Simpadm5120_uart_enable_txintr(struct uart_softc *sc) 206178173Simp{ 207178173Simp uint8_t cr; 208178173Simp 209178173Simp cr = uart_getreg(&sc->sc_bas, UART_CR_REG); 210178173Simp cr |= UART_CR_TX_INT_EN; 211178173Simp uart_setreg(&sc->sc_bas, UART_CR_REG, cr); 212178173Simp} 213178173Simp 214178173Simpstatic int 215178173Simpadm5120_uart_bus_attach(struct uart_softc *sc) 216178173Simp{ 217178173Simp struct uart_bas *bas; 218178173Simp struct uart_devinfo *di; 219178173Simp 220178173Simp bas = &sc->sc_bas; 221178173Simp if (sc->sc_sysdev != NULL) { 222178173Simp di = sc->sc_sysdev; 223178173Simp /* TODO: set parameters from di */ 224178173Simp } else { 225178173Simp /* TODO: set parameters 115200, 8N1 */ 226178173Simp } 227178173Simp 228178173Simp (void)adm5120_uart_bus_getsig(sc); 229178173Simp 230178173Simp#if 1 231178173Simp /* Enable FIFO */ 232178173Simp uart_setreg(bas, UART_LCR_H_REG, 233178173Simp uart_getreg(bas, UART_LCR_H_REG) | UART_LCR_H_FEN); 234178173Simp#endif 235178173Simp /* Enable interrupts */ 236178173Simp uart_setreg(bas, UART_CR_REG, 237178173Simp UART_CR_PORT_EN|UART_CR_RX_INT_EN|UART_CR_RX_TIMEOUT_INT_EN| 238178173Simp UART_CR_MODEM_STATUS_INT_EN); 239178173Simp 240178173Simp return (0); 241178173Simp} 242178173Simp 243178173Simpstatic int 244178173Simpadm5120_uart_bus_detach(struct uart_softc *sc) 245178173Simp{ 246178173Simp 247178173Simp return (0); 248178173Simp} 249178173Simp 250178173Simpstatic int 251178173Simpadm5120_uart_bus_flush(struct uart_softc *sc, int what) 252178173Simp{ 253178173Simp 254178173Simp return (0); 255178173Simp} 256178173Simp 257178173Simpstatic int 258178173Simpadm5120_uart_bus_getsig(struct uart_softc *sc) 259178173Simp{ 260178173Simp uint32_t new, old, sig; 261178173Simp uint8_t bes; 262178173Simp 263178173Simp do { 264178173Simp old = sc->sc_hwsig; 265178173Simp sig = old; 266178173Simp uart_lock(sc->sc_hwmtx); 267178173Simp bes = uart_getreg(&sc->sc_bas, UART_FR_REG); 268178173Simp uart_unlock(sc->sc_hwmtx); 269178173Simp SIGCHG(bes & UART_FR_CTS, sig, SER_CTS, SER_DCTS); 270178173Simp SIGCHG(bes & UART_FR_DCD, sig, SER_DCD, SER_DDCD); 271178173Simp SIGCHG(bes & UART_FR_DSR, sig, SER_DSR, SER_DDSR); 272178173Simp new = sig & ~SER_MASK_DELTA; 273178173Simp } while (!atomic_cmpset_32(&sc->sc_hwsig, old, new)); 274178173Simp 275178173Simp return (sig); 276178173Simp} 277178173Simp 278178173Simpstatic int 279178173Simpadm5120_uart_bus_ioctl(struct uart_softc *sc, int request, intptr_t data) 280178173Simp{ 281178173Simp struct uart_bas *bas; 282178173Simp int baudrate, divisor, error; 283178173Simp 284178173Simp bas = &sc->sc_bas; 285178173Simp error = 0; 286178173Simp uart_lock(sc->sc_hwmtx); 287178173Simp switch (request) { 288178173Simp case UART_IOCTL_BREAK: 289178173Simp /* TODO: Send BREAK */ 290178173Simp break; 291178173Simp case UART_IOCTL_BAUD: 292178173Simp divisor = uart_getreg(bas, UART_LCR_M_REG); 293178173Simp divisor = (divisor << 8) | 294178173Simp uart_getreg(bas, UART_LCR_L_REG); 295178173Simp baudrate = bas->rclk / 2 / (divisor + 2); 296178173Simp *(int*)data = baudrate; 297178173Simp break; 298178173Simp default: 299178173Simp error = EINVAL; 300178173Simp break; 301178173Simp } 302178173Simp uart_unlock(sc->sc_hwmtx); 303178173Simp return (error); 304178173Simp} 305178173Simp 306178173Simpstatic int 307178173Simpadm5120_uart_bus_ipend(struct uart_softc *sc) 308178173Simp{ 309178173Simp struct uart_bas *bas; 310178173Simp int ipend; 311178173Simp uint8_t ir, fr, rsr; 312178173Simp 313178173Simp bas = &sc->sc_bas; 314178173Simp ipend = 0; 315178173Simp 316178173Simp uart_lock(sc->sc_hwmtx); 317178173Simp ir = uart_getreg(&sc->sc_bas, UART_IR_REG); 318178173Simp fr = uart_getreg(&sc->sc_bas, UART_FR_REG); 319178173Simp rsr = uart_getreg(&sc->sc_bas, UART_RSR_REG); 320178173Simp 321178173Simp if (ir & UART_IR_RX_INT) 322178173Simp ipend |= SER_INT_RXREADY; 323178173Simp 324178173Simp if (ir & UART_IR_RX_TIMEOUT_INT) 325178173Simp ipend |= SER_INT_RXREADY; 326178173Simp 327178173Simp if (ir & UART_IR_MODEM_STATUS_INT) 328178173Simp ipend |= SER_INT_SIGCHG; 329178173Simp 330178173Simp if (rsr & UART_RSR_BE) 331178173Simp ipend |= SER_INT_BREAK; 332178173Simp 333178173Simp if (rsr & UART_RSR_OE) 334178173Simp ipend |= SER_INT_OVERRUN; 335178173Simp 336178173Simp if (fr & UART_FR_TX_FIFO_EMPTY) { 337178173Simp if (ir & UART_IR_TX_INT) { 338178173Simp adm5120_uart_disable_txintr(sc); 339178173Simp ipend |= SER_INT_TXIDLE; 340178173Simp } 341178173Simp } 342178173Simp 343178173Simp if (ipend) 344178173Simp uart_setreg(bas, UART_IR_REG, ir | UART_IR_UICR); 345178173Simp 346178173Simp uart_unlock(sc->sc_hwmtx); 347178173Simp 348178173Simp return (ipend); 349178173Simp} 350178173Simp 351178173Simpstatic int 352178173Simpadm5120_uart_bus_param(struct uart_softc *sc, int baudrate, int databits, 353178173Simp int stopbits, int parity) 354178173Simp{ 355178173Simp 356178173Simp /* TODO: Set parameters for uart, meanwhile stick with 115200 8N1 */ 357178173Simp return (0); 358178173Simp} 359178173Simp 360178173Simpstatic int 361178173Simpadm5120_uart_bus_probe(struct uart_softc *sc) 362178173Simp{ 363178173Simp char buf[80]; 364178173Simp int error; 365178173Simp char ch; 366178173Simp 367178173Simp error = adm5120_uart_probe(&sc->sc_bas); 368178173Simp if (error) 369178173Simp return (error); 370178173Simp 371248965Sian sc->sc_rxfifosz = 16; 372248965Sian sc->sc_txfifosz = 16; 373248965Sian 374178173Simp ch = sc->sc_bas.chan + 'A'; 375178173Simp 376178173Simp snprintf(buf, sizeof(buf), "adm5120_uart, channel %c", ch); 377178173Simp device_set_desc_copy(sc->sc_dev, buf); 378178173Simp 379178173Simp return (0); 380178173Simp} 381178173Simp 382178173Simpstatic int 383178173Simpadm5120_uart_bus_receive(struct uart_softc *sc) 384178173Simp{ 385178173Simp struct uart_bas *bas; 386178173Simp int xc; 387178173Simp uint8_t fr, rsr; 388178173Simp 389178173Simp bas = &sc->sc_bas; 390178173Simp uart_lock(sc->sc_hwmtx); 391178173Simp fr = uart_getreg(bas, UART_FR_REG); 392178173Simp while (!(fr & UART_FR_RX_FIFO_EMPTY)) { 393178173Simp if (uart_rx_full(sc)) { 394178173Simp sc->sc_rxbuf[sc->sc_rxput] = UART_STAT_OVERRUN; 395178173Simp break; 396178173Simp } 397178173Simp xc = 0; 398178173Simp rsr = uart_getreg(bas, UART_RSR_REG); 399178173Simp if (rsr & UART_RSR_FE) 400178173Simp xc |= UART_STAT_FRAMERR; 401178173Simp if (rsr & UART_RSR_PE) 402178173Simp xc |= UART_STAT_PARERR; 403178173Simp if (rsr & UART_RSR_OE) 404178173Simp xc |= UART_STAT_OVERRUN; 405178173Simp xc |= uart_getreg(bas, UART_DR_REG); 406178173Simp uart_barrier(bas); 407178173Simp uart_rx_put(sc, xc); 408178173Simp if (rsr & (UART_RSR_FE | UART_RSR_PE | UART_RSR_OE)) { 409178173Simp uart_setreg(bas, UART_ECR_REG, UART_ECR_RSR); 410178173Simp uart_barrier(bas); 411178173Simp } 412178173Simp fr = uart_getreg(bas, UART_FR_REG); 413178173Simp } 414178173Simp 415178173Simp /* Discard everything left in the Rx FIFO. */ 416178173Simp while (!(fr & UART_FR_RX_FIFO_EMPTY)) { 417178173Simp ( void)uart_getreg(bas, UART_DR_REG); 418178173Simp uart_barrier(bas); 419178173Simp rsr = uart_getreg(bas, UART_RSR_REG); 420178173Simp if (rsr & (UART_RSR_FE | UART_RSR_PE | UART_RSR_OE)) { 421178173Simp uart_setreg(bas, UART_ECR_REG, UART_ECR_RSR); 422178173Simp uart_barrier(bas); 423178173Simp } 424178173Simp fr = uart_getreg(bas, UART_FR_REG); 425178173Simp } 426178173Simp uart_unlock(sc->sc_hwmtx); 427178173Simp return (0); 428178173Simp} 429178173Simp 430178173Simpstatic int 431178173Simpadm5120_uart_bus_setsig(struct uart_softc *sc, int sig) 432178173Simp{ 433178173Simp 434178173Simp /* TODO: implement (?) */ 435178173Simp return (0); 436178173Simp} 437178173Simp 438178173Simpstatic int 439178173Simpadm5120_uart_bus_transmit(struct uart_softc *sc) 440178173Simp{ 441178173Simp struct uart_bas *bas; 442178173Simp 443178173Simp bas = &sc->sc_bas; 444178173Simp uart_lock(sc->sc_hwmtx); 445178173Simp sc->sc_txbusy = 1; 446178173Simp for (int i = 0; i < sc->sc_txdatasz; i++) { 447178173Simp if (uart_getreg(bas, UART_FR_REG) & UART_FR_TX_FIFO_FULL) 448178173Simp break; 449178173Simp uart_setreg(bas, UART_DR_REG, sc->sc_txbuf[i]); 450178173Simp } 451178173Simp 452178173Simp /* Enable TX interrupt */ 453178173Simp adm5120_uart_enable_txintr(sc); 454178173Simp uart_unlock(sc->sc_hwmtx); 455178173Simp return (0); 456178173Simp} 457262649Simp 458262649Simpstatic void 459262649Simpadm5120_uart_bus_grab(struct uart_softc *sc) 460262649Simp{ 461262649Simp 462262649Simp /* Enable interrupts - no RX_INT or RX_TIMEOUT */ 463262649Simp uart_lock(sc->sc_hwmtx); 464262649Simp uart_setreg(&sc->sc_bas, UART_CR_REG, 465262649Simp UART_CR_PORT_EN | UART_CR_MODEM_STATUS_INT_EN); 466262649Simp uart_unlock(sc->sc_hwmtx); 467262649Simp} 468262649Simp 469262649Simpstatic void 470262649Simpadm5120_uart_bus_ungrab(struct uart_softc *sc) 471262649Simp{ 472262649Simp 473262649Simp /* Enable interrupts */ 474262649Simp uart_lock(sc->sc_hwmtx); 475262649Simp uart_setreg(&sc->sc_bas, UART_CR_REG, 476262649Simp UART_CR_PORT_EN|UART_CR_RX_INT_EN|UART_CR_RX_TIMEOUT_INT_EN| 477262649Simp UART_CR_MODEM_STATUS_INT_EN); 478262649Simp uart_unlock(sc->sc_hwmtx); 479262649Simp} 480