Files
E3_boot/drivers/source/pvt/sdrv_pvt.c
2025-11-07 10:05:24 +08:00

481 lines
14 KiB
C

/**
* @file sdrv_pvt.c
* @brief SemiDrive Temperature sensor driver source file.
*
* @copyright Copyright (c) 2022 Semidrive Semiconductor.
* All rights reserved.
*/
#include <bits.h>
#include <debug.h>
#include <irq.h>
#include <reg.h>
#include "sdrv_pvt.h"
/* Length of PVT DOUT and Threshold register bits. */
#define PVT_THRESH_BITS (12)
/* PVT registers */
#define PVT_CORE_REG_OFFSET (0x34)
#define PVT_CORE_REGISTER(core, reg) ((core)*PVT_CORE_REG_OFFSET + (reg))
#define PVT_CTRL (0x0)
#define PVT_CTRL_PDTSEN 1
#define PVT_CTRL_PDPSEN 2
#define PVT_CTRL_PTSEL 4
#define PVT_CTRL_LPMODE 7
#define PVT_CTRL_HL_MODE_EN(core) (8 + (core)*2)
#define PVT_CTRL_RF_MODE_EN(core) (9 + (core)*2)
#define PVT_DOUT (0x4)
#define PVT_DOUT_VALID 0
#define PVT_DOUT_DATA 1
#define PVT_HYST_H(core) PVT_CORE_REGISTER(core, 0x8)
#define PVT_HYST_H_THRESH_H 0
#define PVT_HYST_H_THRESH_L 12
#define PVT_HYST_L(core) PVT_CORE_REGISTER(core, 0xc)
#define PVT_HYST_L_THRESH_H 0
#define PVT_HYST_L_THRESH_L 12
#define PVT_HYST_R(core) PVT_CORE_REGISTER(core, 0x10)
#define PVT_HYST_R_ALARM 0
#define PVT_HYST_R_HYST 12
#define PVT_HYST_F(core) PVT_CORE_REGISTER(core, 0x14)
#define PVT_HYST_F_ALARM 0
#define PVT_HYST_F_HYST 12
#define PVT_INT_EN(core) PVT_CORE_REGISTER(core, 0x30)
#define PVT_INT_STATUS(core) PVT_CORE_REGISTER(core, 0x34)
#define PVT_INT_CLR(core) PVT_CORE_REGISTER(core, 0x38)
#define PVT_ALARM_INT(alarm) (uint32_t)(alarm)
static inline sdrv_pvt_core_t *pvt_core(sdrv_pvt_t *dev,
sdrv_pvt_core_id_e core)
{
return &dev->core[core];
}
/* Convert DOUT register value to Celsius. */
static inline sdrv_pvt_type dout_to_celsius(uint32_t dout)
{
dout &= BIT_MASK(PVT_THRESH_BITS);
#if CONFIG_PVT_TYPE_INT
return (30676 * dout / 4096 - 7598 + 50) / 100;
#else
return 306.76f * dout / 4096 - 75.98f;
#endif
}
/* Convert Celsius to DOUT register value. */
static inline uint32_t celsius_to_dout(sdrv_pvt_type celsius)
{
uint32_t dout = (uint32_t)((celsius * 100 + 7598) * 4096 / 30676);
return dout & BIT_MASK(PVT_THRESH_BITS);
}
static int pvt_core_irq_handler(sdrv_pvt_t *dev, sdrv_pvt_core_id_e core)
{
sdrv_pvt_core_t *_core = pvt_core(dev, core);
paddr_t base = dev->base;
sdrv_pvt_alarm_e alarm;
uint32_t en, status;
en = readl(base + PVT_INT_EN(core));
status = readl(base + PVT_INT_STATUS(core));
/* Handle alarm IRQ, if it's enabled. */
for (alarm = PVT_HIGH_LEVEL_ALARM; alarm < PVT_ALARM_NR; alarm++) {
if (BIT_SET(en, PVT_ALARM_INT(alarm)) &&
BIT_SET(status, PVT_ALARM_INT(alarm))) {
if (_core->alarm_cb != NULL) {
/* Call user alarm callback. */
_core->alarm_cb(dev, core, alarm, _core->cb_arg);
}
/*
* Try to clear interrupt status. Level interrupt status are
* cleared when INT_CLR bits are set, and DOUT has returned to
* normal range. Edge interrupt status are cleared when INT_CLR
* bits are set.
*
* Note that INT_CLR itself is not auto cleared, so we have to
* clear INT_CLR manually.
*/
RMWREG32(base + PVT_INT_CLR(core), PVT_ALARM_INT(alarm), 1, 1);
RMWREG32(base + PVT_INT_CLR(core), PVT_ALARM_INT(alarm), 1, 0);
}
}
return 0;
}
static int pvt_irq_handler(uint32_t irq, void *arg)
{
sdrv_pvt_t *dev = arg;
sdrv_pvt_core_id_e core;
for (core = PVT_CORE0; core < PVT_CORE_NR; core++) {
sdrv_pvt_core_t *_core = pvt_core(dev, core);
if (irq == _core->irq) {
pvt_core_irq_handler(dev, core);
break;
}
}
return 0;
}
static status_t update_alarm_irq(sdrv_pvt_t *dev, sdrv_pvt_core_id_e core)
{
sdrv_pvt_core_t *_core = pvt_core(dev, core);
paddr_t base = dev->base;
/* Update High/Low mode bit in PVT_CTRL register, which controls
* Temperature and Process High/Low modes.
*/
if (BIT_SET(_core->alarm_enable_mask, PVT_HIGH_LEVEL_ALARM) ||
BIT_SET(_core->alarm_enable_mask, PVT_LOW_LEVEL_ALARM)) {
RMWREG32(base + PVT_CTRL, PVT_CTRL_HL_MODE_EN(core), 1, 1);
} else {
RMWREG32(base + PVT_CTRL, PVT_CTRL_HL_MODE_EN(core), 1, 0);
}
#if SDRV_PVT_USE_EDGE_ALARM
/* Update Rising/Falling mode bit in PVT_CTRL register, which controls
* Temperature and Process Rising/Falling modes.
*/
if (BIT_SET(_core->alarm_enable_mask, PVT_RISING_EDGE_ALARM) ||
BIT_SET(_core->alarm_enable_mask, PVT_FALLING_EDGE_ALARM)) {
RMWREG32(base + PVT_CTRL, PVT_CTRL_RF_MODE_EN(core), 1, 1);
} else {
RMWREG32(base + PVT_CTRL, PVT_CTRL_RF_MODE_EN(core), 1, 0);
}
#endif
/* Enable alarm interrupt if necessary. */
if (_core->alarm_cb != NULL && _core->alarm_enable_mask != 0) {
sdrv_pvt_alarm_e alarm;
for (alarm = PVT_HIGH_LEVEL_ALARM; alarm < PVT_ALARM_NR; alarm++) {
if (BIT_SET(_core->alarm_enable_mask, alarm)) {
RMWREG32(base + PVT_INT_EN(core), PVT_ALARM_INT(alarm), 1, 1);
} else {
RMWREG32(base + PVT_INT_EN(core), PVT_ALARM_INT(alarm), 1, 0);
}
}
/* Enable PVT interrupt. */
irq_enable(_core->irq);
} else {
/* All alarm disabled. Disable PVT interrupt. */
irq_disable(_core->irq);
}
return SDRV_STATUS_OK;
}
static status_t update_alarm_mode(sdrv_pvt_t *dev, sdrv_pvt_core_id_e core,
sdrv_pvt_alarm_e alarm, bool enable)
{
sdrv_pvt_core_t *_core = pvt_core(dev, core);
if (enable) {
_core->alarm_enable_mask |= (1ul << alarm);
} else {
_core->alarm_enable_mask &= ~(1ul << alarm);
}
return update_alarm_irq(dev, core);
}
static bool
is_valid_level_alarm_config(sdrv_pvt_alarm_e alarm,
const sdrv_pvt_level_alarm_config_t *config)
{
(void)alarm;
return config != NULL && config->thresh_low >= PVT_TEMPERATURE_MIN &&
config->thresh_high <= PVT_TEMPERATURE_MAX &&
celsius_to_dout(config->thresh_low) <=
celsius_to_dout(config->thresh_high);
}
#if SDRV_PVT_USE_EDGE_ALARM
static bool
is_valid_edge_alarm_config(sdrv_pvt_alarm_e alarm,
const sdrv_pvt_edge_alarm_config_t *config)
{
if (alarm == PVT_RISING_EDGE_ALARM) {
return config != NULL && config->hyst >= PVT_TEMPERATURE_MIN &&
config->alarm <= PVT_TEMPERATURE_MAX &&
celsius_to_dout(config->hyst) < celsius_to_dout(config->alarm);
} else { /* PVT_FALLING_EDGE_ALARM */
return config != NULL && config->alarm >= PVT_TEMPERATURE_MIN &&
config->hyst <= PVT_TEMPERATURE_MAX &&
celsius_to_dout(config->alarm) < celsius_to_dout(config->hyst);
}
}
#endif
static status_t enable_level_alarm(sdrv_pvt_t *dev, sdrv_pvt_core_id_e core,
sdrv_pvt_alarm_e alarm,
const sdrv_pvt_level_alarm_config_t *config,
bool enable)
{
paddr_t base = dev->base;
if (enable) {
if (!is_valid_level_alarm_config(alarm, config)) {
ssdk_printf(SSDK_ERR, "Invalid level alarm config!\r\n");
return SDRV_PVT_INVALID_PARAM;
}
/* Update High/Low alarm thresholds. */
if (alarm == PVT_HIGH_LEVEL_ALARM) {
RMWREG32(base + PVT_HYST_H(core), PVT_HYST_H_THRESH_H,
PVT_THRESH_BITS, celsius_to_dout(config->thresh_high));
RMWREG32(base + PVT_HYST_H(core), PVT_HYST_H_THRESH_L,
PVT_THRESH_BITS, celsius_to_dout(config->thresh_low));
} else { /* PVT_LOW_LEVEL_ALARM */
RMWREG32(base + PVT_HYST_L(core), PVT_HYST_L_THRESH_H,
PVT_THRESH_BITS, celsius_to_dout(config->thresh_high));
RMWREG32(base + PVT_HYST_L(core), PVT_HYST_L_THRESH_L,
PVT_THRESH_BITS, celsius_to_dout(config->thresh_low));
}
}
return update_alarm_mode(dev, core, alarm, enable);
}
#if SDRV_PVT_USE_EDGE_ALARM
static status_t enable_edge_alarm(sdrv_pvt_t *dev, sdrv_pvt_core_id_e core,
sdrv_pvt_alarm_e alarm,
const sdrv_pvt_edge_alarm_config_t *config,
bool enable)
{
paddr_t base = dev->base;
if (enable) {
if (!is_valid_edge_alarm_config(alarm, config)) {
ssdk_printf(SSDK_ERR, "Invalid edge alarm config!\r\n");
return SDRV_PVT_INVALID_PARAM;
}
/* Update Rising/Falling alarm thresholds. */
if (alarm == PVT_RISING_EDGE_ALARM) {
RMWREG32(base + PVT_HYST_R(core), PVT_HYST_R_ALARM, PVT_THRESH_BITS,
celsius_to_dout(config->alarm));
RMWREG32(base + PVT_HYST_R(core), PVT_HYST_R_HYST, PVT_THRESH_BITS,
celsius_to_dout(config->hyst));
} else { /* PVT_FALLING_EDGE_ALARM */
RMWREG32(base + PVT_HYST_F(core), PVT_HYST_F_ALARM, PVT_THRESH_BITS,
celsius_to_dout(config->alarm));
RMWREG32(base + PVT_HYST_F(core), PVT_HYST_F_HYST, PVT_THRESH_BITS,
celsius_to_dout(config->hyst));
}
}
return update_alarm_mode(dev, core, alarm, enable);
}
#endif
static bool is_temperature_sensor_running(sdrv_pvt_t *dev)
{
uint32_t ctrl = readl(dev->base + PVT_CTRL);
return !BIT_SET(ctrl, PVT_CTRL_PDTSEN) && !BIT_SET(ctrl, PVT_CTRL_PTSEL);
}
status_t sdrv_pvt_init(sdrv_pvt_t *dev)
{
ASSERT(dev != NULL);
paddr_t base = dev->base;
sdrv_pvt_core_id_e core;
for (core = PVT_CORE0; core < PVT_CORE_NR; core++) {
sdrv_pvt_core_t *_core = pvt_core(dev, core);
/* Disable all interrupts by default. */
writel(0ul, base + PVT_INT_EN(core));
_core->alarm_cb = NULL;
_core->cb_arg = NULL;
_core->alarm_enable_mask = 0ul;
irq_attach(_core->irq, pvt_irq_handler, dev);
irq_disable(_core->irq);
}
return SDRV_STATUS_OK;
}
status_t sdrv_pvt_deinit(sdrv_pvt_t *dev)
{
ASSERT(dev != NULL);
paddr_t base = dev->base;
sdrv_pvt_core_id_e core;
for (core = PVT_CORE0; core < PVT_CORE_NR; core++) {
sdrv_pvt_core_t *_core = pvt_core(dev, core);
irq_disable(_core->irq);
irq_detach(_core->irq);
/* Disable all interrupts. */
writel(0, base + PVT_INT_EN(core));
_core->alarm_cb = NULL;
_core->cb_arg = NULL;
_core->alarm_enable_mask = 0ul;
}
return SDRV_STATUS_OK;
}
status_t sdrv_pvt_start(sdrv_pvt_t *dev)
{
ASSERT(dev != NULL);
paddr_t base = dev->base;
/* Low power mode disable */
RMWREG32(base + PVT_CTRL, PVT_CTRL_LPMODE, 1, 0);
/* Disable T sensor */
RMWREG32(base + PVT_CTRL, PVT_CTRL_PDTSEN, 1, 1);
/* Select temperature sensor output. */
RMWREG32(base + PVT_CTRL, PVT_CTRL_PTSEL, 1, 0);
/* Enable T sensor. */
RMWREG32(base + PVT_CTRL, PVT_CTRL_PDTSEN, 1, 0);
return SDRV_STATUS_OK;
}
status_t sdrv_pvt_stop(sdrv_pvt_t *dev)
{
ASSERT(dev != NULL);
/* Disable T sensor */
RMWREG32(dev->base + PVT_CTRL, PVT_CTRL_PDTSEN, 1, 1);
return SDRV_STATUS_OK;
}
status_t sdrv_pvt_power_down(sdrv_pvt_t *dev)
{
ASSERT(dev != NULL);
paddr_t base = dev->base;
/* Low power mode enable */
RMWREG32(base + PVT_CTRL, PVT_CTRL_LPMODE, 1, 1);
/* Power down T-sensor, P-sensor */
RMWREG32(base + PVT_CTRL, PVT_CTRL_PDTSEN, 2, 3);
RMWREG32(base + PVT_CTRL, PVT_CTRL_PDTSEN, 2, 0);
RMWREG32(base + PVT_CTRL, PVT_CTRL_PDTSEN, 2, 3);
return SDRV_STATUS_OK;
}
status_t sdrv_pvt_get(sdrv_pvt_t *dev, sdrv_pvt_type *temperature)
{
uint32_t dout;
ASSERT(dev != NULL && temperature != NULL);
if (!is_temperature_sensor_running(dev)) {
ssdk_printf(SSDK_ERR, "Temperature sensor is not started!\r\n");
return SDRV_PVT_NOT_STARTED;
}
/*
* Read temperature data. PVT_DOUT is automatically updated every 280ms
* after sdrv_pvt_start() is called.
*/
dout = (readl(dev->base + PVT_DOUT) >> PVT_DOUT_DATA) &
BIT_MASK(PVT_THRESH_BITS);
/* Convert data to Celsius. */
*temperature = dout_to_celsius(dout);
return SDRV_STATUS_OK;
}
status_t sdrv_pvt_register_alarm_cb(sdrv_pvt_t *dev, sdrv_pvt_core_id_e core,
sdrv_pvt_alarm_cb_t alarm_cb, void *arg)
{
ASSERT(dev != NULL && core < PVT_CORE_NR);
sdrv_pvt_core_t *_core = pvt_core(dev, core);
_core->alarm_cb = alarm_cb;
_core->cb_arg = arg;
return update_alarm_irq(dev, core);
}
status_t sdrv_pvt_enable_alarm(sdrv_pvt_t *dev, sdrv_pvt_core_id_e core,
sdrv_pvt_alarm_e alarm,
const sdrv_pvt_alarm_config_t *config)
{
ASSERT(dev != NULL && core < PVT_CORE_NR && config != NULL);
status_t ret = SDRV_PVT_INVALID_PARAM;
if (!is_temperature_sensor_running(dev)) {
ssdk_printf(SSDK_ERR, "Temperature sensor is not started!\r\n");
return SDRV_PVT_NOT_STARTED;
}
switch (alarm) {
case PVT_HIGH_LEVEL_ALARM:
case PVT_LOW_LEVEL_ALARM:
ret = enable_level_alarm(dev, core, alarm, &config->level_alarm, true);
break;
#if SDRV_PVT_USE_EDGE_ALARM
case PVT_RISING_EDGE_ALARM:
case PVT_FALLING_EDGE_ALARM:
ret = enable_edge_alarm(dev, core, alarm, &config->edge_alarm, true);
break;
#endif
default:
break;
}
return ret;
}
status_t sdrv_pvt_disable_alarm(sdrv_pvt_t *dev, sdrv_pvt_core_id_e core,
sdrv_pvt_alarm_e alarm)
{
ASSERT(dev != NULL && core < PVT_CORE_NR);
status_t ret = SDRV_PVT_INVALID_PARAM;
switch (alarm) {
case PVT_HIGH_LEVEL_ALARM:
case PVT_LOW_LEVEL_ALARM:
ret = enable_level_alarm(dev, core, alarm, NULL, false);
break;
#if SDRV_PVT_USE_EDGE_ALARM
case PVT_RISING_EDGE_ALARM:
case PVT_FALLING_EDGE_ALARM:
ret = enable_edge_alarm(dev, core, alarm, NULL, false);
break;
#endif
default:
break;
}
return ret;
}