exynos_uart.c revision 283323
1252391Sray/* 2252391Sray * Copyright (c) 2003 Marcel Moolenaar 3252391Sray * Copyright (c) 2007-2009 Andrew Turner 4252391Sray * Copyright (c) 2013 Ruslan Bukin <br@bsdpad.com> 5252391Sray * All rights reserved. 6252391Sray * 7252391Sray * Redistribution and use in source and binary forms, with or without 8252391Sray * modification, are permitted provided that the following conditions 9252391Sray * are met: 10252391Sray * 11252391Sray * 1. Redistributions of source code must retain the above copyright 12252391Sray * notice, this list of conditions and the following disclaimer. 13252391Sray * 2. Redistributions in binary form must reproduce the above copyright 14252391Sray * notice, this list of conditions and the following disclaimer in the 15252391Sray * documentation and/or other materials provided with the distribution. 16252391Sray * 17252391Sray * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18252391Sray * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19252391Sray * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20252391Sray * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21252391Sray * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22252391Sray * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23252391Sray * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24252391Sray * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25252391Sray * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26252391Sray * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27252391Sray */ 28252391Sray 29252391Sray#include <sys/cdefs.h> 30252391Sray__FBSDID("$FreeBSD: stable/10/sys/arm/samsung/exynos/exynos_uart.c 283323 2015-05-23 19:57:44Z ian $"); 31252391Sray 32252391Sray#include <sys/param.h> 33252391Sray#include <sys/systm.h> 34252391Sray#include <sys/bus.h> 35252391Sray#include <sys/conf.h> 36252391Sray#include <sys/cons.h> 37252391Sray#include <sys/tty.h> 38252391Sray#include <sys/rman.h> 39252391Sray#include <machine/bus.h> 40252391Sray#include <machine/intr.h> 41252391Sray 42252391Sray#include <dev/uart/uart.h> 43252391Sray#include <dev/uart/uart_cpu.h> 44252391Sray#include <dev/uart/uart_bus.h> 45252391Sray 46266944Sbr#include <arm/samsung/exynos/exynos_uart.h> 47252391Sray 48252391Sray#include "uart_if.h" 49252391Sray 50252391Sray#define DEF_CLK 100000000 51252391Sray 52252391Sraystatic int sscomspeed(long, long); 53283323Sianstatic int exynos4210_uart_param(struct uart_bas *, int, int, int, int); 54252391Sray 55252391Sray/* 56252391Sray * Low-level UART interface. 57252391Sray */ 58283323Sianstatic int exynos4210_probe(struct uart_bas *bas); 59283323Sianstatic void exynos4210_init(struct uart_bas *bas, int, int, int, int); 60283323Sianstatic void exynos4210_term(struct uart_bas *bas); 61283323Sianstatic void exynos4210_putc(struct uart_bas *bas, int); 62283323Sianstatic int exynos4210_rxready(struct uart_bas *bas); 63283323Sianstatic int exynos4210_getc(struct uart_bas *bas, struct mtx *mtx); 64252391Sray 65252391Srayextern SLIST_HEAD(uart_devinfo_list, uart_devinfo) uart_sysdevs; 66252391Sray 67252391Sraystatic int 68252391Sraysscomspeed(long speed, long frequency) 69252391Sray{ 70252391Sray int x; 71252391Sray 72252391Sray if (speed <= 0 || frequency <= 0) 73252391Sray return (-1); 74252391Sray x = (frequency / 16) / speed; 75252391Sray return (x-1); 76252391Sray} 77252391Sray 78252391Sraystatic int 79283323Sianexynos4210_uart_param(struct uart_bas *bas, int baudrate, int databits, 80252391Sray int stopbits, int parity) 81252391Sray{ 82252391Sray int brd, ulcon; 83252391Sray 84252391Sray ulcon = 0; 85252391Sray 86252391Sray switch(databits) { 87252391Sray case 5: 88252391Sray ulcon |= ULCON_LENGTH_5; 89252391Sray break; 90252391Sray case 6: 91252391Sray ulcon |= ULCON_LENGTH_6; 92252391Sray break; 93252391Sray case 7: 94252391Sray ulcon |= ULCON_LENGTH_7; 95252391Sray break; 96252391Sray case 8: 97252391Sray ulcon |= ULCON_LENGTH_8; 98252391Sray break; 99252391Sray default: 100252391Sray return (EINVAL); 101252391Sray } 102252391Sray 103252391Sray switch (parity) { 104252391Sray case UART_PARITY_NONE: 105252391Sray ulcon |= ULCON_PARITY_NONE; 106252391Sray break; 107252391Sray case UART_PARITY_ODD: 108252391Sray ulcon |= ULCON_PARITY_ODD; 109252391Sray break; 110252391Sray case UART_PARITY_EVEN: 111252391Sray ulcon |= ULCON_PARITY_EVEN; 112252391Sray break; 113252391Sray case UART_PARITY_MARK: 114252391Sray case UART_PARITY_SPACE: 115252391Sray default: 116252391Sray return (EINVAL); 117252391Sray } 118252391Sray 119252391Sray if (stopbits == 2) 120252391Sray ulcon |= ULCON_STOP; 121252391Sray 122252391Sray uart_setreg(bas, SSCOM_ULCON, ulcon); 123252391Sray 124252391Sray brd = sscomspeed(baudrate, bas->rclk); 125252391Sray uart_setreg(bas, SSCOM_UBRDIV, brd); 126252391Sray 127252391Sray return (0); 128252391Sray} 129252391Sray 130283323Sianstruct uart_ops uart_exynos4210_ops = { 131283323Sian .probe = exynos4210_probe, 132283323Sian .init = exynos4210_init, 133283323Sian .term = exynos4210_term, 134283323Sian .putc = exynos4210_putc, 135283323Sian .rxready = exynos4210_rxready, 136283323Sian .getc = exynos4210_getc, 137252391Sray}; 138252391Sray 139252391Sraystatic int 140283323Sianexynos4210_probe(struct uart_bas *bas) 141252391Sray{ 142252391Sray 143252391Sray return (0); 144252391Sray} 145252391Sray 146252391Sraystatic void 147283323Sianexynos4210_init(struct uart_bas *bas, int baudrate, int databits, int stopbits, 148252391Sray int parity) 149252391Sray{ 150252391Sray 151252391Sray if (bas->rclk == 0) 152252391Sray bas->rclk = DEF_CLK; 153252391Sray 154283323Sian KASSERT(bas->rclk != 0, ("exynos4210_init: Invalid rclk")); 155252391Sray 156252391Sray uart_setreg(bas, SSCOM_UCON, 0); 157252391Sray uart_setreg(bas, SSCOM_UFCON, 158252391Sray UFCON_TXTRIGGER_8 | UFCON_RXTRIGGER_8 | 159252391Sray UFCON_TXFIFO_RESET | UFCON_RXFIFO_RESET | 160252391Sray UFCON_FIFO_ENABLE); 161283323Sian exynos4210_uart_param(bas, baudrate, databits, stopbits, parity); 162252391Sray 163252391Sray /* Enable UART. */ 164252391Sray uart_setreg(bas, SSCOM_UCON, UCON_TXMODE_INT | UCON_RXMODE_INT | 165252391Sray UCON_TOINT); 166252391Sray uart_setreg(bas, SSCOM_UMCON, UMCON_RTS); 167252391Sray} 168252391Sray 169252391Sraystatic void 170283323Sianexynos4210_term(struct uart_bas *bas) 171252391Sray{ 172252391Sray /* XXX */ 173252391Sray} 174252391Sray 175252391Sraystatic void 176283323Sianexynos4210_putc(struct uart_bas *bas, int c) 177252391Sray{ 178252391Sray 179252391Sray while ((bus_space_read_4(bas->bst, bas->bsh, SSCOM_UFSTAT) & 180252391Sray UFSTAT_TXFULL) == UFSTAT_TXFULL) 181252391Sray continue; 182252391Sray 183252391Sray uart_setreg(bas, SSCOM_UTXH, c); 184252391Sray} 185252391Sray 186252391Sraystatic int 187283323Sianexynos4210_rxready(struct uart_bas *bas) 188252391Sray{ 189252391Sray 190252391Sray return ((uart_getreg(bas, SSCOM_UTRSTAT) & UTRSTAT_RXREADY) == 191252391Sray UTRSTAT_RXREADY); 192252391Sray} 193252391Sray 194252391Sraystatic int 195283323Sianexynos4210_getc(struct uart_bas *bas, struct mtx *mtx) 196252391Sray{ 197252391Sray int utrstat; 198252391Sray 199252391Sray utrstat = bus_space_read_1(bas->bst, bas->bsh, SSCOM_UTRSTAT); 200252391Sray while (!(utrstat & UTRSTAT_RXREADY)) { 201252391Sray utrstat = bus_space_read_1(bas->bst, bas->bsh, SSCOM_UTRSTAT); 202252391Sray continue; 203252391Sray } 204252391Sray 205252391Sray return (bus_space_read_1(bas->bst, bas->bsh, SSCOM_URXH)); 206252391Sray} 207252391Sray 208283323Sianstatic int exynos4210_bus_probe(struct uart_softc *sc); 209283323Sianstatic int exynos4210_bus_attach(struct uart_softc *sc); 210283323Sianstatic int exynos4210_bus_flush(struct uart_softc *, int); 211283323Sianstatic int exynos4210_bus_getsig(struct uart_softc *); 212283323Sianstatic int exynos4210_bus_ioctl(struct uart_softc *, int, intptr_t); 213283323Sianstatic int exynos4210_bus_ipend(struct uart_softc *); 214283323Sianstatic int exynos4210_bus_param(struct uart_softc *, int, int, int, int); 215283323Sianstatic int exynos4210_bus_receive(struct uart_softc *); 216283323Sianstatic int exynos4210_bus_setsig(struct uart_softc *, int); 217283323Sianstatic int exynos4210_bus_transmit(struct uart_softc *); 218252391Sray 219283323Sianstatic kobj_method_t exynos4210_methods[] = { 220283323Sian KOBJMETHOD(uart_probe, exynos4210_bus_probe), 221283323Sian KOBJMETHOD(uart_attach, exynos4210_bus_attach), 222283323Sian KOBJMETHOD(uart_flush, exynos4210_bus_flush), 223283323Sian KOBJMETHOD(uart_getsig, exynos4210_bus_getsig), 224283323Sian KOBJMETHOD(uart_ioctl, exynos4210_bus_ioctl), 225283323Sian KOBJMETHOD(uart_ipend, exynos4210_bus_ipend), 226283323Sian KOBJMETHOD(uart_param, exynos4210_bus_param), 227283323Sian KOBJMETHOD(uart_receive, exynos4210_bus_receive), 228283323Sian KOBJMETHOD(uart_setsig, exynos4210_bus_setsig), 229283323Sian KOBJMETHOD(uart_transmit, exynos4210_bus_transmit), 230252391Sray 231252391Sray {0, 0 } 232252391Sray}; 233252391Sray 234252391Srayint 235283323Sianexynos4210_bus_probe(struct uart_softc *sc) 236252391Sray{ 237252391Sray 238252391Sray sc->sc_txfifosz = 16; 239252391Sray sc->sc_rxfifosz = 16; 240252391Sray 241252391Sray return (0); 242252391Sray} 243252391Sray 244252391Sraystatic int 245283323Sianexynos4210_bus_attach(struct uart_softc *sc) 246252391Sray{ 247252391Sray 248252391Sray sc->sc_hwiflow = 0; 249252391Sray sc->sc_hwoflow = 0; 250252391Sray 251252391Sray return (0); 252252391Sray} 253252391Sray 254252391Sraystatic int 255283323Sianexynos4210_bus_transmit(struct uart_softc *sc) 256252391Sray{ 257252391Sray int i; 258252391Sray int reg; 259252391Sray 260252391Sray uart_lock(sc->sc_hwmtx); 261252391Sray 262252391Sray for (i = 0; i < sc->sc_txdatasz; i++) { 263283323Sian exynos4210_putc(&sc->sc_bas, sc->sc_txbuf[i]); 264252391Sray uart_barrier(&sc->sc_bas); 265252391Sray } 266252391Sray 267252391Sray sc->sc_txbusy = 1; 268252391Sray 269252391Sray uart_unlock(sc->sc_hwmtx); 270252391Sray 271252391Sray /* unmask TX interrupt */ 272252391Sray reg = bus_space_read_4(sc->sc_bas.bst, sc->sc_bas.bsh, SSCOM_UINTM); 273252391Sray reg &= ~(1 << 2); 274252391Sray bus_space_write_4(sc->sc_bas.bst, sc->sc_bas.bsh, SSCOM_UINTM, reg); 275252391Sray 276252391Sray return (0); 277252391Sray} 278252391Sray 279252391Sraystatic int 280283323Sianexynos4210_bus_setsig(struct uart_softc *sc, int sig) 281252391Sray{ 282252391Sray 283252391Sray return (0); 284252391Sray} 285252391Sray 286252391Sraystatic int 287283323Sianexynos4210_bus_receive(struct uart_softc *sc) 288252391Sray{ 289266942Sbr struct uart_bas *bas; 290252391Sray 291266942Sbr bas = &sc->sc_bas; 292266942Sbr while (bus_space_read_4(bas->bst, bas->bsh, 293266942Sbr SSCOM_UFSTAT) & UFSTAT_RXCOUNT) 294266942Sbr uart_rx_put(sc, uart_getreg(&sc->sc_bas, SSCOM_URXH)); 295266942Sbr 296252391Sray return (0); 297252391Sray} 298252391Sray 299252391Sraystatic int 300283323Sianexynos4210_bus_param(struct uart_softc *sc, int baudrate, int databits, 301252391Sray int stopbits, int parity) 302252391Sray{ 303252391Sray int error; 304252391Sray 305252391Sray if (sc->sc_bas.rclk == 0) 306252391Sray sc->sc_bas.rclk = DEF_CLK; 307252391Sray 308283323Sian KASSERT(sc->sc_bas.rclk != 0, ("exynos4210_init: Invalid rclk")); 309252391Sray 310252391Sray uart_lock(sc->sc_hwmtx); 311283323Sian error = exynos4210_uart_param(&sc->sc_bas, baudrate, databits, stopbits, 312252391Sray parity); 313252391Sray uart_unlock(sc->sc_hwmtx); 314252391Sray 315252391Sray return (error); 316252391Sray} 317252391Sray 318252391Sraystatic int 319283323Sianexynos4210_bus_ipend(struct uart_softc *sc) 320252391Sray{ 321252391Sray uint32_t ints; 322252391Sray uint32_t txempty, rxready; 323252391Sray int reg; 324252391Sray int ipend; 325252391Sray 326252391Sray uart_lock(sc->sc_hwmtx); 327252391Sray ints = bus_space_read_4(sc->sc_bas.bst, sc->sc_bas.bsh, SSCOM_UINTP); 328252391Sray bus_space_write_4(sc->sc_bas.bst, sc->sc_bas.bsh, SSCOM_UINTP, ints); 329252391Sray 330252391Sray txempty = (1 << 2); 331252391Sray rxready = (1 << 0); 332252391Sray 333252391Sray ipend = 0; 334252391Sray if ((ints & txempty) > 0) { 335252391Sray if (sc->sc_txbusy != 0) 336252391Sray ipend |= SER_INT_TXIDLE; 337252391Sray 338252391Sray /* mask TX interrupt */ 339252391Sray reg = bus_space_read_4(sc->sc_bas.bst, sc->sc_bas.bsh, 340252391Sray SSCOM_UINTM); 341252391Sray reg |= (1 << 2); 342252391Sray bus_space_write_4(sc->sc_bas.bst, sc->sc_bas.bsh, 343252391Sray SSCOM_UINTM, reg); 344252391Sray } 345252391Sray 346252391Sray if ((ints & rxready) > 0) { 347252391Sray ipend |= SER_INT_RXREADY; 348252391Sray } 349252391Sray 350252391Sray uart_unlock(sc->sc_hwmtx); 351252391Sray return (ipend); 352252391Sray} 353252391Sray 354252391Sraystatic int 355283323Sianexynos4210_bus_flush(struct uart_softc *sc, int what) 356252391Sray{ 357252391Sray 358252391Sray return (0); 359252391Sray} 360252391Sray 361252391Sraystatic int 362283323Sianexynos4210_bus_getsig(struct uart_softc *sc) 363252391Sray{ 364252391Sray 365252391Sray return (0); 366252391Sray} 367252391Sray 368252391Sraystatic int 369283323Sianexynos4210_bus_ioctl(struct uart_softc *sc, int request, intptr_t data) 370252391Sray{ 371252391Sray 372252391Sray return (EINVAL); 373252391Sray} 374252391Sray 375283323Sianstruct uart_class uart_exynos4210_class = { 376283323Sian "exynos4210 class", 377283323Sian exynos4210_methods, 378252391Sray 1, 379283323Sian .uc_ops = &uart_exynos4210_ops, 380252391Sray .uc_range = 8, 381252391Sray .uc_rclk = 0, 382252391Sray}; 383