Path: blob/main/sys/arm64/qoriq/clk/ls1028a_flexspi_clk.c
39536 views
/*-1* SPDX-License-Identifier: BSD-2-Clause2*3* Copyright (c) 2021 Alstom Group.4* Copyright (c) 2021 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/kernel.h>32#include <sys/kobj.h>33#include <sys/module.h>34#include <sys/malloc.h>35#include <sys/rman.h>36#include <sys/lock.h>37#include <sys/mutex.h>3839#include <machine/bus.h>40#include <machine/cpu.h>4142#include <dev/clk/clk_div.h>43#include <dev/ofw/openfirm.h>44#include <dev/ofw/ofw_bus.h>45#include <dev/ofw/ofw_bus_subr.h>4647#include "clkdev_if.h"48#include "syscon_if.h"495051struct ls1028a_flexspi_clk_softc {52device_t dev;53struct clkdom *clkdom;54uint64_t reg_offset;55struct syscon *syscon;56struct clk_div_def clk_def;57struct mtx mtx;58};5960static struct clk_div_table ls1028a_flexspi_div_tbl[] = {61{ .value = 0, .divider = 1, },62{ .value = 1, .divider = 2, },63{ .value = 2, .divider = 3, },64{ .value = 3, .divider = 4, },65{ .value = 4, .divider = 5, },66{ .value = 5, .divider = 6, },67{ .value = 6, .divider = 7, },68{ .value = 7, .divider = 8, },69{ .value = 11, .divider = 12, },70{ .value = 15, .divider = 16, },71{ .value = 16, .divider = 20, },72{ .value = 17, .divider = 24, },73{ .value = 18, .divider = 28, },74{ .value = 19, .divider = 32, },75{ .value = 20, .divider = 80, },76{}77};78static struct clk_div_table lx2160a_flexspi_div_tbl[] = {79{ .value = 1, .divider = 2, },80{ .value = 3, .divider = 4, },81{ .value = 5, .divider = 6, },82{ .value = 7, .divider = 8, },83{ .value = 11, .divider = 12, },84{ .value = 15, .divider = 16, },85{ .value = 16, .divider = 20, },86{ .value = 17, .divider = 24, },87{ .value = 18, .divider = 28, },88{ .value = 19, .divider = 32, },89{ .value = 20, .divider = 80, },90{}91};9293static struct ofw_compat_data compat_data[] = {94{ "fsl,ls1028a-flexspi-clk", (uintptr_t)ls1028a_flexspi_div_tbl },95{ "fsl,lx2160a-flexspi-clk", (uintptr_t)lx2160a_flexspi_div_tbl },96{ NULL, 0 }97};9899static int100ls1028a_flexspi_clk_probe(device_t dev)101{102103if (!ofw_bus_status_okay(dev))104return (ENXIO);105106if (ofw_bus_search_compatible(dev, compat_data)->ocd_data != 0) {107device_set_desc(dev, "NXP FlexSPI clock driver");108return (BUS_PROBE_DEFAULT);109}110111return (ENXIO);112}113114static int115ls1028a_flexspi_clk_attach(device_t dev)116{117struct ls1028a_flexspi_clk_softc *sc;118const char *oclkname = NULL;119const char *pclkname[1];120uint32_t acells;121uint32_t scells;122pcell_t cells[4];123phandle_t node;124uint64_t reg_size;125int ret;126clk_t clk;127128sc = device_get_softc(dev);129sc->dev = dev;130node = ofw_bus_get_node(dev);131132/* Parse address-cells and size-cells from the parent node as a fallback */133if (OF_getencprop(node, "#address-cells", &acells,134sizeof(acells)) == -1) {135if (OF_getencprop(OF_parent(node), "#address-cells", &acells,136sizeof(acells)) == -1) {137acells = 2;138}139}140if (OF_getencprop(node, "#size-cells", &scells,141sizeof(scells)) == -1) {142if (OF_getencprop(OF_parent(node), "#size-cells", &scells,143sizeof(scells)) == -1) {144scells = 1;145}146}147ret = OF_getencprop(node, "reg", cells, (acells + scells) * sizeof(pcell_t));148if (ret < 0) {149device_printf(dev, "ERROR: failed to read REG property\n");150return (ENOMEM);151}152sc->reg_offset = (uint64_t)cells[0];153if (acells == 2)154sc->reg_offset = (sc->reg_offset << 32) | (uint64_t)cells[1];155reg_size = (uint64_t)cells[acells];156if (scells == 2)157reg_size = (reg_size << 32) | (uint64_t)cells[acells + 1];158159if (reg_size != 4) {160device_printf(dev, "ERROR, expected only single register\n");161return (EINVAL);162}163if (sc->reg_offset >> 32UL) {164device_printf(dev, "ERROR, only 32-bit address offset is supported\n");165return (EINVAL);166}167168/* Get syscon handle */169ret = SYSCON_GET_HANDLE(dev, &sc->syscon);170if ((ret != 0) || (sc->syscon == NULL)) {171device_printf(dev, "ERROR: failed to get syscon\n");172return (EFAULT);173}174175/* Initialize access mutex */176mtx_init(&sc->mtx, "FSL clock mtx", NULL, MTX_DEF);177178/* Get clock names */179ret = clk_get_by_ofw_index(dev, node, 0, &clk);180if (ret) {181device_printf(dev, "ERROR: failed to get parent clock\n");182return (EINVAL);183}184pclkname[0] = clk_get_name(clk);185ret = clk_parse_ofw_clk_name(dev, node, &oclkname);186if (ret) {187device_printf(dev, "ERROR: failed to get output clock name\n");188return (EINVAL);189}190191#ifdef DEBUG192device_printf(dev, "INFO: pclkname %s, oclkname %s\n", pclkname[0], oclkname);193#endif194195/* Fixup CLK structure */196sc->clk_def.clkdef.name = oclkname;197sc->clk_def.clkdef.parent_names = (const char **)pclkname;198sc->clk_def.offset = (uint32_t)sc->reg_offset;199sc->clk_def.clkdef.id = 1;200sc->clk_def.clkdef.parent_cnt = 1;201sc->clk_def.clkdef.flags = 0;202sc->clk_def.div_flags = CLK_DIV_WITH_TABLE;203sc->clk_def.i_shift = 0;204sc->clk_def.i_width = 5;205sc->clk_def.div_table = (struct clk_div_table*)ofw_bus_search_compatible(dev, compat_data)->ocd_data;206207/* Create clock */208sc->clkdom = clkdom_create(dev);209if (sc->clkdom == NULL)210panic("clkdom == NULL");211ret = clknode_div_register(sc->clkdom, &sc->clk_def);212if (ret) {213device_printf(dev, "ERROR: unable to register clock\n");214return (EINVAL);215}216clkdom_finit(sc->clkdom);217218if (bootverbose)219clkdom_dump(sc->clkdom);220221return (0);222}223224static int225ls1028a_flexspi_clk_detach(device_t dev)226{227228/* Clock detaching is not supported */229return (EACCES);230}231232static int233ls1028a_flexspi_clk_read_4(device_t dev, bus_addr_t addr, uint32_t *val)234{235struct ls1028a_flexspi_clk_softc *sc;236sc = device_get_softc(dev);237238*val = SYSCON_READ_4(sc->syscon, addr);239240return (0);241}242243static int244ls1028a_flexspi_clk_write_4(device_t dev, bus_addr_t addr, uint32_t val)245{246struct ls1028a_flexspi_clk_softc *sc;247int ret;248249sc = device_get_softc(dev);250251ret = SYSCON_WRITE_4(sc->syscon, addr, val);252253return (ret);254}255256static int257ls1028a_flexspi_clk_modify_4(device_t dev, bus_addr_t addr, uint32_t clr, uint32_t set)258{259struct ls1028a_flexspi_clk_softc *sc;260int ret;261262sc = device_get_softc(dev);263264ret = SYSCON_MODIFY_4(sc->syscon, addr, clr, set);265266return (ret);267}268269static void270ls1028a_flexspi_clk_device_lock(device_t dev)271{272struct ls1028a_flexspi_clk_softc *sc;273sc = device_get_softc(dev);274275mtx_lock(&sc->mtx);276}277278static void279ls1028a_flexspi_clk_device_unlock(device_t dev)280{281struct ls1028a_flexspi_clk_softc *sc;282283sc = device_get_softc(dev);284285mtx_unlock(&sc->mtx);286}287288static device_method_t ls1028a_flexspi_clk_methods[] = {289/* Device interface */290DEVMETHOD(device_probe, ls1028a_flexspi_clk_probe),291DEVMETHOD(device_attach, ls1028a_flexspi_clk_attach),292DEVMETHOD(device_detach, ls1028a_flexspi_clk_detach),293294DEVMETHOD(clkdev_read_4, ls1028a_flexspi_clk_read_4),295DEVMETHOD(clkdev_write_4, ls1028a_flexspi_clk_write_4),296DEVMETHOD(clkdev_modify_4, ls1028a_flexspi_clk_modify_4),297DEVMETHOD(clkdev_device_lock, ls1028a_flexspi_clk_device_lock),298DEVMETHOD(clkdev_device_unlock, ls1028a_flexspi_clk_device_unlock),299300DEVMETHOD_END301};302303static DEFINE_CLASS_0(fspi_clk, ls1028a_flexspi_clk_driver, ls1028a_flexspi_clk_methods,304sizeof(struct ls1028a_flexspi_clk_softc));305EARLY_DRIVER_MODULE(ls1028a_flexspi_clk, simple_mfd, ls1028a_flexspi_clk_driver,306NULL, NULL, BUS_PASS_TIMER);307308309