mirror of
https://gitee.com/Vancouver2017/luban-lite-t3e-pro.git
synced 2025-12-14 18:38:55 +00:00
385 lines
11 KiB
C
385 lines
11 KiB
C
/*
|
|
* Copyright (c) 2022-2023, ArtInChip Technology Co., Ltd
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
* Authors: matteo <duanmt@artinchip.com>
|
|
*/
|
|
|
|
#include "aic_core.h"
|
|
#include "hal_tsen.h"
|
|
#include "aic_hal_clk.h"
|
|
|
|
#define AIC_TSEN_TIMEOUT 3000
|
|
|
|
/* Register definition of Thermal Sensor Controller */
|
|
#define TSEN_MCR 0x000
|
|
#define TSEN_INTR 0x004
|
|
#define TSENn_CFG(n) (0x100 + (((n) & 0x3) << 5))
|
|
#define TSENn_ITV(n) (0x100 + (((n) & 0x3) << 5) + 0x4)
|
|
#define TSENn_FIL(n) (0x100 + (((n) & 0x3) << 5) + 0x8)
|
|
#define TSENn_DATA(n) (0x100 + (((n) & 0x3) << 5) + 0xC)
|
|
#define TSENn_INT(n) (0x100 + (((n) & 0x3) << 5) + 0x10)
|
|
#define TSENn_HTAV(n) (0x100 + (((n) & 0x3) << 5) + 0x14)
|
|
#define TSENn_LTAV(n) (0x100 + (((n) & 0x3) << 5) + 0x18)
|
|
#define TSENn_OTPV(n) (0x100 + (((n) & 0x3) << 5) + 0x1C)
|
|
#define TSEN_VERSION 0xFFC
|
|
|
|
#define TSEN_MCR_CTLX_EN(n) (BIT(20) << (n))
|
|
#define TSEN_MCR_TSEN_EN(n) (BIT(16) << n)
|
|
|
|
#define TSEN_MCR_ACQ_SHIFT 8
|
|
#define TSEN_MCR_ACQ_MASK GENMASK(15, 8)
|
|
#define TSEN_MCR_MAX_SHIFT 4
|
|
#define TSEN_MCR_MAX_MASK GENMASK(5, 4)
|
|
#define TSEN_MCR_EN BIT(0)
|
|
|
|
#define TSEN_INTR_CH_INT_FLAG(n) (BIT(16) << (n))
|
|
#define TSEN_INTR_CH_INT_EN(n) (BIT(0) << (n))
|
|
|
|
#define TSENn_CFG_DIFF_MODE_SELECT BIT(31)
|
|
#define TSENn_CFG_INVERTED_SELECT BIT(27)
|
|
#define TSENn_CFG_HIGH_ADC_PRIORITY BIT(4)
|
|
#define TSENn_CFG_PERIOD_SAMPLE_EN BIT(1)
|
|
#define TSENn_CFG_SINGLE_SAMPLE_EN BIT(0)
|
|
|
|
#define TSENn_ITV_SHIFT 16
|
|
|
|
#define TSENn_FIL_2_POINTS 1
|
|
#define TSENn_FIL_4_POINTS 2
|
|
#define TSENn_FIL_8_POINTS 3
|
|
|
|
#define TSENn_INT_OTP_FLAG BIT(28)
|
|
#define TSENn_INT_LTA_RM_FLAG BIT(27)
|
|
#define TSENn_INT_LTA_VALID_FLAG BIT(26)
|
|
#define TSENn_INT_HTA_RM_FLAG BIT(25)
|
|
#define TSENn_INT_HTA_VALID_FLAG BIT(24)
|
|
#define TSENn_INT_DAT_OVW_FLAG BIT(17)
|
|
#define TSENn_INT_DAT_RDY_FLAG BIT(16)
|
|
#define TSENn_INT_OTP_RESET (0xA << 12)
|
|
#define TSENn_INT_OTP_IE (0x5 << 12)
|
|
#define TSENn_INT_LTA_RM_IE BIT(11)
|
|
#define TSENn_INT_LTA_VALID_IE BIT(10)
|
|
#define TSENn_INT_HTA_RM_IE BIT(9)
|
|
#define TSENn_INT_HTA_VALID_IE BIT(8)
|
|
#define TSENn_INT_DAT_OVW_IE BIT(1)
|
|
#define TSENn_INT_DATA_RDY_IE BIT(0)
|
|
|
|
#define TSENn_HLTA_EN BIT(31)
|
|
#define TSENn_HLTA_RM_THD_SHIFT 16
|
|
#define TSENn_HLTA_RM_THD_MASK GENMASK(27, 16)
|
|
#define TSENn_HLTA_THD_MASK GENMASK(11, 0)
|
|
#define TSENn_HTA_RM_THD(t) ((t) - 3)
|
|
#define TSENn_LTA_RM_THD(t) ((t) + 3)
|
|
|
|
#define TSENn_OTPV_EN BIT(31)
|
|
#define TSENn_OTPV_VAL_MASK GENMASK(11, 0)
|
|
|
|
#define THERMAL_CORE_TEMP_AMPN_SCALE 10 // in RT-Thread sensor framework
|
|
#define TSEN_CALIB_ACCURACY_SCALE 1000 // = 10000 / 10
|
|
|
|
static inline void tsen_writel(u32 val, int reg)
|
|
{
|
|
writel(val, TSEN_BASE + reg);
|
|
}
|
|
|
|
static inline u32 tsen_readl(int reg)
|
|
{
|
|
return readl(TSEN_BASE + reg);
|
|
}
|
|
|
|
/* Temperature = ADC data * slope + offset.
|
|
* 1. Temperature accuracy adopts 1000.
|
|
* 2. Slope and Offset accuracy adopts 10000.
|
|
*/
|
|
s32 hal_tsen_data2temp(struct aic_tsen_ch *chan)
|
|
{
|
|
int temp = 0;
|
|
|
|
if (chan->latest_data == 4095 || chan->latest_data == 0)
|
|
return 0;
|
|
|
|
if (!chan->slope)
|
|
return chan->latest_data;
|
|
|
|
temp = chan->slope * chan->latest_data;
|
|
temp += chan->offset;
|
|
|
|
if ((temp % TSEN_CALIB_ACCURACY_SCALE) < (TSEN_CALIB_ACCURACY_SCALE / 2))
|
|
temp = temp / TSEN_CALIB_ACCURACY_SCALE;
|
|
else
|
|
temp = temp / TSEN_CALIB_ACCURACY_SCALE + 1;
|
|
|
|
hal_log_debug("%s temperature: %d -> %d.%d\n", chan->name,
|
|
chan->latest_data,
|
|
temp / THERMAL_CORE_TEMP_AMPN_SCALE,
|
|
temp % THERMAL_CORE_TEMP_AMPN_SCALE);
|
|
return temp;
|
|
}
|
|
|
|
/* ADC data = (Temperature - offset) / slope */
|
|
u16 hal_tsen_temp2data(struct aic_tsen_ch *chan, s32 temp)
|
|
{
|
|
int data = temp * TSEN_CALIB_ACCURACY_SCALE - chan->offset;
|
|
|
|
if (!chan->slope)
|
|
return (u16)temp;
|
|
|
|
data /= chan->slope;
|
|
hal_log_debug("%s data: %d -> %d\n", chan->name, temp, data);
|
|
return (u16)data;
|
|
}
|
|
|
|
static u32 tsen_sec2itv(u32 pclk, u32 sec)
|
|
{
|
|
u32 tmp = pclk >> 16;
|
|
|
|
tmp *= sec;
|
|
return tmp;
|
|
}
|
|
|
|
void hal_tsen_enable(int enable)
|
|
{
|
|
int mcr;
|
|
|
|
mcr = tsen_readl(TSEN_MCR);
|
|
if (enable)
|
|
mcr |= TSEN_MCR_EN;
|
|
else
|
|
mcr &= ~TSEN_MCR_EN;
|
|
|
|
tsen_writel(mcr, TSEN_MCR);
|
|
}
|
|
|
|
void hal_tsen_ch_enable(u32 ch, int enable)
|
|
{
|
|
int mcr;
|
|
|
|
mcr = tsen_readl(TSEN_MCR);
|
|
if (enable) {
|
|
mcr |= TSEN_MCR_TSEN_EN(ch);
|
|
mcr |= TSEN_MCR_CTLX_EN(ch);
|
|
} else {
|
|
mcr &= ~TSEN_MCR_TSEN_EN(ch);
|
|
mcr &= ~TSEN_MCR_CTLX_EN(ch);
|
|
}
|
|
|
|
tsen_writel(mcr, TSEN_MCR);
|
|
}
|
|
|
|
static void tsen_int_enable(u32 ch, u32 enable, u32 detail)
|
|
{
|
|
u32 val = 0;
|
|
|
|
val = tsen_readl(TSEN_INTR);
|
|
if (enable) {
|
|
val |= TSEN_INTR_CH_INT_EN(ch);
|
|
tsen_writel(detail, TSENn_INT(ch));
|
|
} else {
|
|
val &= ~TSEN_INTR_CH_INT_EN(ch);
|
|
tsen_writel(0, TSENn_INT(ch));
|
|
}
|
|
tsen_writel(val, TSEN_INTR);
|
|
}
|
|
|
|
static void tsen_single_mode(u32 ch)
|
|
{
|
|
tsen_writel(TSENn_FIL_8_POINTS, TSENn_FIL(ch));
|
|
tsen_writel(TSENn_CFG_SINGLE_SAMPLE_EN | tsen_readl(TSENn_CFG(ch)),
|
|
TSENn_CFG(ch));
|
|
|
|
tsen_int_enable(ch, 1, TSENn_INT_DATA_RDY_IE);
|
|
}
|
|
|
|
/* Only in period mode, HTA, LTA and OTP are available */
|
|
static void tsen_period_mode(struct aic_tsen_ch *chan, u32 pclk)
|
|
{
|
|
u32 val, detail = TSENn_INT_DATA_RDY_IE;
|
|
|
|
if (chan->hta_enable) {
|
|
detail |= TSENn_INT_HTA_RM_IE | TSENn_INT_HTA_VALID_IE;
|
|
val = TSENn_HLTA_EN
|
|
| ((chan->hta_rm_thd << TSENn_HLTA_RM_THD_SHIFT)
|
|
& TSENn_HLTA_RM_THD_MASK)
|
|
| (chan->hta_thd & TSENn_HLTA_THD_MASK);
|
|
tsen_writel(val, TSENn_HTAV(chan->id));
|
|
}
|
|
|
|
if (chan->lta_enable) {
|
|
detail |= TSENn_INT_LTA_RM_IE | TSENn_INT_LTA_VALID_IE;
|
|
val = TSENn_HLTA_EN
|
|
| ((chan->lta_rm_thd << TSENn_HLTA_RM_THD_SHIFT)
|
|
& TSENn_HLTA_RM_THD_MASK)
|
|
| (chan->lta_thd & TSENn_HLTA_THD_MASK);
|
|
tsen_writel(val, TSENn_LTAV(chan->id));
|
|
}
|
|
|
|
if (chan->otp_enable) {
|
|
detail |= TSENn_INT_OTP_RESET;
|
|
val = TSENn_OTPV_EN | (chan->otp_thd & TSENn_OTPV_VAL_MASK);
|
|
tsen_writel(val, TSENn_OTPV(chan->id));
|
|
}
|
|
tsen_int_enable(chan->id, 1, detail);
|
|
|
|
tsen_writel(TSENn_FIL_8_POINTS, TSENn_FIL(chan->id));
|
|
|
|
val = tsen_sec2itv(pclk, chan->smp_period);
|
|
tsen_writel(val << TSENn_ITV_SHIFT | 0xFFFF, TSENn_ITV(chan->id));
|
|
|
|
tsen_writel(tsen_readl(TSENn_CFG(chan->id)) | TSENn_CFG_PERIOD_SAMPLE_EN,
|
|
TSENn_CFG(chan->id));
|
|
|
|
hal_tsen_ch_enable(chan->id, 1);
|
|
}
|
|
|
|
static void tsen_diff_mode(u32 ch, u32 diff, u32 inverted)
|
|
{
|
|
u32 val = tsen_readl(TSENn_CFG(ch));
|
|
|
|
if (diff)
|
|
val |= TSENn_CFG_DIFF_MODE_SELECT;
|
|
else
|
|
val &= ~TSENn_CFG_DIFF_MODE_SELECT;
|
|
|
|
if (inverted)
|
|
val |= TSENn_CFG_INVERTED_SELECT;
|
|
else
|
|
val &= ~TSENn_CFG_INVERTED_SELECT;
|
|
|
|
tsen_writel(val, TSENn_CFG(ch));
|
|
}
|
|
|
|
int hal_tsen_ch_init(struct aic_tsen_ch *chan, u32 pclk)
|
|
{
|
|
if (chan->mode == AIC_TSEN_MODE_PERIOD)
|
|
tsen_period_mode(chan, pclk);
|
|
|
|
tsen_diff_mode(chan->id, chan->diff_mode, chan->inverted);
|
|
|
|
/* For single mode, should init the channel in .get_temp() */
|
|
return 0;
|
|
}
|
|
|
|
int hal_tsen_get_temp(struct aic_tsen_ch *chan, s32 *val)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (!chan->available) {
|
|
hal_log_err("%s is unavailable!\n", chan->name);
|
|
return -ENODATA;
|
|
}
|
|
|
|
#ifndef CONFIG_ARTINCHIP_ADCIM_DM
|
|
if (chan->mode == AIC_TSEN_MODE_PERIOD)
|
|
return hal_tsen_data2temp(chan);
|
|
#endif
|
|
|
|
tsen_single_mode(chan->id);
|
|
hal_tsen_ch_enable(chan->id, 1);
|
|
|
|
ret = aicos_sem_take(chan->complete, AIC_TSEN_TIMEOUT);
|
|
if (ret < 0) {
|
|
hal_log_err("%s read timeout!\n", chan->name);
|
|
hal_tsen_ch_enable(chan->id, 0);
|
|
return -ETIMEDOUT;
|
|
}
|
|
hal_tsen_ch_enable(chan->id, 0);
|
|
|
|
if (val)
|
|
*val = hal_tsen_data2temp(chan);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void hal_tsen_status_show(struct aic_tsen_ch *chan)
|
|
{
|
|
int mcr = tsen_readl(TSEN_MCR);
|
|
int version = tsen_readl(TSEN_VERSION);
|
|
|
|
printf("In Thermal Sensor V%d.%02d:\n"
|
|
"Ch Name Mode Diff Inv Enable Value LTA HTA OTP\n"
|
|
"%d %-13s %4s %4d %3d %6d %5d %4d %4d %4d\n",
|
|
version >> 8, version & 0xff,
|
|
chan->id, chan->name, chan->mode ? "P" : "S",
|
|
chan->diff_mode, chan->inverted,
|
|
mcr & TSEN_MCR_TSEN_EN(chan->id) ? 1 : 0,
|
|
chan->latest_data, chan->lta_thd, chan->hta_thd, chan->otp_thd);
|
|
}
|
|
|
|
// TODO: irq_handle() should get 'struct aic_tsen_ch *' from 'void *arg'
|
|
extern struct aic_tsen_ch aic_tsen_chs[AIC_TSEN_CH_NUM];
|
|
|
|
irqreturn_t hal_tsen_irq_handle(int irq, void *arg)
|
|
{
|
|
int i, status, detail = 0;
|
|
struct aic_tsen_ch *chan = NULL;
|
|
|
|
aicos_irq_disable(TSEN_IRQn);
|
|
status = tsen_readl(TSEN_INTR);
|
|
hal_log_debug("Module IRQ status: %#x\n", status);
|
|
for (i = 0; i < AIC_TSEN_CH_NUM; i++) {
|
|
if (!(status & TSEN_INTR_CH_INT_FLAG(i)))
|
|
continue;
|
|
|
|
chan = &aic_tsen_chs[i];
|
|
detail = tsen_readl(TSENn_INT(i));
|
|
tsen_writel(detail, TSENn_INT(i));
|
|
hal_log_debug("ch%d IRQ status: %#x\n", i, detail);
|
|
if (detail & TSENn_INT_DAT_RDY_FLAG) {
|
|
chan->latest_data = tsen_readl(TSENn_DATA(i));
|
|
hal_log_debug("ch%d data %d\n", i, chan->latest_data);
|
|
|
|
if (chan->mode == AIC_TSEN_MODE_SINGLE)
|
|
aicos_sem_give(chan->complete);
|
|
}
|
|
|
|
if (detail & TSENn_INT_LTA_VALID_FLAG)
|
|
hal_log_warn("LTA: ch%d %d(%d)!\n", i,
|
|
hal_tsen_data2temp(chan), chan->latest_data);
|
|
if (detail & TSENn_INT_LTA_RM_FLAG)
|
|
hal_log_warn("LTA removed: ch%d %d(%d)\n", i,
|
|
hal_tsen_data2temp(chan), chan->latest_data);
|
|
if (detail & TSENn_INT_HTA_VALID_FLAG)
|
|
hal_log_warn("HTA: ch%d %d(%d)!\n", i,
|
|
hal_tsen_data2temp(chan), chan->latest_data);
|
|
if (detail & TSENn_INT_HTA_RM_FLAG)
|
|
hal_log_warn("HTA removed: ch%d %d(%d)\n", i,
|
|
hal_tsen_data2temp(chan), chan->latest_data);
|
|
if (detail & TSENn_INT_OTP_FLAG)
|
|
hal_log_warn("OTP: ch%d %d(%d)!\n", i,
|
|
hal_tsen_data2temp(chan), chan->latest_data);
|
|
if (detail & TSENn_INT_DAT_OVW_FLAG)
|
|
hal_log_warn("Data Over Write: ch%d %d(%d)!\n", i,
|
|
hal_tsen_data2temp(chan), chan->latest_data);
|
|
}
|
|
|
|
aicos_irq_enable(TSEN_IRQn);
|
|
hal_log_debug("IRQ status %#x, detail %#x\n", status, detail);
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
s32 hal_tsen_clk_init(void)
|
|
{
|
|
s32 ret = 0;
|
|
|
|
ret = hal_clk_enable(CLK_TSEN);
|
|
if (ret < 0) {
|
|
pr_err("TSensor clk enable failed!\n");
|
|
return -1;
|
|
}
|
|
|
|
ret = hal_clk_enable_deassertrst(CLK_TSEN);
|
|
if (ret < 0) {
|
|
pr_err("TSensor reset deassert failed!\n");
|
|
return -1;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
void hal_tsen_pclk_get(struct aic_tsen_ch *chan)
|
|
{
|
|
chan->pclk_rate = hal_clk_get_freq(hal_clk_get_parent(CLK_TSEN));
|
|
}
|