1/*- 2 * Copyright (c) 2013 Ian Lepore 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27#include "opt_platform.h" 28 29#include <sys/cdefs.h> 30__FBSDID("$FreeBSD$"); 31 32#include <sys/param.h> 33#include <sys/systm.h> 34#include <sys/bus.h> 35#include <sys/conf.h> 36#include <sys/kernel.h> 37#include <sys/module.h> 38#include <sys/sysctl.h> 39#include <machine/bus.h> 40 41#include <dev/extres/clk/clk.h> 42#include <dev/fdt/fdt_common.h> 43#include <dev/ofw/ofw_bus.h> 44#include <dev/ofw/ofw_bus_subr.h> 45#include <dev/uart/uart.h> 46#include <dev/uart/uart_cpu.h> 47#include <dev/uart/uart_cpu_fdt.h> 48#include <dev/uart/uart_bus.h> 49#include <dev/uart/uart_dev_ns8250.h> 50#include <dev/ic/ns16550.h> 51#include "uart_if.h" 52 53/* 54 * High-level UART interface. 55 */ 56struct jz4780_uart_softc { 57 struct ns8250_softc ns8250_base; 58 clk_t clk_mod; 59 clk_t clk_baud; 60}; 61 62static int 63jz4780_bus_attach(struct uart_softc *sc) 64{ 65 struct ns8250_softc *ns8250; 66 struct uart_bas *bas; 67 int rv; 68 69 ns8250 = (struct ns8250_softc *)sc; 70 bas = &sc->sc_bas; 71 72 rv = ns8250_bus_attach(sc); 73 if (rv != 0) 74 return (0); 75 76 /* Configure uart to use extra IER_RXTMOUT bit */ 77 ns8250->ier_rxbits = IER_RXTMOUT | IER_EMSC | IER_ERLS | IER_ERXRDY; 78 ns8250->ier_mask = ~(ns8250->ier_rxbits); 79 ns8250->ier = uart_getreg(bas, REG_IER) & ns8250->ier_mask; 80 ns8250->ier |= ns8250->ier_rxbits; 81 uart_setreg(bas, REG_IER, ns8250->ier); 82 uart_barrier(bas); 83 return (0); 84} 85 86static kobj_method_t jz4780_uart_methods[] = { 87 KOBJMETHOD(uart_probe, ns8250_bus_probe), 88 KOBJMETHOD(uart_attach, jz4780_bus_attach), 89 KOBJMETHOD(uart_detach, ns8250_bus_detach), 90 KOBJMETHOD(uart_flush, ns8250_bus_flush), 91 KOBJMETHOD(uart_getsig, ns8250_bus_getsig), 92 KOBJMETHOD(uart_ioctl, ns8250_bus_ioctl), 93 KOBJMETHOD(uart_ipend, ns8250_bus_ipend), 94 KOBJMETHOD(uart_param, ns8250_bus_param), 95 KOBJMETHOD(uart_receive, ns8250_bus_receive), 96 KOBJMETHOD(uart_setsig, ns8250_bus_setsig), 97 KOBJMETHOD(uart_transmit, ns8250_bus_transmit), 98 KOBJMETHOD(uart_grab, ns8250_bus_grab), 99 KOBJMETHOD(uart_ungrab, ns8250_bus_ungrab), 100 KOBJMETHOD_END 101}; 102 103static struct uart_class jz4780_uart_class = { 104 "jz4780_uart_class", 105 jz4780_uart_methods, 106 sizeof(struct jz4780_uart_softc), 107 .uc_ops = &uart_ns8250_ops, 108 .uc_range = 8, 109 .uc_rclk = 0, 110}; 111 112/* Compatible devices. */ 113static struct ofw_compat_data compat_data[] = { 114 {"ingenic,jz4780-uart", (uintptr_t)&jz4780_uart_class}, 115 {NULL, (uintptr_t)NULL}, 116}; 117 118UART_FDT_CLASS(compat_data); 119 120/* 121 * UART Driver interface. 122 */ 123static int 124jz4780_uart_get_shift(device_t dev) 125{ 126 phandle_t node; 127 pcell_t shift; 128 129 node = ofw_bus_get_node(dev); 130 if ((OF_getencprop(node, "reg-shift", &shift, sizeof(shift))) <= 0) 131 shift = 2; 132 return ((int)shift); 133} 134 135static int 136jz4780_uart_probe(device_t dev) 137{ 138 struct jz4780_uart_softc *sc; 139 uint64_t freq; 140 int shift; 141 int rv; 142 const struct ofw_compat_data *cd; 143 144 sc = device_get_softc(dev); 145 if (!ofw_bus_status_okay(dev)) 146 return (ENXIO); 147 cd = ofw_bus_search_compatible(dev, compat_data); 148 if (cd->ocd_data == 0) 149 return (ENXIO); 150 151 /* Figure out clock setup */ 152 rv = clk_get_by_ofw_name(dev, 0, "module", &sc->clk_mod); 153 if (rv != 0) { 154 device_printf(dev, "Cannot get UART clock: %d\n", rv); 155 return (ENXIO); 156 } 157 rv = clk_enable(sc->clk_mod); 158 if (rv != 0) { 159 device_printf(dev, "Cannot enable UART clock: %d\n", rv); 160 return (ENXIO); 161 } 162 rv = clk_get_by_ofw_name(dev, 0, "baud", &sc->clk_baud); 163 if (rv != 0) { 164 device_printf(dev, "Cannot get UART clock: %d\n", rv); 165 return (ENXIO); 166 } 167 rv = clk_enable(sc->clk_baud); 168 if (rv != 0) { 169 device_printf(dev, "Cannot enable UART clock: %d\n", rv); 170 return (ENXIO); 171 } 172 rv = clk_get_freq(sc->clk_baud, &freq); 173 if (rv != 0) { 174 device_printf(dev, "Cannot determine UART clock frequency: %d\n", rv); 175 return (ENXIO); 176 } 177 178 if (bootverbose) 179 device_printf(dev, "got UART clock: %lld\n", freq); 180 sc->ns8250_base.base.sc_class = (struct uart_class *)cd->ocd_data; 181 shift = jz4780_uart_get_shift(dev); 182 return (uart_bus_probe(dev, shift, 0, (int)freq, 0, 0, 0)); 183} 184 185static int 186jz4780_uart_detach(device_t dev) 187{ 188 struct jz4780_uart_softc *sc; 189 int rv; 190 191 rv = uart_bus_detach(dev); 192 if (rv != 0) 193 return (rv); 194 195 sc = device_get_softc(dev); 196 if (sc->clk_mod != NULL) { 197 clk_release(sc->clk_mod); 198 } 199 if (sc->clk_baud != NULL) { 200 clk_release(sc->clk_baud); 201 } 202 return (0); 203} 204 205static device_method_t jz4780_uart_bus_methods[] = { 206 /* Device interface */ 207 DEVMETHOD(device_probe, jz4780_uart_probe), 208 DEVMETHOD(device_attach, uart_bus_attach), 209 DEVMETHOD(device_detach, jz4780_uart_detach), 210 { 0, 0 } 211}; 212 213static driver_t jz4780_uart_driver = { 214 uart_driver_name, 215 jz4780_uart_bus_methods, 216 sizeof(struct jz4780_uart_softc), 217}; 218 219DRIVER_MODULE(jz4780_uart, simplebus, jz4780_uart_driver, uart_devclass, 220 0, 0); 221