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/s3c2xx0/s3c2440reg.h> 45#include <arm/s3c2xx0/uart_dev_s3c2410.h> 46#include <arm/s3c2xx0/s3c2xx0reg.h> 47#include <arm/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} 206 207static int s3c2410_bus_probe(struct uart_softc *sc); 208static int s3c2410_bus_attach(struct uart_softc *sc); 209static int s3c2410_bus_flush(struct uart_softc *, int); 210static int s3c2410_bus_getsig(struct uart_softc *); 211static int s3c2410_bus_ioctl(struct uart_softc *, int, intptr_t); 212static int s3c2410_bus_ipend(struct uart_softc *); 213static int s3c2410_bus_param(struct uart_softc *, int, int, int, int); 214static int s3c2410_bus_receive(struct uart_softc *); 215static int s3c2410_bus_setsig(struct uart_softc *, int); 216static int s3c2410_bus_transmit(struct uart_softc *); 217 218static kobj_method_t s3c2410_methods[] = { 219 KOBJMETHOD(uart_probe, s3c2410_bus_probe), 220 KOBJMETHOD(uart_attach, s3c2410_bus_attach), 221 KOBJMETHOD(uart_flush, s3c2410_bus_flush), 222 KOBJMETHOD(uart_getsig, s3c2410_bus_getsig), 223 KOBJMETHOD(uart_ioctl, s3c2410_bus_ioctl), 224 KOBJMETHOD(uart_ipend, s3c2410_bus_ipend), 225 KOBJMETHOD(uart_param, s3c2410_bus_param), 226 KOBJMETHOD(uart_receive, s3c2410_bus_receive), 227 KOBJMETHOD(uart_setsig, s3c2410_bus_setsig), 228 KOBJMETHOD(uart_transmit, s3c2410_bus_transmit), 229 230 {0, 0 } 231}; 232 233int 234s3c2410_bus_probe(struct uart_softc *sc) 235{ 236 return (0); 237} 238 239static int 240s3c2410_bus_attach(struct uart_softc *sc) 241{ 242 uintptr_t irq; 243 244 switch(s3c2xx0_softc->sc_cpu) { 245 case CPU_S3C2410: 246 sc->sc_txfifosz = 16; 247 sc->sc_rxfifosz = 16; 248 break; 249 case CPU_S3C2440: 250 sc->sc_txfifosz = 64; 251 sc->sc_rxfifosz = 64; 252 break; 253 default: 254 return (ENXIO); 255 } 256 257 sc->sc_hwiflow = 0; 258 sc->sc_hwoflow = 0; 259 260 irq = rman_get_start(sc->sc_ires); 261 arm_unmask_irq(irq); 262 arm_unmask_irq(get_sub_irq(irq, RX_OFF)); 263 arm_unmask_irq(get_sub_irq(irq, TX_OFF)); 264 arm_unmask_irq(get_sub_irq(irq, ERR_OFF)); 265 266 return (0); 267} 268 269static int 270s3c2410_bus_transmit(struct uart_softc *sc) 271{ 272 uintptr_t irq; 273 274 uart_lock(sc->sc_hwmtx); 275 276 for (int i = 0; i < sc->sc_txdatasz; i++) { 277 s3c2410_putc(&sc->sc_bas, sc->sc_txbuf[i]); 278 uart_barrier(&sc->sc_bas); 279 } 280 281 sc->sc_txbusy = 1; 282 283 uart_unlock(sc->sc_hwmtx); 284 285 irq = rman_get_start(sc->sc_ires); 286 arm_unmask_irq(get_sub_irq(irq, TX_OFF)); 287 288 return (0); 289} 290 291static int 292s3c2410_bus_setsig(struct uart_softc *sc, int sig) 293{ 294 return (0); 295} 296 297static int 298s3c2410_bus_receive(struct uart_softc *sc) 299{ 300 301 uart_rx_put(sc, uart_getreg(&sc->sc_bas, SSCOM_URXH)); 302 return (0); 303} 304 305static int 306s3c2410_bus_param(struct uart_softc *sc, int baudrate, int databits, 307 int stopbits, int parity) 308{ 309 int error; 310 311 if (sc->sc_bas.rclk == 0) 312 sc->sc_bas.rclk = s3c2410_pclk; 313 KASSERT(sc->sc_bas.rclk != 0, ("s3c2410_init: Invalid rclk")); 314 315 uart_lock(sc->sc_hwmtx); 316 error = s3c24x0_uart_param(&sc->sc_bas, baudrate, databits, stopbits, 317 parity); 318 uart_unlock(sc->sc_hwmtx); 319 320 return (error); 321} 322 323static int 324s3c2410_bus_ipend(struct uart_softc *sc) 325{ 326 uint32_t ufstat, txmask, rxmask; 327 uintptr_t irq; 328 int ipend = 0; 329 330 uart_lock(sc->sc_hwmtx); 331 ufstat = bus_space_read_4(sc->sc_bas.bst, sc->sc_bas.bsh, SSCOM_UFSTAT); 332 uart_unlock(sc->sc_hwmtx); 333 334 txmask = rxmask = 0; 335 switch (s3c2xx0_softc->sc_cpu) { 336 case CPU_S3C2410: 337 txmask = UFSTAT_TXCOUNT; 338 rxmask = UFSTAT_RXCOUNT; 339 break; 340 case CPU_S3C2440: 341 txmask = S3C2440_UFSTAT_TXCOUNT; 342 rxmask = S3C2440_UFSTAT_RXCOUNT; 343 break; 344 } 345 if ((ufstat & txmask) == 0) { 346 if (sc->sc_txbusy != 0) 347 ipend |= SER_INT_TXIDLE; 348 irq = rman_get_start(sc->sc_ires); 349 arm_mask_irq(get_sub_irq(irq, TX_OFF)); 350 } 351 if ((ufstat & rxmask) > 0) { 352 ipend |= SER_INT_RXREADY; 353 } 354 355 return (ipend); 356} 357 358static int 359s3c2410_bus_flush(struct uart_softc *sc, int what) 360{ 361 return (0); 362} 363 364static int 365s3c2410_bus_getsig(struct uart_softc *sc) 366{ 367 return (0); 368} 369 370static int 371s3c2410_bus_ioctl(struct uart_softc *sc, int request, intptr_t data) 372{ 373 return (EINVAL); 374} 375 376struct uart_class uart_s3c2410_class = { 377 "s3c2410 class", 378 s3c2410_methods, 379 1, 380 .uc_ops = &uart_s3c2410_ops, 381 .uc_range = 8, 382 .uc_rclk = 0, 383}; 384