Path: blob/main/sys/arm64/qoriq/clk/qoriq_clkgen.c
39536 views
/*-1* SPDX-License-Identifier: BSD-2-Clause2*3* Copyright (c) 2020 Alstom Group.4* Copyright (c) 2020 Semihalf.5*6* Redistribution and use in source and binary forms, with or without7* modification, are permitted provided that the following conditions8* are met:9* 1. Redistributions of source code must retain the above copyright10* notice, this list of conditions and the following disclaimer.11* 2. Redistributions in binary form must reproduce the above copyright12* notice, this list of conditions and the following disclaimer in the13* documentation and/or other materials provided with the distribution.14*15* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND16* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE17* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE18* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE19* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL20* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS21* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)22* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT23* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY24* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF25* SUCH DAMAGE.26*/2728#include <sys/param.h>29#include <sys/systm.h>30#include <sys/bus.h>31#include <sys/endian.h>32#include <sys/rman.h>33#include <sys/kernel.h>34#include <sys/lock.h>35#include <sys/module.h>36#include <sys/mutex.h>37#include <machine/bus.h>3839#include <dev/fdt/simplebus.h>4041#include <dev/ofw/ofw_bus.h>42#include <dev/ofw/ofw_bus_subr.h>4344#include <dev/clk/clk_fixed.h>4546#include <arm64/qoriq/clk/qoriq_clkgen.h>4748#include "clkdev_if.h"4950MALLOC_DEFINE(M_QORIQ_CLKGEN, "qoriq_clkgen", "qoriq_clkgen");5152static struct resource_spec qoriq_clkgen_spec[] = {53{ SYS_RES_MEMORY, 0, RF_ACTIVE },54{ -1, 0 }55};5657static const char *qoriq_pll_parents_coreclk[] = {58QORIQ_CORECLK_NAME59};6061static const char *qoriq_pll_parents_sysclk[] = {62QORIQ_SYSCLK_NAME63};6465static int66qoriq_clkgen_ofw_mapper(struct clkdom *clkdom, uint32_t ncells,67phandle_t *cells, struct clknode **clk)68{6970if (ncells != 2)71return (EINVAL);7273if (cells[0] > 5)74return (EINVAL);7576if (cells[0] == QORIQ_TYPE_SYSCLK || cells[0] == QORIQ_TYPE_CORECLK)77if (cells[1] != 0)78return (EINVAL);7980*clk = clknode_find_by_id(clkdom, QORIQ_CLK_ID(cells[0], cells[1]));8182if (*clk == NULL)83return (EINVAL);8485return (0);86}8788static int89qoriq_clkgen_write_4(device_t dev, bus_addr_t addr, uint32_t val)90{91struct qoriq_clkgen_softc *sc;9293sc = device_get_softc(dev);9495if (sc->flags & QORIQ_LITTLE_ENDIAN)96bus_write_4(sc->res, addr, htole32(val));97else98bus_write_4(sc->res, addr, htobe32(val));99return (0);100}101102static int103qoriq_clkgen_read_4(device_t dev, bus_addr_t addr, uint32_t *val)104{105struct qoriq_clkgen_softc *sc;106107sc = device_get_softc(dev);108109if (sc->flags & QORIQ_LITTLE_ENDIAN)110*val = le32toh(bus_read_4(sc->res, addr));111else112*val = be32toh(bus_read_4(sc->res, addr));113return (0);114}115116static int117qoriq_clkgen_modify_4(device_t dev, bus_addr_t addr, uint32_t clr,118uint32_t set)119{120struct qoriq_clkgen_softc *sc;121uint32_t reg;122123sc = device_get_softc(dev);124125if (sc->flags & QORIQ_LITTLE_ENDIAN)126reg = le32toh(bus_read_4(sc->res, addr));127else128reg = be32toh(bus_read_4(sc->res, addr));129130reg &= ~clr;131reg |= set;132133if (sc->flags & QORIQ_LITTLE_ENDIAN)134bus_write_4(sc->res, addr, htole32(reg));135else136bus_write_4(sc->res, addr, htobe32(reg));137138return (0);139}140141static void142qoriq_clkgen_device_lock(device_t dev)143{144struct qoriq_clkgen_softc *sc;145146sc = device_get_softc(dev);147mtx_lock(&sc->mtx);148}149150static void151qoriq_clkgen_device_unlock(device_t dev)152{153struct qoriq_clkgen_softc *sc;154155sc = device_get_softc(dev);156mtx_unlock(&sc->mtx);157}158159static device_method_t qoriq_clkgen_methods[] = {160DEVMETHOD(clkdev_write_4, qoriq_clkgen_write_4),161DEVMETHOD(clkdev_read_4, qoriq_clkgen_read_4),162DEVMETHOD(clkdev_modify_4, qoriq_clkgen_modify_4),163DEVMETHOD(clkdev_device_lock, qoriq_clkgen_device_lock),164DEVMETHOD(clkdev_device_unlock, qoriq_clkgen_device_unlock),165166DEVMETHOD_END167};168169DEFINE_CLASS_0(qoriq_clkgen, qoriq_clkgen_driver, qoriq_clkgen_methods,170sizeof(struct qoriq_clkgen_softc));171172static int173qoriq_clkgen_create_sysclk(device_t dev)174{175struct qoriq_clkgen_softc *sc;176struct clk_fixed_def def;177const char *clkname;178phandle_t node;179uint32_t freq;180clk_t clock;181int rv;182183sc = device_get_softc(dev);184node = ofw_bus_get_node(dev);185sc->has_coreclk = false;186187memset(&def, 0, sizeof(def));188189rv = OF_getencprop(node, "clock-frequency", &freq, sizeof(freq));190if (rv > 0) {191def.clkdef.name = QORIQ_SYSCLK_NAME;192def.clkdef.id = QORIQ_CLK_ID(QORIQ_TYPE_SYSCLK, 0);193def.freq = freq;194195rv = clknode_fixed_register(sc->clkdom, &def);196return (rv);197} else {198/*199* As both sysclk and coreclk need to be accessible from200* device tree, create internal 1:1 divider nodes.201*/202def.clkdef.parent_cnt = 1;203def.freq = 0;204def.mult = 1;205def.div = 1;206207rv = clk_get_by_ofw_name(dev, node, "coreclk", &clock);208if (rv == 0) {209def.clkdef.name = QORIQ_CORECLK_NAME;210clkname = clk_get_name(clock);211def.clkdef.parent_names = &clkname;212def.clkdef.id = QORIQ_CLK_ID(QORIQ_TYPE_CORECLK, 0);213214rv = clknode_fixed_register(sc->clkdom, &def);215if (rv)216return (rv);217218sc->has_coreclk = true;219}220221rv = clk_get_by_ofw_name(dev, node, "sysclk", &clock);222if (rv != 0) {223rv = clk_get_by_ofw_index(dev, node, 0, &clock);224if (rv != 0)225return (rv);226}227228clkname = clk_get_name(clock);229def.clkdef.name = QORIQ_SYSCLK_NAME;230def.clkdef.id = QORIQ_CLK_ID(QORIQ_TYPE_SYSCLK, 0);231def.clkdef.parent_names = &clkname;232233rv = clknode_fixed_register(sc->clkdom, &def);234return (rv);235}236}237238int239qoriq_clkgen_attach(device_t dev)240{241struct qoriq_clkgen_softc *sc;242int i, error;243244sc = device_get_softc(dev);245sc->dev = dev;246247if (bus_alloc_resources(dev, qoriq_clkgen_spec, &sc->res) != 0) {248device_printf(dev, "Cannot allocate resources.\n");249return (ENXIO);250}251252mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF);253254sc->clkdom = clkdom_create(dev);255if (sc->clkdom == NULL)256panic("Cannot create clock domain.\n");257258error = qoriq_clkgen_create_sysclk(dev);259if (error != 0) {260device_printf(dev, "Cannot create sysclk.\n");261return (error);262}263264sc->pltfrm_pll_def->clkdef.parent_names = qoriq_pll_parents_sysclk;265sc->pltfrm_pll_def->clkdef.parent_cnt = 1;266error = qoriq_clk_pll_register(sc->clkdom, sc->pltfrm_pll_def);267if (error != 0) {268device_printf(dev, "Cannot create platform PLL.\n");269return (error);270}271272for (i = 0; i < sc->cga_pll_num; i++) {273if (sc->has_coreclk)274sc->cga_pll[i]->clkdef.parent_names = qoriq_pll_parents_coreclk;275else276sc->cga_pll[i]->clkdef.parent_names = qoriq_pll_parents_sysclk;277sc->cga_pll[i]->clkdef.parent_cnt = 1;278279error = qoriq_clk_pll_register(sc->clkdom, sc->cga_pll[i]);280if (error != 0) {281device_printf(dev, "Cannot create CGA PLLs\n.");282return (error);283}284}285286/*287* Both CMUX and HWACCEL multiplexer nodes can be represented288* by using built in clk_mux nodes.289*/290for (i = 0; i < sc->mux_num; i++) {291error = clknode_mux_register(sc->clkdom, sc->mux[i]);292if (error != 0) {293device_printf(dev, "Cannot create MUX nodes.\n");294return (error);295}296}297298if (sc->init_func != NULL) {299error = sc->init_func(dev);300if (error) {301device_printf(dev, "Clock init function failed.\n");302return (error);303}304}305306clkdom_set_ofw_mapper(sc->clkdom, qoriq_clkgen_ofw_mapper);307308if (clkdom_finit(sc->clkdom) != 0)309panic("Cannot finalize clock domain initialization.\n");310311if (bootverbose)312clkdom_dump(sc->clkdom);313314return (0);315}316317318