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: releng/10.2/sys/arm/samsung/exynos/exynos_uart.c 283327 2015-05-23 20:54:25Z 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> 44283327Sian#include <dev/uart/uart_cpu_fdt.h> 45252391Sray#include <dev/uart/uart_bus.h> 46252391Sray 47266944Sbr#include <arm/samsung/exynos/exynos_uart.h> 48252391Sray 49252391Sray#include "uart_if.h" 50252391Sray 51252391Sray#define DEF_CLK 100000000 52252391Sray 53252391Sraystatic int sscomspeed(long, long); 54283323Sianstatic int exynos4210_uart_param(struct uart_bas *, int, int, int, int); 55252391Sray 56252391Sray/* 57252391Sray * Low-level UART interface. 58252391Sray */ 59283323Sianstatic int exynos4210_probe(struct uart_bas *bas); 60283323Sianstatic void exynos4210_init(struct uart_bas *bas, int, int, int, int); 61283323Sianstatic void exynos4210_term(struct uart_bas *bas); 62283323Sianstatic void exynos4210_putc(struct uart_bas *bas, int); 63283323Sianstatic int exynos4210_rxready(struct uart_bas *bas); 64283323Sianstatic int exynos4210_getc(struct uart_bas *bas, struct mtx *mtx); 65252391Sray 66252391Srayextern SLIST_HEAD(uart_devinfo_list, uart_devinfo) uart_sysdevs; 67252391Sray 68252391Sraystatic int 69252391Sraysscomspeed(long speed, long frequency) 70252391Sray{ 71252391Sray int x; 72252391Sray 73252391Sray if (speed <= 0 || frequency <= 0) 74252391Sray return (-1); 75252391Sray x = (frequency / 16) / speed; 76252391Sray return (x-1); 77252391Sray} 78252391Sray 79252391Sraystatic int 80283323Sianexynos4210_uart_param(struct uart_bas *bas, int baudrate, int databits, 81252391Sray int stopbits, int parity) 82252391Sray{ 83252391Sray int brd, ulcon; 84252391Sray 85252391Sray ulcon = 0; 86252391Sray 87252391Sray switch(databits) { 88252391Sray case 5: 89252391Sray ulcon |= ULCON_LENGTH_5; 90252391Sray break; 91252391Sray case 6: 92252391Sray ulcon |= ULCON_LENGTH_6; 93252391Sray break; 94252391Sray case 7: 95252391Sray ulcon |= ULCON_LENGTH_7; 96252391Sray break; 97252391Sray case 8: 98252391Sray ulcon |= ULCON_LENGTH_8; 99252391Sray break; 100252391Sray default: 101252391Sray return (EINVAL); 102252391Sray } 103252391Sray 104252391Sray switch (parity) { 105252391Sray case UART_PARITY_NONE: 106252391Sray ulcon |= ULCON_PARITY_NONE; 107252391Sray break; 108252391Sray case UART_PARITY_ODD: 109252391Sray ulcon |= ULCON_PARITY_ODD; 110252391Sray break; 111252391Sray case UART_PARITY_EVEN: 112252391Sray ulcon |= ULCON_PARITY_EVEN; 113252391Sray break; 114252391Sray case UART_PARITY_MARK: 115252391Sray case UART_PARITY_SPACE: 116252391Sray default: 117252391Sray return (EINVAL); 118252391Sray } 119252391Sray 120252391Sray if (stopbits == 2) 121252391Sray ulcon |= ULCON_STOP; 122252391Sray 123252391Sray uart_setreg(bas, SSCOM_ULCON, ulcon); 124252391Sray 125252391Sray brd = sscomspeed(baudrate, bas->rclk); 126252391Sray uart_setreg(bas, SSCOM_UBRDIV, brd); 127252391Sray 128252391Sray return (0); 129252391Sray} 130252391Sray 131283323Sianstruct uart_ops uart_exynos4210_ops = { 132283323Sian .probe = exynos4210_probe, 133283323Sian .init = exynos4210_init, 134283323Sian .term = exynos4210_term, 135283323Sian .putc = exynos4210_putc, 136283323Sian .rxready = exynos4210_rxready, 137283323Sian .getc = exynos4210_getc, 138252391Sray}; 139252391Sray 140252391Sraystatic int 141283323Sianexynos4210_probe(struct uart_bas *bas) 142252391Sray{ 143252391Sray 144252391Sray return (0); 145252391Sray} 146252391Sray 147252391Sraystatic void 148283323Sianexynos4210_init(struct uart_bas *bas, int baudrate, int databits, int stopbits, 149252391Sray int parity) 150252391Sray{ 151252391Sray 152252391Sray if (bas->rclk == 0) 153252391Sray bas->rclk = DEF_CLK; 154252391Sray 155283323Sian KASSERT(bas->rclk != 0, ("exynos4210_init: Invalid rclk")); 156252391Sray 157252391Sray uart_setreg(bas, SSCOM_UCON, 0); 158252391Sray uart_setreg(bas, SSCOM_UFCON, 159252391Sray UFCON_TXTRIGGER_8 | UFCON_RXTRIGGER_8 | 160252391Sray UFCON_TXFIFO_RESET | UFCON_RXFIFO_RESET | 161252391Sray UFCON_FIFO_ENABLE); 162283323Sian exynos4210_uart_param(bas, baudrate, databits, stopbits, parity); 163252391Sray 164252391Sray /* Enable UART. */ 165252391Sray uart_setreg(bas, SSCOM_UCON, UCON_TXMODE_INT | UCON_RXMODE_INT | 166252391Sray UCON_TOINT); 167252391Sray uart_setreg(bas, SSCOM_UMCON, UMCON_RTS); 168252391Sray} 169252391Sray 170252391Sraystatic void 171283323Sianexynos4210_term(struct uart_bas *bas) 172252391Sray{ 173252391Sray /* XXX */ 174252391Sray} 175252391Sray 176252391Sraystatic void 177283323Sianexynos4210_putc(struct uart_bas *bas, int c) 178252391Sray{ 179252391Sray 180252391Sray while ((bus_space_read_4(bas->bst, bas->bsh, SSCOM_UFSTAT) & 181252391Sray UFSTAT_TXFULL) == UFSTAT_TXFULL) 182252391Sray continue; 183252391Sray 184252391Sray uart_setreg(bas, SSCOM_UTXH, c); 185252391Sray} 186252391Sray 187252391Sraystatic int 188283323Sianexynos4210_rxready(struct uart_bas *bas) 189252391Sray{ 190252391Sray 191252391Sray return ((uart_getreg(bas, SSCOM_UTRSTAT) & UTRSTAT_RXREADY) == 192252391Sray UTRSTAT_RXREADY); 193252391Sray} 194252391Sray 195252391Sraystatic int 196283323Sianexynos4210_getc(struct uart_bas *bas, struct mtx *mtx) 197252391Sray{ 198252391Sray int utrstat; 199252391Sray 200252391Sray utrstat = bus_space_read_1(bas->bst, bas->bsh, SSCOM_UTRSTAT); 201252391Sray while (!(utrstat & UTRSTAT_RXREADY)) { 202252391Sray utrstat = bus_space_read_1(bas->bst, bas->bsh, SSCOM_UTRSTAT); 203252391Sray continue; 204252391Sray } 205252391Sray 206252391Sray return (bus_space_read_1(bas->bst, bas->bsh, SSCOM_URXH)); 207252391Sray} 208252391Sray 209283323Sianstatic int exynos4210_bus_probe(struct uart_softc *sc); 210283323Sianstatic int exynos4210_bus_attach(struct uart_softc *sc); 211283323Sianstatic int exynos4210_bus_flush(struct uart_softc *, int); 212283323Sianstatic int exynos4210_bus_getsig(struct uart_softc *); 213283323Sianstatic int exynos4210_bus_ioctl(struct uart_softc *, int, intptr_t); 214283323Sianstatic int exynos4210_bus_ipend(struct uart_softc *); 215283323Sianstatic int exynos4210_bus_param(struct uart_softc *, int, int, int, int); 216283323Sianstatic int exynos4210_bus_receive(struct uart_softc *); 217283323Sianstatic int exynos4210_bus_setsig(struct uart_softc *, int); 218283323Sianstatic int exynos4210_bus_transmit(struct uart_softc *); 219252391Sray 220283323Sianstatic kobj_method_t exynos4210_methods[] = { 221283323Sian KOBJMETHOD(uart_probe, exynos4210_bus_probe), 222283323Sian KOBJMETHOD(uart_attach, exynos4210_bus_attach), 223283323Sian KOBJMETHOD(uart_flush, exynos4210_bus_flush), 224283323Sian KOBJMETHOD(uart_getsig, exynos4210_bus_getsig), 225283323Sian KOBJMETHOD(uart_ioctl, exynos4210_bus_ioctl), 226283323Sian KOBJMETHOD(uart_ipend, exynos4210_bus_ipend), 227283323Sian KOBJMETHOD(uart_param, exynos4210_bus_param), 228283323Sian KOBJMETHOD(uart_receive, exynos4210_bus_receive), 229283323Sian KOBJMETHOD(uart_setsig, exynos4210_bus_setsig), 230283323Sian KOBJMETHOD(uart_transmit, exynos4210_bus_transmit), 231252391Sray 232252391Sray {0, 0 } 233252391Sray}; 234252391Sray 235252391Srayint 236283323Sianexynos4210_bus_probe(struct uart_softc *sc) 237252391Sray{ 238252391Sray 239252391Sray sc->sc_txfifosz = 16; 240252391Sray sc->sc_rxfifosz = 16; 241252391Sray 242252391Sray return (0); 243252391Sray} 244252391Sray 245252391Sraystatic int 246283323Sianexynos4210_bus_attach(struct uart_softc *sc) 247252391Sray{ 248252391Sray 249252391Sray sc->sc_hwiflow = 0; 250252391Sray sc->sc_hwoflow = 0; 251252391Sray 252252391Sray return (0); 253252391Sray} 254252391Sray 255252391Sraystatic int 256283323Sianexynos4210_bus_transmit(struct uart_softc *sc) 257252391Sray{ 258252391Sray int i; 259252391Sray int reg; 260252391Sray 261252391Sray uart_lock(sc->sc_hwmtx); 262252391Sray 263252391Sray for (i = 0; i < sc->sc_txdatasz; i++) { 264283323Sian exynos4210_putc(&sc->sc_bas, sc->sc_txbuf[i]); 265252391Sray uart_barrier(&sc->sc_bas); 266252391Sray } 267252391Sray 268252391Sray sc->sc_txbusy = 1; 269252391Sray 270252391Sray uart_unlock(sc->sc_hwmtx); 271252391Sray 272252391Sray /* unmask TX interrupt */ 273252391Sray reg = bus_space_read_4(sc->sc_bas.bst, sc->sc_bas.bsh, SSCOM_UINTM); 274252391Sray reg &= ~(1 << 2); 275252391Sray bus_space_write_4(sc->sc_bas.bst, sc->sc_bas.bsh, SSCOM_UINTM, reg); 276252391Sray 277252391Sray return (0); 278252391Sray} 279252391Sray 280252391Sraystatic int 281283323Sianexynos4210_bus_setsig(struct uart_softc *sc, int sig) 282252391Sray{ 283252391Sray 284252391Sray return (0); 285252391Sray} 286252391Sray 287252391Sraystatic int 288283323Sianexynos4210_bus_receive(struct uart_softc *sc) 289252391Sray{ 290266942Sbr struct uart_bas *bas; 291252391Sray 292266942Sbr bas = &sc->sc_bas; 293266942Sbr while (bus_space_read_4(bas->bst, bas->bsh, 294266942Sbr SSCOM_UFSTAT) & UFSTAT_RXCOUNT) 295266942Sbr uart_rx_put(sc, uart_getreg(&sc->sc_bas, SSCOM_URXH)); 296266942Sbr 297252391Sray return (0); 298252391Sray} 299252391Sray 300252391Sraystatic int 301283323Sianexynos4210_bus_param(struct uart_softc *sc, int baudrate, int databits, 302252391Sray int stopbits, int parity) 303252391Sray{ 304252391Sray int error; 305252391Sray 306252391Sray if (sc->sc_bas.rclk == 0) 307252391Sray sc->sc_bas.rclk = DEF_CLK; 308252391Sray 309283323Sian KASSERT(sc->sc_bas.rclk != 0, ("exynos4210_init: Invalid rclk")); 310252391Sray 311252391Sray uart_lock(sc->sc_hwmtx); 312283323Sian error = exynos4210_uart_param(&sc->sc_bas, baudrate, databits, stopbits, 313252391Sray parity); 314252391Sray uart_unlock(sc->sc_hwmtx); 315252391Sray 316252391Sray return (error); 317252391Sray} 318252391Sray 319252391Sraystatic int 320283323Sianexynos4210_bus_ipend(struct uart_softc *sc) 321252391Sray{ 322252391Sray uint32_t ints; 323252391Sray uint32_t txempty, rxready; 324252391Sray int reg; 325252391Sray int ipend; 326252391Sray 327252391Sray uart_lock(sc->sc_hwmtx); 328252391Sray ints = bus_space_read_4(sc->sc_bas.bst, sc->sc_bas.bsh, SSCOM_UINTP); 329252391Sray bus_space_write_4(sc->sc_bas.bst, sc->sc_bas.bsh, SSCOM_UINTP, ints); 330252391Sray 331252391Sray txempty = (1 << 2); 332252391Sray rxready = (1 << 0); 333252391Sray 334252391Sray ipend = 0; 335252391Sray if ((ints & txempty) > 0) { 336252391Sray if (sc->sc_txbusy != 0) 337252391Sray ipend |= SER_INT_TXIDLE; 338252391Sray 339252391Sray /* mask TX interrupt */ 340252391Sray reg = bus_space_read_4(sc->sc_bas.bst, sc->sc_bas.bsh, 341252391Sray SSCOM_UINTM); 342252391Sray reg |= (1 << 2); 343252391Sray bus_space_write_4(sc->sc_bas.bst, sc->sc_bas.bsh, 344252391Sray SSCOM_UINTM, reg); 345252391Sray } 346252391Sray 347252391Sray if ((ints & rxready) > 0) { 348252391Sray ipend |= SER_INT_RXREADY; 349252391Sray } 350252391Sray 351252391Sray uart_unlock(sc->sc_hwmtx); 352252391Sray return (ipend); 353252391Sray} 354252391Sray 355252391Sraystatic int 356283323Sianexynos4210_bus_flush(struct uart_softc *sc, int what) 357252391Sray{ 358252391Sray 359252391Sray return (0); 360252391Sray} 361252391Sray 362252391Sraystatic int 363283323Sianexynos4210_bus_getsig(struct uart_softc *sc) 364252391Sray{ 365252391Sray 366252391Sray return (0); 367252391Sray} 368252391Sray 369252391Sraystatic int 370283323Sianexynos4210_bus_ioctl(struct uart_softc *sc, int request, intptr_t data) 371252391Sray{ 372252391Sray 373252391Sray return (EINVAL); 374252391Sray} 375252391Sray 376283327Sianstatic struct uart_class uart_exynos4210_class = { 377283323Sian "exynos4210 class", 378283323Sian exynos4210_methods, 379252391Sray 1, 380283323Sian .uc_ops = &uart_exynos4210_ops, 381252391Sray .uc_range = 8, 382252391Sray .uc_rclk = 0, 383252391Sray}; 384283327Sian 385283327Sianstatic struct ofw_compat_data compat_data[] = { 386283327Sian {"exynos", (uintptr_t)&uart_exynos4210_class}, 387283327Sian {NULL, (uintptr_t)NULL}, 388283327Sian}; 389283327SianUART_FDT_CLASS_AND_DEVICE(compat_data); 390