Path: blob/main/sys/arm64/nvidia/tegra210/tegra210_car.c
48262 views
/*-1* SPDX-License-Identifier: BSD-2-Clause2*3* Copyright 2020 Michal Meloun <[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*/2627#include <sys/param.h>28#include <sys/systm.h>29#include <sys/bus.h>30#include <sys/kernel.h>31#include <sys/kobj.h>32#include <sys/module.h>33#include <sys/malloc.h>34#include <sys/rman.h>35#include <sys/lock.h>36#include <sys/mutex.h>3738#include <machine/bus.h>39#include <machine/cpu.h>4041#include <dev/clk/clk_div.h>42#include <dev/clk/clk_fixed.h>43#include <dev/clk/clk_gate.h>44#include <dev/clk/clk_mux.h>45#include <dev/hwreset/hwreset.h>46#include <dev/ofw/openfirm.h>47#include <dev/ofw/ofw_bus.h>48#include <dev/ofw/ofw_bus_subr.h>4950#include <dt-bindings/clock/tegra210-car.h>5152#include "clkdev_if.h"53#include "hwreset_if.h"54#include "tegra210_car.h"5556static struct ofw_compat_data compat_data[] = {57{"nvidia,tegra210-car", 1},58{NULL, 0},59};6061#define PLIST(x) static const char *x[]6263/* Pure multiplexer. */64#define MUX(_id, cname, plists, o, s, w) \65{ \66.clkdef.id = _id, \67.clkdef.name = cname, \68.clkdef.parent_names = plists, \69.clkdef.parent_cnt = nitems(plists), \70.clkdef.flags = CLK_NODE_STATIC_STRINGS, \71.offset = o, \72.shift = s, \73.width = w, \74}7576/* Fractional divider (7.1). */77#define DIV7_1(_id, cname, plist, o, s) \78{ \79.clkdef.id = _id, \80.clkdef.name = cname, \81.clkdef.parent_names = (const char *[]){plist}, \82.clkdef.parent_cnt = 1, \83.clkdef.flags = CLK_NODE_STATIC_STRINGS, \84.offset = o, \85.i_shift = (s) + 1, \86.i_width = 7, \87.f_shift = s, \88.f_width = 1, \89}9091/* Integer divider. */92#define DIV(_id, cname, plist, o, s, w, f) \93{ \94.clkdef.id = _id, \95.clkdef.name = cname, \96.clkdef.parent_names = (const char *[]){plist}, \97.clkdef.parent_cnt = 1, \98.clkdef.flags = CLK_NODE_STATIC_STRINGS, \99.offset = o, \100.i_shift = s, \101.i_width = w, \102.div_flags = f, \103}104105/* Gate in PLL block. */106#define GATE_PLL(_id, cname, plist, o, s) \107{ \108.clkdef.id = _id, \109.clkdef.name = cname, \110.clkdef.parent_names = (const char *[]){plist}, \111.clkdef.parent_cnt = 1, \112.clkdef.flags = CLK_NODE_STATIC_STRINGS, \113.offset = o, \114.shift = s, \115.mask = 3, \116.on_value = 3, \117.off_value = 0, \118}119120/* Standard gate. */121#define GATE(_id, cname, plist, o, s) \122{ \123.clkdef.id = _id, \124.clkdef.name = cname, \125.clkdef.parent_names = (const char *[]){plist}, \126.clkdef.parent_cnt = 1, \127.clkdef.flags = CLK_NODE_STATIC_STRINGS, \128.offset = o, \129.shift = s, \130.mask = 1, \131.on_value = 1, \132.off_value = 0, \133}134135/* Inverted gate. */136#define GATE_INV(_id, cname, plist, o, s) \137{ \138.clkdef.id = _id, \139.clkdef.name = cname, \140.clkdef.parent_names = (const char *[]){plist}, \141.clkdef.parent_cnt = 1, \142.clkdef.flags = CLK_NODE_STATIC_STRINGS, \143.offset = o, \144.shift = s, \145.mask = 1, \146.on_value = 0, \147.off_value = 1, \148}149150/* Fixed rate clock. */151#define FRATE(_id, cname, _freq) \152{ \153.clkdef.id = _id, \154.clkdef.name = cname, \155.clkdef.parent_names = NULL, \156.clkdef.parent_cnt = 0, \157.clkdef.flags = CLK_NODE_STATIC_STRINGS, \158.freq = _freq, \159}160161/* Fixed rate multipier/divider. */162#define FACT(_id, cname, pname, _mult, _div) \163{ \164.clkdef.id = _id, \165.clkdef.name = cname, \166.clkdef.parent_names = (const char *[]){pname}, \167.clkdef.parent_cnt = 1, \168.clkdef.flags = CLK_NODE_STATIC_STRINGS, \169.mult = _mult, \170.div = _div, \171}172173static uint32_t osc_freqs[16] = {174[0] = 13000000,175[1] = 16800000,176[4] = 19200000,177[5] = 38400000,178[8] = 12000000,179[9] = 48000000,180};181182183/* Parent lists. */184PLIST(mux_xusb_hs) = {"xusb_ss_div2", "pllU_60", "pc_xusb_ss" };185PLIST(mux_xusb_ssp) = {"xusb_ss", "osc_div_clk"};186187188/* Clocks adjusted online. */189static struct clk_fixed_def fixed_osc =190FRATE(TEGRA210_CLK_CLK_M, "osc", 38400000);191static struct clk_fixed_def fixed_clk_m =192FACT(0, "clk_m", "osc", 1, 1);193static struct clk_fixed_def fixed_osc_div =194FACT(0, "osc_div_clk", "osc", 1, 1);195196static struct clk_fixed_def tegra210_fixed_clks[] = {197/* Core clocks. */198FRATE(0, "bogus", 1),199FRATE(0, "clk_s", 32768),200201/* Audio clocks. */202FRATE(0, "vimclk_sync", 1),203FRATE(0, "i2s1_sync", 1),204FRATE(0, "i2s2_sync", 1),205FRATE(0, "i2s3_sync", 1),206FRATE(0, "i2s4_sync", 1),207FRATE(0, "i2s5_sync", 1),208FRATE(0, "spdif_in_sync", 1),209210/* XUSB */211FACT(TEGRA210_CLK_XUSB_SS_DIV2, "xusb_ss_div2", "xusb_ss", 1, 2),212213/* SOR */214FACT(0, "sor_safe_div", "pllP_out0", 1, 17),215FACT(0, "dpaux_div", "sor_safe", 1, 17),216FACT(0, "dpaux1_div", "sor_safe", 1, 17),217218/* Not Yet Implemented */219FRATE(0, "audio", 10000000),220FRATE(0, "audio0", 10000000),221FRATE(0, "audio1", 10000000),222FRATE(0, "audio2", 10000000),223FRATE(0, "audio3", 10000000),224FRATE(0, "audio4", 10000000),225FRATE(0, "ext_vimclk", 10000000),226FRATE(0, "audiod1", 10000000),227FRATE(0, "audiod2", 10000000),228FRATE(0, "audiod3", 10000000),229FRATE(0, "dfllCPU_out", 10000000),230231};232233234static struct clk_mux_def tegra210_mux_clks[] = {235/* USB. */236MUX(TEGRA210_CLK_XUSB_HS_SRC, "xusb_hs", mux_xusb_hs, CLK_SOURCE_XUSB_SS, 25, 2),237MUX(0, "xusb_ssp", mux_xusb_ssp, CLK_SOURCE_XUSB_SS, 24, 1),238239};240241242static struct clk_gate_def tegra210_gate_clks[] = {243/* Base peripheral clocks. */244GATE_INV(TEGRA210_CLK_HCLK, "hclk", "hclk_div", CLK_SYSTEM_RATE, 7),245GATE_INV(TEGRA210_CLK_PCLK, "pclk", "pclk_div", CLK_SYSTEM_RATE, 3),246GATE(TEGRA210_CLK_CML0, "cml0", "pllE_out0", PLLE_AUX, 0),247GATE(TEGRA210_CLK_CML1, "cml1", "pllE_out0", PLLE_AUX, 1),248GATE(0, "pllD_dsi_csi", "pllD_out0", PLLD_MISC, 21),249GATE(0, "pllP_hsio", "pllP_out0", PLLP_MISC1, 29),250GATE(0, "pllP_xusb", "pllP_hsio", PLLP_MISC1, 28),251};252253static struct clk_div_def tegra210_div_clks[] = {254/* Base peripheral clocks. */255DIV(0, "hclk_div", "sclk", CLK_SYSTEM_RATE, 4, 2, 0),256DIV(0, "pclk_div", "hclk", CLK_SYSTEM_RATE, 0, 2, 0),257};258259/* Initial setup table. */260static struct tegra210_init_item clk_init_table[] = {261/* clock, partent, frequency, enable */262{"uarta", "pllP_out0", 408000000, 0},263{"uartb", "pllP_out0", 408000000, 0},264{"uartc", "pllP_out0", 408000000, 0},265{"uartd", "pllP_out0", 408000000, 0},266{"pllA", NULL, 564480000, 1},267{"pllA_out0", NULL, 11289600, 1},268{"extperiph1", "pllA_out0", 0, 1},269{"i2s1", "pllA_out0", 11289600, 0},270{"i2s2", "pllA_out0", 11289600, 0},271{"i2s3", "pllA_out0", 11289600, 0},272{"i2s4", "pllA_out0", 11289600, 0},273{"i2s5", "pllA_out0", 11289600, 0},274{"host1x", "pllP_out0", 136000000, 1},275{"sclk", "pllP_out2", 102000000, 1},276{"dvfs_soc", "pllP_out0", 51000000, 1},277{"dvfs_ref", "pllP_out0", 51000000, 1},278{"spi4", "pllP_out0", 12000000, 1},279{"pllREFE", NULL, 672000000, 0},280281{"xusb", NULL, 0, 1},282{"xusb_ss", "pllU_480", 120000000, 0},283{"pc_xusb_fs", "pllU_48", 48000000, 0},284{"xusb_hs", "pc_xusb_ss", 120000000, 0},285{"xusb_ssp", "xusb_ss", 120000000, 0},286{"pc_xusb_falcon", "pllP_xusb", 204000000, 0},287{"pc_xusb_core_host", "pllP_xusb", 102000000, 0},288{"pc_xusb_core_dev", "pllP_xusb", 102000000, 0},289290{"sata", "pllP_out0", 104000000, 0},291{"sata_oob", "pllP_out0", 204000000, 0},292{"emc", NULL, 0, 1},293{"mselect", NULL, 0, 1},294{"csite", NULL, 0, 1},295296{"dbgapb", NULL, 0, 1 },297{"tsensor", "clk_m", 400000, 0},298{"i2c1", "pllP_out0", 0, 0},299{"i2c2", "pllP_out0", 0, 0},300{"i2c3", "pllP_out0", 0, 0},301{"i2c4", "pllP_out0", 0, 0},302{"i2c5", "pllP_out0", 0, 0},303{"i2c6", "pllP_out0", 0, 0},304305{"pllDP_out0", NULL, 270000000, 0},306{"soc_therm", "pllP_out0", 51000000, 0},307{"cclk_g", NULL, 0, 1},308{"pllU_out1", NULL, 48000000, 1},309{"pllU_out2", NULL, 60000000, 1},310{"pllC4", NULL, 1000000000, 1},311{"pllC4_out0", NULL, 1000000000, 1},312};313314static void315init_divs(struct tegra210_car_softc *sc, struct clk_div_def *clks, int nclks)316{317int i, rv;318319for (i = 0; i < nclks; i++) {320rv = clknode_div_register(sc->clkdom, clks + i);321if (rv != 0)322panic("clk_div_register failed");323}324}325326static void327init_gates(struct tegra210_car_softc *sc, struct clk_gate_def *clks, int nclks)328{329int i, rv;330331332for (i = 0; i < nclks; i++) {333rv = clknode_gate_register(sc->clkdom, clks + i);334if (rv != 0)335panic("clk_gate_register failed");336}337}338339static void340init_muxes(struct tegra210_car_softc *sc, struct clk_mux_def *clks, int nclks)341{342int i, rv;343344345for (i = 0; i < nclks; i++) {346rv = clknode_mux_register(sc->clkdom, clks + i);347if (rv != 0)348panic("clk_mux_register failed");349}350}351352static void353init_fixeds(struct tegra210_car_softc *sc, struct clk_fixed_def *clks,354int nclks)355{356int i, rv;357uint32_t val;358int osc_idx;359360CLKDEV_READ_4(sc->dev, OSC_CTRL, &val);361osc_idx = OSC_CTRL_OSC_FREQ_GET(val);362fixed_osc.freq = osc_freqs[osc_idx];363if (fixed_osc.freq == 0)364panic("Undefined input frequency");365rv = clknode_fixed_register(sc->clkdom, &fixed_osc);366if (rv != 0)367panic("clk_fixed_register failed");368369fixed_osc_div.div = 1 << OSC_CTRL_PLL_REF_DIV_GET(val);370rv = clknode_fixed_register(sc->clkdom, &fixed_osc_div);371if (rv != 0)372panic("clk_fixed_register failed");373374CLKDEV_READ_4(sc->dev, SPARE_REG0, &val);375fixed_clk_m.div = SPARE_REG0_MDIV_GET(val) + 1;376rv = clknode_fixed_register(sc->clkdom, &fixed_clk_m);377if (rv != 0)378panic("clk_fixed_register failed");379380for (i = 0; i < nclks; i++) {381rv = clknode_fixed_register(sc->clkdom, clks + i);382if (rv != 0)383panic("clk_fixed_register failed");384}385}386387static void388postinit_clock(struct tegra210_car_softc *sc)389{390int i;391struct tegra210_init_item *tbl;392struct clknode *clknode;393int rv;394395for (i = 0; i < nitems(clk_init_table); i++) {396tbl = &clk_init_table[i];397398clknode = clknode_find_by_name(tbl->name);399if (clknode == NULL) {400device_printf(sc->dev, "Cannot find clock %s\n",401tbl->name);402continue;403}404if (tbl->parent != NULL) {405rv = clknode_set_parent_by_name(clknode, tbl->parent);406if (rv != 0) {407device_printf(sc->dev,408"Cannot set parent for %s (to %s): %d\n",409tbl->name, tbl->parent, rv);410continue;411}412}413if (tbl->frequency != 0) {414rv = clknode_set_freq(clknode, tbl->frequency, 0 , 9999);415if (rv != 0) {416device_printf(sc->dev,417"Cannot set frequency for %s: %d\n",418tbl->name, rv);419continue;420}421}422if (tbl->enable!= 0) {423rv = clknode_enable(clknode);424if (rv != 0) {425device_printf(sc->dev,426"Cannot enable %s: %d\n", tbl->name, rv);427continue;428}429}430}431}432433static void434register_clocks(device_t dev)435{436struct tegra210_car_softc *sc;437438sc = device_get_softc(dev);439sc->clkdom = clkdom_create(dev);440if (sc->clkdom == NULL)441panic("clkdom == NULL");442443init_fixeds(sc, tegra210_fixed_clks, nitems(tegra210_fixed_clks));444tegra210_init_plls(sc);445init_muxes(sc, tegra210_mux_clks, nitems(tegra210_mux_clks));446init_divs(sc, tegra210_div_clks, nitems(tegra210_div_clks));447init_gates(sc, tegra210_gate_clks, nitems(tegra210_gate_clks));448tegra210_periph_clock(sc);449tegra210_super_mux_clock(sc);450clkdom_finit(sc->clkdom);451clkdom_xlock(sc->clkdom);452postinit_clock(sc);453clkdom_unlock(sc->clkdom);454if (bootverbose)455clkdom_dump(sc->clkdom);456}457458static int459tegra210_car_clkdev_read_4(device_t dev, bus_addr_t addr, uint32_t *val)460{461struct tegra210_car_softc *sc;462463sc = device_get_softc(dev);464*val = bus_read_4(sc->mem_res, addr);465return (0);466}467468static int469tegra210_car_clkdev_write_4(device_t dev, bus_addr_t addr, uint32_t val)470{471struct tegra210_car_softc *sc;472473sc = device_get_softc(dev);474bus_write_4(sc->mem_res, addr, val);475return (0);476}477478static int479tegra210_car_clkdev_modify_4(device_t dev, bus_addr_t addr, uint32_t clear_mask,480uint32_t set_mask)481{482struct tegra210_car_softc *sc;483uint32_t reg;484485sc = device_get_softc(dev);486reg = bus_read_4(sc->mem_res, addr);487reg &= ~clear_mask;488reg |= set_mask;489bus_write_4(sc->mem_res, addr, reg);490return (0);491}492493static void494tegra210_car_clkdev_device_lock(device_t dev)495{496struct tegra210_car_softc *sc;497498sc = device_get_softc(dev);499mtx_lock(&sc->mtx);500}501502static void503tegra210_car_clkdev_device_unlock(device_t dev)504{505struct tegra210_car_softc *sc;506507sc = device_get_softc(dev);508mtx_unlock(&sc->mtx);509}510511static int512tegra210_car_detach(device_t dev)513{514515device_printf(dev, "Error: Clock driver cannot be detached\n");516return (EBUSY);517}518519static int520tegra210_car_probe(device_t dev)521{522523if (!ofw_bus_status_okay(dev))524return (ENXIO);525526if (ofw_bus_search_compatible(dev, compat_data)->ocd_data != 0) {527device_set_desc(dev, "Tegra Clock Driver");528return (BUS_PROBE_DEFAULT);529}530531return (ENXIO);532}533534static int535tegra210_car_attach(device_t dev)536{537struct tegra210_car_softc *sc = device_get_softc(dev);538int rid, rv;539540sc->dev = dev;541542mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF);543sc->type = ofw_bus_search_compatible(dev, compat_data)->ocd_data;544545/* Resource setup. */546rid = 0;547sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,548RF_ACTIVE);549if (!sc->mem_res) {550device_printf(dev, "cannot allocate memory resource\n");551rv = ENXIO;552goto fail;553}554555register_clocks(dev);556hwreset_register_ofw_provider(dev);557return (0);558559fail:560if (sc->mem_res)561bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);562563return (rv);564}565566static int567tegra210_car_hwreset_assert(device_t dev, intptr_t id, bool value)568{569struct tegra210_car_softc *sc = device_get_softc(dev);570571return (tegra210_hwreset_by_idx(sc, id, value));572}573574static device_method_t tegra210_car_methods[] = {575/* Device interface */576DEVMETHOD(device_probe, tegra210_car_probe),577DEVMETHOD(device_attach, tegra210_car_attach),578DEVMETHOD(device_detach, tegra210_car_detach),579580/* Clkdev interface*/581DEVMETHOD(clkdev_read_4, tegra210_car_clkdev_read_4),582DEVMETHOD(clkdev_write_4, tegra210_car_clkdev_write_4),583DEVMETHOD(clkdev_modify_4, tegra210_car_clkdev_modify_4),584DEVMETHOD(clkdev_device_lock, tegra210_car_clkdev_device_lock),585DEVMETHOD(clkdev_device_unlock, tegra210_car_clkdev_device_unlock),586587/* Reset interface */588DEVMETHOD(hwreset_assert, tegra210_car_hwreset_assert),589590DEVMETHOD_END591};592593static DEFINE_CLASS_0(car, tegra210_car_driver, tegra210_car_methods,594sizeof(struct tegra210_car_softc));595EARLY_DRIVER_MODULE(tegra210_car, simplebus, tegra210_car_driver, NULL, NULL,596BUS_PASS_TIMER);597598599