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 0000000000000000000000000000000000000000..776fb2482f5fa2e562c01eb5ace8b2ed52eb6f51 --- /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 +#include +#include +#include +#include +#include +#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_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; +} diff --git a/drivers/marvell/secure_dfx_access/dfx.h b/drivers/marvell/secure_dfx_access/dfx.h new file mode 100644 index 0000000000000000000000000000000000000000..e60d2f9c5edfcca6b049c123fce7e3a928ee416e --- /dev/null +++ b/drivers/marvell/secure_dfx_access/dfx.h @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2019 Marvell International Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + * https://spdx.org/licenses + */ + +/* DFX sub-FID */ +#define MV_SIP_DFX_THERMAL_INIT 1 +#define MV_SIP_DFX_THERMAL_READ 2 +#define MV_SIP_DFX_THERMAL_IS_VALID 3 +#define MV_SIP_DFX_THERMAL_IRQ 4 +#define MV_SIP_DFX_THERMAL_THRESH 5 +#define MV_SIP_DFX_THERMAL_SEL_CHANNEL 6 + +int mvebu_dfx_handle(u_register_t func, u_register_t *read, u_register_t x2, + u_register_t x3); diff --git a/plat/marvell/armada/a8k/common/a8k_common.mk b/plat/marvell/armada/a8k/common/a8k_common.mk index 8a463eafe5d77d5488bea7f2d475d0115b02d166..e20cf788a8f3a3866317949d028adaad7715f3e2 100644 --- a/plat/marvell/armada/a8k/common/a8k_common.mk +++ b/plat/marvell/armada/a8k/common/a8k_common.mk @@ -112,9 +112,10 @@ MARVELL_DRV := $(MARVELL_DRV_BASE)/io_win.c \ $(MARVELL_DRV_BASE)/amb_adec.c \ $(MARVELL_DRV_BASE)/ccu.c \ $(MARVELL_DRV_BASE)/cache_llc.c \ - $(MARVELL_DRV_BASE)/comphy/phy-comphy-cp110.c \ - $(MARVELL_DRV_BASE)/mc_trustzone/mc_trustzone.c \ - $(MARVELL_DRV_BASE)/mg_conf_cm3/mg_conf_cm3.c \ + $(MARVELL_DRV_BASE)/comphy/phy-comphy-cp110.c \ + $(MARVELL_DRV_BASE)/mc_trustzone/mc_trustzone.c \ + $(MARVELL_DRV_BASE)/mg_conf_cm3/mg_conf_cm3.c \ + $(MARVELL_DRV_BASE)/secure_dfx_access/armada_thermal.c \ drivers/rambus/trng_ip_76.c BL31_PORTING_SOURCES := $(BOARD_DIR)/board/marvell_plat_config.c diff --git a/plat/marvell/armada/common/mrvl_sip_svc.c b/plat/marvell/armada/common/mrvl_sip_svc.c index 64187fb48b4b07fb0384687445fea23565ee8fb9..bdf79b2112793fc7a54404cb090e122d4159e1e5 100644 --- a/plat/marvell/armada/common/mrvl_sip_svc.c +++ b/plat/marvell/armada/common/mrvl_sip_svc.c @@ -16,6 +16,7 @@ #include #include "comphy/phy-comphy-cp110.h" +#include "secure_dfx_access/dfx.h" #include /* #define DEBUG_COMPHY */ @@ -37,6 +38,7 @@ #define MV_SIP_LLC_ENABLE 0x82000011 #define MV_SIP_PMU_IRQ_ENABLE 0x82000012 #define MV_SIP_PMU_IRQ_DISABLE 0x82000013 +#define MV_SIP_DFX 0x82000014 /* TRNG */ #define MV_SIP_RNG_64 0xC200FF11 @@ -71,7 +73,7 @@ uintptr_t mrvl_sip_smc_handler(uint32_t smc_fid, void *handle, u_register_t flags) { - u_register_t ret; + u_register_t ret, read; uint32_t w2[2] = {0, 0}; int i; @@ -136,6 +138,9 @@ uintptr_t mrvl_sip_smc_handler(uint32_t smc_fid, mvebu_pmu_interrupt_disable(); SMC_RET1(handle, 0); #endif + case MV_SIP_DFX: + ret = mvebu_dfx_handle(x1, &read, x2, x3); + SMC_RET2(handle, ret, read); case MV_SIP_RNG_64: ret = eip76_rng_get_random((uint8_t *)&w2, 4 * (x1 % 2 + 1)); SMC_RET3(handle, ret, w2[0], w2[1]);