xref: /arm-trusted-firmware/drivers/marvell/secure_dfx_access/armada_thermal.c (revision 91f16700b400a8c0651d24a598fc48ee2997a0d7)
1*91f16700Schasinglulu /*
2*91f16700Schasinglulu  * Copyright (C) 2019 Marvell International Ltd.
3*91f16700Schasinglulu  *
4*91f16700Schasinglulu  * SPDX-License-Identifier:     BSD-3-Clause
5*91f16700Schasinglulu  * https://spdx.org/licenses
6*91f16700Schasinglulu  */
7*91f16700Schasinglulu #include <common/debug.h>
8*91f16700Schasinglulu #include <drivers/delay_timer.h>
9*91f16700Schasinglulu #include <errno.h>
10*91f16700Schasinglulu #include <lib/mmio.h>
11*91f16700Schasinglulu #include <mvebu.h>
12*91f16700Schasinglulu #include <stdbool.h>
13*91f16700Schasinglulu #include "dfx.h"
14*91f16700Schasinglulu 
15*91f16700Schasinglulu /* #define DEBUG_DFX */
16*91f16700Schasinglulu #ifdef DEBUG_DFX
17*91f16700Schasinglulu #define debug(format...) NOTICE(format)
18*91f16700Schasinglulu #else
19*91f16700Schasinglulu #define debug(format, arg...)
20*91f16700Schasinglulu #endif
21*91f16700Schasinglulu 
22*91f16700Schasinglulu #define TSEN_CTRL0			0xf06f8084
23*91f16700Schasinglulu  #define TSEN_CTRL0_START		BIT(0)
24*91f16700Schasinglulu  #define TSEN_CTRL0_RESET		BIT(1)
25*91f16700Schasinglulu  #define TSEN_CTRL0_ENABLE		BIT(2)
26*91f16700Schasinglulu  #define TSEN_CTRL0_AVG_BYPASS		BIT(6)
27*91f16700Schasinglulu  #define TSEN_CTRL0_CHAN_SHIFT		13
28*91f16700Schasinglulu  #define TSEN_CTRL0_CHAN_MASK		0xF
29*91f16700Schasinglulu  #define TSEN_CTRL0_OSR_SHIFT		24
30*91f16700Schasinglulu  #define TSEN_CTRL0_OSR_MAX		0x3
31*91f16700Schasinglulu  #define TSEN_CTRL0_MODE_SHIFT		30
32*91f16700Schasinglulu  #define TSEN_CTRL0_MODE_EXTERNAL	0x2U
33*91f16700Schasinglulu  #define TSEN_CTRL0_MODE_MASK		0x3U
34*91f16700Schasinglulu 
35*91f16700Schasinglulu #define TSEN_CTRL1			0xf06f8088
36*91f16700Schasinglulu  #define TSEN_CTRL1_INT_EN		BIT(25)
37*91f16700Schasinglulu  #define TSEN_CTRL1_HYST_SHIFT		19
38*91f16700Schasinglulu  #define TSEN_CTRL1_HYST_MASK		(0x3 << TSEN_CTRL1_HYST_SHIFT)
39*91f16700Schasinglulu  #define TSEN_CTRL1_THRESH_SHIFT	3
40*91f16700Schasinglulu  #define TSEN_CTRL1_THRESH_MASK		(0x3ff << TSEN_CTRL1_THRESH_SHIFT)
41*91f16700Schasinglulu 
42*91f16700Schasinglulu #define TSEN_STATUS			0xf06f808c
43*91f16700Schasinglulu  #define TSEN_STATUS_VALID_OFFSET	16
44*91f16700Schasinglulu  #define TSEN_STATUS_VALID_MASK		(0x1 << TSEN_STATUS_VALID_OFFSET)
45*91f16700Schasinglulu  #define TSEN_STATUS_TEMP_OUT_OFFSET	0
46*91f16700Schasinglulu  #define TSEN_STATUS_TEMP_OUT_MASK	(0x3FF << TSEN_STATUS_TEMP_OUT_OFFSET)
47*91f16700Schasinglulu 
48*91f16700Schasinglulu #define DFX_SERVER_IRQ_SUM_MASK_REG	0xf06f8104
49*91f16700Schasinglulu  #define DFX_SERVER_IRQ_EN		BIT(1)
50*91f16700Schasinglulu 
51*91f16700Schasinglulu #define DFX_IRQ_CAUSE_REG		0xf06f8108
52*91f16700Schasinglulu 
53*91f16700Schasinglulu #define DFX_IRQ_MASK_REG		0xf06f810c
54*91f16700Schasinglulu  #define DFX_IRQ_TSEN_OVERHEAT_OFFSET	BIT(22)
55*91f16700Schasinglulu 
56*91f16700Schasinglulu #define THERMAL_SEN_OUTPUT_MSB		512
57*91f16700Schasinglulu #define THERMAL_SEN_OUTPUT_COMP		1024
58*91f16700Schasinglulu 
59*91f16700Schasinglulu #define COEF_M 423
60*91f16700Schasinglulu #define COEF_B -150000LL
61*91f16700Schasinglulu 
62*91f16700Schasinglulu static void armada_ap806_thermal_read(u_register_t *temp)
63*91f16700Schasinglulu {
64*91f16700Schasinglulu 	uint32_t reg;
65*91f16700Schasinglulu 
66*91f16700Schasinglulu 	reg = mmio_read_32(TSEN_STATUS);
67*91f16700Schasinglulu 
68*91f16700Schasinglulu 	reg = ((reg & TSEN_STATUS_TEMP_OUT_MASK) >>
69*91f16700Schasinglulu 	      TSEN_STATUS_TEMP_OUT_OFFSET);
70*91f16700Schasinglulu 
71*91f16700Schasinglulu 	/*
72*91f16700Schasinglulu 	 * TSEN output format is signed as a 2s complement number
73*91f16700Schasinglulu 	 * ranging from-512 to +511. when MSB is set, need to
74*91f16700Schasinglulu 	 * calculate the complement number
75*91f16700Schasinglulu 	 */
76*91f16700Schasinglulu 	if (reg >= THERMAL_SEN_OUTPUT_MSB)
77*91f16700Schasinglulu 		reg -= THERMAL_SEN_OUTPUT_COMP;
78*91f16700Schasinglulu 
79*91f16700Schasinglulu 	*temp = ((COEF_M * ((signed int)reg)) - COEF_B);
80*91f16700Schasinglulu }
81*91f16700Schasinglulu 
82*91f16700Schasinglulu static void armada_ap806_thermal_irq(void)
83*91f16700Schasinglulu {
84*91f16700Schasinglulu 	/* Dummy read, register ROC */
85*91f16700Schasinglulu 	mmio_read_32(DFX_IRQ_CAUSE_REG);
86*91f16700Schasinglulu }
87*91f16700Schasinglulu 
88*91f16700Schasinglulu static void armada_ap806_thermal_overheat_irq_init(void)
89*91f16700Schasinglulu {
90*91f16700Schasinglulu 	uint32_t reg;
91*91f16700Schasinglulu 
92*91f16700Schasinglulu 	/* Clear DFX temperature IRQ cause */
93*91f16700Schasinglulu 	reg = mmio_read_32(DFX_IRQ_CAUSE_REG);
94*91f16700Schasinglulu 
95*91f16700Schasinglulu 	/* Enable DFX Temperature IRQ */
96*91f16700Schasinglulu 	reg = mmio_read_32(DFX_IRQ_MASK_REG);
97*91f16700Schasinglulu 	reg |= DFX_IRQ_TSEN_OVERHEAT_OFFSET;
98*91f16700Schasinglulu 	mmio_write_32(DFX_IRQ_MASK_REG, reg);
99*91f16700Schasinglulu 
100*91f16700Schasinglulu 	/* Enable DFX server IRQ */
101*91f16700Schasinglulu 	reg = mmio_read_32(DFX_SERVER_IRQ_SUM_MASK_REG);
102*91f16700Schasinglulu 	reg |= DFX_SERVER_IRQ_EN;
103*91f16700Schasinglulu 	mmio_write_32(DFX_SERVER_IRQ_SUM_MASK_REG, reg);
104*91f16700Schasinglulu 
105*91f16700Schasinglulu 	/* Enable overheat interrupt */
106*91f16700Schasinglulu 	reg = mmio_read_32(TSEN_CTRL1);
107*91f16700Schasinglulu 	reg |= TSEN_CTRL1_INT_EN;
108*91f16700Schasinglulu 	mmio_write_32(TSEN_CTRL1, reg);
109*91f16700Schasinglulu }
110*91f16700Schasinglulu 
111*91f16700Schasinglulu static unsigned int armada_mc_to_reg_temp(unsigned int temp_mc)
112*91f16700Schasinglulu {
113*91f16700Schasinglulu 	unsigned int sample;
114*91f16700Schasinglulu 
115*91f16700Schasinglulu 	sample = (temp_mc + COEF_B) / COEF_M;
116*91f16700Schasinglulu 
117*91f16700Schasinglulu 	return sample & 0x3ff;
118*91f16700Schasinglulu }
119*91f16700Schasinglulu 
120*91f16700Schasinglulu /*
121*91f16700Schasinglulu  * The documentation states:
122*91f16700Schasinglulu  * high/low watermark = threshold +/- 0.4761 * 2^(hysteresis + 2)
123*91f16700Schasinglulu  * which is the mathematical derivation for:
124*91f16700Schasinglulu  * 0x0 <=> 1.9°C, 0x1 <=> 3.8°C, 0x2 <=> 7.6°C, 0x3 <=> 15.2°C
125*91f16700Schasinglulu  */
126*91f16700Schasinglulu static unsigned int hyst_levels_mc[] = {1900, 3800, 7600, 15200};
127*91f16700Schasinglulu 
128*91f16700Schasinglulu static unsigned int armada_mc_to_reg_hyst(int hyst_mc)
129*91f16700Schasinglulu {
130*91f16700Schasinglulu 	int i;
131*91f16700Schasinglulu 
132*91f16700Schasinglulu 	/*
133*91f16700Schasinglulu 	 * We will always take the smallest possible hysteresis to avoid risking
134*91f16700Schasinglulu 	 * the hardware integrity by enlarging the threshold by +8°C in the
135*91f16700Schasinglulu 	 * worst case.
136*91f16700Schasinglulu 	 */
137*91f16700Schasinglulu 	for (i = ARRAY_SIZE(hyst_levels_mc) - 1; i > 0; i--)
138*91f16700Schasinglulu 		if (hyst_mc >= hyst_levels_mc[i])
139*91f16700Schasinglulu 			break;
140*91f16700Schasinglulu 
141*91f16700Schasinglulu 	return i;
142*91f16700Schasinglulu }
143*91f16700Schasinglulu 
144*91f16700Schasinglulu static void armada_ap806_thermal_threshold(int thresh_mc, int hyst_mc)
145*91f16700Schasinglulu {
146*91f16700Schasinglulu 	uint32_t ctrl1;
147*91f16700Schasinglulu 	unsigned int threshold = armada_mc_to_reg_temp(thresh_mc);
148*91f16700Schasinglulu 	unsigned int hysteresis = armada_mc_to_reg_hyst(hyst_mc);
149*91f16700Schasinglulu 
150*91f16700Schasinglulu 	ctrl1 = mmio_read_32(TSEN_CTRL1);
151*91f16700Schasinglulu 	/* Set Threshold */
152*91f16700Schasinglulu 	if (thresh_mc >= 0) {
153*91f16700Schasinglulu 		ctrl1 &= ~(TSEN_CTRL1_THRESH_MASK);
154*91f16700Schasinglulu 		ctrl1 |= threshold << TSEN_CTRL1_THRESH_SHIFT;
155*91f16700Schasinglulu 	}
156*91f16700Schasinglulu 
157*91f16700Schasinglulu 	/* Set Hysteresis */
158*91f16700Schasinglulu 	if (hyst_mc >= 0) {
159*91f16700Schasinglulu 		ctrl1 &= ~(TSEN_CTRL1_HYST_MASK);
160*91f16700Schasinglulu 		ctrl1 |= hysteresis << TSEN_CTRL1_HYST_SHIFT;
161*91f16700Schasinglulu 	}
162*91f16700Schasinglulu 
163*91f16700Schasinglulu 	mmio_write_32(TSEN_CTRL1, ctrl1);
164*91f16700Schasinglulu }
165*91f16700Schasinglulu 
166*91f16700Schasinglulu static void armada_select_channel(int channel)
167*91f16700Schasinglulu {
168*91f16700Schasinglulu 	uint32_t ctrl0;
169*91f16700Schasinglulu 
170*91f16700Schasinglulu 	/* Stop the measurements */
171*91f16700Schasinglulu 	ctrl0 = mmio_read_32(TSEN_CTRL0);
172*91f16700Schasinglulu 	ctrl0 &= ~TSEN_CTRL0_START;
173*91f16700Schasinglulu 	mmio_write_32(TSEN_CTRL0, ctrl0);
174*91f16700Schasinglulu 
175*91f16700Schasinglulu 	/* Reset the mode, internal sensor will be automatically selected */
176*91f16700Schasinglulu 	ctrl0 &= ~(TSEN_CTRL0_MODE_MASK << TSEN_CTRL0_MODE_SHIFT);
177*91f16700Schasinglulu 
178*91f16700Schasinglulu 	/* Other channels are external and should be selected accordingly */
179*91f16700Schasinglulu 	if (channel) {
180*91f16700Schasinglulu 		/* Change the mode to external */
181*91f16700Schasinglulu 		ctrl0 |= TSEN_CTRL0_MODE_EXTERNAL <<
182*91f16700Schasinglulu 			 TSEN_CTRL0_MODE_SHIFT;
183*91f16700Schasinglulu 		/* Select the sensor */
184*91f16700Schasinglulu 		ctrl0 &= ~(TSEN_CTRL0_CHAN_MASK << TSEN_CTRL0_CHAN_SHIFT);
185*91f16700Schasinglulu 		ctrl0 |= (channel - 1) << TSEN_CTRL0_CHAN_SHIFT;
186*91f16700Schasinglulu 	}
187*91f16700Schasinglulu 
188*91f16700Schasinglulu 	/* Actually set the mode/channel */
189*91f16700Schasinglulu 	mmio_write_32(TSEN_CTRL0, ctrl0);
190*91f16700Schasinglulu 
191*91f16700Schasinglulu 	/* Re-start the measurements */
192*91f16700Schasinglulu 	ctrl0 |= TSEN_CTRL0_START;
193*91f16700Schasinglulu 	mmio_write_32(TSEN_CTRL0, ctrl0);
194*91f16700Schasinglulu }
195*91f16700Schasinglulu 
196*91f16700Schasinglulu static void armada_ap806_thermal_init(void)
197*91f16700Schasinglulu {
198*91f16700Schasinglulu 	uint32_t reg;
199*91f16700Schasinglulu 
200*91f16700Schasinglulu 	reg = mmio_read_32(TSEN_CTRL0);
201*91f16700Schasinglulu 	reg &= ~TSEN_CTRL0_RESET;
202*91f16700Schasinglulu 	reg |= TSEN_CTRL0_START | TSEN_CTRL0_ENABLE;
203*91f16700Schasinglulu 
204*91f16700Schasinglulu 	/* Sample every ~2ms */
205*91f16700Schasinglulu 	reg |= TSEN_CTRL0_OSR_MAX << TSEN_CTRL0_OSR_SHIFT;
206*91f16700Schasinglulu 
207*91f16700Schasinglulu 	/* Enable average (2 samples by default) */
208*91f16700Schasinglulu 	reg &= ~TSEN_CTRL0_AVG_BYPASS;
209*91f16700Schasinglulu 
210*91f16700Schasinglulu 	mmio_write_32(TSEN_CTRL0, reg);
211*91f16700Schasinglulu 
212*91f16700Schasinglulu 	debug("thermal: Initialization done\n");
213*91f16700Schasinglulu }
214*91f16700Schasinglulu 
215*91f16700Schasinglulu static void armada_is_valid(u_register_t *read)
216*91f16700Schasinglulu {
217*91f16700Schasinglulu 	*read = (mmio_read_32(TSEN_STATUS) & TSEN_STATUS_VALID_MASK);
218*91f16700Schasinglulu }
219*91f16700Schasinglulu 
220*91f16700Schasinglulu int mvebu_dfx_thermal_handle(u_register_t func, u_register_t *read,
221*91f16700Schasinglulu 			     u_register_t x2, u_register_t x3)
222*91f16700Schasinglulu {
223*91f16700Schasinglulu 	debug_enter();
224*91f16700Schasinglulu 
225*91f16700Schasinglulu 	switch (func) {
226*91f16700Schasinglulu 	case MV_SIP_DFX_THERMAL_INIT:
227*91f16700Schasinglulu 		armada_ap806_thermal_init();
228*91f16700Schasinglulu 		break;
229*91f16700Schasinglulu 	case MV_SIP_DFX_THERMAL_READ:
230*91f16700Schasinglulu 		armada_ap806_thermal_read(read);
231*91f16700Schasinglulu 		break;
232*91f16700Schasinglulu 	case MV_SIP_DFX_THERMAL_IRQ:
233*91f16700Schasinglulu 		armada_ap806_thermal_irq();
234*91f16700Schasinglulu 		break;
235*91f16700Schasinglulu 	case MV_SIP_DFX_THERMAL_THRESH:
236*91f16700Schasinglulu 		armada_ap806_thermal_threshold(x2, x3);
237*91f16700Schasinglulu 		armada_ap806_thermal_overheat_irq_init();
238*91f16700Schasinglulu 		break;
239*91f16700Schasinglulu 	case MV_SIP_DFX_THERMAL_IS_VALID:
240*91f16700Schasinglulu 		armada_is_valid(read);
241*91f16700Schasinglulu 		break;
242*91f16700Schasinglulu 	case MV_SIP_DFX_THERMAL_SEL_CHANNEL:
243*91f16700Schasinglulu 		armada_select_channel(x2);
244*91f16700Schasinglulu 		break;
245*91f16700Schasinglulu 	default:
246*91f16700Schasinglulu 		ERROR("unsupported dfx func\n");
247*91f16700Schasinglulu 		return -EINVAL;
248*91f16700Schasinglulu 	}
249*91f16700Schasinglulu 
250*91f16700Schasinglulu 	debug_exit();
251*91f16700Schasinglulu 
252*91f16700Schasinglulu 	return 0;
253*91f16700Schasinglulu }
254