1205354Simp/* 2205354Simp * Copyright (c) 2003 Marcel Moolenaar 3205354Simp * Copyright (c) 2007-2009 Andrew Turner 4205354Simp * All rights reserved. 5205354Simp * 6205354Simp * Redistribution and use in source and binary forms, with or without 7205354Simp * modification, are permitted provided that the following conditions 8205354Simp * are met: 9205354Simp * 10205354Simp * 1. Redistributions of source code must retain the above copyright 11205354Simp * notice, this list of conditions and the following disclaimer. 12205354Simp * 2. Redistributions in binary form must reproduce the above copyright 13205354Simp * notice, this list of conditions and the following disclaimer in the 14205354Simp * documentation and/or other materials provided with the distribution. 15205354Simp * 16205354Simp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17205354Simp * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18205354Simp * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19205354Simp * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20205354Simp * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21205354Simp * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22205354Simp * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23205354Simp * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24205354Simp * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25205354Simp * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26205354Simp */ 27205354Simp 28205354Simp#include <sys/cdefs.h> 29205354Simp__FBSDID("$FreeBSD$"); 30205354Simp 31205354Simp#include <sys/param.h> 32205354Simp#include <sys/systm.h> 33205354Simp#include <sys/bus.h> 34205354Simp#include <sys/conf.h> 35205354Simp#include <sys/cons.h> 36205354Simp#include <sys/tty.h> 37205354Simp#include <sys/rman.h> 38205354Simp#include <machine/bus.h> 39205354Simp#include <machine/intr.h> 40205354Simp 41205354Simp#include <dev/uart/uart.h> 42205354Simp#include <dev/uart/uart_cpu.h> 43205354Simp#include <dev/uart/uart_bus.h> 44205354Simp#include <arm/s3c2xx0/s3c2440reg.h> 45205354Simp#include <arm/s3c2xx0/uart_dev_s3c2410.h> 46205354Simp#include <arm/s3c2xx0/s3c2xx0reg.h> 47205354Simp#include <arm/s3c2xx0/s3c2xx0var.h> 48205354Simp#include "uart_if.h" 49205354Simp 50205354Simp/* Finds the subirq from the parent */ 51205354Simp#define get_sub_irq(parent, offset) \ 52205354Simp ((parent == S3C24X0_INT_UART0) ? S3C24X0_SUBIRQ_MIN + offset : \ 53205354Simp ((parent == S3C24X0_INT_UART1) ? S3C24X0_SUBIRQ_MIN + 3 + offset : \ 54205354Simp S3C24X0_SUBIRQ_MIN + 6 + offset)) 55205354Simp#define RX_OFF 0 56205354Simp#define TX_OFF 1 57205354Simp#define ERR_OFF 2 58205354Simp 59205354Simpextern unsigned int s3c2410_pclk; 60205354Simp 61205354Simpstatic int sscomspeed(long, long); 62205354Simpstatic int s3c24x0_uart_param(struct uart_bas *, int, int, int, int); 63205354Simp 64205354Simp/* 65205354Simp * Low-level UART interface. 66205354Simp */ 67205354Simpstatic int s3c2410_probe(struct uart_bas *bas); 68205354Simpstatic void s3c2410_init(struct uart_bas *bas, int, int, int, int); 69205354Simpstatic void s3c2410_term(struct uart_bas *bas); 70205354Simpstatic void s3c2410_putc(struct uart_bas *bas, int); 71205354Simpstatic int s3c2410_rxready(struct uart_bas *bas); 72205354Simpstatic int s3c2410_getc(struct uart_bas *bas, struct mtx *mtx); 73205354Simp 74205354Simpextern SLIST_HEAD(uart_devinfo_list, uart_devinfo) uart_sysdevs; 75205354Simp 76205354Simpstatic int 77205354Simpsscomspeed(long speed, long frequency) 78205354Simp{ 79205354Simp int x; 80205354Simp 81205354Simp if (speed <= 0 || frequency <= 0) 82205354Simp return -1; 83205354Simp x = (frequency / 16) / speed; 84205354Simp return x-1; 85205354Simp} 86205354Simp 87205354Simpstatic int 88205354Simps3c24x0_uart_param(struct uart_bas *bas, int baudrate, int databits, 89205354Simp int stopbits, int parity) 90205354Simp{ 91205354Simp int brd, ulcon; 92205354Simp 93205354Simp ulcon = 0; 94205354Simp 95205354Simp switch(databits) { 96205354Simp case 5: 97205354Simp ulcon |= ULCON_LENGTH_5; 98205354Simp break; 99205354Simp case 6: 100205354Simp ulcon |= ULCON_LENGTH_6; 101205354Simp break; 102205354Simp case 7: 103205354Simp ulcon |= ULCON_LENGTH_7; 104205354Simp break; 105205354Simp case 8: 106205354Simp ulcon |= ULCON_LENGTH_8; 107205354Simp break; 108205354Simp default: 109205354Simp return (EINVAL); 110205354Simp } 111205354Simp 112205354Simp switch (parity) { 113205354Simp case UART_PARITY_NONE: 114205354Simp ulcon |= ULCON_PARITY_NONE; 115205354Simp break; 116205354Simp case UART_PARITY_ODD: 117205354Simp ulcon |= ULCON_PARITY_ODD; 118205354Simp break; 119205354Simp case UART_PARITY_EVEN: 120205354Simp ulcon |= ULCON_PARITY_EVEN; 121205354Simp break; 122205354Simp case UART_PARITY_MARK: 123205354Simp case UART_PARITY_SPACE: 124205354Simp default: 125205354Simp return (EINVAL); 126205354Simp } 127205354Simp 128205354Simp if (stopbits == 2) 129205354Simp ulcon |= ULCON_STOP; 130205354Simp 131205354Simp uart_setreg(bas, SSCOM_ULCON, ulcon); 132205354Simp 133205354Simp brd = sscomspeed(baudrate, bas->rclk); 134205354Simp uart_setreg(bas, SSCOM_UBRDIV, brd); 135205354Simp 136205354Simp return (0); 137205354Simp} 138205354Simp 139205354Simpstruct uart_ops uart_s3c2410_ops = { 140205354Simp .probe = s3c2410_probe, 141205354Simp .init = s3c2410_init, 142205354Simp .term = s3c2410_term, 143205354Simp .putc = s3c2410_putc, 144205354Simp .rxready = s3c2410_rxready, 145205354Simp .getc = s3c2410_getc, 146205354Simp}; 147205354Simp 148205354Simpstatic int 149205354Simps3c2410_probe(struct uart_bas *bas) 150205354Simp{ 151205354Simp return (0); 152205354Simp} 153205354Simp 154205354Simpstatic void 155205354Simps3c2410_init(struct uart_bas *bas, int baudrate, int databits, int stopbits, 156205354Simp int parity) 157205354Simp{ 158205354Simp if (bas->rclk == 0) 159205354Simp bas->rclk = s3c2410_pclk; 160205354Simp KASSERT(bas->rclk != 0, ("s3c2410_init: Invalid rclk")); 161205354Simp 162205354Simp uart_setreg(bas, SSCOM_UCON, 0); 163205354Simp uart_setreg(bas, SSCOM_UFCON, 164205354Simp UFCON_TXTRIGGER_8 | UFCON_RXTRIGGER_8 | 165205354Simp UFCON_TXFIFO_RESET | UFCON_RXFIFO_RESET | 166205354Simp UFCON_FIFO_ENABLE); 167205354Simp s3c24x0_uart_param(bas, baudrate, databits, stopbits, parity); 168205354Simp 169205354Simp /* Enable UART. */ 170205354Simp uart_setreg(bas, SSCOM_UCON, UCON_TXMODE_INT | UCON_RXMODE_INT | 171205354Simp UCON_TOINT); 172205354Simp uart_setreg(bas, SSCOM_UMCON, UMCON_RTS); 173205354Simp} 174205354Simp 175205354Simpstatic void 176205354Simps3c2410_term(struct uart_bas *bas) 177205354Simp{ 178205354Simp /* XXX */ 179205354Simp} 180205354Simp 181205354Simpstatic void 182205354Simps3c2410_putc(struct uart_bas *bas, int c) 183205354Simp{ 184205354Simp while ((bus_space_read_4(bas->bst, bas->bsh, SSCOM_UFSTAT) & 185205354Simp UFSTAT_TXFULL) == UFSTAT_TXFULL) 186205354Simp continue; 187205354Simp 188205354Simp uart_setreg(bas, SSCOM_UTXH, c); 189205354Simp} 190205354Simp 191205354Simpstatic int 192205354Simps3c2410_rxready(struct uart_bas *bas) 193205354Simp{ 194205354Simp return ((uart_getreg(bas, SSCOM_UTRSTAT) & UTRSTAT_RXREADY) == 195205354Simp UTRSTAT_RXREADY); 196205354Simp} 197205354Simp 198205354Simpstatic int 199205354Simps3c2410_getc(struct uart_bas *bas, struct mtx *mtx) 200205354Simp{ 201205354Simp while (!sscom_rxrdy(bas->bst, bas->bsh)) 202205354Simp continue; 203205354Simp 204205354Simp return sscom_getc(bas->bst, bas->bsh); 205205354Simp} 206205354Simp 207205354Simpstatic int s3c2410_bus_probe(struct uart_softc *sc); 208205354Simpstatic int s3c2410_bus_attach(struct uart_softc *sc); 209205354Simpstatic int s3c2410_bus_flush(struct uart_softc *, int); 210205354Simpstatic int s3c2410_bus_getsig(struct uart_softc *); 211205354Simpstatic int s3c2410_bus_ioctl(struct uart_softc *, int, intptr_t); 212205354Simpstatic int s3c2410_bus_ipend(struct uart_softc *); 213205354Simpstatic int s3c2410_bus_param(struct uart_softc *, int, int, int, int); 214205354Simpstatic int s3c2410_bus_receive(struct uart_softc *); 215205354Simpstatic int s3c2410_bus_setsig(struct uart_softc *, int); 216205354Simpstatic int s3c2410_bus_transmit(struct uart_softc *); 217205354Simp 218205354Simpstatic kobj_method_t s3c2410_methods[] = { 219205354Simp KOBJMETHOD(uart_probe, s3c2410_bus_probe), 220205354Simp KOBJMETHOD(uart_attach, s3c2410_bus_attach), 221205354Simp KOBJMETHOD(uart_flush, s3c2410_bus_flush), 222205354Simp KOBJMETHOD(uart_getsig, s3c2410_bus_getsig), 223205354Simp KOBJMETHOD(uart_ioctl, s3c2410_bus_ioctl), 224205354Simp KOBJMETHOD(uart_ipend, s3c2410_bus_ipend), 225205354Simp KOBJMETHOD(uart_param, s3c2410_bus_param), 226205354Simp KOBJMETHOD(uart_receive, s3c2410_bus_receive), 227205354Simp KOBJMETHOD(uart_setsig, s3c2410_bus_setsig), 228205354Simp KOBJMETHOD(uart_transmit, s3c2410_bus_transmit), 229205354Simp 230205354Simp {0, 0 } 231205354Simp}; 232205354Simp 233205354Simpint 234205354Simps3c2410_bus_probe(struct uart_softc *sc) 235205354Simp{ 236205354Simp return (0); 237205354Simp} 238205354Simp 239205354Simpstatic int 240205354Simps3c2410_bus_attach(struct uart_softc *sc) 241205354Simp{ 242205354Simp uintptr_t irq; 243205354Simp 244205354Simp switch(s3c2xx0_softc->sc_cpu) { 245205354Simp case CPU_S3C2410: 246205354Simp sc->sc_txfifosz = 16; 247205354Simp sc->sc_rxfifosz = 16; 248205354Simp break; 249205354Simp case CPU_S3C2440: 250205354Simp sc->sc_txfifosz = 64; 251205354Simp sc->sc_rxfifosz = 64; 252205354Simp break; 253205354Simp default: 254205354Simp return (ENXIO); 255205354Simp } 256205354Simp 257205354Simp sc->sc_hwiflow = 0; 258205354Simp sc->sc_hwoflow = 0; 259205354Simp 260205354Simp irq = rman_get_start(sc->sc_ires); 261205354Simp arm_unmask_irq(irq); 262205354Simp arm_unmask_irq(get_sub_irq(irq, RX_OFF)); 263205354Simp arm_unmask_irq(get_sub_irq(irq, TX_OFF)); 264205354Simp arm_unmask_irq(get_sub_irq(irq, ERR_OFF)); 265205354Simp 266205354Simp return (0); 267205354Simp} 268205354Simp 269205354Simpstatic int 270205354Simps3c2410_bus_transmit(struct uart_softc *sc) 271205354Simp{ 272205354Simp uintptr_t irq; 273205354Simp 274205354Simp uart_lock(sc->sc_hwmtx); 275205354Simp 276205354Simp for (int i = 0; i < sc->sc_txdatasz; i++) { 277205354Simp s3c2410_putc(&sc->sc_bas, sc->sc_txbuf[i]); 278205354Simp uart_barrier(&sc->sc_bas); 279205354Simp } 280205354Simp 281205354Simp sc->sc_txbusy = 1; 282205354Simp 283205354Simp uart_unlock(sc->sc_hwmtx); 284205354Simp 285205354Simp irq = rman_get_start(sc->sc_ires); 286205354Simp arm_unmask_irq(get_sub_irq(irq, TX_OFF)); 287205354Simp 288205354Simp return (0); 289205354Simp} 290205354Simp 291205354Simpstatic int 292205354Simps3c2410_bus_setsig(struct uart_softc *sc, int sig) 293205354Simp{ 294205354Simp return (0); 295205354Simp} 296205354Simp 297205354Simpstatic int 298205354Simps3c2410_bus_receive(struct uart_softc *sc) 299205354Simp{ 300205354Simp 301205354Simp uart_rx_put(sc, uart_getreg(&sc->sc_bas, SSCOM_URXH)); 302205354Simp return (0); 303205354Simp} 304205354Simp 305205354Simpstatic int 306205354Simps3c2410_bus_param(struct uart_softc *sc, int baudrate, int databits, 307205354Simp int stopbits, int parity) 308205354Simp{ 309205354Simp int error; 310205354Simp 311205354Simp if (sc->sc_bas.rclk == 0) 312205354Simp sc->sc_bas.rclk = s3c2410_pclk; 313205354Simp KASSERT(sc->sc_bas.rclk != 0, ("s3c2410_init: Invalid rclk")); 314205354Simp 315205354Simp uart_lock(sc->sc_hwmtx); 316205354Simp error = s3c24x0_uart_param(&sc->sc_bas, baudrate, databits, stopbits, 317205354Simp parity); 318205354Simp uart_unlock(sc->sc_hwmtx); 319205354Simp 320205354Simp return (error); 321205354Simp} 322205354Simp 323205354Simpstatic int 324205354Simps3c2410_bus_ipend(struct uart_softc *sc) 325205354Simp{ 326205354Simp uint32_t ufstat, txmask, rxmask; 327205354Simp uintptr_t irq; 328205354Simp int ipend = 0; 329205354Simp 330205354Simp uart_lock(sc->sc_hwmtx); 331205354Simp ufstat = bus_space_read_4(sc->sc_bas.bst, sc->sc_bas.bsh, SSCOM_UFSTAT); 332205354Simp uart_unlock(sc->sc_hwmtx); 333205354Simp 334205354Simp txmask = rxmask = 0; 335205354Simp switch (s3c2xx0_softc->sc_cpu) { 336205354Simp case CPU_S3C2410: 337205354Simp txmask = UFSTAT_TXCOUNT; 338205354Simp rxmask = UFSTAT_RXCOUNT; 339205354Simp break; 340205354Simp case CPU_S3C2440: 341205354Simp txmask = S3C2440_UFSTAT_TXCOUNT; 342205354Simp rxmask = S3C2440_UFSTAT_RXCOUNT; 343205354Simp break; 344205354Simp } 345205354Simp if ((ufstat & txmask) == 0) { 346205354Simp if (sc->sc_txbusy != 0) 347205354Simp ipend |= SER_INT_TXIDLE; 348205354Simp irq = rman_get_start(sc->sc_ires); 349205354Simp arm_mask_irq(get_sub_irq(irq, TX_OFF)); 350205354Simp } 351205354Simp if ((ufstat & rxmask) > 0) { 352205354Simp ipend |= SER_INT_RXREADY; 353205354Simp } 354205354Simp 355205354Simp return (ipend); 356205354Simp} 357205354Simp 358205354Simpstatic int 359205354Simps3c2410_bus_flush(struct uart_softc *sc, int what) 360205354Simp{ 361205354Simp return (0); 362205354Simp} 363205354Simp 364205354Simpstatic int 365205354Simps3c2410_bus_getsig(struct uart_softc *sc) 366205354Simp{ 367205354Simp return (0); 368205354Simp} 369205354Simp 370205354Simpstatic int 371205354Simps3c2410_bus_ioctl(struct uart_softc *sc, int request, intptr_t data) 372205354Simp{ 373205354Simp return (EINVAL); 374205354Simp} 375205354Simp 376205354Simpstruct uart_class uart_s3c2410_class = { 377205354Simp "s3c2410 class", 378205354Simp s3c2410_methods, 379205354Simp 1, 380205354Simp .uc_ops = &uart_s3c2410_ops, 381205354Simp .uc_range = 8, 382205354Simp .uc_rclk = 0, 383205354Simp}; 384