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 
ext_tsen_probe(struct tsen_config * tsen_cfg)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 
ext_tsen_read(struct tsen_config * tsen_cfg,int * temp)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 
marvell_thermal_config_get(void)127 struct tsen_config *marvell_thermal_config_get(void)
128 {
129 	return &tsen_cfg;
130 }
131