1 /* 2 * Copyright (C) 2018 Marvell International Ltd. 3 * 4 * SPDX-License-Identifier: BSD-3-Clause 5 * https://spdx.org/licenses 6 */ 7 8 #include <common/debug.h> 9 #include <drivers/delay_timer.h> 10 #include <drivers/marvell/thermal.h> 11 #include <lib/mmio.h> 12 13 #include <mvebu_def.h> 14 15 #define THERMAL_TIMEOUT 1200 16 17 #define THERMAL_SEN_CTRL_LSB_STRT_OFFSET 0 18 #define THERMAL_SEN_CTRL_LSB_STRT_MASK \ 19 (0x1 << THERMAL_SEN_CTRL_LSB_STRT_OFFSET) 20 #define THERMAL_SEN_CTRL_LSB_RST_OFFSET 1 21 #define THERMAL_SEN_CTRL_LSB_RST_MASK \ 22 (0x1 << THERMAL_SEN_CTRL_LSB_RST_OFFSET) 23 #define THERMAL_SEN_CTRL_LSB_EN_OFFSET 2 24 #define THERMAL_SEN_CTRL_LSB_EN_MASK \ 25 (0x1 << THERMAL_SEN_CTRL_LSB_EN_OFFSET) 26 27 #define THERMAL_SEN_CTRL_STATS_VALID_OFFSET 16 28 #define THERMAL_SEN_CTRL_STATS_VALID_MASK \ 29 (0x1 << THERMAL_SEN_CTRL_STATS_VALID_OFFSET) 30 #define THERMAL_SEN_CTRL_STATS_TEMP_OUT_OFFSET 0 31 #define THERMAL_SEN_CTRL_STATS_TEMP_OUT_MASK \ 32 (0x3FF << THERMAL_SEN_CTRL_STATS_TEMP_OUT_OFFSET) 33 34 #define THERMAL_SEN_OUTPUT_MSB 512 35 #define THERMAL_SEN_OUTPUT_COMP 1024 36 37 struct tsen_regs { 38 uint32_t ext_tsen_ctrl_lsb; 39 uint32_t ext_tsen_ctrl_msb; 40 uint32_t ext_tsen_status; 41 }; 42 43 static int ext_tsen_probe(struct tsen_config *tsen_cfg) 44 { 45 uint32_t reg, timeout = 0; 46 struct tsen_regs *base; 47 48 if (tsen_cfg == NULL && tsen_cfg->regs_base == NULL) { 49 ERROR("initial thermal sensor configuration is missing\n"); 50 return -1; 51 } 52 base = (struct tsen_regs *)tsen_cfg->regs_base; 53 54 INFO("initializing thermal sensor\n"); 55 56 /* initialize thermal sensor hardware reset once */ 57 reg = mmio_read_32((uintptr_t)&base->ext_tsen_ctrl_lsb); 58 reg &= ~THERMAL_SEN_CTRL_LSB_RST_OFFSET; /* de-assert TSEN_RESET */ 59 reg |= THERMAL_SEN_CTRL_LSB_EN_MASK; /* set TSEN_EN to 1 */ 60 reg |= THERMAL_SEN_CTRL_LSB_STRT_MASK; /* set TSEN_START to 1 */ 61 mmio_write_32((uintptr_t)&base->ext_tsen_ctrl_lsb, reg); 62 63 reg = mmio_read_32((uintptr_t)&base->ext_tsen_status); 64 while ((reg & THERMAL_SEN_CTRL_STATS_VALID_MASK) == 0 && 65 timeout < THERMAL_TIMEOUT) { 66 udelay(100); 67 reg = mmio_read_32((uintptr_t)&base->ext_tsen_status); 68 timeout++; 69 } 70 71 if ((reg & THERMAL_SEN_CTRL_STATS_VALID_MASK) == 0) { 72 ERROR("thermal sensor is not ready\n"); 73 return -1; 74 } 75 76 tsen_cfg->tsen_ready = 1; 77 78 VERBOSE("thermal sensor was initialized\n"); 79 80 return 0; 81 } 82 83 static int ext_tsen_read(struct tsen_config *tsen_cfg, int *temp) 84 { 85 uint32_t reg; 86 struct tsen_regs *base; 87 88 if (tsen_cfg == NULL && !tsen_cfg->tsen_ready) { 89 ERROR("thermal sensor was not initialized\n"); 90 return -1; 91 } 92 base = (struct tsen_regs *)tsen_cfg->regs_base; 93 94 reg = mmio_read_32((uintptr_t)&base->ext_tsen_status); 95 reg = ((reg & THERMAL_SEN_CTRL_STATS_TEMP_OUT_MASK) >> 96 THERMAL_SEN_CTRL_STATS_TEMP_OUT_OFFSET); 97 98 /* 99 * TSEN output format is signed as a 2s complement number 100 * ranging from-512 to +511. when MSB is set, need to 101 * calculate the complement number 102 */ 103 if (reg >= THERMAL_SEN_OUTPUT_MSB) 104 reg -= THERMAL_SEN_OUTPUT_COMP; 105 106 if (tsen_cfg->tsen_divisor == 0) { 107 ERROR("thermal sensor divisor cannot be zero\n"); 108 return -1; 109 } 110 111 *temp = ((tsen_cfg->tsen_gain * ((int)reg)) + 112 tsen_cfg->tsen_offset) / tsen_cfg->tsen_divisor; 113 114 return 0; 115 } 116 117 static struct tsen_config tsen_cfg = { 118 .tsen_offset = 153400, 119 .tsen_gain = 425, 120 .tsen_divisor = 1000, 121 .tsen_ready = 0, 122 .regs_base = (void *)MVEBU_AP_EXT_TSEN_BASE, 123 .ptr_tsen_probe = ext_tsen_probe, 124 .ptr_tsen_read = ext_tsen_read 125 }; 126 127 struct tsen_config *marvell_thermal_config_get(void) 128 { 129 return &tsen_cfg; 130 } 131