1/*
2 * Copyright 2017, Data61
3 * Commonwealth Scientific and Industrial Research Organisation (CSIRO)
4 * ABN 41 687 119 230.
5 *
6 * This software may be distributed and modified according to the terms of
7 * the BSD 2-Clause license. Note that NO WARRANTY is provided.
8 * See "LICENSE_BSD2.txt" for details.
9 *
10 * @TAG(DATA61_BSD)
11 */
12
13#include <platsupport/mach/tmu.h>
14#include "../../services.h"
15
16#include <string.h>
17
18#define TRIMINFO_RELOAD        (1)
19
20#define CORE_EN                (1)
21#define TRIP_EN                (1<<12)
22#define TRIP_ONLYCURRENT       (0<<13)
23#define TRIP_CUR_PAST3_0       (4<<13)
24#define TRIP_CUR_PAST7_0       (5<<13)
25#define TRIP_CUR_PAST11_0      (6<<13)
26#define TRIP_CUR_PAST15_0      (7<<13)
27
28#define INTEN_RISE0            (1)
29#define INTEN_RISE1            (1<<4)
30#define INTEN_RISE2            (1<<8)
31#define INTEN_FALL0            (1<<16)
32#define INTEN_FALL1            (1<<20)
33#define INTEN_FALL2            (1<<24)
34
35#define INT_RISE(x)            (BIT(((x) * 4)))
36#define INT_FALL(x)            INT_RISE(3 + (x))
37
38#define TRIM_INFO_MASK         (0xFF)
39
40#define INT_ALL                ( INT_RISE(0) | INT_RISE(1) | INT_RISE(2) \
41                               | INT_FALL(0) | INT_FALL(1) | INT_FALL(2) )
42
43#define EMUL_EN                (1)
44
45/* EFUSE related definition. */
46#define EFUSE_MIN_VALUE        40
47#define EFUSE_MAX_VALUE        100
48#define EFUSE_INIT_VALUE       55
49
50#define TMU_SAVE_NUM           10
51#define TMU_DC_OFFSET          25
52
53/* Miscellaneous definitions. */
54#define SLOPE                  0x10008802
55#define MUX_ADDR_VALUE         6
56
57/* Device access macros. */
58#define TMU_REG(vbase, offset)    (*(volatile unsigned int *)(vbase + offset))
59
60struct tmu_regs {
61    uint32_t res0[5];           /* 0x00 */
62    uint32_t triminfo_con;      /* 0x14 */
63    uint32_t res1[2];           /* 0x18 */
64    uint32_t con;               /* 0x20 */
65    uint32_t res2[1];           /* 0x24 */
66    uint32_t stat;              /* 0x28 */
67    uint32_t sampling_interval; /* 0x2C */
68    uint32_t cnt0;              /* 0x30 */
69    uint32_t cnt1;              /* 0x34 */
70    uint32_t res3[2];           /* 0x38 */
71    uint32_t temperature;       /* 0x40 */
72    uint32_t res4[3];           /* 0x44 */
73    uint32_t threshold_rise;    /* 0x50 */
74    uint32_t threshold_fall;    /* 0x54 */
75    uint32_t res5[2];           /* 0x58 */
76    uint32_t past_temp[4];      /* 0x60 */
77    uint32_t int_enable;        /* 0x70 */
78    uint32_t int_stat;          /* 0x74 */
79    uint32_t int_clear;         /* 0x78 */
80    uint32_t res6[1];           /* 0x7C */
81    uint32_t emul_con;          /* 0x80 */
82};
83typedef volatile struct tmu_regs tmu_regs_t;
84
85static tmu_regs_t* _tmu_regs[NTMU];
86
87static inline tmu_regs_t*
88tmu_priv_get_regs(tmu_t* tmu)
89{
90    return (tmu_regs_t*)tmu->priv;
91}
92
93static int
94do_exynos_tmu_init(enum tmu_id id, void* vaddr, tmu_t* tmu)
95{
96    tmu_regs_t* regs;
97    uint32_t v;
98    uint32_t te1, te2;
99
100    memset(tmu, 0, sizeof(*tmu));
101
102    /* Check bounds */
103    if (id < 0 || id >= NTMU) {
104        return -1;
105    }
106    /* Initialise memory map */
107    if (vaddr) {
108        _tmu_regs[id] = vaddr;
109    }
110    if (_tmu_regs[id] == NULL) {
111        return -1;
112    }
113
114    regs = _tmu_regs[id];
115    tmu->priv = (void*)_tmu_regs[id];
116
117    /* Reset alarms */
118    regs->int_enable = 0;
119    regs->int_clear = INT_ALL;
120    regs->threshold_rise = 0;
121    regs->threshold_fall = 0;
122
123    /* Reload TRIMINFO_CON for using efuse. */
124    regs->triminfo_con = TRIMINFO_RELOAD;
125    while (regs->triminfo_con & TRIMINFO_RELOAD);
126    /* Get the compensation parameter. */
127    v = regs->triminfo_con;
128    te1 = (v >> 0) & TRIM_INFO_MASK;
129    te2 = (v >> 8) & TRIM_INFO_MASK;
130
131    /* Ensure the parameters are in range */
132    if ((EFUSE_MIN_VALUE > te1) || (te1 > EFUSE_MAX_VALUE) || (te2 != 0)) {
133        te1 = EFUSE_INIT_VALUE;
134    }
135    tmu->t_off = te1 - TMU_DC_OFFSET;
136
137    /* Need to initialize register setting after getting parameter info. */
138    /* [28:23] vref [11:8] slope - Tunning parameter */
139    regs->con = SLOPE;
140
141    /* Enable the TMU */
142    regs->con |= (MUX_ADDR_VALUE << 20) | CORE_EN;
143    while (regs->temperature == 0);
144
145    /* Clear the reading */
146    v = regs->temperature;
147    return 0;
148}
149
150int
151exynos4_tmu_init(enum tmu_id id, void* vaddr, tmu_t* tmu)
152{
153    return do_exynos_tmu_init(id, vaddr, tmu);
154}
155
156int
157exynos5_tmu_init(enum tmu_id id, void* vaddr, tmu_t* tmu)
158{
159    return do_exynos_tmu_init(id, vaddr, tmu);
160}
161
162int
163exynos_tmu_init(enum tmu_id id, ps_io_ops_t* io_ops, tmu_t* tmu)
164{
165    /* Check bounds */
166    if (id < 0 || id >= NTMU) {
167        return -1;
168    }
169    /* Map the memory */
170    MAP_IF_NULL(io_ops, EXYNOS_TMU, _tmu_regs[id]);
171    if (_tmu_regs[id] == NULL) {
172        return -1;
173    }
174    return do_exynos_tmu_init(id, (void*)_tmu_regs[id], tmu);
175}
176
177int
178exynos_tmu_get_temperature(tmu_t* tmu)
179{
180    tmu_regs_t* regs;
181    uint32_t currTemp;
182    temperature_t temperature;
183    regs = tmu_priv_get_regs(tmu);
184
185    /* After reading temperature code from register, compensating
186     * its value and calculating celsius temperature,
187     * get current temperature.
188     */
189    currTemp = regs->temperature & 0xff;
190
191    /* compensate and calculate current temperature */
192    temperature = currTemp - tmu->t_off;
193    if (temperature < 0) {
194        /* temperature code range are between min 25 and 125 */
195        LOG_ERROR("Invalid temperature from TMU");
196    }
197
198    return temperature;
199}
200
201void
202exynos_tmu_handle_irq(tmu_t* tmu)
203{
204    tmu_regs_t* regs;
205    uint32_t sts;
206    temperature_t t = exynos_tmu_get_temperature(tmu);
207    regs = tmu_priv_get_regs(tmu);
208
209    sts = regs->int_stat;
210    if (sts & INT_RISE(0)) {
211        assert(tmu->rising_alarm);
212        tmu->rising_alarm(tmu, t, 0, 1, tmu->rising_token);
213    }
214    if (sts & INT_RISE(1)) {
215        assert(tmu->rising_alarm);
216        tmu->rising_alarm(tmu, t, 1, 1, tmu->rising_token);
217    }
218    if (sts & INT_RISE(2)) {
219        assert(tmu->rising_alarm);
220        tmu->rising_alarm(tmu, t, 2, 1, tmu->rising_token);
221    }
222    if (sts & INT_FALL(0)) {
223        assert(tmu->falling_alarm);
224        tmu->rising_alarm(tmu, t, 0, 0, tmu->falling_token);
225    }
226    if (sts & INT_FALL(1)) {
227        assert(tmu->falling_alarm);
228        tmu->rising_alarm(tmu, t, 1, 0, tmu->falling_token);
229    }
230    if (sts & INT_FALL(2)) {
231        assert(tmu->falling_alarm);
232        tmu->rising_alarm(tmu, t, 2, 0, tmu->falling_token);
233    }
234    regs->int_clear = sts;
235}
236
237int
238exynos_tmu_set_alarms_rising(tmu_t* tmu,
239                             temperature_t level0,
240                             temperature_t level1,
241                             temperature_t level2,
242                             tmu_alarm_callback cb,
243                             void* token)
244{
245    tmu_regs_t* regs;
246    uint32_t threshold;
247    uint32_t int_enable;
248
249    regs = tmu_priv_get_regs(tmu);
250
251    threshold = 0;
252    int_enable = regs->int_enable & ~(INT_RISE(0) | INT_RISE(1) | INT_RISE(2));
253    if (level0 >= 0) {
254        threshold |= (level0 + tmu->t_off) <<  0;
255        int_enable |= INT_RISE(0);
256    }
257    if (level1 >= 0) {
258        threshold |= (level0 + tmu->t_off) <<  0;
259        int_enable |= INT_RISE(1);
260    }
261    if (level2 >= 0) {
262        threshold |= (level0 + tmu->t_off) <<  0;
263        int_enable |= INT_RISE(2);
264    }
265    regs->threshold_rise = threshold;
266    regs->int_enable = int_enable;
267    return 0;
268}
269
270int
271exynos_tmu_set_alarms_falling(tmu_t* tmu,
272                              temperature_t level0,
273                              temperature_t level1,
274                              temperature_t level2,
275                              tmu_alarm_callback cb,
276                              void* token)
277{
278    tmu_regs_t* regs;
279    uint32_t threshold;
280    uint32_t int_enable;
281
282    regs = tmu_priv_get_regs(tmu);
283
284    threshold = 0;
285    int_enable = regs->int_enable & ~(INT_FALL(0) | INT_FALL(1) | INT_FALL(2));
286    if (level0 >= 0) {
287        threshold |= (level0 + tmu->t_off) <<  0;
288        int_enable |= INT_FALL(0);
289    }
290    if (level1 >= 0) {
291        threshold |= (level0 + tmu->t_off) <<  0;
292        int_enable |= INT_FALL(1);
293    }
294    if (level2 >= 0) {
295        threshold |= (level0 + tmu->t_off) <<  0;
296        int_enable |= INT_FALL(2);
297    }
298    regs->threshold_fall = threshold;
299    regs->int_enable = int_enable;
300    return 0;
301}
302