1/* 2 * Copyright (c) 2003 Marcel Moolenaar 3 * Copyright (c) 2007-2009 Andrew Turner 4 * Copyright (c) 2013 Ruslan Bukin <br@bsdpad.com> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29#include <sys/cdefs.h> 30__FBSDID("$FreeBSD: stable/11/sys/arm/samsung/exynos/exynos_uart.c 356020 2019-12-22 19:06:45Z kevans $"); 31 32#include <sys/param.h> 33#include <sys/systm.h> 34#include <sys/bus.h> 35#include <sys/conf.h> 36#include <sys/cons.h> 37#include <sys/rman.h> 38#include <machine/bus.h> 39#include <machine/intr.h> 40 41#include <dev/uart/uart.h> 42#include <dev/uart/uart_cpu.h> 43#include <dev/uart/uart_cpu_fdt.h> 44#include <dev/uart/uart_bus.h> 45 46#include <arm/samsung/exynos/exynos_uart.h> 47 48#include "uart_if.h" 49 50#define DEF_CLK 100000000 51 52static int sscomspeed(long, long); 53static int exynos4210_uart_param(struct uart_bas *, int, int, int, int); 54 55/* 56 * Low-level UART interface. 57 */ 58static int exynos4210_probe(struct uart_bas *bas); 59static void exynos4210_init(struct uart_bas *bas, int, int, int, int); 60static void exynos4210_term(struct uart_bas *bas); 61static void exynos4210_putc(struct uart_bas *bas, int); 62static int exynos4210_rxready(struct uart_bas *bas); 63static int exynos4210_getc(struct uart_bas *bas, struct mtx *mtx); 64 65extern SLIST_HEAD(uart_devinfo_list, uart_devinfo) uart_sysdevs; 66 67static int 68sscomspeed(long speed, long frequency) 69{ 70 int x; 71 72 if (speed <= 0 || frequency <= 0) 73 return (-1); 74 x = (frequency / 16) / speed; 75 return (x-1); 76} 77 78static int 79exynos4210_uart_param(struct uart_bas *bas, int baudrate, int databits, 80 int stopbits, int parity) 81{ 82 int brd, ulcon; 83 84 ulcon = 0; 85 86 switch(databits) { 87 case 5: 88 ulcon |= ULCON_LENGTH_5; 89 break; 90 case 6: 91 ulcon |= ULCON_LENGTH_6; 92 break; 93 case 7: 94 ulcon |= ULCON_LENGTH_7; 95 break; 96 case 8: 97 ulcon |= ULCON_LENGTH_8; 98 break; 99 default: 100 return (EINVAL); 101 } 102 103 switch (parity) { 104 case UART_PARITY_NONE: 105 ulcon |= ULCON_PARITY_NONE; 106 break; 107 case UART_PARITY_ODD: 108 ulcon |= ULCON_PARITY_ODD; 109 break; 110 case UART_PARITY_EVEN: 111 ulcon |= ULCON_PARITY_EVEN; 112 break; 113 case UART_PARITY_MARK: 114 case UART_PARITY_SPACE: 115 default: 116 return (EINVAL); 117 } 118 119 if (stopbits == 2) 120 ulcon |= ULCON_STOP; 121 122 uart_setreg(bas, SSCOM_ULCON, ulcon); 123 124 brd = sscomspeed(baudrate, bas->rclk); 125 uart_setreg(bas, SSCOM_UBRDIV, brd); 126 127 return (0); 128} 129 130struct uart_ops uart_exynos4210_ops = { 131 .probe = exynos4210_probe, 132 .init = exynos4210_init, 133 .term = exynos4210_term, 134 .putc = exynos4210_putc, 135 .rxready = exynos4210_rxready, 136 .getc = exynos4210_getc, 137}; 138 139static int 140exynos4210_probe(struct uart_bas *bas) 141{ 142 143 return (0); 144} 145 146static void 147exynos4210_init(struct uart_bas *bas, int baudrate, int databits, int stopbits, 148 int parity) 149{ 150 151 if (bas->rclk == 0) 152 bas->rclk = DEF_CLK; 153 154 KASSERT(bas->rclk != 0, ("exynos4210_init: Invalid rclk")); 155 156 uart_setreg(bas, SSCOM_UCON, 0); 157 uart_setreg(bas, SSCOM_UFCON, 158 UFCON_TXTRIGGER_8 | UFCON_RXTRIGGER_8 | 159 UFCON_TXFIFO_RESET | UFCON_RXFIFO_RESET | 160 UFCON_FIFO_ENABLE); 161 exynos4210_uart_param(bas, baudrate, databits, stopbits, parity); 162 163 /* Enable UART. */ 164 uart_setreg(bas, SSCOM_UCON, UCON_TXMODE_INT | UCON_RXMODE_INT | 165 UCON_TOINT); 166 uart_setreg(bas, SSCOM_UMCON, UMCON_RTS); 167} 168 169static void 170exynos4210_term(struct uart_bas *bas) 171{ 172 /* XXX */ 173} 174 175static void 176exynos4210_putc(struct uart_bas *bas, int c) 177{ 178 179 while ((bus_space_read_4(bas->bst, bas->bsh, SSCOM_UFSTAT) & 180 UFSTAT_TXFULL) == UFSTAT_TXFULL) 181 continue; 182 183 uart_setreg(bas, SSCOM_UTXH, c); 184} 185 186static int 187exynos4210_rxready(struct uart_bas *bas) 188{ 189 190 return ((uart_getreg(bas, SSCOM_UTRSTAT) & UTRSTAT_RXREADY) == 191 UTRSTAT_RXREADY); 192} 193 194static int 195exynos4210_getc(struct uart_bas *bas, struct mtx *mtx) 196{ 197 int utrstat; 198 199 utrstat = bus_space_read_1(bas->bst, bas->bsh, SSCOM_UTRSTAT); 200 while (!(utrstat & UTRSTAT_RXREADY)) { 201 utrstat = bus_space_read_1(bas->bst, bas->bsh, SSCOM_UTRSTAT); 202 continue; 203 } 204 205 return (bus_space_read_1(bas->bst, bas->bsh, SSCOM_URXH)); 206} 207 208static int exynos4210_bus_probe(struct uart_softc *sc); 209static int exynos4210_bus_attach(struct uart_softc *sc); 210static int exynos4210_bus_flush(struct uart_softc *, int); 211static int exynos4210_bus_getsig(struct uart_softc *); 212static int exynos4210_bus_ioctl(struct uart_softc *, int, intptr_t); 213static int exynos4210_bus_ipend(struct uart_softc *); 214static int exynos4210_bus_param(struct uart_softc *, int, int, int, int); 215static int exynos4210_bus_receive(struct uart_softc *); 216static int exynos4210_bus_setsig(struct uart_softc *, int); 217static int exynos4210_bus_transmit(struct uart_softc *); 218 219static kobj_method_t exynos4210_methods[] = { 220 KOBJMETHOD(uart_probe, exynos4210_bus_probe), 221 KOBJMETHOD(uart_attach, exynos4210_bus_attach), 222 KOBJMETHOD(uart_flush, exynos4210_bus_flush), 223 KOBJMETHOD(uart_getsig, exynos4210_bus_getsig), 224 KOBJMETHOD(uart_ioctl, exynos4210_bus_ioctl), 225 KOBJMETHOD(uart_ipend, exynos4210_bus_ipend), 226 KOBJMETHOD(uart_param, exynos4210_bus_param), 227 KOBJMETHOD(uart_receive, exynos4210_bus_receive), 228 KOBJMETHOD(uart_setsig, exynos4210_bus_setsig), 229 KOBJMETHOD(uart_transmit, exynos4210_bus_transmit), 230 231 {0, 0 } 232}; 233 234int 235exynos4210_bus_probe(struct uart_softc *sc) 236{ 237 238 sc->sc_txfifosz = 16; 239 sc->sc_rxfifosz = 16; 240 241 return (0); 242} 243 244static int 245exynos4210_bus_attach(struct uart_softc *sc) 246{ 247 248 sc->sc_hwiflow = 0; 249 sc->sc_hwoflow = 0; 250 251 return (0); 252} 253 254static int 255exynos4210_bus_transmit(struct uart_softc *sc) 256{ 257 int i; 258 int reg; 259 260 uart_lock(sc->sc_hwmtx); 261 262 for (i = 0; i < sc->sc_txdatasz; i++) { 263 exynos4210_putc(&sc->sc_bas, sc->sc_txbuf[i]); 264 uart_barrier(&sc->sc_bas); 265 } 266 267 sc->sc_txbusy = 1; 268 269 uart_unlock(sc->sc_hwmtx); 270 271 /* unmask TX interrupt */ 272 reg = bus_space_read_4(sc->sc_bas.bst, sc->sc_bas.bsh, SSCOM_UINTM); 273 reg &= ~(1 << 2); 274 bus_space_write_4(sc->sc_bas.bst, sc->sc_bas.bsh, SSCOM_UINTM, reg); 275 276 return (0); 277} 278 279static int 280exynos4210_bus_setsig(struct uart_softc *sc, int sig) 281{ 282 283 return (0); 284} 285 286static int 287exynos4210_bus_receive(struct uart_softc *sc) 288{ 289 struct uart_bas *bas; 290 291 bas = &sc->sc_bas; 292 while (bus_space_read_4(bas->bst, bas->bsh, 293 SSCOM_UFSTAT) & UFSTAT_RXCOUNT) 294 uart_rx_put(sc, uart_getreg(&sc->sc_bas, SSCOM_URXH)); 295 296 return (0); 297} 298 299static int 300exynos4210_bus_param(struct uart_softc *sc, int baudrate, int databits, 301 int stopbits, int parity) 302{ 303 int error; 304 305 if (sc->sc_bas.rclk == 0) 306 sc->sc_bas.rclk = DEF_CLK; 307 308 KASSERT(sc->sc_bas.rclk != 0, ("exynos4210_init: Invalid rclk")); 309 310 uart_lock(sc->sc_hwmtx); 311 error = exynos4210_uart_param(&sc->sc_bas, baudrate, databits, stopbits, 312 parity); 313 uart_unlock(sc->sc_hwmtx); 314 315 return (error); 316} 317 318static int 319exynos4210_bus_ipend(struct uart_softc *sc) 320{ 321 uint32_t ints; 322 uint32_t txempty, rxready; 323 int reg; 324 int ipend; 325 326 uart_lock(sc->sc_hwmtx); 327 ints = bus_space_read_4(sc->sc_bas.bst, sc->sc_bas.bsh, SSCOM_UINTP); 328 bus_space_write_4(sc->sc_bas.bst, sc->sc_bas.bsh, SSCOM_UINTP, ints); 329 330 txempty = (1 << 2); 331 rxready = (1 << 0); 332 333 ipend = 0; 334 if ((ints & txempty) > 0) { 335 if (sc->sc_txbusy != 0) 336 ipend |= SER_INT_TXIDLE; 337 338 /* mask TX interrupt */ 339 reg = bus_space_read_4(sc->sc_bas.bst, sc->sc_bas.bsh, 340 SSCOM_UINTM); 341 reg |= (1 << 2); 342 bus_space_write_4(sc->sc_bas.bst, sc->sc_bas.bsh, 343 SSCOM_UINTM, reg); 344 } 345 346 if ((ints & rxready) > 0) { 347 ipend |= SER_INT_RXREADY; 348 } 349 350 uart_unlock(sc->sc_hwmtx); 351 return (ipend); 352} 353 354static int 355exynos4210_bus_flush(struct uart_softc *sc, int what) 356{ 357 358 return (0); 359} 360 361static int 362exynos4210_bus_getsig(struct uart_softc *sc) 363{ 364 365 return (0); 366} 367 368static int 369exynos4210_bus_ioctl(struct uart_softc *sc, int request, intptr_t data) 370{ 371 372 return (EINVAL); 373} 374 375static struct uart_class uart_exynos4210_class = { 376 "exynos4210 class", 377 exynos4210_methods, 378 1, 379 .uc_ops = &uart_exynos4210_ops, 380 .uc_range = 8, 381 .uc_rclk = 0, 382 .uc_rshift = 0 383}; 384 385static struct ofw_compat_data compat_data[] = { 386 {"exynos", (uintptr_t)&uart_exynos4210_class}, 387 {NULL, (uintptr_t)NULL}, 388}; 389UART_FDT_CLASS_AND_DEVICE(compat_data); 390