1270957Sian/*- 2270957Sian * Copyright (c) 2014 Ian Lepore <ian@freebsd.org> 3270957Sian * All rights reserved. 4270957Sian * 5270957Sian * Redistribution and use in source and binary forms, with or without 6270957Sian * modification, are permitted provided that the following conditions 7270957Sian * are met: 8270957Sian * 1. Redistributions of source code must retain the above copyright 9270957Sian * notice, this list of conditions and the following disclaimer. 10270957Sian * 2. Redistributions in binary form must reproduce the above copyright 11270957Sian * notice, this list of conditions and the following disclaimer in the 12270957Sian * documentation and/or other materials provided with the distribution. 13270957Sian * 14270957Sian * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15270957Sian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16270957Sian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17270957Sian * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18270957Sian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19270957Sian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20270957Sian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21270957Sian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22270957Sian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23270957Sian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24270957Sian * SUCH DAMAGE. 25270957Sian * 26270957Sian * $FreeBSD$ 27270957Sian */ 28270957Sian 29270957Sian#include <sys/cdefs.h> 30270957Sian#include <sys/param.h> 31270957Sian#include <sys/kernel.h> 32270957Sian#include <sys/lock.h> 33270957Sian#include <sys/mutex.h> 34270957Sian#include <sys/queue.h> 35270959Sian#include <sys/systm.h> 36270957Sian 37270957Sian#include <dev/ofw/ofw_bus.h> 38270957Sian#include <dev/ofw/ofw_bus_subr.h> 39270957Sian 40270957Sian#include "fdt_clock_if.h" 41270957Sian#include <dev/fdt/fdt_clock.h> 42270957Sian 43270957Sian/* 44270957Sian * Loop through all the tuples in the clocks= property for a device, enabling or 45270957Sian * disabling each clock. 46270957Sian * 47270957Sian * Be liberal about errors for now: warn about a failure to enable but keep 48270957Sian * trying with any other clocks in the list. Return ENXIO if any errors were 49270957Sian * found, and let the caller decide whether the problem is fatal. 50270957Sian */ 51270957Sianstatic int 52270957Sianenable_disable_all(device_t consumer, boolean_t enable) 53270957Sian{ 54270957Sian phandle_t cnode; 55270957Sian device_t clockdev; 56270957Sian int clocknum, err, i, ncells; 57270957Sian uint32_t *clks; 58270957Sian boolean_t anyerrors; 59270957Sian 60270957Sian cnode = ofw_bus_get_node(consumer); 61270957Sian ncells = OF_getencprop_alloc(cnode, "clocks", sizeof(*clks), 62270957Sian (void **)&clks); 63270957Sian if (enable && ncells < 2) { 64270957Sian device_printf(consumer, "Warning: No clocks specified in fdt " 65270957Sian "data; device may not function."); 66270957Sian return (ENXIO); 67270957Sian } 68270957Sian anyerrors = false; 69270957Sian for (i = 0; i < ncells; i += 2) { 70270957Sian clockdev = OF_device_from_xref(clks[i]); 71270957Sian clocknum = clks[i + 1]; 72270957Sian if (clockdev == NULL) { 73270957Sian if (enable) 74270957Sian device_printf(consumer, "Warning: can not find " 75270957Sian "driver for clock number %u; device may not " 76270957Sian "function\n", clocknum); 77270957Sian anyerrors = true; 78270957Sian continue; 79270957Sian } 80270957Sian if (enable) 81270957Sian err = FDT_CLOCK_ENABLE(clockdev, clocknum); 82270957Sian else 83270957Sian err = FDT_CLOCK_DISABLE(clockdev, clocknum); 84270957Sian if (err != 0) { 85270957Sian if (enable) 86270957Sian device_printf(consumer, "Warning: failed to " 87270957Sian "enable clock number %u; device may not " 88270957Sian "function\n", clocknum); 89270957Sian anyerrors = true; 90270957Sian } 91270957Sian } 92299477Sgonzo OF_prop_free(clks); 93270957Sian return (anyerrors ? ENXIO : 0); 94270957Sian} 95270957Sian 96270957Sianint 97270957Sianfdt_clock_get_info(device_t consumer, int n, struct fdt_clock_info *info) 98270957Sian{ 99270957Sian phandle_t cnode; 100270957Sian device_t clockdev; 101270957Sian int clocknum, err, ncells; 102270957Sian uint32_t *clks; 103270957Sian 104270957Sian cnode = ofw_bus_get_node(consumer); 105270957Sian ncells = OF_getencprop_alloc(cnode, "clocks", sizeof(*clks), 106270957Sian (void **)&clks); 107270957Sian if (ncells <= 0) 108270957Sian return (ENXIO); 109270957Sian n *= 2; 110270957Sian if (ncells <= n) 111270957Sian err = ENXIO; 112270957Sian else { 113270957Sian clockdev = OF_device_from_xref(clks[n]); 114270957Sian if (clockdev == NULL) 115270957Sian err = ENXIO; 116270957Sian else { 117270957Sian /* 118270957Sian * Make struct contents minimally valid, then call 119270957Sian * provider to fill in what it knows (provider can 120270957Sian * override anything it wants to). 121270957Sian */ 122270957Sian clocknum = clks[n + 1]; 123270959Sian bzero(info, sizeof(*info)); 124270957Sian info->provider = clockdev; 125270957Sian info->index = clocknum; 126270957Sian info->name = ""; 127270957Sian err = FDT_CLOCK_GET_INFO(clockdev, clocknum, info); 128270957Sian } 129270957Sian } 130299477Sgonzo OF_prop_free(clks); 131270957Sian return (err); 132270957Sian} 133270957Sian 134270957Sianint 135270957Sianfdt_clock_enable_all(device_t consumer) 136270957Sian{ 137270957Sian 138270957Sian return (enable_disable_all(consumer, true)); 139270957Sian} 140270957Sian 141270957Sianint 142270957Sianfdt_clock_disable_all(device_t consumer) 143270957Sian{ 144270957Sian 145270957Sian return (enable_disable_all(consumer, false)); 146270957Sian} 147270957Sian 148270957Sianvoid 149270957Sianfdt_clock_register_provider(device_t provider) 150270957Sian{ 151270957Sian 152277655Sian OF_device_register_xref( 153277989Sian OF_xref_from_node(ofw_bus_get_node(provider)), provider); 154270957Sian} 155270957Sian 156270957Sianvoid 157270957Sianfdt_clock_unregister_provider(device_t provider) 158270957Sian{ 159270957Sian 160270959Sian OF_device_register_xref(OF_xref_from_device(provider), NULL); 161270957Sian} 162270957Sian 163