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