Path: blob/main/sys/arm/broadcom/bcm2835/bcm2835_clkman.c
39566 views
/*-1* SPDX-License-Identifier: BSD-2-Clause2*3* Copyright (c) 2017 Poul-Henning Kamp <[email protected]>4*5* Redistribution and use in source and binary forms, with or without6* modification, are permitted provided that the following conditions7* are met:8* 1. Redistributions of source code must retain the above copyright9* notice, this list of conditions and the following disclaimer.10* 2. Redistributions in binary form must reproduce the above copyright11* notice, this list of conditions and the following disclaimer in the12* documentation and/or other materials provided with the distribution.13*14* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND15* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE16* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE17* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE18* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL19* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS20* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)21* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT22* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY23* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF24* SUCH DAMAGE.25*26*/2728#include <sys/param.h>29#include <sys/systm.h>30#include <sys/bus.h>31#include <sys/cpu.h>32#include <sys/kernel.h>33#include <sys/lock.h>34#include <sys/malloc.h>35#include <sys/module.h>36#include <sys/mutex.h>37#include <sys/rman.h>38#include <sys/sema.h>39#include <sys/sysctl.h>4041#include <machine/bus.h>42#include <machine/cpu.h>4344#include <dev/ofw/ofw_bus.h>45#include <dev/ofw/ofw_bus_subr.h>4647#include <arm/broadcom/bcm2835/bcm2835_clkman.h>4849static struct ofw_compat_data compat_data[] = {50{"brcm,bcm2711-cprman", 1},51{"brcm,bcm2835-cprman", 1},52{"broadcom,bcm2835-cprman", 1},53{NULL, 0}54};5556struct bcm2835_clkman_softc {57device_t sc_dev;5859struct resource * sc_m_res;60bus_space_tag_t sc_m_bst;61bus_space_handle_t sc_m_bsh;62};6364#define BCM_CLKMAN_WRITE(_sc, _off, _val) \65bus_space_write_4(_sc->sc_m_bst, _sc->sc_m_bsh, _off, _val)66#define BCM_CLKMAN_READ(_sc, _off) \67bus_space_read_4(_sc->sc_m_bst, _sc->sc_m_bsh, _off)6869#define W_CMCLK(_sc, unit, _val) BCM_CLKMAN_WRITE(_sc, unit, 0x5a000000 | (_val))70#define R_CMCLK(_sc, unit) BCM_CLKMAN_READ(_sc, unit)71#define W_CMDIV(_sc, unit, _val) BCM_CLKMAN_WRITE(_sc, (unit) + 4, 0x5a000000 | (_val))72#define R_CMDIV(_sc, unit) BCM_CLKMAN_READ(_sc, (unit) + 4)7374static int75bcm2835_clkman_probe(device_t dev)76{7778if (!ofw_bus_status_okay(dev))79return (ENXIO);8081if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)82return (ENXIO);8384device_set_desc(dev, "BCM283x Clock Manager");8586return (BUS_PROBE_DEFAULT);87}8889static int90bcm2835_clkman_attach(device_t dev)91{92struct bcm2835_clkman_softc *sc;93int rid;9495if (device_get_unit(dev) != 0) {96device_printf(dev, "only one clk manager supported\n");97return (ENXIO);98}99100sc = device_get_softc(dev);101sc->sc_dev = dev;102103rid = 0;104sc->sc_m_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,105RF_ACTIVE);106if (!sc->sc_m_res) {107device_printf(dev, "cannot allocate memory window\n");108return (ENXIO);109}110111sc->sc_m_bst = rman_get_bustag(sc->sc_m_res);112sc->sc_m_bsh = rman_get_bushandle(sc->sc_m_res);113114bus_attach_children(dev);115return (0);116}117118uint32_t119bcm2835_clkman_set_frequency(device_t dev, uint32_t unit, uint32_t hz)120{121struct bcm2835_clkman_softc *sc;122int i;123uint32_t u;124125sc = device_get_softc(dev);126127if (unit != BCM_PWM_CLKSRC) {128device_printf(sc->sc_dev,129"Unsupported unit 0x%x", unit);130return (0);131}132133W_CMCLK(sc, unit, 6);134for (i = 0; i < 10; i++) {135u = R_CMCLK(sc, unit);136if (!(u&0x80))137break;138DELAY(1000);139}140if (u & 0x80) {141device_printf(sc->sc_dev,142"Failed to stop clock for unit 0x%x", unit);143return (0);144}145if (hz == 0)146return (0);147148u = 500000000/hz;149if (u < 4) {150device_printf(sc->sc_dev,151"Frequency too high for unit 0x%x (max: 125 MHz)",152unit);153return (0);154}155if (u > 0xfff) {156device_printf(sc->sc_dev,157"Frequency too low for unit 0x%x (min: 123 kHz)",158unit);159return (0);160}161hz = 500000000/u;162W_CMDIV(sc, unit, u << 12);163164W_CMCLK(sc, unit, 0x16);165for (i = 0; i < 10; i++) {166u = R_CMCLK(sc, unit);167if ((u&0x80))168break;169DELAY(1000);170}171if (!(u & 0x80)) {172device_printf(sc->sc_dev,173"Failed to start clock for unit 0x%x", unit);174return (0);175}176return (hz);177}178179static int180bcm2835_clkman_detach(device_t dev)181{182struct bcm2835_clkman_softc *sc;183184bus_generic_detach(dev);185186sc = device_get_softc(dev);187if (sc->sc_m_res)188bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_m_res);189190return (0);191}192193static device_method_t bcm2835_clkman_methods[] = {194/* Device interface */195DEVMETHOD(device_probe, bcm2835_clkman_probe),196DEVMETHOD(device_attach, bcm2835_clkman_attach),197DEVMETHOD(device_detach, bcm2835_clkman_detach),198199DEVMETHOD_END200};201202static driver_t bcm2835_clkman_driver = {203"bcm2835_clkman",204bcm2835_clkman_methods,205sizeof(struct bcm2835_clkman_softc),206};207208DRIVER_MODULE(bcm2835_clkman, simplebus, bcm2835_clkman_driver, 0, 0);209MODULE_VERSION(bcm2835_clkman, 1);210211212