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