From c96ddcce819116effc4f61d0bddc65aa56c99f6e Mon Sep 17 00:00:00 2001 From: Nicolas Royer Date: Sun, 27 Sep 2020 17:46:43 +0200 Subject: rcar/module: add rcar reg_sensor module Change-Id: I59ba3eb358fcfc9b76e0b7daa7621abfaeec5028 Signed-off-by: Tsutomu Muroya Signed-off-by: Chikara Asou Signed-off-by: Nicolas Royer --- .../rcar_reg_sensor/include/mod_rcar_reg_sensor.h | 154 +++++++++++++ .../rcar_reg_sensor/include/mod_reg_sensor.h | 19 ++ product/rcar/module/rcar_reg_sensor/src/Makefile | 11 + .../rcar_reg_sensor/src/mod_rcar_reg_sensor.c | 240 +++++++++++++++++++++ 4 files changed, 424 insertions(+) create mode 100644 product/rcar/module/rcar_reg_sensor/include/mod_rcar_reg_sensor.h create mode 100644 product/rcar/module/rcar_reg_sensor/include/mod_reg_sensor.h create mode 100644 product/rcar/module/rcar_reg_sensor/src/Makefile create mode 100644 product/rcar/module/rcar_reg_sensor/src/mod_rcar_reg_sensor.c diff --git a/product/rcar/module/rcar_reg_sensor/include/mod_rcar_reg_sensor.h b/product/rcar/module/rcar_reg_sensor/include/mod_rcar_reg_sensor.h new file mode 100644 index 00000000..5c57fec5 --- /dev/null +++ b/product/rcar/module/rcar_reg_sensor/include/mod_rcar_reg_sensor.h @@ -0,0 +1,154 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef MOD_RCAR_REG_SENSOR_H +#define MOD_RCAR_REG_SENSOR_H + +#include + +#include + +/*! + * \addtogroup GroupRCARModule RCAR Product Modules + * @{ + */ + +/*! + * \defgroup GroupRCARRegSensor Register Sensor Driver + * + * \brief Driver for simple, register-based sensors. + * @{ + */ + +/*! \brief Element configuration */ +struct mod_reg_sensor_dev_config { + /*! Address of the sensor register */ + uintptr_t reg; + + /*! Auxiliary sensor information */ + struct mod_sensor_info *info; +}; + +/*! + * @cond + */ + +/* Register base */ +#define GEN3_THERMAL_BASE (PERIPHERAL_BASE + 0x198000) +#define GEN3_THERMAL_OFFSET (0x8000) +/* Register offsets */ +#define REG_GEN3_IRQSTR (0x04) +#define REG_GEN3_IRQMSK (0x08) +#define REG_GEN3_IRQCTL (0x0C) +#define REG_GEN3_IRQEN (0x10) +#define REG_GEN3_IRQTEMP1 (0x14) +#define REG_GEN3_IRQTEMP2 (0x18) +#define REG_GEN3_IRQTEMP3 (0x1C) +#define REG_GEN3_CTSR (0x20) +#define REG_GEN3_THCTR (0x20) +#define REG_GEN3_TEMP (0x28) +#define REG_GEN3_THCODE1 (0x50) +#define REG_GEN3_THCODE2 (0x54) +#define REG_GEN3_THCODE3 (0x58) +#define REG_GEN3_PTAT1 (0x5C) +#define REG_GEN3_PTAT2 (0x60) +#define REG_GEN3_PTAT3 (0x64) +#define REG_GEN3_THSCP (0x68) +#define NEXT_REG_OFFSET (4) + +/* THCTR bits */ +#define THCTR_PONM BIT(6) +#define THCTR_THSST BIT(0) + +#define CTEMP_MASK (0xFFF) +#define MCELSIUS(temp) ((temp)*1000) + +#define AVAILABLE_HARDWARE_PARAM \ + (mmio_read_32(GEN3_THERMAL_BASE + REG_GEN3_THSCP) == (0x03 << 14)) + +/* Structure for thermal temperature calculation */ +struct equation_coefs { + int a1; + int b1; + int a2; + int b2; +}; + +struct rcar_gen3_thermal_tsc { + uintptr_t base; + struct thermal_zone_device *zone; + struct equation_coefs coef; + int low; + int high; + int tj_t; + int id; /* thermal channel id */ +}; + +/* + * Linear approximation for temperature + * + * [reg] = [temp] * a + b => [temp] = ([reg] - b) / a + * + * The constants a and b are calculated using two triplets of int values PTAT + * and THCODE. PTAT and THCODE can either be read from hardware or use hard + * coded values from driver. The formula to calculate a and b are taken from + * BSP and sparsely documented and understood. + * + * Examining the linear formula and the formula used to calculate constants a + * and b while knowing that the span for PTAT and THCODE values are between + * 0x000 and 0xfff the largest integer possible is 0xfff * 0xfff == 0xffe001. + * Integer also needs to be signed so that leaves 7 bits for binary + * fixed point scaling. + */ +#define DIV_ROUND_CLOSEST(x, divisor) \ + ({ \ + __typeof(x) __x = x; \ + __typeof(divisor) __d = divisor; \ + (((__typeof(x)) - 1) > 0 || ((__typeof(divisor)) - 1) > 0 || \ + (__x) > 0) ? \ + (((__x) + ((__d) / 2)) / (__d)) : \ + (((__x) - ((__d) / 2)) / (__d)); \ + }) +#define FIXPT_SHIFT (7) +#define FIXPT_INT(_x) ((_x) * (1 << FIXPT_SHIFT)) +#define INT_FIXPT(_x) ((_x) >> FIXPT_SHIFT) +#define FIXPT_DIV(_a, _b) DIV_ROUND_CLOSEST(((_a) * (1 << FIXPT_SHIFT)), (_b)) +#define FIXPT_TO_MCELSIUS(_x) ((_x)*1000 >> FIXPT_SHIFT) + +#define RCAR3_THERMAL_GRAN (500) /* mili Celsius */ + +#define TSC_MAX_NUM (3) +#define TSC_PARAM_NUM (3) +#define TEMP_UPPER_LIMIT (125) +#define TEMP_LOWER_LIMIT (-40) + +/* no idea where these constants come from */ +#define TJ_1 (116) +#define TJ_3 (-41) + +#define SENSOR_ADR_BASE (SENSOR_SOC_TEMP1 & SENSOR_SOC_TEMP2 & SENSOR_SOC_TEMP3) +#define SENSOR_ADR_MASK \ + ((SENSOR_SOC_TEMP1 | SENSOR_SOC_TEMP2 | SENSOR_SOC_TEMP3) - SENSOR_ADR_BASE) +#define IS_SENSOR_ADR(adr) ((adr & ~SENSOR_ADR_MASK) == SENSOR_ADR_BASE) +#define CV_ADR2INDEX(adr) (int)(((adr & SENSOR_ADR_MASK) >> 15) - 3) +#define ADR2INDEX(adr) (IS_SENSOR_ADR(adr) ? CV_ADR2INDEX(adr) : (-1)) + +void reg_sensor_resume(); + +/*! + * @endcond + */ + +/*! + * @} + */ + +/*! + * @} + */ + +#endif /* MOD_RCAR_REG_SENSOR_H */ diff --git a/product/rcar/module/rcar_reg_sensor/include/mod_reg_sensor.h b/product/rcar/module/rcar_reg_sensor/include/mod_reg_sensor.h new file mode 100644 index 00000000..b42ff068 --- /dev/null +++ b/product/rcar/module/rcar_reg_sensor/include/mod_reg_sensor.h @@ -0,0 +1,19 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef MOD_REG_SENSOR_H +#define MOD_REG_SENSOR_H + +#include + +#include + +#define FWK_MODULE_IDX_REG_SENSOR FWK_MODULE_IDX_RCAR_REG_SENSOR + +#include "mod_rcar_reg_sensor.h" + +#endif /* MOD_REG_SENSOR_H */ diff --git a/product/rcar/module/rcar_reg_sensor/src/Makefile b/product/rcar/module/rcar_reg_sensor/src/Makefile new file mode 100644 index 00000000..9a7056e2 --- /dev/null +++ b/product/rcar/module/rcar_reg_sensor/src/Makefile @@ -0,0 +1,11 @@ +# +# Renesas SCP/MCP Software +# Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +BS_LIB_NAME := rcar_reg_sensor +BS_LIB_SOURCES := mod_rcar_reg_sensor.c + +include $(BS_DIR)/lib.mk diff --git a/product/rcar/module/rcar_reg_sensor/src/mod_rcar_reg_sensor.c b/product/rcar/module/rcar_reg_sensor/src/mod_rcar_reg_sensor.c new file mode 100644 index 00000000..554b93ac --- /dev/null +++ b/product/rcar/module/rcar_reg_sensor/src/mod_rcar_reg_sensor.c @@ -0,0 +1,240 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include + +/* default values if FUSEs are missing */ +/* TODO: Read values from hardware on supported platforms */ +int ptat[3] = { 2631, 1509, 435 }; +int thcode[TSC_MAX_NUM][3] = { + { 3397, 2800, 2221 }, + { 3393, 2795, 2216 }, + { 3389, 2805, 2237 }, +}; +struct rcar_gen3_thermal_tsc *tscs[TSC_MAX_NUM]; + +static struct mod_reg_sensor_dev_config **config_table; +static const int rcar_gen3_ths_tj_1 = 126; + +static inline uint32_t rcar_gen3_thermal_read( + struct rcar_gen3_thermal_tsc *tsc, + uint32_t reg) +{ + return mmio_read_32(tsc->base + reg); +} + +static inline void rcar_gen3_thermal_write( + struct rcar_gen3_thermal_tsc *tsc, + uint32_t reg, + uint32_t data) +{ + mmio_write_32(tsc->base + reg, data); +} + +static void rcar_gen3_thermal_calc_coefs( + struct rcar_gen3_thermal_tsc *tsc, + int *ptat, + const int *thcode, + int ths_tj_1) +{ + /* TODO: Find documentation and document constant calculation formula */ + + /* + * Division is not scaled in BSP and if scaled it might overflow + * the dividend (4095 * 4095 << 14 > INT_MAX) so keep it unscaled + */ + tsc->tj_t = (FIXPT_INT((ptat[1] - ptat[2]) * 157) / (ptat[0] - ptat[2])) + + FIXPT_INT(TJ_3); + + tsc->coef.a1 = FIXPT_DIV( + FIXPT_INT(thcode[1] - thcode[2]), tsc->tj_t - FIXPT_INT(TJ_3)); + tsc->coef.b1 = FIXPT_INT(thcode[2]) - tsc->coef.a1 * TJ_3; + + tsc->coef.a2 = FIXPT_DIV( + FIXPT_INT(thcode[1] - thcode[0]), tsc->tj_t - FIXPT_INT(ths_tj_1)); + tsc->coef.b2 = FIXPT_INT(thcode[0]) - tsc->coef.a2 * ths_tj_1; +} + +static int rcar_gen3_thermal_round(int temp) +{ + int result, round_offs; + + round_offs = temp >= 0 ? RCAR3_THERMAL_GRAN / 2 : -RCAR3_THERMAL_GRAN / 2; + result = (temp + round_offs) / RCAR3_THERMAL_GRAN; + return result * RCAR3_THERMAL_GRAN; +} + +static int rcar_gen3_thermal_get_temp(void *devdata, int *temp) +{ + struct rcar_gen3_thermal_tsc *tsc = devdata; + int mcelsius, val; + int reg; + + /* Read register and convert to mili Celsius */ + reg = rcar_gen3_thermal_read(tsc, REG_GEN3_TEMP) & CTEMP_MASK; + + if (reg <= thcode[tsc->id][1]) + val = FIXPT_DIV(FIXPT_INT(reg) - tsc->coef.b1, tsc->coef.a1); + else + val = FIXPT_DIV(FIXPT_INT(reg) - tsc->coef.b2, tsc->coef.a2); + mcelsius = FIXPT_TO_MCELSIUS(val); + + /* Make sure we are inside specifications */ + if ((mcelsius < MCELSIUS(TEMP_LOWER_LIMIT)) || + (mcelsius > MCELSIUS(TEMP_UPPER_LIMIT))) + return -1 /*-EIO*/; + + /* Round value to device granularity setting */ + *temp = rcar_gen3_thermal_round(mcelsius); + + return 0; +} + +/* + * Module API + */ +static int get_value(fwk_id_t id, uint64_t *value) +{ + int tmp; + int64_t *ivalue; + + if (value == NULL) { + assert(false); + return FWK_E_PARAM; + } + + if (rcar_gen3_thermal_get_temp(tscs[fwk_id_get_element_idx(id)], &tmp)) + return FWK_E_DATA; + + ivalue = (void *)value; + *ivalue = (int64_t)tmp; + + return FWK_SUCCESS; +} + +static int get_info(fwk_id_t id, struct mod_sensor_info *info) +{ + struct mod_reg_sensor_dev_config *config; + + config = config_table[fwk_id_get_element_idx(id)]; + fwk_assert(config != NULL); + + if (info == NULL) + return FWK_E_PARAM; + + *info = *(config->info); + + return FWK_SUCCESS; +} + +static const struct mod_sensor_driver_api reg_sensor_api = { + .get_value = get_value, + .get_info = get_info, +}; + +/* + * Framework handlers + */ +static int reg_sensor_start(fwk_id_t id) +{ + int eid, pid; + struct rcar_gen3_thermal_tsc *tsc; + struct mod_reg_sensor_dev_config *config; + + /* for Module */ + if (fwk_id_is_type(id, FWK_ID_TYPE_MODULE)) + return FWK_SUCCESS; + + /* for Elements */ + eid = fwk_id_get_element_idx(id); + tsc = fwk_mm_alloc(1, sizeof(*tsc)); + if (!tsc) + return FWK_E_NOMEM; + + config = config_table[eid]; + pid = ADR2INDEX(config->reg); + if (pid < 0) + return FWK_E_DATA; + tsc->base = config->reg; + rcar_gen3_thermal_calc_coefs(tsc, ptat, thcode[pid], rcar_gen3_ths_tj_1); + tsc->id = pid; + tscs[eid] = tsc; + rcar_gen3_thermal_write(tsc, REG_GEN3_THCTR, THCTR_THSST); + + return FWK_SUCCESS; +} + +static int reg_sensor_init( + fwk_id_t module_id, + unsigned int element_count, + const void *unused) +{ + config_table = fwk_mm_alloc(element_count, sizeof(*config_table)); + if (config_table == NULL) + return FWK_E_NOMEM; + + return FWK_SUCCESS; +} + +static int reg_sensor_element_init( + fwk_id_t element_id, + unsigned int sub_element_count, + const void *data) +{ + struct mod_reg_sensor_dev_config *config = + (struct mod_reg_sensor_dev_config *)data; + + if (config->reg == 0) + return FWK_E_DATA; + + config_table[fwk_id_get_element_idx(element_id)] = config; + + return FWK_SUCCESS; +} + +void reg_sensor_resume() +{ + int i; + + for (i = 0; i < TSC_MAX_NUM; i++) { + if (tscs[i]) { + rcar_gen3_thermal_write(tscs[i], REG_GEN3_THCTR, THCTR_THSST); + } + } +} + +static int reg_sensor_process_bind_request( + fwk_id_t source_id, + fwk_id_t target_id, + fwk_id_t api_type, + const void **api) +{ + *api = ®_sensor_api; + return FWK_SUCCESS; +} + +const struct fwk_module module_rcar_reg_sensor = { + .name = "Rcar Thermal Sensor", + .api_count = 1, + .type = FWK_MODULE_TYPE_DRIVER, + .init = reg_sensor_init, + .start = reg_sensor_start, + .element_init = reg_sensor_element_init, + .process_bind_request = reg_sensor_process_bind_request, +}; -- cgit v1.2.3