blob: d0d0f9f5c3db8349affb7e8b311957030f4898b3 [file]
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2020 MediaTek Inc.
*/
#include <linux/bits.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/nvmem-consumer.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/of_irq.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/thermal.h>
#include <linux/reset.h>
#include <linux/types.h>
#include <linux/seq_file.h>
#include <linux/uaccess.h>
#include <linux/debugfs.h>
#include "thermal_hwmon.h"
#include "soc_temp.h"
static int thermal_debug_log;
#define thermal_printk(fmt, args...) \
do { \
if (thermal_debug_log & 0x1) { \
pr_info("[Module: Soc_Thermal] " fmt, ##args); \
} \
} while (0)
#define thermal_dprintk(fmt, args...) \
do { \
if (thermal_debug_log & 0x2) { \
pr_info("[Module: Soc_Thermal] " fmt, ##args); \
} \
} while (0)
/* MT8173 thermal sensors */
#define MT8173_TS1 0
#define MT8173_TS2 1
#define MT8173_TS3 2
#define MT8173_TS4 3
#define MT8173_TSABB 4
/* AUXADC channel 11 is used for the temperature sensors */
#define MT8173_TEMP_AUXADC_CHANNEL 11
/* The total number of temperature sensors in the MT8173 */
#define MT8173_NUM_SENSORS 5
/* The number of banks in the MT8173 */
#define MT8173_NUM_ZONES 4
/* The number of sensing points per bank */
#define MT8173_NUM_SENSORS_PER_ZONE 4
/*
* Layout of the fuses providing the calibration data
* These macros could be used for MT8173
* MT8173 has 5 sensors and needs 5 VTS calibration data.
*/
#define MT8173_CALIB_BUF0_VALID BIT(0)
#define MT8173_CALIB_BUF1_ADC_GE(x) (((x) >> 22) & 0x3ff)
#define MT8173_CALIB_BUF0_VTS_TS1(x) (((x) >> 17) & 0x1ff)
#define MT8173_CALIB_BUF0_VTS_TS2(x) (((x) >> 8) & 0x1ff)
#define MT8173_CALIB_BUF1_VTS_TS3(x) (((x) >> 0) & 0x1ff)
#define MT8173_CALIB_BUF2_VTS_TS4(x) (((x) >> 23) & 0x1ff)
#define MT8173_CALIB_BUF2_VTS_TSABB(x) (((x) >> 14) & 0x1ff)
#define MT8173_CALIB_BUF0_DEGC_CALI(x) (((x) >> 1) & 0x3f)
#define MT8173_CALIB_BUF0_O_SLOPE(x) (((x) >> 26) & 0x3f)
#define MT8173_CALIB_BUF0_O_SLOPE_SIGN(x) (((x) >> 7) & 0x1)
#define MT8173_CALIB_BUF1_ID(x) (((x) >> 9) & 0x1)
/* MT8173 thermal sensor data */
static const int mt8173_bank_data[MT8173_NUM_ZONES][3] = {
{ MT8173_TS2, MT8173_TS3 },
{ MT8173_TS2, MT8173_TS4 },
{ MT8173_TS1, MT8173_TS2, MT8173_TSABB },
{ MT8173_TS2 },
};
static const int mt8173_msr[MT8173_NUM_SENSORS_PER_ZONE] = {
TEMP_MSR0, TEMP_MSR1, TEMP_MSR2, TEMP_MSR3
};
static const int mt8173_adcpnp[MT8173_NUM_SENSORS_PER_ZONE] = {
TEMP_ADCPNP0, TEMP_ADCPNP1, TEMP_ADCPNP2, TEMP_ADCPNP3
};
static const int mt8173_mux_values[MT8173_NUM_SENSORS] = { 0, 1, 2, 3, 16 };
/**
* The MT8519 thermal controller info.
*/
//=====================MT8519==========================
/* MT8173 thermal sensors */
#define MT8519_TS1 (0)
#define MT8519_TS2 (1)
#define MT8519_TS3 (2)
#define MT8519_TS4 (3)
#define MT8519_TS5 (4)
#define MT8519_TS6 (5)
/* AUXADC channel 11 is used for the temperature sensors */
#define MT8519_TEMP_AUXADC_CHANNEL 11
/* The total number of temperature sensors in the MT8519 */
#define MT8519_NUM_SENSORS 6
/* The number of banks in the MT8519 */
#define MT8519_NUM_ZONES 2
/* The number of sensing points per bank */
#define MT8519_NUM_SENSORS_PER_ZONE 4
static const int mt8519_bank_data[MT8519_NUM_ZONES]
[MT8519_NUM_SENSORS_PER_ZONE] = {
{ MT8519_TS1, MT8519_TS2, MT8519_TS3, MT8519_TS4 },
{ MT8519_TS5, MT8519_TS6 },
};
static const int mt8519_msr[MT8519_NUM_SENSORS_PER_ZONE] = {
TEMP_MSR0, TEMP_MSR1, TEMP_MSR2, TEMP_MSR3
};
static const int mt8519_adcpnp[MT8519_NUM_SENSORS_PER_ZONE] = {
TEMP_ADCPNP0, TEMP_ADCPNP1, TEMP_ADCPNP2, TEMP_ADCPNP3
};
static const int mt8519_mux_values[MT8519_NUM_SENSORS]
= { 0, 1, 2, 3, 4, 5 };
static int mt8519_vts_values[MT8519_NUM_SENSORS]
= { 260, 260, 260, 260, 260, 260 };
static struct soc_thermal_data mt8519_thermal_data = {
.auxadc_channel = MT8519_TEMP_AUXADC_CHANNEL,
.num_banks = MT8519_NUM_ZONES,
.num_sensors = MT8519_NUM_SENSORS,
.adc_ge = 512,
.degc_cali = 40,
.o_slope = 0,
.o_const = 1490,/*1490 = 0.00149 * 100000 * 10*/
.o_offset = 3350,
.vts = mt8519_vts_values,
.bank_data = {
{
.num_sensors = 4,
.bank_offset = 0x0,
.protect_high = 105000,
.protect_middle = -275000,
.sensors = mt8519_bank_data[0],
}, {
.num_sensors = 2,
.protect_high = 105000,
.protect_middle = -275000,
.bank_offset = 0x100,
.sensors = mt8519_bank_data[1],
},
},
/* bus clock 66M counting unit is 12 * 15.15ns * 256 = 46.540us
* filt interval is 1 * 46.540us = 46.54us,
* sen interval is ox18 * 46.540us = 1117ms
*/
.tc_speed = {
0x0000000C,
0x00010018,
0x0000030D
},
.msr = mt8519_msr,
.adcpnp = mt8519_adcpnp,
.sensor_mux_values = mt8519_mux_values,
.TS2ADCMUX_Addr_offset = APMIXED_SYS_TS_CON0,
.TS2ADCMUX_BITS = _BITMASK_(29:28),
.TS2ADCMUX_EnVAL = 0x0 << 28,
.TSVBE_SEL_Addr_offset = APMIXED_SYS_TS_CON1,
};
//=====================MT8519==========================
/**
* The MT8696 thermal controller info.
*/
//=====================MT8519==========================
/* MT8696 thermal sensors */
#define MT8696_TS1 (0)
#define MT8696_TS2 (1)
#define MT8696_TS3 (2)
#define MT8696_TS4 (3)
#define MT8696_TS5 (4)
#define MT8696_TS6 (5)
/* AUXADC channel 11 is used for the temperature sensors */
#define MT8696_TEMP_AUXADC_CHANNEL 11
/* The total number of temperature sensors in the MT8696 */
#define MT8696_NUM_SENSORS 6
/* The number of banks in the MT8696 */
#define MT8696_NUM_ZONES 2
/* The number of sensing points per bank */
#define MT8696_NUM_SENSORS_PER_ZONE 4
static const int mt8696_bank_data[MT8696_NUM_ZONES]
[MT8696_NUM_SENSORS_PER_ZONE] = {
{ MT8696_TS1, MT8696_TS2, MT8696_TS3, MT8696_TS4 },
{ MT8696_TS5, MT8696_TS6 },
};
static const int mt8696_msr[MT8696_NUM_SENSORS_PER_ZONE] = {
TEMP_MSR0, TEMP_MSR1, TEMP_MSR2, TEMP_MSR3
};
static const int mt8696_adcpnp[MT8696_NUM_SENSORS_PER_ZONE] = {
TEMP_ADCPNP0, TEMP_ADCPNP1, TEMP_ADCPNP2, TEMP_ADCPNP3
};
static const int mt8696_mux_values[MT8696_NUM_SENSORS]
= { 0, 1, 2, 3, 4, 5 };
static int mt8696_vts_values[MT8696_NUM_SENSORS]
= { 260, 260, 260, 260, 260, 260 };
static struct soc_thermal_data mt8696_thermal_data = {
.auxadc_channel = MT8696_TEMP_AUXADC_CHANNEL,
.num_banks = MT8696_NUM_ZONES,
.num_sensors = MT8696_NUM_SENSORS,
.adc_ge = 512,
.degc_cali = 40,
.o_slope = 0,
.o_const = 1470,/*1470 = 0.00147 * 100000 * 10*/
.o_offset = 3350,
.vts = mt8696_vts_values,
.bank_data = {
{
.num_sensors = 4,
.bank_offset = 0x0,
.protect_high = 117000,
.protect_middle = -275000,
.sensors = mt8696_bank_data[0],
}, {
.num_sensors = 2,
.protect_high = 117000,
.protect_middle = -275000,
.bank_offset = 0x100,
.sensors = mt8696_bank_data[1],
},
},
/* bus clock 66M counting unit is 12 * 15.15ns * 256 = 46.540us
* filt interval is 1 * 46.540us = 46.54us,
* sen interval is ox18 * 46.540us = 1117ms
*/
.tc_speed = {
0x0000000C,
0x00010018,
0x0000030D
},
.msr = mt8696_msr,
.adcpnp = mt8696_adcpnp,
.sensor_mux_values = mt8696_mux_values,
.TS2ADCMUX_Addr_offset = APMIXED_SYS_TS_CON1,
.TS2ADCMUX_BITS = _BITMASK_(5:4),
.TS2ADCMUX_EnVAL = 0x0 << 4,
.TSVBE_SEL_Addr_offset = APMIXED_SYS_TS_CON1,
};
//=====================MT8696==========================
/**
* The MT8173 thermal controller has four banks. Each bank can read up to
* four temperature sensors simultaneously. The MT8173 has a total of 5
* temperature sensors. We use each bank to measure a certain area of the
* SoC. Since TS2 is located centrally in the SoC it is influenced by multiple
* areas, hence is used in different banks.
*
* The thermal core only gets the maximum temperature of all banks, so
* the bank concept wouldn't be necessary here. However, the SVS (Smart
* Voltage Scaling) unit makes its decisions based on the same bank
* data, and this indeed needs the temperatures of the individual banks
* for making better decisions.
*/
static const struct soc_thermal_data mt8173_thermal_data = {
.auxadc_channel = MT8173_TEMP_AUXADC_CHANNEL,
.num_banks = MT8173_NUM_ZONES,
.num_sensors = MT8173_NUM_SENSORS,
.bank_data = {
{
.num_sensors = 2,
.sensors = mt8173_bank_data[0],
}, {
.num_sensors = 2,
.sensors = mt8173_bank_data[1],
}, {
.num_sensors = 3,
.sensors = mt8173_bank_data[2],
}, {
.num_sensors = 1,
.sensors = mt8173_bank_data[3],
},
},
.msr = mt8173_msr,
.adcpnp = mt8173_adcpnp,
.sensor_mux_values = mt8173_mux_values,
};
static DEFINE_SPINLOCK(thermal_spinlock);
static void mt_thermal_lock(unsigned long *x)
{
spin_lock_irqsave(&thermal_spinlock, *x);
};
static void mt_thermal_unlock(unsigned long *x)
{
spin_unlock_irqrestore(&thermal_spinlock, *x);
};
static void thermal_buffer_turn_on(struct soc_thermal *mt)
{
int temp = 0;
temp = readl(mt->tsconf_base + mt->conf->TS2ADCMUX_Addr_offset);
pr_info("Before write the register TS_CONx: 0x%x\n",
temp);
temp &= ~mt->conf->TS2ADCMUX_BITS;
temp |= mt->conf->TS2ADCMUX_BITS & mt->conf->TS2ADCMUX_EnVAL;
/* TS_CON0[y:x]=2'b00, 00: Buffer on, TSMCU to AUXADC */
writel(temp, mt->tsconf_base + mt->conf->TS2ADCMUX_Addr_offset);
udelay(200);
temp = readl(mt->tsconf_base + mt->conf->TS2ADCMUX_Addr_offset);
pr_info("After write the register TS_CONx: 0x%x\n",
temp);
}
static void thermal_buffer_turn_off(struct soc_thermal *mt)
{
int temp;
temp = readl(mt->tsconf_base + mt->conf->TS2ADCMUX_Addr_offset);
temp &= ~mt->conf->TS2ADCMUX_BITS;
temp |= mt->conf->TS2ADCMUX_BITS & (~mt->conf->TS2ADCMUX_EnVAL);
/* TS_CON0[y:x]=2'b00, 00: Buffer on, TSMCU to AUXADC */
writel(temp, mt->tsconf_base + mt->conf->TS2ADCMUX_Addr_offset);
udelay(200);
pr_info("%s :TS_CONx=0x%x\n", __func__, temp);
}
/**
* raw_to_mcelsius - convert a raw ADC value to mcelsius
* @mt: The thermal controller
* @raw: raw ADC value
*
* This converts the raw ADC value to mcelsius using the SoC specific
* calibration constants
*/
static int raw_to_mcelsius(struct soc_thermal *mt, int sensno, s32 raw)
{
s32 tmp;
raw &= 0xfff;
tmp = 100000 * 15/18 * 1000 * 10;
tmp /= 4096 - 512 + mt->conf->adc_ge;
tmp /= mt->conf->o_const + mt->conf->o_slope * 10;
/*eg: (mt->conf->o_slope * 10 = 165* 10 = 1650 = 0.00165 * 100000 * 10)*/
tmp *= raw - mt->conf->vts[sensno] - mt->conf->o_offset;
/* pr_info(" raw:0x%x, temp: %d\n",
* raw, (mt->conf->degc_cali * 500 - tmp));
*/
return (mt->conf->degc_cali * 500 - tmp);
}
/** * mcelsius_to_raw - convert mcelsius to a raw ADC value
* @mt: The thermal controller
* @temp: mcelsius
*
* This converts the raw ADC value to mcelsius using the SoC specific
* calibration constants
*/
int mcelsius_to_raw(struct soc_thermal *mt, int sensno, s32 tmp)
{
u32 raw;
s32 tmp1, tmp2;
tmp1 = 100000 * 15/18 * 1000 * 10;
tmp1 /= mt->conf->o_const + mt->conf->o_slope * 10;
tmp1 /= 4096 - 512 + mt->conf->adc_ge;
tmp2 = mt->conf->degc_cali * 500 - tmp;
raw = (tmp2 / tmp1) + mt->conf->vts[sensno]
+ mt->conf->o_offset;
raw &= 0xfff;
return raw;
}
/**
* soc_thermal_get_bank - get bank
* @bank: The bank
*
* The bank registers are banked, we have to select a bank in the
* PTPCORESEL register to access it.
*/
static void soc_thermal_get_bank(struct soc_thermal_bank *bank)
{
struct soc_thermal *mt = bank->mt;
mutex_lock(&mt->lock);
/* mt->thermal_base = bank->bank_base;
*
* val = readl(mt->thermal_base + PTPCORESEL);
* val &= ~0xf;
* val |= bank->id;
* writel(val, mt->thermal_base + PTPCORESEL);
*/
}
/**
* soc_thermal_put_bank - release bank
* @bank: The bank
*
* release a bank previously taken with soc_thermal_get_bank,
*/
static void soc_thermal_put_bank(struct soc_thermal_bank *bank)
{
struct soc_thermal *mt = bank->mt;
mutex_unlock(&mt->lock);
}
/**
* soc_thermal_bank_temperature - get the temperature of a bank
* @bank: The bank
*
* The temperature of a bank is considered the maximum temperature of
* the sensors associated to the bank.
*/
static int soc_thermal_bank_temperature(struct soc_thermal_bank *bank)
{
struct soc_thermal *mt = bank->mt;
const struct soc_thermal_data *conf = mt->conf;
int i, temp = INT_MIN, max = INT_MIN;
u32 raw, sensor_id, cnt = 0;
for (i = 0; i < conf->bank_data[bank->id].num_sensors; i++) {
sensor_id = conf->bank_data[bank->id].sensors[i];
raw = readl(mt->banks[bank->id].bank_base
+ conf->msr[i]);
while (raw == 0 && cnt < 20) {
udelay(500);
raw = readl(mt->banks[bank->id].bank_base
+ conf->msr[i]);
}
if (!raw || !(raw & _BIT_(15))) {
/* msr not ready or has been updated*/
temp = conf->sen_data[sensor_id].temp;
thermal_dprintk(
"raw = 0x%x not ready or has been updated!\n",
raw);
} else {
temp = raw_to_mcelsius(mt, sensor_id, raw);
conf->sen_data[sensor_id].msr_raw = raw;
conf->sen_data[sensor_id].temp = temp;
}
/*
* The first read of a sensor often contains very high bogus
* temperature value. Filter these out so that the system does
* not immediately shut down.
*/
if (temp > 200000)
temp = 0;
thermal_dprintk(
"bank_id: %d, sensor_id: %d, temp: %d!\n",
bank->id, sensor_id, temp);
if (temp > max)
max = temp;
}
return max;
}
static int soc_thermal_read_temp(void *data, int *temperature)
{
struct sensor_data *ts_data = (struct sensor_data *)data;
struct soc_thermal *mt = ts_data->soc_thermal;
int i;
static int tempmax = INT_MIN;
static unsigned long update_flag;
thermal_dprintk(
"%s, sensor_id: %d, sensor_flag: %lu, update_flag: %lu,\n ",
__func__, ts_data->id, ts_data->update_flag,
update_flag);
if (update_flag > ts_data->update_flag) {
ts_data->update_flag = update_flag;
goto direct_update;
}
/*update all thermal sensor */
tempmax = INT_MIN;
for (i = 0; i < mt->conf->num_banks; i++) {
struct soc_thermal_bank *bank = &mt->banks[i];
/*soc_thermal_get_bank(bank);*/
tempmax = max(tempmax, soc_thermal_bank_temperature(bank));
/*soc_thermal_put_bank(bank);*/
}
update_flag == ~0U ? update_flag = 0U : ++update_flag;
ts_data->update_flag = update_flag;
thermal_printk("max_temp: %d\n", tempmax);
/* other thermal zone has update all sensor data,
* so can read temp directly
*/
direct_update:
if (ts_data->id == 0xFF) {
mt->conf->vir_sen_data.temp = tempmax;
*temperature = tempmax;
} else if (ts_data->id < mt->conf->num_sensors)
*temperature = ts_data->temp;
else
return -EINVAL;
return 0;
}
static const struct thermal_zone_of_device_ops soc_thermal_ops = {
.get_temp = soc_thermal_read_temp,
};
static void soc_thermal_init_bank(struct soc_thermal *mt, int num,
u32 tsconf_phys_base, u32 auxadc_phys_base)
{
struct soc_thermal_bank *bank = &mt->banks[num];
const struct soc_thermal_data *conf = mt->conf;
int i, temp;
int sensor_id, protect_high_temp, protect_middle_temp;
int protect_high_raw = 0xfff, protect_middle_raw = 0xfff;
soc_thermal_get_bank(bank);
/* bus clock 66M counting unit is 12 * 15.15ns * 256 = 46.540us */
writel(mt->conf->tc_speed.tempMonCtl1,
bank->bank_base + TEMP_MONCTL1);
/*
* filt interval is 1 * 46.540us = 46.54us,
* sen interval is x* 46.540us = ms
*/
writel(mt->conf->tc_speed.tempMonCtl2,
bank->bank_base + TEMP_MONCTL2);
/* poll is set to 10u */
writel(mt->conf->tc_speed.tempAhbPoll,
bank->bank_base + TEMP_AHBPOLL);
/* temperature sampling control, 1 sample */
writel(0x924, bank->bank_base + TEMP_MSRCTL0);
/* exceed this polling time, IRQ would be inserted */
writel(0xffffffff, bank->bank_base + TEMP_AHBTO);
/* number of interrupts per event, 1 is enough */
writel(0x0, bank->bank_base + TEMP_MONIDET0);
writel(0x0, bank->bank_base + TEMP_MONIDET1);
/*
* thermal controller does not have its own ADC. Instead it
* uses AHB bus accesses to control the AUXADC. To do this the thermal
* controller has to be programmed with the physical addresses of the
* AUXADC registers and with the various bit positions in the AUXADC.
* Also the thermal controller controls a mux in the APMIXEDSYS register
* space.
*/
/*
* this value will be stored to TEMP_PNPMUXADDR (TEMP_SPARE0)
* automatically by hw
*/
writel(BIT(conf->auxadc_channel), bank->bank_base + TEMP_ADCMUX);
/* AHB address for auxadc mux selection */
writel(auxadc_phys_base + AUXADC_CON1_CLR_V,
bank->bank_base + TEMP_ADCMUXADDR);
/* AHB address for pnp sensor mux selection */
writel(mt->tsconf_phys_base + mt->conf->TSVBE_SEL_Addr_offset,
bank->bank_base + TEMP_PNPMUXADDR);
/*writel(tsconf_phys_base + APMIXED_SYS_TS_CON1,
*mt->thermal_base + TEMP_PNPMUXADDR);
*/
/* AHB value for auxadc enable */
writel(BIT(conf->auxadc_channel), bank->bank_base + TEMP_ADCEN);
/* AHB address for auxadc enable (channel 0 immediate mode selected) */
writel(auxadc_phys_base + AUXADC_CON1_SET_V,
bank->bank_base + TEMP_ADCENADDR);
/* AHB address for auxadc valid bit */
writel(auxadc_phys_base + AUXADC_DATA(conf->auxadc_channel),
bank->bank_base + TEMP_ADCVALIDADDR);
/* AHB address for auxadc voltage output */
writel(auxadc_phys_base + AUXADC_DATA(conf->auxadc_channel),
bank->bank_base + TEMP_ADCVOLTADDR);
/* read valid & voltage are at the same register */
writel(0x0, bank->bank_base + TEMP_RDCTRL);
/* indicate where the valid bit is */
writel(TEMP_ADCVALIDMASK_VALID_HIGH | TEMP_ADCVALIDMASK_VALID_POS(12),
bank->bank_base + TEMP_ADCVALIDMASK);
/* no shift */
writel(0x0, bank->bank_base + TEMP_ADCVOLTAGESHIFT);
/* enable auxadc mux write transaction */
writel(TEMP_ADCWRITECTRL_ADC_MUX_WRITE,
bank->bank_base + TEMP_ADCWRITECTRL);
protect_high_temp = conf->bank_data[num].protect_high;
protect_middle_temp = conf->bank_data[num].protect_middle;
for (i = 0; i < conf->bank_data[num].num_sensors; i++) {
sensor_id =
conf->sensor_mux_values
[conf->bank_data[num].sensors[i]];
writel(sensor_id,
bank->bank_base + conf->adcpnp[i]);
protect_high_raw = MIN(protect_high_raw,
mcelsius_to_raw(mt, sensor_id, protect_high_temp));
if (protect_middle_temp != -275000)
protect_middle_raw = MIN(protect_middle_temp,
mcelsius_to_raw(mt,
sensor_id, protect_middle_temp));
}
/*disadle protect setting & int setting*/
temp = readl(bank->bank_base + TEMP_MONINT);
writel(temp & 0x00000000, bank->bank_base + TEMP_MONINT);
/*select max temp to protect*/
writel(0x10000, bank->bank_base + TEMP_PROTCTL);
/*set protect value*/
writel(protect_high_raw, bank->bank_base + TEMP_PROTTC);
if (protect_middle_temp != -275000)
writel(protect_middle_raw, bank->bank_base + TEMP_PROTTB);
/*enable protect_c setting & int setting*/
if (protect_middle_temp != -275000)
writel(temp | 0xC0000000,
bank->bank_base + TEMP_MONINT);
else
writel(temp | 0x80000000,
bank->bank_base + TEMP_MONINT);
writel((1 << conf->bank_data[num].num_sensors) - 1,
bank->bank_base + TEMP_MONCTL0);
writel(TEMP_ADCWRITECTRL_ADC_PNP_WRITE |
TEMP_ADCWRITECTRL_ADC_MUX_WRITE,
bank->bank_base + TEMP_ADCWRITECTRL);
soc_thermal_put_bank(bank);
}
static u64 of_get_phys_base(struct device_node *np)
{
u64 size64;
const __be32 *regaddr_p;
regaddr_p = of_get_address(np, 0, &size64, NULL);
if (!regaddr_p)
return OF_BAD_ADDR;
return of_translate_address(np, regaddr_p);
}
static int __maybe_unused
thermal_rst_init(struct platform_device *pdev,
struct soc_thermal *mt)
{
int num, i;
struct device_node *np = pdev->dev.of_node;
struct of_phandle_args args;
num = of_property_count_strings(np, "therm-rst-name");
mt->therm_rst_num = num;
mt->therm_rst_ctrl = devm_kzalloc(&pdev->dev,
sizeof(*mt->therm_rst_ctrl) * num,
GFP_KERNEL);
if (!mt->therm_rst_ctrl)
return -ENODEV;
for (i = 0; i < num; i++) {
if (of_parse_phandle_with_fixed_args(np,
"mediatek,therm-rst", 6, i, &args))
return -EINVAL;
mt->therm_rst_ctrl[i].rst_phys_base = of_get_phys_base(args.np);
mt->therm_rst_ctrl[i].rst_base = of_iomap(args.np, 0);
mt->therm_rst_ctrl[i].rst_set_offset = args.args[0];
mt->therm_rst_ctrl[i].rst_set_bit = args.args[1];
mt->therm_rst_ctrl[i].rst_clr_offset = args.args[2];
mt->therm_rst_ctrl[i].rst_clr_bit = args.args[3];
mt->therm_rst_ctrl[i].rst_sta_offset = args.args[4];
mt->therm_rst_ctrl[i].rst_sta_bit = args.args[5];
}
return 0;
}
static void __maybe_unused
thermal_rst_trigger(struct soc_thermal *mt)
{
int temp, i;
for (i = 0; i < mt->therm_rst_num; i++) {
temp = readl(mt->therm_rst_ctrl[i].rst_base +
mt->therm_rst_ctrl[i].rst_set_offset);
temp |= mt->therm_rst_ctrl[i].rst_set_bit;
writel(temp, mt->therm_rst_ctrl[i].rst_base +
mt->therm_rst_ctrl[i].rst_set_offset);
pr_info("%s: 0x%x, 0x%x\n", __func__,
(uintptr_t)(mt->therm_rst_ctrl[i].rst_base +
mt->therm_rst_ctrl[i].rst_set_offset),
readl(mt->therm_rst_ctrl[i].rst_base +
mt->therm_rst_ctrl[i].rst_set_offset));
temp = readl(mt->therm_rst_ctrl[i].rst_base +
mt->therm_rst_ctrl[i].rst_clr_offset);
temp |= mt->therm_rst_ctrl[i].rst_clr_bit;
writel(temp, mt->therm_rst_ctrl[i].rst_base +
mt->therm_rst_ctrl[i].rst_clr_offset);
pr_info("%s: 0x%x, 0x%x\n", __func__,
(uintptr_t)(mt->therm_rst_ctrl[i].rst_base +
mt->therm_rst_ctrl[i].rst_clr_offset),
readl(mt->therm_rst_ctrl[i].rst_base +
mt->therm_rst_ctrl[i].rst_clr_offset));
pr_info("%s: 0x%x, 0x%x\n", __func__,
(uintptr_t)(mt->therm_rst_ctrl[i].rst_base +
mt->therm_rst_ctrl[i].rst_set_offset),
readl(mt->therm_rst_ctrl[i].rst_base +
mt->therm_rst_ctrl[i].rst_set_offset));
}
}
static __maybe_unused
int thermal_parse_calibration_table(struct device *dev,
struct soc_thermal *mt, u32 *buf)
{
struct device_node *np = dev->of_node;
int i, ret, ncell = 0;
unsigned int *cell;
unsigned int data;
unsigned int (*cell_table)[3];
const char *cali_data_name;
const __be32 *prop;
u64 buf_offset, msb, lsb;
if (!buf)
return 0;
prop = of_get_property(np, "calibration-map", &ncell);
/*4 bytes on prop in dts*/
if (ncell / 4 % 3 || ncell == 0) {
dev_notice(dev, "no lookup table, use default value\n");
return -EINVAL;
}
ncell = of_property_count_strings(np,
"calibration-map-name");
for (i = 0; i < ncell; i++) {
of_property_read_string_index(np,
"calibration-map-name",
i, &cali_data_name);
buf_offset = of_read_number(prop + i * 3, 1);
msb = of_read_number(prop + i * 3 + 1, 1);
lsb = of_read_number(prop + i * 3 + 2, 1);
thermal_dprintk("buf_offset:%llu, msb:%llu, lsb:%llu\n",
buf_offset, msb, lsb);
data = MASK_FILED_VALUE(buf[buf_offset], msb, lsb);
if (!strcmp(cali_data_name, "cali_en")) {
mt->conf->o_cali_en = data;
thermal_dprintk("efuse o_cali_en: %d\n",
mt->conf->o_cali_en);
} else if (!strcmp(cali_data_name, "adc_ge")) {
mt->conf->adc_ge = data;
thermal_dprintk("efuse adc_ge: %d\n",
mt->conf->adc_ge);
} else if (!strcmp(cali_data_name, "degc_cali")) {
mt->conf->degc_cali = data;
thermal_dprintk("efuse degc_cali: %d\n",
mt->conf->degc_cali);
} else if (!strcmp(cali_data_name, "slope_sign")) {
mt->conf->o_slope_sign = data;
thermal_dprintk("efuse o_slope_sign: %d\n",
mt->conf->o_slope_sign);
} else if (!strcmp(cali_data_name, "slope")) {
mt->conf->o_slope = data;
thermal_dprintk("efuse o_slope: %d\n",
mt->conf->o_slope);
} else if (!strcmp(cali_data_name, "id")) {
mt->conf->o_cali_id = data;
thermal_dprintk("efuse o_cali_id: %d\n",
mt->conf->o_cali_id);
}
}
if (!mt->conf->o_cali_en) {
/* Start with default values */
mt->conf->adc_ge = 512;
for (i = 0; i < mt->conf->num_sensors; i++)
mt->conf->vts[i] = 260;
mt->conf->degc_cali = 40;
mt->conf->o_slope = 0;
dev_info(dev,
"Device not calibrated, using default calibration values\n");
return 0;
}
if (mt->conf->o_slope_sign == 1) {
mt->conf->o_slope = -mt->conf->o_slope;
thermal_dprintk("efuse o_slope: %d\n", mt->conf->o_slope);
}
if (mt->conf->o_cali_id == 0) {
mt->conf->o_slope = 0;
thermal_dprintk("efuse o_slope: %d\n", mt->conf->o_slope);
}
ncell = of_property_count_elems_of_size(np, "calibration-map-vts",
sizeof(u32));
if (ncell <= 0) {
dev_notice(dev, "no lookup table, use default value\n");
return 0;
}
if ((ncell % 3) || (ncell / 3 != mt->conf->num_sensors)) {
dev_info(dev, "calibration value not match!\n");
return -EINVAL;
}
cell = devm_kcalloc(dev, ncell, sizeof(u32), GFP_KERNEL);
if (!cell)
return -ENOMEM;
ret = of_property_read_u32_array(np, "calibration-map-vts",
cell, ncell);
if (ret < 0) {
dev_info(dev, "Failed to read temperature lookup table: %d\n",
ret);
return ret;
}
cell_table = (void *)cell;
for (i = 0; i < mt->conf->num_sensors; i++) {
buf_offset = (*cell_table)[0];
msb = (*cell_table)[1];
lsb = (*cell_table)[2];
thermal_dprintk(
"buf_offset:%llu, msb:%llu, lsb:%llu\n", buf_offset, msb, lsb);
data = MASK_FILED_VALUE(buf[buf_offset], msb, lsb);
mt->conf->vts[i] = data;
thermal_dprintk("efuse vts[%d]: 0x%0x\n", i, mt->conf->vts[i]);
cell_table++;
}
return 0;
}
static int soc_thermal_get_calibration_data(struct device *dev,
struct soc_thermal *mt)
{
int ret = -1;
#if defined(NVMEM_USE)
struct nvmem_cell *cell;
u32 *buf;
size_t len = 0;
cell = nvmem_cell_get(dev, "efuse-data");
if (IS_ERR(cell)) {
dev_info(dev, "Failed to read thermal efuse data\n");
if (PTR_ERR(cell) == -EPROBE_DEFER)
return PTR_ERR(cell);
return 0;
}
buf = (u32 *)nvmem_cell_read(cell, &len);
nvmem_cell_put(cell);
if (!buf || IS_ERR(buf))
return PTR_ERR(buf);
#elif defined(CONFIG_MTK_SECURE_EFUSE) || defined(CONFIG_MTK_SECURE_EFUSE_MODULE)
u32 buf[4] = {0};
buf[0] = get_devinfo_with_index(61);
buf[1] = get_devinfo_with_index(62);
buf[2] = get_devinfo_with_index(63);
buf[3] = get_devinfo_with_index(100);
thermal_dprintk(
"efuse: buf[0]: 0x%0x, buf[1]: 0x%0x, buf[2]: 0x%0x, buf[3]: 0x%0x\n",
buf[0], buf[1], buf[2], buf[3]);
#else
u32 __maybe_unused buf[4] = {0};
#endif
ret = thermal_parse_calibration_table(dev, mt, buf);
return ret;
}
static const struct of_device_id soc_thermal_of_match[] = {
{
.compatible = "mediatek,mt8696-thermal",
.data = (void *)&mt8696_thermal_data,
},
{
.compatible = "mediatek,mt8519-thermal",
.data = (void *)&mt8519_thermal_data,
},
{
.compatible = "mediatek,mt8173-thermal",
.data = (void *)&mt8173_thermal_data,
},
{
},
};
MODULE_DEVICE_TABLE(of, soc_thermal_of_match);
static void __maybe_unused
thermal_interrupt_handler(int tc_id, void *dev_id)
{
u32 ret = 0;
void __iomem *offset;
unsigned long flags;
struct soc_thermal *mt = (struct soc_thermal *)dev_id;
offset = mt->banks[tc_id].bank_base;
mt_thermal_lock(&flags);
ret = readl(offset + TEMP_MONINTSTS);
mt_thermal_unlock(&flags);
pr_info("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n");
pr_info("%s,ret=0x%08x\n", __func__, ret);
pr_info("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n");
if (ret & THERMAL_tri_SPM_State0)
pr_info(
"thermal_isr: Thermal state0 to trigger SPM state0\n");
if (ret & THERMAL_tri_SPM_State1)
pr_info(
"thermal_isr: Thermal state1 to trigger SPM state1\n");
if (ret & THERMAL_tri_SPM_State2)
pr_info(
"thermal_isr: Thermal state2 to trigger SPM state2\n");
}
static irqreturn_t __maybe_unused irq_handler(int irq, void *dev_id)
{
struct soc_thermal *mt = (struct soc_thermal *)dev_id;
unsigned int ret = 0, i = 0, mask = 1;
unsigned long flags;
mt_thermal_lock(&flags);
ret = readl(mt->thermal_base + THERMINTST);
ret = ret & 0xF;
pr_info("thermal_interrupt_handler : THERMINTST = 0x%x\n", ret);
mt_thermal_unlock(&flags);
for (i = 0; i < mt->conf->num_banks; i++) {
mask = 1 << i;
if ((ret & mask) == 0)
thermal_interrupt_handler(i, dev_id);
}
return IRQ_HANDLED;
}
/**************************
* Thermal Debug OPS
**************************
*/
static ssize_t thermal_debug_proc_write(struct file *file,
const char *buffer, size_t count, loff_t *data)
{
int ret, cmd;
char kbuf[256];
size_t len = 0;
len = min(count, (sizeof(kbuf) - 1));
pr_info("count: %d", count);
if (count == 0)
return -1;
if (count > 255)
count = 255;
ret = copy_from_user(kbuf, buffer, count);
if (ret < 0)
return -1;
kbuf[count] = '\0';
ret = kstrtoint(kbuf, 10, &cmd);
if (ret)
return ret;
thermal_debug_log = cmd;
return count;
}
static int thermal_debug_proc_read(struct seq_file *m, void *v)
{
seq_printf(m, "====== thermal_debug_log: %d ======\n",
thermal_debug_log);
return 0;
}
static int thermal_debug_proc_open(struct inode *inode, struct file *file)
{
return single_open(file, thermal_debug_proc_read, inode->i_private);
}
static const struct file_operations thermal_debug_fops = {
.owner = THIS_MODULE,
.open = thermal_debug_proc_open,
.read = seq_read,
.write = thermal_debug_proc_write,
.release = single_release,
};
static void soc_thermal_debugfs(struct platform_device *pdev)
{
struct dentry *root;
struct dentry *thermal_debug;
struct device *dev = &pdev->dev;
root = debugfs_lookup("thermal", NULL);
if (!root)
root = debugfs_create_dir("thermal", NULL);
if (IS_ERR(root) || !root) {
dev_info(dev, "create debugfs fail");
return;
}
/* /sys/kernel/debug/thermal/thermal_debug */
thermal_debug = debugfs_create_file("thermal_debug", 0664,
root, NULL, &thermal_debug_fops);
if (IS_ERR(thermal_debug)) {
dev_info(dev, "failed to create ctrl debugfs");
return;
}
dev_dbg(dev, "Create debugfs success!");
}
static int soc_thermal_info_parse(struct platform_device *pdev, struct soc_thermal *mt)
{
int ret;
struct resource *res;
struct device_node *auxadc, *apmixedsys,
*tsconfbase, *np = pdev->dev.of_node;
mt->clk_peri_therm = devm_clk_get(&pdev->dev, "therm");
if (IS_ERR(mt->clk_peri_therm))
return PTR_ERR(mt->clk_peri_therm);
mt->clk_auxadc = devm_clk_get(&pdev->dev, "auxadc");
if (IS_ERR(mt->clk_auxadc))
return PTR_ERR(mt->clk_auxadc);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
mt->thermal_base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(mt->thermal_base))
return PTR_ERR(mt->thermal_base);
ret = soc_thermal_get_calibration_data(&pdev->dev, mt);
if (ret)
return ret;
mutex_init(&mt->lock);
mt->dev = &pdev->dev;
auxadc = of_parse_phandle(np, "mediatek,auxadc", 0);
if (!auxadc) {
dev_info(&pdev->dev, "missing auxadc node\n");
return -ENODEV;
}
mt->auxadc_phys_base = of_get_phys_base(auxadc);
mt->auxadc_base = of_iomap(auxadc, 0);
of_node_put(auxadc);
if (mt->auxadc_phys_base == OF_BAD_ADDR) {
dev_info(&pdev->dev, "Can't get auxadc phys address\n");
return -EINVAL;
}
apmixedsys = of_parse_phandle(np, "mediatek,apmixedsys", 0);
if (!apmixedsys) {
dev_info(&pdev->dev, "missing apmixedsys node\n");
return -ENODEV;
}
tsconfbase = of_parse_phandle(np, "mediatek,tsconfbase", 0);
if (!tsconfbase) {
dev_info(&pdev->dev, "missing tsconfbase node\n");
return -ENODEV;
}
mt->tsconf_phys_base = of_get_phys_base(tsconfbase);
mt->tsconf_base = of_iomap(tsconfbase, 0);
of_node_put(tsconfbase);
if (mt->tsconf_phys_base == OF_BAD_ADDR) {
dev_info(&pdev->dev, "Can't get auxadc phys address\n");
return -EINVAL;
}
return 0;
}
static struct thermal_zone_device **TZDEV;
static int soc_thermal_probe(struct platform_device *pdev)
{
int ret, i;
struct soc_thermal *mt;
struct resource *res;
struct thermal_zone_device *tzdev;
struct soc_thermal_data *conf;
conf = (struct soc_thermal_data *)of_device_get_match_data(&pdev->dev);
if (!conf)
return -ENODEV;
conf->sen_data = devm_kzalloc(&pdev->dev,
sizeof(*conf->sen_data) * conf->num_sensors,
GFP_KERNEL);
if (!conf->sen_data)
return -ENODEV;
mt = devm_kzalloc(&pdev->dev,
sizeof(*mt) +
conf->num_banks * sizeof(struct soc_thermal_bank),
GFP_KERNEL);
if (!mt)
return -ENOMEM;
mt->conf = conf;
ret = soc_thermal_info_parse(pdev, mt);
if (ret) {
dev_info(&pdev->dev, "%s parse infor fail: %d\n", __func__, ret);
return ret;
}
thermal_rst_init(pdev, mt);
thermal_rst_trigger(mt);
ret = clk_prepare_enable(mt->clk_auxadc);
if (ret) {
dev_info(&pdev->dev, "Can't enable auxadc clk: %d\n", ret);
return ret;
}
ret = clk_prepare_enable(mt->clk_peri_therm);
if (ret) {
dev_info(&pdev->dev, "Can't enable peri clk: %d\n", ret);
goto err_disable_clk_auxadc;
}
thermal_buffer_turn_on(mt);
udelay(200);
for (i = 0; i < mt->conf->num_banks; i++) {
mt->banks[i].id = i;
mt->banks[i].mt = mt;
mt->banks[i].bank_base =
mt->thermal_base + mt->conf->bank_data[i].bank_offset;
}
for (i = 0; i < mt->conf->num_banks; i++)
soc_thermal_init_bank(mt, i, mt->tsconf_phys_base,
mt->auxadc_phys_base);
mdelay(20);
platform_set_drvdata(pdev, mt);
TZDEV = devm_kzalloc(&pdev->dev,
sizeof(*TZDEV) * (mt->conf->num_sensors + 1), GFP_KERNEL);
if (!TZDEV)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!res) {
dev_info(&pdev->dev, "No irq resource, index %d\n", 0);
return -EINVAL;
}
ret = devm_request_irq(&pdev->dev, res->start, irq_handler,
IRQF_TRIGGER_HIGH, "soc_temp", mt);
if (ret) {
dev_info(&pdev->dev, "failed to register irq (%d)\n", ret);
return -EINVAL;
}
soc_thermal_debugfs(pdev);
conf->vir_sen_data.id = 0xFF;
conf->vir_sen_data.soc_thermal = mt;
tzdev = devm_thermal_zone_of_sensor_register(&pdev->dev,
conf->vir_sen_data.id,
&conf->vir_sen_data,
&soc_thermal_ops);
if (IS_ERR(tzdev)) {
dev_info(&pdev->dev, "Can't register soc_max sensor\n");
ret = PTR_ERR(tzdev);
goto err_disable_clk_peri_therm;
}
ret = thermal_add_hwmon_sysfs(tzdev);
if (ret)
dev_info(&pdev->dev, "register thermal hwmon fail: %d, ret = %d\n",
tzdev->id, ret);
TZDEV[0] = tzdev;
for (i = 0; i < mt->conf->num_sensors ; i++) {
conf->sen_data[i].id = i;
conf->sen_data[i].soc_thermal = mt;
tzdev = devm_thermal_zone_of_sensor_register(
&pdev->dev, i, &conf->sen_data[i],
&soc_thermal_ops);
if (IS_ERR(tzdev))
return 0;
ret = thermal_add_hwmon_sysfs(tzdev);
if (ret)
dev_info(&pdev->dev, "register thermal hwmon fail: %d, ret = %d\n",
tzdev->id, ret);
TZDEV[i + 1] = tzdev;
}
return 0;
err_disable_clk_peri_therm:
clk_disable_unprepare(mt->clk_peri_therm);
err_disable_clk_auxadc:
clk_disable_unprepare(mt->clk_auxadc);
return ret;
}
static int soc_thermal_remove(struct platform_device *pdev)
{
struct soc_thermal *mt = platform_get_drvdata(pdev);
int i;
clk_disable_unprepare(mt->clk_peri_therm);
clk_disable_unprepare(mt->clk_auxadc);
if (!TZDEV)
return 0;
for (i = 0; i < mt->conf->num_sensors + 1; i++) {
if (!TZDEV[i])
continue;
devm_thermal_zone_of_sensor_unregister(&pdev->dev,
TZDEV[i]);
thermal_remove_hwmon_sysfs(TZDEV[i]);
}
return 0;
}
static int soc_thermal_suspend(struct platform_device *pdev, pm_message_t state)
{
struct soc_thermal *mt = platform_get_drvdata(pdev);
int temp, i, cnt = 0;
/* disable periodic temp measurement on sensor 0~x */
for (i = 0; i < mt->conf->num_banks; i++)
writel(0, mt->banks[i].bank_base + TEMP_MONCTL0);
while (cnt < 50) {
temp = (readl(mt->banks[0].bank_base + TEMP_AHBST) >> 16);
if (temp == 0x0)/* bus no data handle and transfer */
break;
udelay(2);
cnt++;
}
clk_disable_unprepare(mt->clk_peri_therm);
clk_disable_unprepare(mt->clk_auxadc);
thermal_buffer_turn_off(mt);
return 0;
}
static int soc_thermal_resume(struct platform_device *pdev)
{
struct soc_thermal *mt = platform_get_drvdata(pdev);
int ret, i;
thermal_rst_trigger(mt);
ret = clk_prepare_enable(mt->clk_auxadc);
if (ret) {
dev_info(&pdev->dev, "Can't enable auxadc clk: %d\n", ret);
return ret;
}
ret = clk_prepare_enable(mt->clk_peri_therm);
if (ret) {
dev_info(&pdev->dev, "Can't enable peri clk: %d\n", ret);
return ret;
}
thermal_buffer_turn_on(mt);
udelay(200);
for (i = 0; i < mt->conf->num_banks; i++)
soc_thermal_init_bank(mt, i, mt->tsconf_phys_base,
mt->auxadc_phys_base);
udelay(500);
return 0;
}
static struct platform_driver soc_thermal_driver = {
.probe = soc_thermal_probe,
.remove = soc_thermal_remove,
.suspend = soc_thermal_suspend,
.resume = soc_thermal_resume,
.driver = {
.name = "mtk-soc-thermal",
.of_match_table = soc_thermal_of_match,
},
};
module_platform_driver(soc_thermal_driver);
MODULE_AUTHOR("Kunlong Wang <kunlong.wang@mediatek.com>");
MODULE_AUTHOR("Xing Fang <xing.fang@mediatek.com>");
MODULE_AUTHOR("Hanyi Wu <hanyi.wu@mediatek.com>");
MODULE_DESCRIPTION("Mediatek SoC thermal driver");
MODULE_LICENSE("GPL v2");