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