1208748Sraj/*- 2208748Sraj * Copyright (c) 2009-2010 The FreeBSD Foundation 3208748Sraj * All rights reserved. 4208748Sraj * 5208748Sraj * This software was developed by Semihalf under sponsorship from 6208748Sraj * the FreeBSD Foundation. 7208748Sraj * 8208748Sraj * Redistribution and use in source and binary forms, with or without 9208748Sraj * modification, are permitted provided that the following conditions 10208748Sraj * are met: 11208748Sraj * 1. Redistributions of source code must retain the above copyright 12208748Sraj * notice, this list of conditions and the following disclaimer. 13208748Sraj * 2. Redistributions in binary form must reproduce the above copyright 14208748Sraj * notice, this list of conditions and the following disclaimer in the 15208748Sraj * documentation and/or other materials provided with the distribution. 16208748Sraj * 17208748Sraj * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18208748Sraj * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19208748Sraj * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20208748Sraj * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21208748Sraj * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22208748Sraj * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23208748Sraj * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24208748Sraj * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25208748Sraj * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26208748Sraj * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27208748Sraj * SUCH DAMAGE. 28208748Sraj */ 29208748Sraj 30208748Sraj#include <sys/cdefs.h> 31208748Sraj__FBSDID("$FreeBSD$"); 32208748Sraj 33259357Sian#include "opt_platform.h" 34259357Sian 35208748Sraj#include <sys/param.h> 36208748Sraj#include <sys/bus.h> 37208748Sraj#include <sys/kernel.h> 38208748Sraj#include <sys/module.h> 39208748Sraj 40208748Sraj#include <machine/bus.h> 41208748Sraj 42208748Sraj#include <dev/fdt/fdt_common.h> 43208748Sraj#include <dev/ofw/ofw_bus.h> 44208748Sraj#include <dev/ofw/ofw_bus_subr.h> 45208748Sraj#include <dev/uart/uart.h> 46208748Sraj#include <dev/uart/uart_bus.h> 47208748Sraj#include <dev/uart/uart_cpu.h> 48208748Sraj 49208748Srajstatic int uart_fdt_probe(device_t); 50208748Sraj 51208748Srajstatic device_method_t uart_fdt_methods[] = { 52208748Sraj /* Device interface */ 53208748Sraj DEVMETHOD(device_probe, uart_fdt_probe), 54208748Sraj DEVMETHOD(device_attach, uart_bus_attach), 55208748Sraj DEVMETHOD(device_detach, uart_bus_detach), 56208748Sraj { 0, 0 } 57208748Sraj}; 58208748Sraj 59208748Srajstatic driver_t uart_fdt_driver = { 60208748Sraj uart_driver_name, 61208748Sraj uart_fdt_methods, 62208748Sraj sizeof(struct uart_softc), 63208748Sraj}; 64208748Sraj 65259323Sian/* 66259323Sian * Compatible devices. Keep this sorted in most- to least-specific order first, 67259323Sian * alphabetical second. That is, "zwie,ns16550" should appear before "ns16550" 68259323Sian * on the theory that the zwie driver knows how to make better use of the 69259323Sian * hardware than the generic driver. Likewise with chips within a family, the 70259323Sian * highest-numbers / most recent models should probably appear earlier. 71259323Sian */ 72259323Sianstatic struct ofw_compat_data compat_data[] = { 73259323Sian {"arm,pl011", (uintptr_t)&uart_pl011_class}, 74266095Sian {"atmel,at91rm9200-usart",(uintptr_t)&at91_usart_class}, 75266095Sian {"atmel,at91sam9260-usart",(uintptr_t)&at91_usart_class}, 76259323Sian {"cadence,uart", (uintptr_t)&uart_cdnc_class}, 77259323Sian {"exynos", (uintptr_t)&uart_s3c2410_class}, 78259323Sian {"fsl,imx6q-uart", (uintptr_t)&uart_imx_class}, 79259323Sian {"fsl,imx53-uart", (uintptr_t)&uart_imx_class}, 80259323Sian {"fsl,imx51-uart", (uintptr_t)&uart_imx_class}, 81259323Sian {"fsl,imx31-uart", (uintptr_t)&uart_imx_class}, 82259323Sian {"fsl,imx27-uart", (uintptr_t)&uart_imx_class}, 83259323Sian {"fsl,imx25-uart", (uintptr_t)&uart_imx_class}, 84259323Sian {"fsl,imx21-uart", (uintptr_t)&uart_imx_class}, 85266085Sian {"fsl,mvf600-uart", (uintptr_t)&uart_vybrid_class}, 86259323Sian {"lpc,uart", (uintptr_t)&uart_lpc_class}, 87259323Sian {"ti,ns16550", (uintptr_t)&uart_ti8250_class}, 88259323Sian {"ns16550", (uintptr_t)&uart_ns8250_class}, 89259323Sian {NULL, (uintptr_t)NULL}, 90259323Sian}; 91259323Sian 92259357Sian/* Export the compat_data table for use by the uart_cpu_fdt.c probe routine. */ 93259357Sianconst struct ofw_compat_data *uart_fdt_compat_data = compat_data; 94259357Sian 95208748Srajstatic int 96208748Srajuart_fdt_get_clock(phandle_t node, pcell_t *cell) 97208748Sraj{ 98208748Sraj pcell_t clock; 99208748Sraj 100266277Sian /* 101266277Sian * clock-frequency is a FreeBSD-specific hack. Make its presence optional. 102266277Sian */ 103208748Sraj if ((OF_getprop(node, "clock-frequency", &clock, 104208748Sraj sizeof(clock))) <= 0) 105266277Sian clock = 0; 106208748Sraj 107208748Sraj if (clock == 0) 108208748Sraj /* Try to retrieve parent 'bus-frequency' */ 109208748Sraj /* XXX this should go to simple-bus fixup or so */ 110208748Sraj if ((OF_getprop(OF_parent(node), "bus-frequency", &clock, 111208748Sraj sizeof(clock))) <= 0) 112208748Sraj clock = 0; 113208748Sraj 114208748Sraj *cell = fdt32_to_cpu(clock); 115208748Sraj return (0); 116208748Sraj} 117208748Sraj 118208748Srajstatic int 119208748Srajuart_fdt_get_shift(phandle_t node, pcell_t *cell) 120208748Sraj{ 121208748Sraj pcell_t shift; 122208748Sraj 123208748Sraj if ((OF_getprop(node, "reg-shift", &shift, sizeof(shift))) <= 0) 124208748Sraj shift = 0; 125208748Sraj *cell = fdt32_to_cpu(shift); 126208748Sraj return (0); 127208748Sraj} 128208748Sraj 129208748Srajstatic int 130208748Srajuart_fdt_probe(device_t dev) 131208748Sraj{ 132208748Sraj struct uart_softc *sc; 133208748Sraj phandle_t node; 134208748Sraj pcell_t clock, shift; 135208748Sraj int err; 136259323Sian const struct ofw_compat_data * cd; 137208748Sraj 138239278Sgonzo sc = device_get_softc(dev); 139259323Sian 140266152Sian if (!ofw_bus_status_okay(dev)) 141266152Sian return (ENXIO); 142266152Sian 143259323Sian cd = ofw_bus_search_compatible(dev, compat_data); 144259323Sian if (cd->ocd_data == (uintptr_t)NULL) 145208748Sraj return (ENXIO); 146208748Sraj 147259323Sian sc->sc_class = (struct uart_class *)cd->ocd_data; 148259323Sian 149208748Sraj node = ofw_bus_get_node(dev); 150208748Sraj 151208748Sraj if ((err = uart_fdt_get_clock(node, &clock)) != 0) 152208748Sraj return (err); 153208748Sraj uart_fdt_get_shift(node, &shift); 154208748Sraj 155208748Sraj return (uart_bus_probe(dev, (int)shift, (int)clock, 0, 0)); 156208748Sraj} 157208748Sraj 158208748SrajDRIVER_MODULE(uart, simplebus, uart_fdt_driver, uart_devclass, 0, 0); 159