1*6b6d6c44SIan Lepore /*- 2*6b6d6c44SIan Lepore * Copyright (c) 2014 Ian Lepore <ian@freebsd.org> 3*6b6d6c44SIan Lepore * All rights reserved. 4*6b6d6c44SIan Lepore * 5*6b6d6c44SIan Lepore * Redistribution and use in source and binary forms, with or without 6*6b6d6c44SIan Lepore * modification, are permitted provided that the following conditions 7*6b6d6c44SIan Lepore * are met: 8*6b6d6c44SIan Lepore * 1. Redistributions of source code must retain the above copyright 9*6b6d6c44SIan Lepore * notice, this list of conditions and the following disclaimer. 10*6b6d6c44SIan Lepore * 2. Redistributions in binary form must reproduce the above copyright 11*6b6d6c44SIan Lepore * notice, this list of conditions and the following disclaimer in the 12*6b6d6c44SIan Lepore * documentation and/or other materials provided with the distribution. 13*6b6d6c44SIan Lepore * 14*6b6d6c44SIan Lepore * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15*6b6d6c44SIan Lepore * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16*6b6d6c44SIan Lepore * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17*6b6d6c44SIan Lepore * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18*6b6d6c44SIan Lepore * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19*6b6d6c44SIan Lepore * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20*6b6d6c44SIan Lepore * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21*6b6d6c44SIan Lepore * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22*6b6d6c44SIan Lepore * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23*6b6d6c44SIan Lepore * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24*6b6d6c44SIan Lepore * SUCH DAMAGE. 25*6b6d6c44SIan Lepore * 26*6b6d6c44SIan Lepore * $FreeBSD$ 27*6b6d6c44SIan Lepore */ 28*6b6d6c44SIan Lepore 29*6b6d6c44SIan Lepore #include <sys/cdefs.h> 30*6b6d6c44SIan Lepore #include <sys/param.h> 31*6b6d6c44SIan Lepore #include <sys/kernel.h> 32*6b6d6c44SIan Lepore #include <sys/lock.h> 33*6b6d6c44SIan Lepore #include <sys/mutex.h> 34*6b6d6c44SIan Lepore #include <sys/queue.h> 35*6b6d6c44SIan Lepore 36*6b6d6c44SIan Lepore #include <dev/ofw/ofw_bus.h> 37*6b6d6c44SIan Lepore #include <dev/ofw/ofw_bus_subr.h> 38*6b6d6c44SIan Lepore 39*6b6d6c44SIan Lepore #include "fdt_clock_if.h" 40*6b6d6c44SIan Lepore #include <dev/fdt/fdt_clock.h> 41*6b6d6c44SIan Lepore 42*6b6d6c44SIan Lepore /* 43*6b6d6c44SIan Lepore * Loop through all the tuples in the clocks= property for a device, enabling or 44*6b6d6c44SIan Lepore * disabling each clock. 45*6b6d6c44SIan Lepore * 46*6b6d6c44SIan Lepore * Be liberal about errors for now: warn about a failure to enable but keep 47*6b6d6c44SIan Lepore * trying with any other clocks in the list. Return ENXIO if any errors were 48*6b6d6c44SIan Lepore * found, and let the caller decide whether the problem is fatal. 49*6b6d6c44SIan Lepore */ 50*6b6d6c44SIan Lepore static int 51*6b6d6c44SIan Lepore enable_disable_all(device_t consumer, boolean_t enable) 52*6b6d6c44SIan Lepore { 53*6b6d6c44SIan Lepore phandle_t cnode; 54*6b6d6c44SIan Lepore device_t clockdev; 55*6b6d6c44SIan Lepore int clocknum, err, i, ncells; 56*6b6d6c44SIan Lepore uint32_t *clks; 57*6b6d6c44SIan Lepore boolean_t anyerrors; 58*6b6d6c44SIan Lepore 59*6b6d6c44SIan Lepore cnode = ofw_bus_get_node(consumer); 60*6b6d6c44SIan Lepore ncells = OF_getencprop_alloc(cnode, "clocks", sizeof(*clks), 61*6b6d6c44SIan Lepore (void **)&clks); 62*6b6d6c44SIan Lepore if (enable && ncells < 2) { 63*6b6d6c44SIan Lepore device_printf(consumer, "Warning: No clocks specified in fdt " 64*6b6d6c44SIan Lepore "data; device may not function."); 65*6b6d6c44SIan Lepore return (ENXIO); 66*6b6d6c44SIan Lepore } 67*6b6d6c44SIan Lepore anyerrors = false; 68*6b6d6c44SIan Lepore for (i = 0; i < ncells; i += 2) { 69*6b6d6c44SIan Lepore clockdev = OF_device_from_xref(clks[i]); 70*6b6d6c44SIan Lepore clocknum = clks[i + 1]; 71*6b6d6c44SIan Lepore if (clockdev == NULL) { 72*6b6d6c44SIan Lepore if (enable) 73*6b6d6c44SIan Lepore device_printf(consumer, "Warning: can not find " 74*6b6d6c44SIan Lepore "driver for clock number %u; device may not " 75*6b6d6c44SIan Lepore "function\n", clocknum); 76*6b6d6c44SIan Lepore anyerrors = true; 77*6b6d6c44SIan Lepore continue; 78*6b6d6c44SIan Lepore } 79*6b6d6c44SIan Lepore if (enable) 80*6b6d6c44SIan Lepore err = FDT_CLOCK_ENABLE(clockdev, clocknum); 81*6b6d6c44SIan Lepore else 82*6b6d6c44SIan Lepore err = FDT_CLOCK_DISABLE(clockdev, clocknum); 83*6b6d6c44SIan Lepore if (err != 0) { 84*6b6d6c44SIan Lepore if (enable) 85*6b6d6c44SIan Lepore device_printf(consumer, "Warning: failed to " 86*6b6d6c44SIan Lepore "enable clock number %u; device may not " 87*6b6d6c44SIan Lepore "function\n", clocknum); 88*6b6d6c44SIan Lepore anyerrors = true; 89*6b6d6c44SIan Lepore } 90*6b6d6c44SIan Lepore } 91*6b6d6c44SIan Lepore free(clks, M_OFWPROP); 92*6b6d6c44SIan Lepore return (anyerrors ? ENXIO : 0); 93*6b6d6c44SIan Lepore } 94*6b6d6c44SIan Lepore 95*6b6d6c44SIan Lepore int 96*6b6d6c44SIan Lepore fdt_clock_get_info(device_t consumer, int n, struct fdt_clock_info *info) 97*6b6d6c44SIan Lepore { 98*6b6d6c44SIan Lepore phandle_t cnode; 99*6b6d6c44SIan Lepore device_t clockdev; 100*6b6d6c44SIan Lepore int clocknum, err, ncells; 101*6b6d6c44SIan Lepore uint32_t *clks; 102*6b6d6c44SIan Lepore 103*6b6d6c44SIan Lepore cnode = ofw_bus_get_node(consumer); 104*6b6d6c44SIan Lepore ncells = OF_getencprop_alloc(cnode, "clocks", sizeof(*clks), 105*6b6d6c44SIan Lepore (void **)&clks); 106*6b6d6c44SIan Lepore if (ncells <= 0) 107*6b6d6c44SIan Lepore return (ENXIO); 108*6b6d6c44SIan Lepore n *= 2; 109*6b6d6c44SIan Lepore if (ncells <= n) 110*6b6d6c44SIan Lepore err = ENXIO; 111*6b6d6c44SIan Lepore else { 112*6b6d6c44SIan Lepore clockdev = OF_device_from_xref(clks[n]); 113*6b6d6c44SIan Lepore if (clockdev == NULL) 114*6b6d6c44SIan Lepore err = ENXIO; 115*6b6d6c44SIan Lepore else { 116*6b6d6c44SIan Lepore /* 117*6b6d6c44SIan Lepore * Make struct contents minimally valid, then call 118*6b6d6c44SIan Lepore * provider to fill in what it knows (provider can 119*6b6d6c44SIan Lepore * override anything it wants to). 120*6b6d6c44SIan Lepore */ 121*6b6d6c44SIan Lepore clocknum = clks[n + 1]; 122*6b6d6c44SIan Lepore memset(info, 0, sizeof(*info)); 123*6b6d6c44SIan Lepore info->provider = clockdev; 124*6b6d6c44SIan Lepore info->index = clocknum; 125*6b6d6c44SIan Lepore info->name = ""; 126*6b6d6c44SIan Lepore err = FDT_CLOCK_GET_INFO(clockdev, clocknum, info); 127*6b6d6c44SIan Lepore } 128*6b6d6c44SIan Lepore } 129*6b6d6c44SIan Lepore free(clks, M_OFWPROP); 130*6b6d6c44SIan Lepore return (err); 131*6b6d6c44SIan Lepore } 132*6b6d6c44SIan Lepore 133*6b6d6c44SIan Lepore int 134*6b6d6c44SIan Lepore fdt_clock_enable_all(device_t consumer) 135*6b6d6c44SIan Lepore { 136*6b6d6c44SIan Lepore 137*6b6d6c44SIan Lepore return (enable_disable_all(consumer, true)); 138*6b6d6c44SIan Lepore } 139*6b6d6c44SIan Lepore 140*6b6d6c44SIan Lepore int 141*6b6d6c44SIan Lepore fdt_clock_disable_all(device_t consumer) 142*6b6d6c44SIan Lepore { 143*6b6d6c44SIan Lepore 144*6b6d6c44SIan Lepore return (enable_disable_all(consumer, false)); 145*6b6d6c44SIan Lepore } 146*6b6d6c44SIan Lepore 147*6b6d6c44SIan Lepore void 148*6b6d6c44SIan Lepore fdt_clock_register_provider(device_t provider) 149*6b6d6c44SIan Lepore { 150*6b6d6c44SIan Lepore 151*6b6d6c44SIan Lepore OF_device_register_xref(OF_xref_from_node(provider), provider); 152*6b6d6c44SIan Lepore } 153*6b6d6c44SIan Lepore 154*6b6d6c44SIan Lepore void 155*6b6d6c44SIan Lepore fdt_clock_unregister_provider(device_t provider) 156*6b6d6c44SIan Lepore { 157*6b6d6c44SIan Lepore 158*6b6d6c44SIan Lepore OF_device_register_xref(OF_xref_from_node(provider), NULL); 159*6b6d6c44SIan Lepore } 160*6b6d6c44SIan Lepore 161