Konstantin Porotchkin | f69ec58 | 2018-06-07 18:31:14 +0300 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2018 Marvell International Ltd. |
| 3 | * |
| 4 | * SPDX-License-Identifier: BSD-3-Clause |
| 5 | * https://spdx.org/licenses |
| 6 | */ |
| 7 | |
Antonio Nino Diaz | e0f9063 | 2018-12-14 00:18:21 +0000 | [diff] [blame] | 8 | #include <common/debug.h> |
| 9 | #include <drivers/delay_timer.h> |
| 10 | #include <drivers/marvell/thermal.h> |
| 11 | #include <lib/mmio.h> |
| 12 | |
Konstantin Porotchkin | f69ec58 | 2018-06-07 18:31:14 +0300 | [diff] [blame] | 13 | #include <mvebu_def.h> |
Konstantin Porotchkin | f69ec58 | 2018-06-07 18:31:14 +0300 | [diff] [blame] | 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 | } |