/* * Copyright (C) 2018 Marvell International Ltd. * * SPDX-License-Identifier: BSD-3-Clause * https://spdx.org/licenses */ #include #include #include #include #include #define THERMAL_TIMEOUT 1200 #define THERMAL_SEN_CTRL_LSB_STRT_OFFSET 0 #define THERMAL_SEN_CTRL_LSB_STRT_MASK \ (0x1 << THERMAL_SEN_CTRL_LSB_STRT_OFFSET) #define THERMAL_SEN_CTRL_LSB_RST_OFFSET 1 #define THERMAL_SEN_CTRL_LSB_RST_MASK \ (0x1 << THERMAL_SEN_CTRL_LSB_RST_OFFSET) #define THERMAL_SEN_CTRL_LSB_EN_OFFSET 2 #define THERMAL_SEN_CTRL_LSB_EN_MASK \ (0x1 << THERMAL_SEN_CTRL_LSB_EN_OFFSET) #define THERMAL_SEN_CTRL_STATS_VALID_OFFSET 16 #define THERMAL_SEN_CTRL_STATS_VALID_MASK \ (0x1 << THERMAL_SEN_CTRL_STATS_VALID_OFFSET) #define THERMAL_SEN_CTRL_STATS_TEMP_OUT_OFFSET 0 #define THERMAL_SEN_CTRL_STATS_TEMP_OUT_MASK \ (0x3FF << THERMAL_SEN_CTRL_STATS_TEMP_OUT_OFFSET) #define THERMAL_SEN_OUTPUT_MSB 512 #define THERMAL_SEN_OUTPUT_COMP 1024 struct tsen_regs { uint32_t ext_tsen_ctrl_lsb; uint32_t ext_tsen_ctrl_msb; uint32_t ext_tsen_status; }; static int ext_tsen_probe(struct tsen_config *tsen_cfg) { uint32_t reg, timeout = 0; struct tsen_regs *base; if (tsen_cfg == NULL && tsen_cfg->regs_base == NULL) { ERROR("initial thermal sensor configuration is missing\n"); return -1; } base = (struct tsen_regs *)tsen_cfg->regs_base; INFO("initializing thermal sensor\n"); /* initialize thermal sensor hardware reset once */ reg = mmio_read_32((uintptr_t)&base->ext_tsen_ctrl_lsb); reg &= ~THERMAL_SEN_CTRL_LSB_RST_OFFSET; /* de-assert TSEN_RESET */ reg |= THERMAL_SEN_CTRL_LSB_EN_MASK; /* set TSEN_EN to 1 */ reg |= THERMAL_SEN_CTRL_LSB_STRT_MASK; /* set TSEN_START to 1 */ mmio_write_32((uintptr_t)&base->ext_tsen_ctrl_lsb, reg); reg = mmio_read_32((uintptr_t)&base->ext_tsen_status); while ((reg & THERMAL_SEN_CTRL_STATS_VALID_MASK) == 0 && timeout < THERMAL_TIMEOUT) { udelay(100); reg = mmio_read_32((uintptr_t)&base->ext_tsen_status); timeout++; } if ((reg & THERMAL_SEN_CTRL_STATS_VALID_MASK) == 0) { ERROR("thermal sensor is not ready\n"); return -1; } tsen_cfg->tsen_ready = 1; VERBOSE("thermal sensor was initialized\n"); return 0; } static int ext_tsen_read(struct tsen_config *tsen_cfg, int *temp) { uint32_t reg; struct tsen_regs *base; if (tsen_cfg == NULL && !tsen_cfg->tsen_ready) { ERROR("thermal sensor was not initialized\n"); return -1; } base = (struct tsen_regs *)tsen_cfg->regs_base; reg = mmio_read_32((uintptr_t)&base->ext_tsen_status); reg = ((reg & THERMAL_SEN_CTRL_STATS_TEMP_OUT_MASK) >> THERMAL_SEN_CTRL_STATS_TEMP_OUT_OFFSET); /* * TSEN output format is signed as a 2s complement number * ranging from-512 to +511. when MSB is set, need to * calculate the complement number */ if (reg >= THERMAL_SEN_OUTPUT_MSB) reg -= THERMAL_SEN_OUTPUT_COMP; if (tsen_cfg->tsen_divisor == 0) { ERROR("thermal sensor divisor cannot be zero\n"); return -1; } *temp = ((tsen_cfg->tsen_gain * ((int)reg)) + tsen_cfg->tsen_offset) / tsen_cfg->tsen_divisor; return 0; } static struct tsen_config tsen_cfg = { .tsen_offset = 153400, .tsen_gain = 425, .tsen_divisor = 1000, .tsen_ready = 0, .regs_base = (void *)MVEBU_AP_EXT_TSEN_BASE, .ptr_tsen_probe = ext_tsen_probe, .ptr_tsen_read = ext_tsen_read }; struct tsen_config *marvell_thermal_config_get(void) { return &tsen_cfg; }