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: stable/11/sys/dev/uart/uart_bus_fdt.c 340145 2018-11-04 23:28:56Z mmacy $"); 32208748Sraj 33257556Sian#include "opt_platform.h" 34257556Sian 35208748Sraj#include <sys/param.h> 36295474Sandrew#include <sys/systm.h> 37208748Sraj#include <sys/bus.h> 38208748Sraj#include <sys/kernel.h> 39208748Sraj#include <sys/module.h> 40208748Sraj 41208748Sraj#include <machine/bus.h> 42208748Sraj 43208748Sraj#include <dev/fdt/fdt_common.h> 44208748Sraj#include <dev/ofw/ofw_bus.h> 45208748Sraj#include <dev/ofw/ofw_bus_subr.h> 46208748Sraj#include <dev/uart/uart.h> 47208748Sraj#include <dev/uart/uart_bus.h> 48208748Sraj#include <dev/uart/uart_cpu.h> 49279723Sian#include <dev/uart/uart_cpu_fdt.h> 50208748Sraj 51208748Srajstatic int uart_fdt_probe(device_t); 52208748Sraj 53208748Srajstatic device_method_t uart_fdt_methods[] = { 54208748Sraj /* Device interface */ 55208748Sraj DEVMETHOD(device_probe, uart_fdt_probe), 56208748Sraj DEVMETHOD(device_attach, uart_bus_attach), 57208748Sraj DEVMETHOD(device_detach, uart_bus_detach), 58208748Sraj { 0, 0 } 59208748Sraj}; 60208748Sraj 61208748Srajstatic driver_t uart_fdt_driver = { 62208748Sraj uart_driver_name, 63208748Sraj uart_fdt_methods, 64208748Sraj sizeof(struct uart_softc), 65208748Sraj}; 66208748Sraj 67281077Sandrewint 68281077Sandrewuart_fdt_get_clock(phandle_t node, pcell_t *cell) 69281077Sandrew{ 70281077Sandrew 71298955Spfg /* clock-frequency is a FreeBSD-only extension. */ 72281200Sandrew if ((OF_getencprop(node, "clock-frequency", cell, 73281200Sandrew sizeof(*cell))) <= 0) { 74281077Sandrew /* Try to retrieve parent 'bus-frequency' */ 75281077Sandrew /* XXX this should go to simple-bus fixup or so */ 76281200Sandrew if ((OF_getencprop(OF_parent(node), "bus-frequency", cell, 77281200Sandrew sizeof(*cell))) <= 0) 78281200Sandrew *cell = 0; 79281200Sandrew } 80281077Sandrew 81281077Sandrew return (0); 82281077Sandrew} 83281077Sandrew 84281077Sandrewint 85281077Sandrewuart_fdt_get_shift(phandle_t node, pcell_t *cell) 86281077Sandrew{ 87281077Sandrew 88281077Sandrew if ((OF_getencprop(node, "reg-shift", cell, sizeof(*cell))) <= 0) 89281438Sandrew return (-1); 90281077Sandrew return (0); 91281077Sandrew} 92281077Sandrew 93340145Smmacyint 94340145Smmacyuart_fdt_get_io_width(phandle_t node, pcell_t *cell) 95340145Smmacy{ 96340145Smmacy 97340145Smmacy if ((OF_getencprop(node, "reg-io-width", cell, sizeof(*cell))) <= 0) 98340145Smmacy return (-1); 99340145Smmacy return (0); 100340145Smmacy} 101340145Smmacy 102279723Sianstatic uintptr_t 103279723Sianuart_fdt_find_device(device_t dev) 104279723Sian{ 105279723Sian struct ofw_compat_data **cd; 106279723Sian const struct ofw_compat_data *ocd; 107279723Sian 108279723Sian SET_FOREACH(cd, uart_fdt_class_and_device_set) { 109279723Sian ocd = ofw_bus_search_compatible(dev, *cd); 110279723Sian if (ocd->ocd_data != 0) 111279723Sian return (ocd->ocd_data); 112279723Sian } 113279723Sian return (0); 114279723Sian} 115279723Sian 116208748Srajstatic int 117331885Sgonzophandle_chosen_propdev(phandle_t chosen, const char *name, phandle_t *node) 118331885Sgonzo{ 119331885Sgonzo char buf[64]; 120331885Sgonzo 121331885Sgonzo if (OF_getprop(chosen, name, buf, sizeof(buf)) <= 0) 122331885Sgonzo return (ENXIO); 123331885Sgonzo if ((*node = OF_finddevice(buf)) == -1) 124331885Sgonzo return (ENXIO); 125331885Sgonzo 126331885Sgonzo return (0); 127331885Sgonzo} 128331885Sgonzo 129331885Sgonzostatic const struct ofw_compat_data * 130331885Sgonzouart_fdt_find_compatible(phandle_t node, const struct ofw_compat_data *cd) 131331885Sgonzo{ 132331885Sgonzo const struct ofw_compat_data *ocd; 133331885Sgonzo 134331885Sgonzo for (ocd = cd; ocd->ocd_str != NULL; ocd++) { 135331886Sgonzo if (ofw_bus_node_is_compatible(node, ocd->ocd_str)) 136331885Sgonzo return (ocd); 137331885Sgonzo } 138331885Sgonzo return (NULL); 139331885Sgonzo} 140331885Sgonzo 141331885Sgonzostatic uintptr_t 142331885Sgonzouart_fdt_find_by_node(phandle_t node, int class_list) 143331885Sgonzo{ 144331885Sgonzo struct ofw_compat_data **cd; 145331885Sgonzo const struct ofw_compat_data *ocd; 146331885Sgonzo 147331885Sgonzo if (class_list) { 148331885Sgonzo SET_FOREACH(cd, uart_fdt_class_set) { 149331885Sgonzo ocd = uart_fdt_find_compatible(node, *cd); 150331885Sgonzo if ((ocd != NULL) && (ocd->ocd_data != 0)) 151331885Sgonzo return (ocd->ocd_data); 152331885Sgonzo } 153331885Sgonzo } else { 154331885Sgonzo SET_FOREACH(cd, uart_fdt_class_and_device_set) { 155331885Sgonzo ocd = uart_fdt_find_compatible(node, *cd); 156331885Sgonzo if ((ocd != NULL) && (ocd->ocd_data != 0)) 157331885Sgonzo return (ocd->ocd_data); 158331885Sgonzo } 159331885Sgonzo } 160331885Sgonzo 161331885Sgonzo return (0); 162331885Sgonzo} 163331885Sgonzo 164331885Sgonzoint 165331885Sgonzouart_cpu_fdt_probe(struct uart_class **classp, bus_space_tag_t *bst, 166331885Sgonzo bus_space_handle_t *bsh, int *baud, u_int *rclk, u_int *shiftp) 167331885Sgonzo{ 168331885Sgonzo const char *propnames[] = {"stdout-path", "linux,stdout-path", "stdout", 169331885Sgonzo "stdin-path", "stdin", NULL}; 170331885Sgonzo const char **name; 171331885Sgonzo struct uart_class *class; 172331885Sgonzo phandle_t node, chosen; 173340145Smmacy pcell_t br, clk, shift, iowidth; 174331885Sgonzo char *cp; 175331885Sgonzo int err; 176331885Sgonzo 177331885Sgonzo /* Has the user forced a specific device node? */ 178331885Sgonzo cp = kern_getenv("hw.fdt.console"); 179331885Sgonzo if (cp == NULL) { 180331885Sgonzo /* 181331885Sgonzo * Retrieve /chosen/std{in,out}. 182331885Sgonzo */ 183331885Sgonzo node = -1; 184331885Sgonzo if ((chosen = OF_finddevice("/chosen")) != -1) { 185331885Sgonzo for (name = propnames; *name != NULL; name++) { 186331885Sgonzo if (phandle_chosen_propdev(chosen, *name, 187331885Sgonzo &node) == 0) 188331885Sgonzo break; 189331885Sgonzo } 190331885Sgonzo } 191331885Sgonzo if (chosen == -1 || *name == NULL) 192331885Sgonzo node = OF_finddevice("serial0"); /* Last ditch */ 193331885Sgonzo } else { 194331885Sgonzo node = OF_finddevice(cp); 195331885Sgonzo } 196331885Sgonzo 197331885Sgonzo if (node == -1) 198331885Sgonzo return (ENXIO); 199331885Sgonzo 200331885Sgonzo /* 201331885Sgonzo * Check old style of UART definition first. Unfortunately, the common 202331885Sgonzo * FDT processing is not possible if we have clock, power domains and 203331885Sgonzo * pinmux stuff. 204331885Sgonzo */ 205331885Sgonzo class = (struct uart_class *)uart_fdt_find_by_node(node, 0); 206331885Sgonzo if (class != NULL) { 207331885Sgonzo if ((err = uart_fdt_get_clock(node, &clk)) != 0) 208331885Sgonzo return (err); 209331885Sgonzo } else { 210331885Sgonzo /* Check class only linker set */ 211331885Sgonzo class = 212331885Sgonzo (struct uart_class *)uart_fdt_find_by_node(node, 1); 213331885Sgonzo if (class == NULL) 214331885Sgonzo return (ENXIO); 215331885Sgonzo clk = 0; 216331885Sgonzo } 217331885Sgonzo 218331885Sgonzo /* 219331885Sgonzo * Retrieve serial attributes. 220331885Sgonzo */ 221331885Sgonzo if (uart_fdt_get_shift(node, &shift) != 0) 222331885Sgonzo shift = uart_getregshift(class); 223331885Sgonzo 224340145Smmacy if (uart_fdt_get_io_width(node, &iowidth) != 0) 225340145Smmacy iowidth = uart_getregiowidth(class); 226340145Smmacy 227331885Sgonzo if (OF_getencprop(node, "current-speed", &br, sizeof(br)) <= 0) 228331885Sgonzo br = 0; 229331885Sgonzo 230331885Sgonzo err = OF_decode_addr(node, 0, bst, bsh, NULL); 231331885Sgonzo if (err != 0) 232331885Sgonzo return (err); 233331885Sgonzo 234331885Sgonzo *classp = class; 235331885Sgonzo *baud = br; 236331885Sgonzo *rclk = clk; 237331885Sgonzo *shiftp = shift; 238331885Sgonzo 239331885Sgonzo return (0); 240331885Sgonzo} 241331885Sgonzo 242331885Sgonzostatic int 243208748Srajuart_fdt_probe(device_t dev) 244208748Sraj{ 245208748Sraj struct uart_softc *sc; 246208748Sraj phandle_t node; 247340145Smmacy pcell_t clock, shift, iowidth; 248208748Sraj int err; 249208748Sraj 250239278Sgonzo sc = device_get_softc(dev); 251257480Sian 252261410Sian if (!ofw_bus_status_okay(dev)) 253261410Sian return (ENXIO); 254261410Sian 255279723Sian sc->sc_class = (struct uart_class *)uart_fdt_find_device(dev); 256279723Sian if (sc->sc_class == NULL) 257208748Sraj return (ENXIO); 258208748Sraj 259208748Sraj node = ofw_bus_get_node(dev); 260208748Sraj 261208748Sraj if ((err = uart_fdt_get_clock(node, &clock)) != 0) 262208748Sraj return (err); 263281438Sandrew if (uart_fdt_get_shift(node, &shift) != 0) 264281438Sandrew shift = uart_getregshift(sc->sc_class); 265340145Smmacy if (uart_fdt_get_io_width(node, &iowidth) != 0) 266340145Smmacy iowidth = uart_getregiowidth(sc->sc_class); 267208748Sraj 268340145Smmacy return (uart_bus_probe(dev, (int)shift, (int)iowidth, (int)clock, 0, 0, 0)); 269208748Sraj} 270208748Sraj 271208748SrajDRIVER_MODULE(uart, simplebus, uart_fdt_driver, uart_devclass, 0, 0); 272274452SzbbDRIVER_MODULE(uart, ofwbus, uart_fdt_driver, uart_devclass, 0, 0); 273