123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253 |
- /*
- * Copyright (C) 2019 Marvell International Ltd.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- * https://spdx.org/licenses
- */
- #include <common/debug.h>
- #include <drivers/delay_timer.h>
- #include <errno.h>
- #include <lib/mmio.h>
- #include <mvebu.h>
- #include <stdbool.h>
- #include "dfx.h"
- /* #define DEBUG_DFX */
- #ifdef DEBUG_DFX
- #define debug(format...) NOTICE(format)
- #else
- #define debug(format, arg...)
- #endif
- #define TSEN_CTRL0 0xf06f8084
- #define TSEN_CTRL0_START BIT(0)
- #define TSEN_CTRL0_RESET BIT(1)
- #define TSEN_CTRL0_ENABLE BIT(2)
- #define TSEN_CTRL0_AVG_BYPASS BIT(6)
- #define TSEN_CTRL0_CHAN_SHIFT 13
- #define TSEN_CTRL0_CHAN_MASK 0xF
- #define TSEN_CTRL0_OSR_SHIFT 24
- #define TSEN_CTRL0_OSR_MAX 0x3
- #define TSEN_CTRL0_MODE_SHIFT 30
- #define TSEN_CTRL0_MODE_EXTERNAL 0x2U
- #define TSEN_CTRL0_MODE_MASK 0x3U
- #define TSEN_CTRL1 0xf06f8088
- #define TSEN_CTRL1_INT_EN BIT(25)
- #define TSEN_CTRL1_HYST_SHIFT 19
- #define TSEN_CTRL1_HYST_MASK (0x3 << TSEN_CTRL1_HYST_SHIFT)
- #define TSEN_CTRL1_THRESH_SHIFT 3
- #define TSEN_CTRL1_THRESH_MASK (0x3ff << TSEN_CTRL1_THRESH_SHIFT)
- #define TSEN_STATUS 0xf06f808c
- #define TSEN_STATUS_VALID_OFFSET 16
- #define TSEN_STATUS_VALID_MASK (0x1 << TSEN_STATUS_VALID_OFFSET)
- #define TSEN_STATUS_TEMP_OUT_OFFSET 0
- #define TSEN_STATUS_TEMP_OUT_MASK (0x3FF << TSEN_STATUS_TEMP_OUT_OFFSET)
- #define DFX_SERVER_IRQ_SUM_MASK_REG 0xf06f8104
- #define DFX_SERVER_IRQ_EN BIT(1)
- #define DFX_IRQ_CAUSE_REG 0xf06f8108
- #define DFX_IRQ_MASK_REG 0xf06f810c
- #define DFX_IRQ_TSEN_OVERHEAT_OFFSET BIT(22)
- #define THERMAL_SEN_OUTPUT_MSB 512
- #define THERMAL_SEN_OUTPUT_COMP 1024
- #define COEF_M 423
- #define COEF_B -150000LL
- static void armada_ap806_thermal_read(u_register_t *temp)
- {
- uint32_t reg;
- reg = mmio_read_32(TSEN_STATUS);
- reg = ((reg & TSEN_STATUS_TEMP_OUT_MASK) >>
- TSEN_STATUS_TEMP_OUT_OFFSET);
- /*
- * TSEN output format is signed as a 2s complement number
- * ranging from-512 to +511. when MSB is set, need to
- * calculate the complement number
- */
- if (reg >= THERMAL_SEN_OUTPUT_MSB)
- reg -= THERMAL_SEN_OUTPUT_COMP;
- *temp = ((COEF_M * ((signed int)reg)) - COEF_B);
- }
- static void armada_ap806_thermal_irq(void)
- {
- /* Dummy read, register ROC */
- mmio_read_32(DFX_IRQ_CAUSE_REG);
- }
- static void armada_ap806_thermal_overheat_irq_init(void)
- {
- uint32_t reg;
- /* Clear DFX temperature IRQ cause */
- reg = mmio_read_32(DFX_IRQ_CAUSE_REG);
- /* Enable DFX Temperature IRQ */
- reg = mmio_read_32(DFX_IRQ_MASK_REG);
- reg |= DFX_IRQ_TSEN_OVERHEAT_OFFSET;
- mmio_write_32(DFX_IRQ_MASK_REG, reg);
- /* Enable DFX server IRQ */
- reg = mmio_read_32(DFX_SERVER_IRQ_SUM_MASK_REG);
- reg |= DFX_SERVER_IRQ_EN;
- mmio_write_32(DFX_SERVER_IRQ_SUM_MASK_REG, reg);
- /* Enable overheat interrupt */
- reg = mmio_read_32(TSEN_CTRL1);
- reg |= TSEN_CTRL1_INT_EN;
- mmio_write_32(TSEN_CTRL1, reg);
- }
- static unsigned int armada_mc_to_reg_temp(unsigned int temp_mc)
- {
- unsigned int sample;
- sample = (temp_mc + COEF_B) / COEF_M;
- return sample & 0x3ff;
- }
- /*
- * The documentation states:
- * high/low watermark = threshold +/- 0.4761 * 2^(hysteresis + 2)
- * which is the mathematical derivation for:
- * 0x0 <=> 1.9°C, 0x1 <=> 3.8°C, 0x2 <=> 7.6°C, 0x3 <=> 15.2°C
- */
- static unsigned int hyst_levels_mc[] = {1900, 3800, 7600, 15200};
- static unsigned int armada_mc_to_reg_hyst(int hyst_mc)
- {
- int i;
- /*
- * We will always take the smallest possible hysteresis to avoid risking
- * the hardware integrity by enlarging the threshold by +8°C in the
- * worst case.
- */
- for (i = ARRAY_SIZE(hyst_levels_mc) - 1; i > 0; i--)
- if (hyst_mc >= hyst_levels_mc[i])
- break;
- return i;
- }
- static void armada_ap806_thermal_threshold(int thresh_mc, int hyst_mc)
- {
- uint32_t ctrl1;
- unsigned int threshold = armada_mc_to_reg_temp(thresh_mc);
- unsigned int hysteresis = armada_mc_to_reg_hyst(hyst_mc);
- ctrl1 = mmio_read_32(TSEN_CTRL1);
- /* Set Threshold */
- if (thresh_mc >= 0) {
- ctrl1 &= ~(TSEN_CTRL1_THRESH_MASK);
- ctrl1 |= threshold << TSEN_CTRL1_THRESH_SHIFT;
- }
- /* Set Hysteresis */
- if (hyst_mc >= 0) {
- ctrl1 &= ~(TSEN_CTRL1_HYST_MASK);
- ctrl1 |= hysteresis << TSEN_CTRL1_HYST_SHIFT;
- }
- mmio_write_32(TSEN_CTRL1, ctrl1);
- }
- static void armada_select_channel(int channel)
- {
- uint32_t ctrl0;
- /* Stop the measurements */
- ctrl0 = mmio_read_32(TSEN_CTRL0);
- ctrl0 &= ~TSEN_CTRL0_START;
- mmio_write_32(TSEN_CTRL0, ctrl0);
- /* Reset the mode, internal sensor will be automatically selected */
- ctrl0 &= ~(TSEN_CTRL0_MODE_MASK << TSEN_CTRL0_MODE_SHIFT);
- /* Other channels are external and should be selected accordingly */
- if (channel) {
- /* Change the mode to external */
- ctrl0 |= TSEN_CTRL0_MODE_EXTERNAL <<
- TSEN_CTRL0_MODE_SHIFT;
- /* Select the sensor */
- ctrl0 &= ~(TSEN_CTRL0_CHAN_MASK << TSEN_CTRL0_CHAN_SHIFT);
- ctrl0 |= (channel - 1) << TSEN_CTRL0_CHAN_SHIFT;
- }
- /* Actually set the mode/channel */
- mmio_write_32(TSEN_CTRL0, ctrl0);
- /* Re-start the measurements */
- ctrl0 |= TSEN_CTRL0_START;
- mmio_write_32(TSEN_CTRL0, ctrl0);
- }
- static void armada_ap806_thermal_init(void)
- {
- uint32_t reg;
- reg = mmio_read_32(TSEN_CTRL0);
- reg &= ~TSEN_CTRL0_RESET;
- reg |= TSEN_CTRL0_START | TSEN_CTRL0_ENABLE;
- /* Sample every ~2ms */
- reg |= TSEN_CTRL0_OSR_MAX << TSEN_CTRL0_OSR_SHIFT;
- /* Enable average (2 samples by default) */
- reg &= ~TSEN_CTRL0_AVG_BYPASS;
- mmio_write_32(TSEN_CTRL0, reg);
- debug("thermal: Initialization done\n");
- }
- static void armada_is_valid(u_register_t *read)
- {
- *read = (mmio_read_32(TSEN_STATUS) & TSEN_STATUS_VALID_MASK);
- }
- int mvebu_dfx_thermal_handle(u_register_t func, u_register_t *read,
- u_register_t x2, u_register_t x3)
- {
- debug_enter();
- switch (func) {
- case MV_SIP_DFX_THERMAL_INIT:
- armada_ap806_thermal_init();
- break;
- case MV_SIP_DFX_THERMAL_READ:
- armada_ap806_thermal_read(read);
- break;
- case MV_SIP_DFX_THERMAL_IRQ:
- armada_ap806_thermal_irq();
- break;
- case MV_SIP_DFX_THERMAL_THRESH:
- armada_ap806_thermal_threshold(x2, x3);
- armada_ap806_thermal_overheat_irq_init();
- break;
- case MV_SIP_DFX_THERMAL_IS_VALID:
- armada_is_valid(read);
- break;
- case MV_SIP_DFX_THERMAL_SEL_CHANNEL:
- armada_select_channel(x2);
- break;
- default:
- ERROR("unsupported dfx func\n");
- return -EINVAL;
- }
- debug_exit();
- return 0;
- }
|