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