summaryrefslogtreecommitdiffstats
path: root/drivers/marvell/secure_dfx_access/armada_thermal.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/marvell/secure_dfx_access/armada_thermal.c')
-rw-r--r--drivers/marvell/secure_dfx_access/armada_thermal.c253
1 files changed, 253 insertions, 0 deletions
diff --git a/drivers/marvell/secure_dfx_access/armada_thermal.c b/drivers/marvell/secure_dfx_access/armada_thermal.c
new file mode 100644
index 0000000..4f7191b
--- /dev/null
+++ b/drivers/marvell/secure_dfx_access/armada_thermal.c
@@ -0,0 +1,253 @@
+/*
+ * 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;
+}