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