新增所有文件
This commit is contained in:
480
drivers/source/pvt/sdrv_pvt.c
Normal file
480
drivers/source/pvt/sdrv_pvt.c
Normal file
@@ -0,0 +1,480 @@
|
||||
/**
|
||||
* @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;
|
||||
}
|
||||
Reference in New Issue
Block a user