Path: blob/main/sys/arm64/nvidia/tegra210/tegra210_pmc.c
48266 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/module.h>32#include <sys/malloc.h>33#include <sys/mutex.h>34#include <sys/rman.h>3536#include <machine/bus.h>3738#include <dev/clk/clk.h>39#include <dev/hwreset/hwreset.h>40#include <dev/ofw/ofw_bus.h>41#include <dev/ofw/ofw_bus_subr.h>42#include <dev/psci/smccc.h>4344#include <arm/nvidia/tegra_pmc.h>4546#define PMC_CNTRL 0x00047#define PMC_CNTRL_SHUTDOWN_OE (1 << 22)48#define PMC_CNTRL_CPUPWRGOOD_SEL_MASK (0x3 << 20)49#define PMC_CNTRL_CPUPWRGOOD_SEL_SHIFT 2050#define PMC_CNTRL_CPUPWRGOOD_EN (1 << 19)51#define PMC_CNTRL_FUSE_OVERRIDE (1 << 18)52#define PMC_CNTRL_INTR_POLARITY (1 << 17)53#define PMC_CNTRL_CPU_PWRREQ_OE (1 << 16)54#define PMC_CNTRL_CPU_PWRREQ_POLARITY (1 << 15)55#define PMC_CNTRL_SIDE_EFFECT_LP0 (1 << 14)56#define PMC_CNTRL_AOINIT (1 << 13)57#define PMC_CNTRL_PWRGATE_DIS (1 << 12)58#define PMC_CNTRL_SYSCLK_OE (1 << 11)59#define PMC_CNTRL_SYSCLK_POLARITY (1 << 10)60#define PMC_CNTRL_PWRREQ_OE (1 << 9)61#define PMC_CNTRL_PWRREQ_POLARITY (1 << 8)62#define PMC_CNTRL_BLINK_EN (1 << 7)63#define PMC_CNTRL_GLITCHDET_DIS (1 << 6)64#define PMC_CNTRL_LATCHWAKE_EN (1 << 5)65#define PMC_CNTRL_MAIN_RST (1 << 4)66#define PMC_CNTRL_KBC_RST (1 << 3)67#define PMC_CNTRL_RTC_RST (1 << 2)68#define PMC_CNTRL_RTC_CLK_DIS (1 << 1)69#define PMC_CNTRL_KBC_CLK_DIS (1 << 0)7071#define PMC_DPD_SAMPLE 0x0207273#define PMC_CLAMP_STATUS 0x02C74#define PMC_CLAMP_STATUS_PARTID(x) (1 << ((x) & 0x1F))7576#define PMC_PWRGATE_TOGGLE 0x03077#define PMC_PWRGATE_TOGGLE_START (1 << 8)78#define PMC_PWRGATE_TOGGLE_PARTID(x) (((x) & 0x1F) << 0)7980#define PMC_REMOVE_CLAMPING_CMD 0x03481#define PMC_REMOVE_CLAMPING_CMD_PARTID(x) (1 << ((x) & 0x1F))8283#define PMC_PWRGATE_STATUS 0x03884#define PMC_PWRGATE_STATUS_PARTID(x) (1 << ((x) & 0x1F))8586#define PMC_SCRATCH0 0x05087#define PMC_SCRATCH0_MODE_RECOVERY (1 << 31)88#define PMC_SCRATCH0_MODE_BOOTLOADER (1 << 30)89#define PMC_SCRATCH0_MODE_RCM (1 << 1)90#define PMC_SCRATCH0_MODE_MASK (PMC_SCRATCH0_MODE_RECOVERY | \91PMC_SCRATCH0_MODE_BOOTLOADER | \92PMC_SCRATCH0_MODE_RCM)9394#define PMC_CPUPWRGOOD_TIMER 0x0c895#define PMC_CPUPWROFF_TIMER 0x0cc9697#define PMC_SCRATCH41 0x1409899#define PMC_SENSOR_CTRL 0x1b0100#define PMC_SENSOR_CTRL_BLOCK_SCRATCH_WRITE (1 << 2)101#define PMC_SENSOR_CTRL_ENABLE_RST (1 << 1)102#define PMC_SENSOR_CTRL_ENABLE_PG (1 << 0)103104#define PMC_IO_DPD_REQ 0x1b8105#define PMC_IO_DPD_REQ_CODE_IDLE (0 << 30)106#define PMC_IO_DPD_REQ_CODE_OFF (1 << 30)107#define PMC_IO_DPD_REQ_CODE_ON (2 << 30)108#define PMC_IO_DPD_REQ_CODE_MASK (3 << 30)109110#define PMC_IO_DPD_STATUS 0x1bc111#define PMC_IO_DPD_STATUS_HDMI (1 << 28)112#define PMC_IO_DPD2_REQ 0x1c0113#define PMC_IO_DPD2_STATUS 0x1c4114#define PMC_IO_DPD2_STATUS_HV (1 << 6)115#define PMC_SEL_DPD_TIM 0x1c8116117#define PMC_SCRATCH54 0x258118#define PMC_SCRATCH54_DATA_SHIFT 8119#define PMC_SCRATCH54_ADDR_SHIFT 0120121#define PMC_SCRATCH55 0x25c122#define PMC_SCRATCH55_RST_ENABLE (1 << 31)123#define PMC_SCRATCH55_CNTRL_TYPE (1 << 30)124#define PMC_SCRATCH55_CNTRL_ID_SHIFT 27125#define PMC_SCRATCH55_CNTRL_ID_MASK 0x07126#define PMC_SCRATCH55_PINMUX_SHIFT 24127#define PMC_SCRATCH55_PINMUX_MASK 0x07128#define PMC_SCRATCH55_CHECKSUM_SHIFT 16129#define PMC_SCRATCH55_CHECKSUM_MASK 0xFF130#define PMC_SCRATCH55_16BITOP (1 << 15)131#define PMC_SCRATCH55_I2CSLV1_SHIFT 0132#define PMC_SCRATCH55_I2CSLV1_MASK 0x7F133134#define PMC_GPU_RG_CNTRL 0x2d4135136/* Secure access */137#define PMC_SMC 0xc2fffe00138#define PMC_SMC_READ 0xaa139#define PMC_SMC_WRITE 0xbb140141#define PMC_LOCK(_sc) mtx_lock(&(_sc)->mtx)142#define PMC_UNLOCK(_sc) mtx_unlock(&(_sc)->mtx)143#define PMC_LOCK_INIT(_sc) mtx_init(&(_sc)->mtx, \144device_get_nameunit(_sc->dev), "tegra210_pmc", MTX_DEF)145#define PMC_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->mtx);146#define PMC_ASSERT_LOCKED(_sc) mtx_assert(&(_sc)->mtx, MA_OWNED);147#define PMC_ASSERT_UNLOCKED(_sc) mtx_assert(&(_sc)->mtx, MA_NOTOWNED);148149struct tegra210_pmc_softc {150device_t dev;151struct resource *mem_res;152clk_t clk;153struct mtx mtx;154bool secure_access;155156uint32_t rate;157enum tegra_suspend_mode suspend_mode;158uint32_t cpu_good_time;159uint32_t cpu_off_time;160uint32_t core_osc_time;161uint32_t core_pmu_time;162uint32_t core_off_time;163int corereq_high;164int sysclkreq_high;165int combined_req;166int cpu_pwr_good_en;167uint32_t lp0_vec_phys;168uint32_t lp0_vec_size;169};170171static struct ofw_compat_data compat_data[] = {172{"nvidia,tegra210-pmc", 1},173{NULL, 0},174};175176static struct tegra210_pmc_softc *pmc_sc;177178static inline struct tegra210_pmc_softc *179tegra210_pmc_get_sc(void)180{181if (pmc_sc == NULL)182panic("To early call to Tegra PMC driver.\n");183return (pmc_sc);184}185186static void187WR4(struct tegra210_pmc_softc *sc, bus_size_t r, uint32_t v)188{189struct arm_smccc_res res;190191if (sc->secure_access) {192arm_smccc_invoke_smc(PMC_SMC, PMC_SMC_WRITE, r, v, &res);193if (res.a0 != 0)194device_printf(sc->dev," PMC SMC write failed: %lu\n",195res.a0);196}197198bus_write_4(sc->mem_res, r, v);199}200201static uint32_t202RD4(struct tegra210_pmc_softc *sc, bus_size_t r)203{204struct arm_smccc_res res;205206if (sc->secure_access) {207arm_smccc_invoke_smc(PMC_SMC, PMC_SMC_READ, r, &res);208if (res.a0 != 0)209device_printf(sc->dev," PMC SMC write failed: %lu\n",210res.a0);211return((uint32_t)res.a1);212}213214return(bus_read_4(sc->mem_res, r));215}216217static int218tegra210_pmc_set_powergate(struct tegra210_pmc_softc *sc,219enum tegra_powergate_id id, int ena)220{221uint32_t reg;222int i;223224PMC_LOCK(sc);225226reg = RD4(sc, PMC_PWRGATE_STATUS) & PMC_PWRGATE_STATUS_PARTID(id);227if (((reg != 0) && ena) || ((reg == 0) && !ena)) {228PMC_UNLOCK(sc);229return (0);230}231232for (i = 100; i > 0; i--) {233reg = RD4(sc, PMC_PWRGATE_TOGGLE);234if ((reg & PMC_PWRGATE_TOGGLE_START) == 0)235break;236DELAY(1);237}238if (i <= 0)239device_printf(sc->dev,240"Timeout when waiting for TOGGLE_START\n");241242WR4(sc, PMC_PWRGATE_TOGGLE,243PMC_PWRGATE_TOGGLE_START | PMC_PWRGATE_TOGGLE_PARTID(id));244245for (i = 100; i > 0; i--) {246reg = RD4(sc, PMC_PWRGATE_TOGGLE);247if ((reg & PMC_PWRGATE_TOGGLE_START) == 0)248break;249DELAY(1);250}251if (i <= 0)252device_printf(sc->dev,253"Timeout when waiting for TOGGLE_START\n");254PMC_UNLOCK(sc);255return (0);256}257258int259tegra_powergate_remove_clamping(enum tegra_powergate_id id)260{261struct tegra210_pmc_softc *sc;262uint32_t reg;263enum tegra_powergate_id swid;264int i;265266sc = tegra210_pmc_get_sc();267268if (id == TEGRA_POWERGATE_3D) {269WR4(sc, PMC_GPU_RG_CNTRL, 0);270return (0);271}272273reg = RD4(sc, PMC_PWRGATE_STATUS);274if ((reg & PMC_PWRGATE_STATUS_PARTID(id)) == 0)275panic("Attempt to remove clamping for unpowered partition.\n");276277if (id == TEGRA_POWERGATE_PCX)278swid = TEGRA_POWERGATE_VDE;279else if (id == TEGRA_POWERGATE_VDE)280swid = TEGRA_POWERGATE_PCX;281else282swid = id;283WR4(sc, PMC_REMOVE_CLAMPING_CMD, PMC_REMOVE_CLAMPING_CMD_PARTID(swid));284285for (i = 100; i > 0; i--) {286reg = RD4(sc, PMC_REMOVE_CLAMPING_CMD);287if ((reg & PMC_REMOVE_CLAMPING_CMD_PARTID(swid)) == 0)288break;289DELAY(1);290}291if (i <= 0)292device_printf(sc->dev, "Timeout when remove clamping\n");293294reg = RD4(sc, PMC_CLAMP_STATUS);295if ((reg & PMC_CLAMP_STATUS_PARTID(id)) != 0)296panic("Cannot remove clamping\n");297298return (0);299}300301int302tegra_powergate_is_powered(enum tegra_powergate_id id)303{304struct tegra210_pmc_softc *sc;305uint32_t reg;306307sc = tegra210_pmc_get_sc();308309reg = RD4(sc, PMC_PWRGATE_STATUS);310return ((reg & PMC_PWRGATE_STATUS_PARTID(id)) ? 1 : 0);311}312313int314tegra_powergate_power_on(enum tegra_powergate_id id)315{316struct tegra210_pmc_softc *sc;317int rv, i;318319sc = tegra210_pmc_get_sc();320321rv = tegra210_pmc_set_powergate(sc, id, 1);322if (rv != 0) {323device_printf(sc->dev, "Cannot set powergate: %d\n", id);324return (rv);325}326327for (i = 100; i > 0; i--) {328if (tegra_powergate_is_powered(id))329break;330DELAY(1);331}332if (i <= 0) {333device_printf(sc->dev, "Timeout when waiting on power up\n");334return(ETIMEDOUT);335}336337return (rv);338}339340int341tegra_powergate_power_off(enum tegra_powergate_id id)342{343struct tegra210_pmc_softc *sc;344int rv, i;345346sc = tegra210_pmc_get_sc();347348rv = tegra210_pmc_set_powergate(sc, id, 0);349if (rv != 0) {350device_printf(sc->dev, "Cannot set powergate: %d\n", id);351return (rv);352}353for (i = 100; i > 0; i--) {354if (!tegra_powergate_is_powered(id))355break;356DELAY(1);357}358if (i <= 0)359device_printf(sc->dev, "Timeout when waiting on power off\n");360361return (rv);362}363364int365tegra_powergate_sequence_power_up(enum tegra_powergate_id id, clk_t clk,366hwreset_t rst)367{368struct tegra210_pmc_softc *sc;369int rv;370371sc = tegra210_pmc_get_sc();372373rv = hwreset_assert(rst);374if (rv != 0) {375device_printf(sc->dev, "Cannot assert reset\n");376return (rv);377}378379rv = clk_stop(clk);380if (rv != 0) {381device_printf(sc->dev, "Cannot stop clock\n");382goto clk_fail;383}384385rv = tegra_powergate_power_on(id);386if (rv != 0) {387device_printf(sc->dev, "Cannot power on powergate\n");388goto clk_fail;389}390391rv = clk_enable(clk);392if (rv != 0) {393device_printf(sc->dev, "Cannot enable clock\n");394goto clk_fail;395}396DELAY(20);397398rv = tegra_powergate_remove_clamping(id);399if (rv != 0) {400device_printf(sc->dev, "Cannot remove clamping\n");401goto fail;402}403rv = hwreset_deassert(rst);404if (rv != 0) {405device_printf(sc->dev, "Cannot unreset reset\n");406goto fail;407}408return 0;409410fail:411clk_disable(clk);412clk_fail:413hwreset_assert(rst);414tegra_powergate_power_off(id);415return (rv);416}417418static int419tegra210_pmc_parse_fdt(struct tegra210_pmc_softc *sc, phandle_t node)420{421int rv;422uint32_t tmp;423uint32_t tmparr[2];424425rv = OF_getencprop(node, "nvidia,suspend-mode", &tmp, sizeof(tmp));426if (rv > 0) {427switch (tmp) {428case 0:429sc->suspend_mode = TEGRA_SUSPEND_LP0;430break;431432case 1:433sc->suspend_mode = TEGRA_SUSPEND_LP1;434break;435436case 2:437sc->suspend_mode = TEGRA_SUSPEND_LP2;438break;439440default:441sc->suspend_mode = TEGRA_SUSPEND_NONE;442break;443}444}445446rv = OF_getencprop(node, "nvidia,cpu-pwr-good-time", &tmp, sizeof(tmp));447if (rv > 0) {448sc->cpu_good_time = tmp;449sc->suspend_mode = TEGRA_SUSPEND_NONE;450}451452rv = OF_getencprop(node, "nvidia,cpu-pwr-off-time", &tmp, sizeof(tmp));453if (rv > 0) {454sc->cpu_off_time = tmp;455sc->suspend_mode = TEGRA_SUSPEND_NONE;456}457458rv = OF_getencprop(node, "nvidia,core-pwr-good-time", tmparr,459sizeof(tmparr));460if (rv == sizeof(tmparr)) {461sc->core_osc_time = tmparr[0];462sc->core_pmu_time = tmparr[1];463sc->suspend_mode = TEGRA_SUSPEND_NONE;464}465466rv = OF_getencprop(node, "nvidia,core-pwr-off-time", &tmp, sizeof(tmp));467if (rv > 0) {468sc->core_off_time = tmp;469sc->suspend_mode = TEGRA_SUSPEND_NONE;470}471472sc->corereq_high =473OF_hasprop(node, "nvidia,core-power-req-active-high");474sc->sysclkreq_high =475OF_hasprop(node, "nvidia,sys-clock-req-active-high");476sc->combined_req =477OF_hasprop(node, "nvidia,combined-power-req");478sc->cpu_pwr_good_en =479OF_hasprop(node, "nvidia,cpu-pwr-good-en");480481rv = OF_getencprop(node, "nvidia,lp0-vec", tmparr, sizeof(tmparr));482if (rv == sizeof(tmparr)) {483484sc->lp0_vec_phys = tmparr[0];485sc->core_pmu_time = tmparr[1];486sc->lp0_vec_size = TEGRA_SUSPEND_NONE;487if (sc->suspend_mode == TEGRA_SUSPEND_LP0)488sc->suspend_mode = TEGRA_SUSPEND_LP1;489}490return 0;491}492493static void494tegra210_pmc_check_secure(struct tegra210_pmc_softc *sc)495{496uint32_t orig;497498sc->secure_access = false;499500/*501* If PMC is coverd by secure trust zone, all reads returns 0,502* Use scratch0 register acvcess test503*/504orig = RD4(sc, PMC_SCRATCH0);505WR4(sc, PMC_SCRATCH0, 0xDEADBEEF);506if (RD4(sc, PMC_SCRATCH0) == 0) {507sc->secure_access = true;508return;509}510WR4(sc, PMC_SCRATCH0, 0xBADC0DE);511if (RD4(sc, PMC_SCRATCH0) == 0) {512sc->secure_access = true;513return;514}515WR4(sc, PMC_SCRATCH0, orig);516}517518static int519tegra210_pmc_probe(device_t dev)520{521522if (!ofw_bus_status_okay(dev))523return (ENXIO);524525if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)526return (ENXIO);527528device_set_desc(dev, "Tegra PMC");529return (BUS_PROBE_DEFAULT);530}531532static int533tegra210_pmc_detach(device_t dev)534{535536/* This device is always present. */537return (EBUSY);538}539540static int541tegra210_pmc_attach(device_t dev)542{543struct tegra210_pmc_softc *sc;544int rid, rv;545uint32_t reg;546phandle_t node;547548sc = device_get_softc(dev);549sc->dev = dev;550node = ofw_bus_get_node(dev);551PMC_LOCK_INIT(sc);552553rv = tegra210_pmc_parse_fdt(sc, node);554if (rv != 0) {555device_printf(sc->dev, "Cannot parse FDT data\n");556return (rv);557}558559rv = clk_get_by_ofw_name(sc->dev, 0, "pclk", &sc->clk);560if (rv != 0) {561device_printf(sc->dev, "Cannot get \"pclk\" clock\n");562return (ENXIO);563}564565rid = 0;566sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,567RF_ACTIVE);568if (sc->mem_res == NULL) {569device_printf(dev, "Cannot allocate memory resources\n");570return (ENXIO);571}572573tegra210_pmc_check_secure(sc);574575/* Enable CPU power request. */576reg = RD4(sc, PMC_CNTRL);577reg |= PMC_CNTRL_CPU_PWRREQ_OE;578WR4(sc, PMC_CNTRL, reg);579580/* Set sysclk output polarity */581reg = RD4(sc, PMC_CNTRL);582if (sc->sysclkreq_high)583reg &= ~PMC_CNTRL_SYSCLK_POLARITY;584else585reg |= PMC_CNTRL_SYSCLK_POLARITY;586WR4(sc, PMC_CNTRL, reg);587588/* Enable sysclk request. */589reg = RD4(sc, PMC_CNTRL);590reg |= PMC_CNTRL_SYSCLK_OE;591WR4(sc, PMC_CNTRL, reg);592593/*594* Remove HDMI from deep power down mode.595* XXX mote this to HDMI driver596*/597reg = RD4(sc, PMC_IO_DPD_STATUS);598reg &= ~ PMC_IO_DPD_STATUS_HDMI;599WR4(sc, PMC_IO_DPD_STATUS, reg);600601reg = RD4(sc, PMC_IO_DPD2_STATUS);602reg &= ~ PMC_IO_DPD2_STATUS_HV;603WR4(sc, PMC_IO_DPD2_STATUS, reg);604605if (pmc_sc != NULL)606panic("tegra210_pmc: double driver attach");607pmc_sc = sc;608return (0);609}610611static device_method_t tegra210_pmc_methods[] = {612/* Device interface */613DEVMETHOD(device_probe, tegra210_pmc_probe),614DEVMETHOD(device_attach, tegra210_pmc_attach),615DEVMETHOD(device_detach, tegra210_pmc_detach),616617DEVMETHOD_END618};619620static DEFINE_CLASS_0(pmc, tegra210_pmc_driver, tegra210_pmc_methods,621sizeof(struct tegra210_pmc_softc));622EARLY_DRIVER_MODULE(tegra210_pmc, simplebus, tegra210_pmc_driver, NULL, NULL,62370);624625626