diff options
Diffstat (limited to 'drivers/staging/omap-thermal')
-rw-r--r-- | drivers/staging/omap-thermal/Kconfig | 46 | ||||
-rw-r--r-- | drivers/staging/omap-thermal/Makefile | 5 | ||||
-rw-r--r-- | drivers/staging/omap-thermal/TODO | 28 | ||||
-rw-r--r-- | drivers/staging/omap-thermal/omap-bandgap.c | 1187 | ||||
-rw-r--r-- | drivers/staging/omap-thermal/omap-bandgap.h | 441 | ||||
-rw-r--r-- | drivers/staging/omap-thermal/omap-thermal-common.c | 364 | ||||
-rw-r--r-- | drivers/staging/omap-thermal/omap-thermal.h | 108 | ||||
-rw-r--r-- | drivers/staging/omap-thermal/omap4-thermal.c | 259 | ||||
-rw-r--r-- | drivers/staging/omap-thermal/omap5-thermal.c | 297 | ||||
-rw-r--r-- | drivers/staging/omap-thermal/omap_bandgap.txt | 30 |
10 files changed, 2765 insertions, 0 deletions
diff --git a/drivers/staging/omap-thermal/Kconfig b/drivers/staging/omap-thermal/Kconfig new file mode 100644 index 0000000..30cbc3b --- /dev/null +++ b/drivers/staging/omap-thermal/Kconfig @@ -0,0 +1,46 @@ +config OMAP_BANDGAP + tristate "Texas Instruments OMAP4+ temperature sensor driver" + depends on THERMAL + depends on ARCH_OMAP4 || SOC_OMAP5 + help + If you say yes here you get support for the Texas Instruments + OMAP4460+ on die bandgap temperature sensor support. The register + set is part of system control module. + + This includes alert interrupts generation and also the TSHUT + support. + +config OMAP_THERMAL + bool "Texas Instruments OMAP4+ thermal framework support" + depends on OMAP_BANDGAP + depends on CPU_THERMAL + help + If you say yes here you want to get support for generic thermal + framework for the Texas Instruments OMAP4460+ on die bandgap + temperature sensor. + +config OMAP4_THERMAL + bool "Texas Instruments OMAP4 thermal support" + depends on OMAP_BANDGAP + depends on ARCH_OMAP4 + help + If you say yes here you get thermal support for the Texas Instruments + OMAP4 SoC family. The current chip supported are: + - OMAP4430 + - OMAP4460 + - OMAP4470 + + This includes alert interrupts generation and also the TSHUT + support. + +config OMAP5_THERMAL + bool "Texas Instruments OMAP5 thermal support" + depends on OMAP_BANDGAP + depends on SOC_OMAP5 + help + If you say yes here you get thermal support for the Texas Instruments + OMAP5 SoC family. The current chip supported are: + - OMAP5430 + + This includes alert interrupts generation and also the TSHUT + support. diff --git a/drivers/staging/omap-thermal/Makefile b/drivers/staging/omap-thermal/Makefile new file mode 100644 index 0000000..091c4d2 --- /dev/null +++ b/drivers/staging/omap-thermal/Makefile @@ -0,0 +1,5 @@ +obj-$(CONFIG_OMAP_BANDGAP) += omap-thermal.o +omap-thermal-y := omap-bandgap.o +omap-thermal-$(CONFIG_OMAP_THERMAL) += omap-thermal-common.o +omap-thermal-$(CONFIG_OMAP4_THERMAL) += omap4-thermal.o +omap-thermal-$(CONFIG_OMAP5_THERMAL) += omap5-thermal.o diff --git a/drivers/staging/omap-thermal/TODO b/drivers/staging/omap-thermal/TODO new file mode 100644 index 0000000..9e23cc4 --- /dev/null +++ b/drivers/staging/omap-thermal/TODO @@ -0,0 +1,28 @@ +List of TODOs (by Eduardo Valentin) + +on omap-bandgap.c: +- Rework locking +- Improve driver code by adding usage of regmap-mmio +- Test every exposed API to userland +- Add support to hwmon +- Review and revisit all API exposed +- Revisit PM support +- Revisit data structures and simplify them +- Once SCM-core api settles, update this driver accordingly + +on omap-thermal-common.c/omap-thermal.h: +- Revisit extrapolation constants for O4/O5 +- Revisit need for locking +- Revisit trips and its definitions +- Revisit trending + +on omap5-thermal.c +- Add support for GPU cooling + +generally: +- write Kconfig dependencies so that omap variants are covered +- make checkpatch.pl and sparse happy +- make sure this code works on OMAP4430, OMAP4460 and OMAP5430 +- update documentation + +Copy patches to Eduardo Valentin <eduardo.valentin@ti.com> diff --git a/drivers/staging/omap-thermal/omap-bandgap.c b/drivers/staging/omap-thermal/omap-bandgap.c new file mode 100644 index 0000000..c556abb --- /dev/null +++ b/drivers/staging/omap-thermal/omap-bandgap.c @@ -0,0 +1,1187 @@ +/* + * OMAP4 Bandgap temperature sensor driver + * + * Copyright (C) 2011-2012 Texas Instruments Incorporated - http://www.ti.com/ + * Author: J Keerthy <j-keerthy@ti.com> + * Author: Moiz Sonasath <m-sonasath@ti.com> + * Couple of fixes, DT and MFD adaptation: + * Eduardo Valentin <eduardo.valentin@ti.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include <linux/module.h> +#include <linux/export.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/interrupt.h> +#include <linux/clk.h> +#include <linux/gpio.h> +#include <linux/platform_device.h> +#include <linux/err.h> +#include <linux/types.h> +#include <linux/mutex.h> +#include <linux/reboot.h> +#include <linux/of_device.h> +#include <linux/of_platform.h> +#include <linux/of_irq.h> + +#include "omap-bandgap.h" + +static u32 omap_bandgap_readl(struct omap_bandgap *bg_ptr, u32 reg) +{ + return readl(bg_ptr->base + reg); +} + +static void omap_bandgap_writel(struct omap_bandgap *bg_ptr, u32 val, u32 reg) +{ + writel(val, bg_ptr->base + reg); +} + +static int omap_bandgap_power(struct omap_bandgap *bg_ptr, bool on) +{ + struct temp_sensor_registers *tsr; + int i; + u32 ctrl; + + if (!OMAP_BANDGAP_HAS(bg_ptr, POWER_SWITCH)) + return 0; + + for (i = 0; i < bg_ptr->conf->sensor_count; i++) { + tsr = bg_ptr->conf->sensors[i].registers; + ctrl = omap_bandgap_readl(bg_ptr, tsr->temp_sensor_ctrl); + ctrl &= ~tsr->bgap_tempsoff_mask; + /* active on 0 */ + ctrl |= !on << __ffs(tsr->bgap_tempsoff_mask); + + /* write BGAP_TEMPSOFF should be reset to 0 */ + omap_bandgap_writel(bg_ptr, ctrl, tsr->temp_sensor_ctrl); + } + + return 0; +} + +/* This is the Talert handler. Call it only if HAS(TALERT) is set */ +static irqreturn_t talert_irq_handler(int irq, void *data) +{ + struct omap_bandgap *bg_ptr = data; + struct temp_sensor_registers *tsr; + u32 t_hot = 0, t_cold = 0, temp, ctrl; + int i; + + bg_ptr = data; + /* Read the status of t_hot */ + for (i = 0; i < bg_ptr->conf->sensor_count; i++) { + tsr = bg_ptr->conf->sensors[i].registers; + t_hot = omap_bandgap_readl(bg_ptr, tsr->bgap_status); + t_hot &= tsr->status_hot_mask; + + /* Read the status of t_cold */ + t_cold = omap_bandgap_readl(bg_ptr, tsr->bgap_status); + t_cold &= tsr->status_cold_mask; + + if (!t_cold && !t_hot) + continue; + + ctrl = omap_bandgap_readl(bg_ptr, tsr->bgap_mask_ctrl); + /* + * One TALERT interrupt: Two sources + * If the interrupt is due to t_hot then mask t_hot and + * and unmask t_cold else mask t_cold and unmask t_hot + */ + if (t_hot) { + ctrl &= ~tsr->mask_hot_mask; + ctrl |= tsr->mask_cold_mask; + } else if (t_cold) { + ctrl &= ~tsr->mask_cold_mask; + ctrl |= tsr->mask_hot_mask; + } + + omap_bandgap_writel(bg_ptr, ctrl, tsr->bgap_mask_ctrl); + + /* read temperature */ + temp = omap_bandgap_readl(bg_ptr, tsr->temp_sensor_ctrl); + temp &= tsr->bgap_dtemp_mask; + + /* report temperature to whom may concern */ + if (bg_ptr->conf->report_temperature) + bg_ptr->conf->report_temperature(bg_ptr, i); + } + + return IRQ_HANDLED; +} + +/* This is the Tshut handler. Call it only if HAS(TSHUT) is set */ +static irqreturn_t omap_bandgap_tshut_irq_handler(int irq, void *data) +{ + orderly_poweroff(true); + + return IRQ_HANDLED; +} + +static +int adc_to_temp_conversion(struct omap_bandgap *bg_ptr, int id, int adc_val, + int *t) +{ + struct temp_sensor_data *ts_data = bg_ptr->conf->sensors[id].ts_data; + + /* look up for temperature in the table and return the temperature */ + if (adc_val < ts_data->adc_start_val || adc_val > ts_data->adc_end_val) + return -ERANGE; + + *t = bg_ptr->conv_table[adc_val - ts_data->adc_start_val]; + + return 0; +} + +static int temp_to_adc_conversion(long temp, struct omap_bandgap *bg_ptr, int i, + int *adc) +{ + struct temp_sensor_data *ts_data = bg_ptr->conf->sensors[i].ts_data; + int high, low, mid; + + low = 0; + high = ts_data->adc_end_val - ts_data->adc_start_val; + mid = (high + low) / 2; + + if (temp < bg_ptr->conv_table[high] || temp > bg_ptr->conv_table[high]) + return -EINVAL; + + while (low < high) { + if (temp < bg_ptr->conv_table[mid]) + high = mid - 1; + else + low = mid + 1; + mid = (low + high) / 2; + } + + *adc = ts_data->adc_start_val + low; + + return 0; +} + +/* Talert masks. Call it only if HAS(TALERT) is set */ +static int temp_sensor_unmask_interrupts(struct omap_bandgap *bg_ptr, int id, + u32 t_hot, u32 t_cold) +{ + struct temp_sensor_registers *tsr; + u32 temp, reg_val; + + /* Read the current on die temperature */ + tsr = bg_ptr->conf->sensors[id].registers; + temp = omap_bandgap_readl(bg_ptr, tsr->temp_sensor_ctrl); + temp &= tsr->bgap_dtemp_mask; + + reg_val = omap_bandgap_readl(bg_ptr, tsr->bgap_mask_ctrl); + if (temp < t_hot) + reg_val |= tsr->mask_hot_mask; + else + reg_val &= ~tsr->mask_hot_mask; + + if (t_cold < temp) + reg_val |= tsr->mask_cold_mask; + else + reg_val &= ~tsr->mask_cold_mask; + omap_bandgap_writel(bg_ptr, reg_val, tsr->bgap_mask_ctrl); + + return 0; +} + +static +int add_hyst(int adc_val, int hyst_val, struct omap_bandgap *bg_ptr, int i, + u32 *sum) +{ + int temp, ret; + + ret = adc_to_temp_conversion(bg_ptr, i, adc_val, &temp); + if (ret < 0) + return ret; + + temp += hyst_val; + + return temp_to_adc_conversion(temp, bg_ptr, i, sum); +} + +/* Talert Thot threshold. Call it only if HAS(TALERT) is set */ +static +int temp_sensor_configure_thot(struct omap_bandgap *bg_ptr, int id, int t_hot) +{ + struct temp_sensor_data *ts_data = bg_ptr->conf->sensors[id].ts_data; + struct temp_sensor_registers *tsr; + u32 thresh_val, reg_val; + int cold, err = 0; + + tsr = bg_ptr->conf->sensors[id].registers; + + /* obtain the T cold value */ + thresh_val = omap_bandgap_readl(bg_ptr, tsr->bgap_threshold); + cold = (thresh_val & tsr->threshold_tcold_mask) >> + __ffs(tsr->threshold_tcold_mask); + if (t_hot <= cold) { + /* change the t_cold to t_hot - 5000 millidegrees */ + err |= add_hyst(t_hot, -ts_data->hyst_val, bg_ptr, id, &cold); + /* write the new t_cold value */ + reg_val = thresh_val & (~tsr->threshold_tcold_mask); + reg_val |= cold << __ffs(tsr->threshold_tcold_mask); + omap_bandgap_writel(bg_ptr, reg_val, tsr->bgap_threshold); + thresh_val = reg_val; + } + + /* write the new t_hot value */ + reg_val = thresh_val & ~tsr->threshold_thot_mask; + reg_val |= (t_hot << __ffs(tsr->threshold_thot_mask)); + omap_bandgap_writel(bg_ptr, reg_val, tsr->bgap_threshold); + if (err) { + dev_err(bg_ptr->dev, "failed to reprogram thot threshold\n"); + return -EIO; + } + + return temp_sensor_unmask_interrupts(bg_ptr, id, t_hot, cold); +} + +/* Talert Thot and Tcold thresholds. Call it only if HAS(TALERT) is set */ +static +int temp_sensor_init_talert_thresholds(struct omap_bandgap *bg_ptr, int id, + int t_hot, int t_cold) +{ + struct temp_sensor_registers *tsr; + u32 reg_val, thresh_val; + + tsr = bg_ptr->conf->sensors[id].registers; + thresh_val = omap_bandgap_readl(bg_ptr, tsr->bgap_threshold); + + /* write the new t_cold value */ + reg_val = thresh_val & ~tsr->threshold_tcold_mask; + reg_val |= (t_cold << __ffs(tsr->threshold_tcold_mask)); + omap_bandgap_writel(bg_ptr, reg_val, tsr->bgap_threshold); + + thresh_val = omap_bandgap_readl(bg_ptr, tsr->bgap_threshold); + + /* write the new t_hot value */ + reg_val = thresh_val & ~tsr->threshold_thot_mask; + reg_val |= (t_hot << __ffs(tsr->threshold_thot_mask)); + omap_bandgap_writel(bg_ptr, reg_val, tsr->bgap_threshold); + + reg_val = omap_bandgap_readl(bg_ptr, tsr->bgap_mask_ctrl); + reg_val |= tsr->mask_hot_mask; + reg_val |= tsr->mask_cold_mask; + omap_bandgap_writel(bg_ptr, reg_val, tsr->bgap_mask_ctrl); + + return 0; +} + +/* Talert Tcold threshold. Call it only if HAS(TALERT) is set */ +static +int temp_sensor_configure_tcold(struct omap_bandgap *bg_ptr, int id, + int t_cold) +{ + struct temp_sensor_data *ts_data = bg_ptr->conf->sensors[id].ts_data; + struct temp_sensor_registers *tsr; + u32 thresh_val, reg_val; + int hot, err = 0; + + tsr = bg_ptr->conf->sensors[id].registers; + /* obtain the T cold value */ + thresh_val = omap_bandgap_readl(bg_ptr, tsr->bgap_threshold); + hot = (thresh_val & tsr->threshold_thot_mask) >> + __ffs(tsr->threshold_thot_mask); + + if (t_cold >= hot) { + /* change the t_hot to t_cold + 5000 millidegrees */ + err |= add_hyst(t_cold, ts_data->hyst_val, bg_ptr, id, &hot); + /* write the new t_hot value */ + reg_val = thresh_val & (~tsr->threshold_thot_mask); + reg_val |= hot << __ffs(tsr->threshold_thot_mask); + omap_bandgap_writel(bg_ptr, reg_val, tsr->bgap_threshold); + thresh_val = reg_val; + } + + /* write the new t_cold value */ + reg_val = thresh_val & ~tsr->threshold_tcold_mask; + reg_val |= (t_cold << __ffs(tsr->threshold_tcold_mask)); + omap_bandgap_writel(bg_ptr, reg_val, tsr->bgap_threshold); + if (err) { + dev_err(bg_ptr->dev, "failed to reprogram tcold threshold\n"); + return -EIO; + } + + return temp_sensor_unmask_interrupts(bg_ptr, id, hot, t_cold); +} + +/* This is Tshut Thot config. Call it only if HAS(TSHUT_CONFIG) is set */ +static int temp_sensor_configure_tshut_hot(struct omap_bandgap *bg_ptr, + int id, int tshut_hot) +{ + struct temp_sensor_registers *tsr; + u32 reg_val; + + tsr = bg_ptr->conf->sensors[id].registers; + reg_val = omap_bandgap_readl(bg_ptr, tsr->tshut_threshold); + reg_val &= ~tsr->tshut_hot_mask; + reg_val |= tshut_hot << __ffs(tsr->tshut_hot_mask); + omap_bandgap_writel(bg_ptr, reg_val, tsr->tshut_threshold); + + return 0; +} + +/* This is Tshut Tcold config. Call it only if HAS(TSHUT_CONFIG) is set */ +static int temp_sensor_configure_tshut_cold(struct omap_bandgap *bg_ptr, + int id, int tshut_cold) +{ + struct temp_sensor_registers *tsr; + u32 reg_val; + + tsr = bg_ptr->conf->sensors[id].registers; + reg_val = omap_bandgap_readl(bg_ptr, tsr->tshut_threshold); + reg_val &= ~tsr->tshut_cold_mask; + reg_val |= tshut_cold << __ffs(tsr->tshut_cold_mask); + omap_bandgap_writel(bg_ptr, reg_val, tsr->tshut_threshold); + + return 0; +} + +/* This is counter config. Call it only if HAS(COUNTER) is set */ +static int configure_temp_sensor_counter(struct omap_bandgap *bg_ptr, int id, + u32 counter) +{ + struct temp_sensor_registers *tsr; + u32 val; + + tsr = bg_ptr->conf->sensors[id].registers; + val = omap_bandgap_readl(bg_ptr, tsr->bgap_counter); + val &= ~tsr->counter_mask; + val |= counter << __ffs(tsr->counter_mask); + omap_bandgap_writel(bg_ptr, val, tsr->bgap_counter); + + return 0; +} + +#define bandgap_is_valid(b) \ + (!IS_ERR_OR_NULL(b)) +#define bandgap_is_valid_sensor_id(b, i) \ + ((i) >= 0 && (i) < (b)->conf->sensor_count) +static inline int omap_bandgap_validate(struct omap_bandgap *bg_ptr, int id) +{ + if (!bandgap_is_valid(bg_ptr)) { + pr_err("%s: invalid bandgap pointer\n", __func__); + return -EINVAL; + } + + if (!bandgap_is_valid_sensor_id(bg_ptr, id)) { + dev_err(bg_ptr->dev, "%s: sensor id out of range (%d)\n", + __func__, id); + return -ERANGE; + } + + return 0; +} + +/* Exposed APIs */ +/** + * omap_bandgap_read_thot() - reads sensor current thot + * @bg_ptr - pointer to bandgap instance + * @id - sensor id + * @thot - resulting current thot value + * + * returns 0 on success or the proper error code + */ +int omap_bandgap_read_thot(struct omap_bandgap *bg_ptr, int id, + int *thot) +{ + struct temp_sensor_registers *tsr; + u32 temp; + int ret; + + ret = omap_bandgap_validate(bg_ptr, id); + if (ret) + return ret; + + if (!OMAP_BANDGAP_HAS(bg_ptr, TALERT)) + return -ENOTSUPP; + + tsr = bg_ptr->conf->sensors[id].registers; + temp = omap_bandgap_readl(bg_ptr, tsr->bgap_threshold); + temp = (temp & tsr->threshold_thot_mask) >> + __ffs(tsr->threshold_thot_mask); + ret |= adc_to_temp_conversion(bg_ptr, id, temp, &temp); + if (ret) { + dev_err(bg_ptr->dev, "failed to read thot\n"); + return -EIO; + } + + *thot = temp; + + return 0; +} + +/** + * omap_bandgap_write_thot() - sets sensor current thot + * @bg_ptr - pointer to bandgap instance + * @id - sensor id + * @val - desired thot value + * + * returns 0 on success or the proper error code + */ +int omap_bandgap_write_thot(struct omap_bandgap *bg_ptr, int id, int val) +{ + struct temp_sensor_data *ts_data; + struct temp_sensor_registers *tsr; + u32 t_hot; + int ret; + + ret = omap_bandgap_validate(bg_ptr, id); + if (ret) + return ret; + + if (!OMAP_BANDGAP_HAS(bg_ptr, TALERT)) + return -ENOTSUPP; + + ts_data = bg_ptr->conf->sensors[id].ts_data; + tsr = bg_ptr->conf->sensors[id].registers; + + if (val < ts_data->min_temp + ts_data->hyst_val) + return -EINVAL; + ret = temp_to_adc_conversion(val, bg_ptr, id, &t_hot); + if (ret < 0) + return ret; + + mutex_lock(&bg_ptr->bg_mutex); + temp_sensor_configure_thot(bg_ptr, id, t_hot); + mutex_unlock(&bg_ptr->bg_mutex); + + return 0; +} + +/** + * omap_bandgap_read_tcold() - reads sensor current tcold + * @bg_ptr - pointer to bandgap instance + * @id - sensor id + * @tcold - resulting current tcold value + * + * returns 0 on success or the proper error code + */ +int omap_bandgap_read_tcold(struct omap_bandgap *bg_ptr, int id, + int *tcold) +{ + struct temp_sensor_registers *tsr; + u32 temp; + int ret; + + ret = omap_bandgap_validate(bg_ptr, id); + if (ret) + return ret; + + if (!OMAP_BANDGAP_HAS(bg_ptr, TALERT)) + return -ENOTSUPP; + + tsr = bg_ptr->conf->sensors[id].registers; + temp = omap_bandgap_readl(bg_ptr, tsr->bgap_threshold); + temp = (temp & tsr->threshold_tcold_mask) + >> __ffs(tsr->threshold_tcold_mask); + ret |= adc_to_temp_conversion(bg_ptr, id, temp, &temp); + if (ret) + return -EIO; + + *tcold = temp; + + return 0; +} + +/** + * omap_bandgap_write_tcold() - sets the sensor tcold + * @bg_ptr - pointer to bandgap instance + * @id - sensor id + * @val - desired tcold value + * + * returns 0 on success or the proper error code + */ +int omap_bandgap_write_tcold(struct omap_bandgap *bg_ptr, int id, int val) +{ + struct temp_sensor_data *ts_data; + struct temp_sensor_registers *tsr; + u32 t_cold; + int ret; + + ret = omap_bandgap_validate(bg_ptr, id); + if (ret) + return ret; + + if (!OMAP_BANDGAP_HAS(bg_ptr, TALERT)) + return -ENOTSUPP; + + ts_data = bg_ptr->conf->sensors[id].ts_data; + tsr = bg_ptr->conf->sensors[id].registers; + if (val > ts_data->max_temp + ts_data->hyst_val) + return -EINVAL; + + ret = temp_to_adc_conversion(val, bg_ptr, id, &t_cold); + if (ret < 0) + return ret; + + mutex_lock(&bg_ptr->bg_mutex); + temp_sensor_configure_tcold(bg_ptr, id, t_cold); + mutex_unlock(&bg_ptr->bg_mutex); + + return 0; +} + +/** + * omap_bandgap_read_update_interval() - read the sensor update interval + * @bg_ptr - pointer to bandgap instance + * @id - sensor id + * @interval - resulting update interval in miliseconds + * + * returns 0 on success or the proper error code + */ +int omap_bandgap_read_update_interval(struct omap_bandgap *bg_ptr, int id, + int *interval) +{ + struct temp_sensor_registers *tsr; + u32 time; + int ret; + + ret = omap_bandgap_validate(bg_ptr, id); + if (ret) + return ret; + + if (!OMAP_BANDGAP_HAS(bg_ptr, COUNTER)) + return -ENOTSUPP; + + tsr = bg_ptr->conf->sensors[id].registers; + time = omap_bandgap_readl(bg_ptr, tsr->bgap_counter); + if (ret) + return ret; + time = (time & tsr->counter_mask) >> __ffs(tsr->counter_mask); + time = time * 1000 / bg_ptr->clk_rate; + + *interval = time; + + return 0; +} + +/** + * omap_bandgap_write_update_interval() - set the update interval + * @bg_ptr - pointer to bandgap instance + * @id - sensor id + * @interval - desired update interval in miliseconds + * + * returns 0 on success or the proper error code + */ +int omap_bandgap_write_update_interval(struct omap_bandgap *bg_ptr, + int id, u32 interval) +{ + int ret = omap_bandgap_validate(bg_ptr, id); + if (ret) + return ret; + + if (!OMAP_BANDGAP_HAS(bg_ptr, COUNTER)) + return -ENOTSUPP; + + interval = interval * bg_ptr->clk_rate / 1000; + mutex_lock(&bg_ptr->bg_mutex); + configure_temp_sensor_counter(bg_ptr, id, interval); + mutex_unlock(&bg_ptr->bg_mutex); + + return 0; +} + +/** + * omap_bandgap_read_temperature() - report current temperature + * @bg_ptr - pointer to bandgap instance + * @id - sensor id + * @temperature - resulting temperature + * + * returns 0 on success or the proper error code + */ +int omap_bandgap_read_temperature(struct omap_bandgap *bg_ptr, int id, + int *temperature) +{ + struct temp_sensor_registers *tsr; + u32 temp; + int ret; + + ret = omap_bandgap_validate(bg_ptr, id); + if (ret) + return ret; + + tsr = bg_ptr->conf->sensors[id].registers; + temp = omap_bandgap_readl(bg_ptr, tsr->temp_sensor_ctrl); + temp &= tsr->bgap_dtemp_mask; + + ret |= adc_to_temp_conversion(bg_ptr, id, temp, &temp); + if (ret) + return -EIO; + + *temperature = temp; + + return 0; +} + +/** + * omap_bandgap_set_sensor_data() - helper function to store thermal + * framework related data. + * @bg_ptr - pointer to bandgap instance + * @id - sensor id + * @data - thermal framework related data to be stored + * + * returns 0 on success or the proper error code + */ +int omap_bandgap_set_sensor_data(struct omap_bandgap *bg_ptr, int id, + void *data) +{ + int ret = omap_bandgap_validate(bg_ptr, id); + if (ret) + return ret; + + bg_ptr->conf->sensors[id].data = data; + + return 0; +} + +/** + * omap_bandgap_get_sensor_data() - helper function to get thermal + * framework related data. + * @bg_ptr - pointer to bandgap instance + * @id - sensor id + * + * returns data stored by set function with sensor id on success or NULL + */ +void *omap_bandgap_get_sensor_data(struct omap_bandgap *bg_ptr, int id) +{ + int ret = omap_bandgap_validate(bg_ptr, id); + if (ret) + return ERR_PTR(ret); + + return bg_ptr->conf->sensors[id].data; +} + +static int +omap_bandgap_force_single_read(struct omap_bandgap *bg_ptr, int id) +{ + struct temp_sensor_registers *tsr; + u32 temp = 0, counter = 1000; + + tsr = bg_ptr->conf->sensors[id].registers; + /* Select single conversion mode */ + if (OMAP_BANDGAP_HAS(bg_ptr, MODE_CONFIG)) { + temp = omap_bandgap_readl(bg_ptr, tsr->bgap_mode_ctrl); + temp &= ~(1 << __ffs(tsr->mode_ctrl_mask)); + omap_bandgap_writel(bg_ptr, temp, tsr->bgap_mode_ctrl); + } + + /* Start of Conversion = 1 */ + temp = omap_bandgap_readl(bg_ptr, tsr->temp_sensor_ctrl); + temp |= 1 << __ffs(tsr->bgap_soc_mask); + omap_bandgap_writel(bg_ptr, temp, tsr->temp_sensor_ctrl); + /* Wait until DTEMP is updated */ + temp = omap_bandgap_readl(bg_ptr, tsr->temp_sensor_ctrl); + temp &= (tsr->bgap_dtemp_mask); + while ((temp == 0) && --counter) { + temp = omap_bandgap_readl(bg_ptr, tsr->temp_sensor_ctrl); + temp &= (tsr->bgap_dtemp_mask); + } + /* Start of Conversion = 0 */ + temp = omap_bandgap_readl(bg_ptr, tsr->temp_sensor_ctrl); + temp &= ~(1 << __ffs(tsr->bgap_soc_mask)); + omap_bandgap_writel(bg_ptr, temp, tsr->temp_sensor_ctrl); + + return 0; +} + +/** + * enable_continuous_mode() - One time enabling of continuous conversion mode + * @bg_ptr - pointer to scm instance + * + * Call this function only if HAS(MODE_CONFIG) is set + */ +static int enable_continuous_mode(struct omap_bandgap *bg_ptr) +{ + struct temp_sensor_registers *tsr; + int i; + u32 val; + + for (i = 0; i < bg_ptr->conf->sensor_count; i++) { + /* Perform a single read just before enabling continuous */ + omap_bandgap_force_single_read(bg_ptr, i); + tsr = bg_ptr->conf->sensors[i].registers; + val = omap_bandgap_readl(bg_ptr, tsr->bgap_mode_ctrl); + val |= 1 << __ffs(tsr->mode_ctrl_mask); + omap_bandgap_writel(bg_ptr, val, tsr->bgap_mode_ctrl); + } + + return 0; +} + +static int omap_bandgap_tshut_init(struct omap_bandgap *bg_ptr, + struct platform_device *pdev) +{ + int gpio_nr = bg_ptr->tshut_gpio; + int status; + + /* Request for gpio_86 line */ + status = gpio_request(gpio_nr, "tshut"); + if (status < 0) { + dev_err(bg_ptr->dev, + "Could not request for TSHUT GPIO:%i\n", 86); + return status; + } + status = gpio_direction_input(gpio_nr); + if (status) { + dev_err(bg_ptr->dev, + "Cannot set input TSHUT GPIO %d\n", gpio_nr); + return status; + } + + status = request_irq(gpio_to_irq(gpio_nr), + omap_bandgap_tshut_irq_handler, + IRQF_TRIGGER_RISING, "tshut", + NULL); + if (status) { + gpio_free(gpio_nr); + dev_err(bg_ptr->dev, "request irq failed for TSHUT"); + } + + return 0; +} + +/* Initialization of Talert. Call it only if HAS(TALERT) is set */ +static int omap_bandgap_talert_init(struct omap_bandgap *bg_ptr, + struct platform_device *pdev) +{ + int ret; + + bg_ptr->irq = platform_get_irq(pdev, 0); + if (bg_ptr->irq < 0) { + dev_err(&pdev->dev, "get_irq failed\n"); + return bg_ptr->irq; + } + ret = request_threaded_irq(bg_ptr->irq, NULL, + talert_irq_handler, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + "talert", bg_ptr); + if (ret) { + dev_err(&pdev->dev, "Request threaded irq failed.\n"); + return ret; + } + + return 0; +} + +static const struct of_device_id of_omap_bandgap_match[]; +static struct omap_bandgap *omap_bandgap_build(struct platform_device *pdev) +{ + struct device_node *node = pdev->dev.of_node; + const struct of_device_id *of_id; + struct omap_bandgap *bg_ptr; + struct resource *res; + u32 prop; + int i; + + /* just for the sake */ + if (!node) { + dev_err(&pdev->dev, "no platform information available\n"); + return ERR_PTR(-EINVAL); + } + + bg_ptr = devm_kzalloc(&pdev->dev, sizeof(struct omap_bandgap), + GFP_KERNEL); + if (!bg_ptr) { + dev_err(&pdev->dev, "Unable to allocate mem for driver ref\n"); + return ERR_PTR(-ENOMEM); + } + + of_id = of_match_device(of_omap_bandgap_match, &pdev->dev); + if (of_id) + bg_ptr->conf = of_id->data; + + i = 0; + do { + void __iomem *chunk; + + res = platform_get_resource(pdev, IORESOURCE_MEM, i); + if (!res) + break; + chunk = devm_request_and_ioremap(&pdev->dev, res); + if (i == 0) + bg_ptr->base = chunk; + if (!chunk) { + dev_err(&pdev->dev, + "failed to request the IO (%d:%pR).\n", + i, res); + return ERR_PTR(-EADDRNOTAVAIL); + } + i++; + } while (res); + + if (OMAP_BANDGAP_HAS(bg_ptr, TSHUT)) { + if (of_property_read_u32(node, "ti,tshut-gpio", &prop) < 0) { + dev_err(&pdev->dev, "missing tshut gpio in device tree\n"); + return ERR_PTR(-EINVAL); + } + bg_ptr->tshut_gpio = prop; + if (!gpio_is_valid(bg_ptr->tshut_gpio)) { + dev_err(&pdev->dev, "invalid gpio for tshut (%d)\n", + bg_ptr->tshut_gpio); + return ERR_PTR(-EINVAL); + } + } + + return bg_ptr; +} + +static +int __devinit omap_bandgap_probe(struct platform_device *pdev) +{ + struct omap_bandgap *bg_ptr; + int clk_rate, ret = 0, i; + + bg_ptr = omap_bandgap_build(pdev); + if (IS_ERR_OR_NULL(bg_ptr)) { + dev_err(&pdev->dev, "failed to fetch platform data\n"); + return PTR_ERR(bg_ptr); + } + bg_ptr->dev = &pdev->dev; + + if (OMAP_BANDGAP_HAS(bg_ptr, TSHUT)) { + ret = omap_bandgap_tshut_init(bg_ptr, pdev); + if (ret) { + dev_err(&pdev->dev, + "failed to initialize system tshut IRQ\n"); + return ret; + } + } + + bg_ptr->fclock = clk_get(NULL, bg_ptr->conf->fclock_name); + ret = IS_ERR_OR_NULL(bg_ptr->fclock); + if (ret) { + dev_err(&pdev->dev, "failed to request fclock reference\n"); + goto free_irqs; + } + + bg_ptr->div_clk = clk_get(NULL, bg_ptr->conf->div_ck_name); + ret = IS_ERR_OR_NULL(bg_ptr->div_clk); + if (ret) { + dev_err(&pdev->dev, + "failed to request div_ts_ck clock ref\n"); + goto free_irqs; + } + + bg_ptr->conv_table = bg_ptr->conf->conv_table; + for (i = 0; i < bg_ptr->conf->sensor_count; i++) { + struct temp_sensor_registers *tsr; + u32 val; + + tsr = bg_ptr->conf->sensors[i].registers; + /* + * check if the efuse has a non-zero value if not + * it is an untrimmed sample and the temperatures + * may not be accurate + */ + val = omap_bandgap_readl(bg_ptr, tsr->bgap_efuse); + if (ret || !val) + dev_info(&pdev->dev, + "Non-trimmed BGAP, Temp not accurate\n"); + } + + clk_rate = clk_round_rate(bg_ptr->div_clk, + bg_ptr->conf->sensors[0].ts_data->max_freq); + if (clk_rate < bg_ptr->conf->sensors[0].ts_data->min_freq || + clk_rate == 0xffffffff) { + ret = -ENODEV; + dev_err(&pdev->dev, "wrong clock rate (%d)\n", clk_rate); + goto put_clks; + } + + ret = clk_set_rate(bg_ptr->div_clk, clk_rate); + if (ret) + dev_err(&pdev->dev, "Cannot re-set clock rate. Continuing\n"); + + bg_ptr->clk_rate = clk_rate; + clk_enable(bg_ptr->fclock); + + mutex_init(&bg_ptr->bg_mutex); + bg_ptr->dev = &pdev->dev; + platform_set_drvdata(pdev, bg_ptr); + + omap_bandgap_power(bg_ptr, true); + + /* Set default counter to 1 for now */ + if (OMAP_BANDGAP_HAS(bg_ptr, COUNTER)) + for (i = 0; i < bg_ptr->conf->sensor_count; i++) + configure_temp_sensor_counter(bg_ptr, i, 1); + + for (i = 0; i < bg_ptr->conf->sensor_count; i++) { + struct temp_sensor_data *ts_data; + + ts_data = bg_ptr->conf->sensors[i].ts_data; + + if (OMAP_BANDGAP_HAS(bg_ptr, TALERT)) + temp_sensor_init_talert_thresholds(bg_ptr, i, + ts_data->t_hot, + ts_data->t_cold); + if (OMAP_BANDGAP_HAS(bg_ptr, TSHUT_CONFIG)) { + temp_sensor_configure_tshut_hot(bg_ptr, i, + ts_data->tshut_hot); + temp_sensor_configure_tshut_cold(bg_ptr, i, + ts_data->tshut_cold); + } + } + + if (OMAP_BANDGAP_HAS(bg_ptr, MODE_CONFIG)) + enable_continuous_mode(bg_ptr); + + /* Set .250 seconds time as default counter */ + if (OMAP_BANDGAP_HAS(bg_ptr, COUNTER)) + for (i = 0; i < bg_ptr->conf->sensor_count; i++) + configure_temp_sensor_counter(bg_ptr, i, + bg_ptr->clk_rate / 4); + + /* Every thing is good? Then expose the sensors */ + for (i = 0; i < bg_ptr->conf->sensor_count; i++) { + char *domain; + + domain = bg_ptr->conf->sensors[i].domain; + if (bg_ptr->conf->expose_sensor) + bg_ptr->conf->expose_sensor(bg_ptr, i, domain); + + if (bg_ptr->conf->sensors[i].register_cooling) + bg_ptr->conf->sensors[i].register_cooling(bg_ptr, i); + } + + /* + * Enable the Interrupts once everything is set. Otherwise irq handler + * might be called as soon as it is enabled where as rest of framework + * is still getting initialised. + */ + if (OMAP_BANDGAP_HAS(bg_ptr, TALERT)) { + ret = omap_bandgap_talert_init(bg_ptr, pdev); + if (ret) { + dev_err(&pdev->dev, "failed to initialize Talert IRQ\n"); + i = bg_ptr->conf->sensor_count; + goto disable_clk; + } + } + + return 0; + +disable_clk: + clk_disable(bg_ptr->fclock); +put_clks: + clk_put(bg_ptr->fclock); + clk_put(bg_ptr->div_clk); +free_irqs: + if (OMAP_BANDGAP_HAS(bg_ptr, TSHUT)) { + free_irq(gpio_to_irq(bg_ptr->tshut_gpio), NULL); + gpio_free(bg_ptr->tshut_gpio); + } + + return ret; +} + +static +int __devexit omap_bandgap_remove(struct platform_device *pdev) +{ + struct omap_bandgap *bg_ptr = platform_get_drvdata(pdev); + int i; + + /* First thing is to remove sensor interfaces */ + for (i = 0; i < bg_ptr->conf->sensor_count; i++) { + if (bg_ptr->conf->sensors[i].register_cooling) + bg_ptr->conf->sensors[i].unregister_cooling(bg_ptr, i); + + if (bg_ptr->conf->remove_sensor) + bg_ptr->conf->remove_sensor(bg_ptr, i); + } + + omap_bandgap_power(bg_ptr, false); + + clk_disable(bg_ptr->fclock); + clk_put(bg_ptr->fclock); + clk_put(bg_ptr->div_clk); + + if (OMAP_BANDGAP_HAS(bg_ptr, TALERT)) + free_irq(bg_ptr->irq, bg_ptr); + + if (OMAP_BANDGAP_HAS(bg_ptr, TSHUT)) { + free_irq(gpio_to_irq(bg_ptr->tshut_gpio), NULL); + gpio_free(bg_ptr->tshut_gpio); + } + + return 0; +} + +#ifdef CONFIG_PM +static int omap_bandgap_save_ctxt(struct omap_bandgap *bg_ptr) +{ + int i; + + for (i = 0; i < bg_ptr->conf->sensor_count; i++) { + struct temp_sensor_registers *tsr; + struct temp_sensor_regval *rval; + + rval = &bg_ptr->conf->sensors[i].regval; + tsr = bg_ptr->conf->sensors[i].registers; + + if (OMAP_BANDGAP_HAS(bg_ptr, MODE_CONFIG)) + rval->bg_mode_ctrl = omap_bandgap_readl(bg_ptr, + tsr->bgap_mode_ctrl); + if (OMAP_BANDGAP_HAS(bg_ptr, COUNTER)) + rval->bg_counter = omap_bandgap_readl(bg_ptr, + tsr->bgap_counter); + if (OMAP_BANDGAP_HAS(bg_ptr, TALERT)) { + rval->bg_threshold = omap_bandgap_readl(bg_ptr, + tsr->bgap_threshold); + rval->bg_ctrl = omap_bandgap_readl(bg_ptr, + tsr->bgap_mask_ctrl); + } + + if (OMAP_BANDGAP_HAS(bg_ptr, TSHUT_CONFIG)) + rval->tshut_threshold = omap_bandgap_readl(bg_ptr, + tsr->tshut_threshold); + } + + return 0; +} + +static int omap_bandgap_restore_ctxt(struct omap_bandgap *bg_ptr) +{ + int i; + u32 temp = 0; + + for (i = 0; i < bg_ptr->conf->sensor_count; i++) { + struct temp_sensor_registers *tsr; + struct temp_sensor_regval *rval; + u32 val = 0; + + rval = &bg_ptr->conf->sensors[i].regval; + tsr = bg_ptr->conf->sensors[i].registers; + + if (OMAP_BANDGAP_HAS(bg_ptr, COUNTER)) + val = omap_bandgap_readl(bg_ptr, tsr->bgap_counter); + + if (val == 0) { + if (OMAP_BANDGAP_HAS(bg_ptr, TSHUT_CONFIG)) + omap_bandgap_writel(bg_ptr, rval->tshut_threshold, + tsr->tshut_threshold); + /* Force immediate temperature measurement and update + * of the DTEMP field + */ + omap_bandgap_force_single_read(bg_ptr, i); + + if (OMAP_BANDGAP_HAS(bg_ptr, COUNTER)) + omap_bandgap_writel(bg_ptr, rval->bg_counter, + tsr->bgap_counter); + if (OMAP_BANDGAP_HAS(bg_ptr, MODE_CONFIG)) + omap_bandgap_writel(bg_ptr, rval->bg_mode_ctrl, + tsr->bgap_mode_ctrl); + if (OMAP_BANDGAP_HAS(bg_ptr, TALERT)) { + omap_bandgap_writel(bg_ptr, + rval->bg_threshold, + tsr->bgap_threshold); + omap_bandgap_writel(bg_ptr, rval->bg_ctrl, + tsr->bgap_mask_ctrl); + } + } else { + temp = omap_bandgap_readl(bg_ptr, + tsr->temp_sensor_ctrl); + temp &= (tsr->bgap_dtemp_mask); + omap_bandgap_force_single_read(bg_ptr, i); + if (temp == 0 && OMAP_BANDGAP_HAS(bg_ptr, TALERT)) { + temp = omap_bandgap_readl(bg_ptr, + tsr->bgap_mask_ctrl); + temp |= 1 << __ffs(tsr->mode_ctrl_mask); + omap_bandgap_writel(bg_ptr, temp, + tsr->bgap_mask_ctrl); + } + } + } + + return 0; +} + +static int omap_bandgap_suspend(struct device *dev) +{ + struct omap_bandgap *bg_ptr = dev_get_drvdata(dev); + int err; + + err = omap_bandgap_save_ctxt(bg_ptr); + omap_bandgap_power(bg_ptr, false); + clk_disable(bg_ptr->fclock); + + return err; +} + +static int omap_bandgap_resume(struct device *dev) +{ + struct omap_bandgap *bg_ptr = dev_get_drvdata(dev); + + clk_enable(bg_ptr->fclock); + omap_bandgap_power(bg_ptr, true); + + return omap_bandgap_restore_ctxt(bg_ptr); +} +static const struct dev_pm_ops omap_bandgap_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(omap_bandgap_suspend, + omap_bandgap_resume) +}; + +#define DEV_PM_OPS (&omap_bandgap_dev_pm_ops) +#else +#define DEV_PM_OPS NULL +#endif + +static const struct of_device_id of_omap_bandgap_match[] = { +#ifdef CONFIG_OMAP4_THERMAL + { + .compatible = "ti,omap4430-bandgap", + .data = (void *)&omap4430_data, + }, + { + .compatible = "ti,omap4460-bandgap", + .data = (void *)&omap4460_data, + }, + { + .compatible = "ti,omap4470-bandgap", + .data = (void *)&omap4470_data, + }, +#endif +#ifdef CONFIG_OMAP5_THERMAL + { + .compatible = "ti,omap5430-bandgap", + .data = (void *)&omap5430_data, + }, +#endif + /* Sentinel */ + { }, +}; +MODULE_DEVICE_TABLE(of, of_omap_bandgap_match); + +static struct platform_driver omap_bandgap_sensor_driver = { + .probe = omap_bandgap_probe, + .remove = omap_bandgap_remove, + .driver = { + .name = "omap-bandgap", + .pm = DEV_PM_OPS, + .of_match_table = of_omap_bandgap_match, + }, +}; + +module_platform_driver(omap_bandgap_sensor_driver); + +MODULE_DESCRIPTION("OMAP4+ bandgap temperature sensor driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:omap-bandgap"); +MODULE_AUTHOR("Texas Instrument Inc."); diff --git a/drivers/staging/omap-thermal/omap-bandgap.h b/drivers/staging/omap-thermal/omap-bandgap.h new file mode 100644 index 0000000..78aed75 --- /dev/null +++ b/drivers/staging/omap-thermal/omap-bandgap.h @@ -0,0 +1,441 @@ +/* + * OMAP4 Bandgap temperature sensor driver + * + * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/ + * Contact: + * Eduardo Valentin <eduardo.valentin@ti.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ +#ifndef __OMAP_BANDGAP_H +#define __OMAP_BANDGAP_H + +#include <linux/mutex.h> +#include <linux/types.h> +#include <linux/err.h> + +/* TEMP_SENSOR OMAP4430 */ +#define OMAP4430_BGAP_TSHUT_SHIFT 11 +#define OMAP4430_BGAP_TSHUT_MASK (1 << 11) + +/* TEMP_SENSOR OMAP4430 */ +#define OMAP4430_BGAP_TEMPSOFF_SHIFT 12 +#define OMAP4430_BGAP_TEMPSOFF_MASK (1 << 12) +#define OMAP4430_SINGLE_MODE_SHIFT 10 +#define OMAP4430_SINGLE_MODE_MASK (1 << 10) +#define OMAP4430_BGAP_TEMP_SENSOR_SOC_SHIFT 9 +#define OMAP4430_BGAP_TEMP_SENSOR_SOC_MASK (1 << 9) +#define OMAP4430_BGAP_TEMP_SENSOR_EOCZ_SHIFT 8 +#define OMAP4430_BGAP_TEMP_SENSOR_EOCZ_MASK (1 << 8) +#define OMAP4430_BGAP_TEMP_SENSOR_DTEMP_SHIFT 0 +#define OMAP4430_BGAP_TEMP_SENSOR_DTEMP_MASK (0xff << 0) + +#define OMAP4430_ADC_START_VALUE 0 +#define OMAP4430_ADC_END_VALUE 127 +#define OMAP4430_MAX_FREQ 32768 +#define OMAP4430_MIN_FREQ 32768 +#define OMAP4430_MIN_TEMP -40000 +#define OMAP4430_MAX_TEMP 125000 +#define OMAP4430_HYST_VAL 5000 + +/* TEMP_SENSOR OMAP4460 */ +#define OMAP4460_BGAP_TEMPSOFF_SHIFT 13 +#define OMAP4460_BGAP_TEMPSOFF_MASK (1 << 13) +#define OMAP4460_BGAP_TEMP_SENSOR_SOC_SHIFT 11 +#define OMAP4460_BGAP_TEMP_SENSOR_SOC_MASK (1 << 11) +#define OMAP4460_BGAP_TEMP_SENSOR_EOCZ_SHIFT 10 +#define OMAP4460_BGAP_TEMP_SENSOR_EOCZ_MASK (1 << 10) +#define OMAP4460_BGAP_TEMP_SENSOR_DTEMP_SHIFT 0 +#define OMAP4460_BGAP_TEMP_SENSOR_DTEMP_MASK (0x3ff << 0) + +/* BANDGAP_CTRL */ +#define OMAP4460_SINGLE_MODE_SHIFT 31 +#define OMAP4460_SINGLE_MODE_MASK (1 << 31) +#define OMAP4460_MASK_HOT_SHIFT 1 +#define OMAP4460_MASK_HOT_MASK (1 << 1) +#define OMAP4460_MASK_COLD_SHIFT 0 +#define OMAP4460_MASK_COLD_MASK (1 << 0) + +/* BANDGAP_COUNTER */ +#define OMAP4460_COUNTER_SHIFT 0 +#define OMAP4460_COUNTER_MASK (0xffffff << 0) + +/* BANDGAP_THRESHOLD */ +#define OMAP4460_T_HOT_SHIFT 16 +#define OMAP4460_T_HOT_MASK (0x3ff << 16) +#define OMAP4460_T_COLD_SHIFT 0 +#define OMAP4460_T_COLD_MASK (0x3ff << 0) + +/* TSHUT_THRESHOLD */ +#define OMAP4460_TSHUT_HOT_SHIFT 16 +#define OMAP4460_TSHUT_HOT_MASK (0x3ff << 16) +#define OMAP4460_TSHUT_COLD_SHIFT 0 +#define OMAP4460_TSHUT_COLD_MASK (0x3ff << 0) + +/* BANDGAP_STATUS */ +#define OMAP4460_CLEAN_STOP_SHIFT 3 +#define OMAP4460_CLEAN_STOP_MASK (1 << 3) +#define OMAP4460_BGAP_ALERT_SHIFT 2 +#define OMAP4460_BGAP_ALERT_MASK (1 << 2) +#define OMAP4460_HOT_FLAG_SHIFT 1 +#define OMAP4460_HOT_FLAG_MASK (1 << 1) +#define OMAP4460_COLD_FLAG_SHIFT 0 +#define OMAP4460_COLD_FLAG_MASK (1 << 0) + +/* TEMP_SENSOR OMAP5430 */ +#define OMAP5430_BGAP_TEMP_SENSOR_SOC_SHIFT 12 +#define OMAP5430_BGAP_TEMP_SENSOR_SOC_MASK (1 << 12) +#define OMAP5430_BGAP_TEMPSOFF_SHIFT 11 +#define OMAP5430_BGAP_TEMPSOFF_MASK (1 << 11) +#define OMAP5430_BGAP_TEMP_SENSOR_EOCZ_SHIFT 10 +#define OMAP5430_BGAP_TEMP_SENSOR_EOCZ_MASK (1 << 10) +#define OMAP5430_BGAP_TEMP_SENSOR_DTEMP_SHIFT 0 +#define OMAP5430_BGAP_TEMP_SENSOR_DTEMP_MASK (0x3ff << 0) + +/* BANDGAP_CTRL */ +#define OMAP5430_MASK_HOT_CORE_SHIFT 5 +#define OMAP5430_MASK_HOT_CORE_MASK (1 << 5) +#define OMAP5430_MASK_COLD_CORE_SHIFT 4 +#define OMAP5430_MASK_COLD_CORE_MASK (1 << 4) +#define OMAP5430_MASK_HOT_MM_SHIFT 3 +#define OMAP5430_MASK_HOT_MM_MASK (1 << 3) +#define OMAP5430_MASK_COLD_MM_SHIFT 2 +#define OMAP5430_MASK_COLD_MM_MASK (1 << 2) +#define OMAP5430_MASK_HOT_MPU_SHIFT 1 +#define OMAP5430_MASK_HOT_MPU_MASK (1 << 1) +#define OMAP5430_MASK_COLD_MPU_SHIFT 0 +#define OMAP5430_MASK_COLD_MPU_MASK (1 << 0) + +/* BANDGAP_COUNTER */ +#define OMAP5430_REPEAT_MODE_SHIFT 31 +#define OMAP5430_REPEAT_MODE_MASK (1 << 31) +#define OMAP5430_COUNTER_SHIFT 0 +#define OMAP5430_COUNTER_MASK (0xffffff << 0) + +/* BANDGAP_THRESHOLD */ +#define OMAP5430_T_HOT_SHIFT 16 +#define OMAP5430_T_HOT_MASK (0x3ff << 16) +#define OMAP5430_T_COLD_SHIFT 0 +#define OMAP5430_T_COLD_MASK (0x3ff << 0) + +/* TSHUT_THRESHOLD */ +#define OMAP5430_TSHUT_HOT_SHIFT 16 +#define OMAP5430_TSHUT_HOT_MASK (0x3ff << 16) +#define OMAP5430_TSHUT_COLD_SHIFT 0 +#define OMAP5430_TSHUT_COLD_MASK (0x3ff << 0) + +/* BANDGAP_STATUS */ +#define OMAP5430_BGAP_ALERT_SHIFT 31 +#define OMAP5430_BGAP_ALERT_MASK (1 << 31) +#define OMAP5430_HOT_CORE_FLAG_SHIFT 5 +#define OMAP5430_HOT_CORE_FLAG_MASK (1 << 5) +#define OMAP5430_COLD_CORE_FLAG_SHIFT 4 +#define OMAP5430_COLD_CORE_FLAG_MASK (1 << 4) +#define OMAP5430_HOT_MM_FLAG_SHIFT 3 +#define OMAP5430_HOT_MM_FLAG_MASK (1 << 3) +#define OMAP5430_COLD_MM_FLAG_SHIFT 2 +#define OMAP5430_COLD_MM_FLAG_MASK (1 << 2) +#define OMAP5430_HOT_MPU_FLAG_SHIFT 1 +#define OMAP5430_HOT_MPU_FLAG_MASK (1 << 1) +#define OMAP5430_COLD_MPU_FLAG_SHIFT 0 +#define OMAP5430_COLD_MPU_FLAG_MASK (1 << 0) + +/* Offsets from the base of temperature sensor registers */ + +/* 4430 - All goes relative to OPP_BGAP */ +#define OMAP4430_FUSE_OPP_BGAP 0x0 +#define OMAP4430_TEMP_SENSOR_CTRL_OFFSET 0xCC + +/* 4460 - All goes relative to OPP_BGAP */ +#define OMAP4460_FUSE_OPP_BGAP 0x0 +#define OMAP4460_TEMP_SENSOR_CTRL_OFFSET 0xCC +#define OMAP4460_BGAP_CTRL_OFFSET 0x118 +#define OMAP4460_BGAP_COUNTER_OFFSET 0x11C +#define OMAP4460_BGAP_THRESHOLD_OFFSET 0x120 +#define OMAP4460_BGAP_TSHUT_OFFSET 0x124 +#define OMAP4460_BGAP_STATUS_OFFSET 0x128 + +/* 5430 - All goes relative to OPP_BGAP_GPU */ +#define OMAP5430_FUSE_OPP_BGAP_GPU 0x0 +#define OMAP5430_TEMP_SENSOR_GPU_OFFSET 0x150 +#define OMAP5430_BGAP_COUNTER_GPU_OFFSET 0x1C0 +#define OMAP5430_BGAP_THRESHOLD_GPU_OFFSET 0x1A8 +#define OMAP5430_BGAP_TSHUT_GPU_OFFSET 0x1B4 + +#define OMAP5430_FUSE_OPP_BGAP_MPU 0x4 +#define OMAP5430_TEMP_SENSOR_MPU_OFFSET 0x14C +#define OMAP5430_BGAP_CTRL_OFFSET 0x1A0 +#define OMAP5430_BGAP_COUNTER_MPU_OFFSET 0x1BC +#define OMAP5430_BGAP_THRESHOLD_MPU_OFFSET 0x1A4 +#define OMAP5430_BGAP_TSHUT_MPU_OFFSET 0x1B0 +#define OMAP5430_BGAP_STATUS_OFFSET 0x1C8 + +#define OMAP5430_FUSE_OPP_BGAP_CORE 0x8 +#define OMAP5430_TEMP_SENSOR_CORE_OFFSET 0x154 +#define OMAP5430_BGAP_COUNTER_CORE_OFFSET 0x1C4 +#define OMAP5430_BGAP_THRESHOLD_CORE_OFFSET 0x1AC +#define OMAP5430_BGAP_TSHUT_CORE_OFFSET 0x1B8 + +#define OMAP4460_TSHUT_HOT 900 /* 122 deg C */ +#define OMAP4460_TSHUT_COLD 895 /* 100 deg C */ +#define OMAP4460_T_HOT 800 /* 73 deg C */ +#define OMAP4460_T_COLD 795 /* 71 deg C */ +#define OMAP4460_MAX_FREQ 1500000 +#define OMAP4460_MIN_FREQ 1000000 +#define OMAP4460_MIN_TEMP -40000 +#define OMAP4460_MAX_TEMP 123000 +#define OMAP4460_HYST_VAL 5000 +#define OMAP4460_ADC_START_VALUE 530 +#define OMAP4460_ADC_END_VALUE 932 + +#define OMAP5430_MPU_TSHUT_HOT 915 +#define OMAP5430_MPU_TSHUT_COLD 900 +#define OMAP5430_MPU_T_HOT 800 +#define OMAP5430_MPU_T_COLD 795 +#define OMAP5430_MPU_MAX_FREQ 1500000 +#define OMAP5430_MPU_MIN_FREQ 1000000 +#define OMAP5430_MPU_MIN_TEMP -40000 +#define OMAP5430_MPU_MAX_TEMP 125000 +#define OMAP5430_MPU_HYST_VAL 5000 +#define OMAP5430_ADC_START_VALUE 532 +#define OMAP5430_ADC_END_VALUE 934 + + +#define OMAP5430_GPU_TSHUT_HOT 915 +#define OMAP5430_GPU_TSHUT_COLD 900 +#define OMAP5430_GPU_T_HOT 800 +#define OMAP5430_GPU_T_COLD 795 +#define OMAP5430_GPU_MAX_FREQ 1500000 +#define OMAP5430_GPU_MIN_FREQ 1000000 +#define OMAP5430_GPU_MIN_TEMP -40000 +#define OMAP5430_GPU_MAX_TEMP 125000 +#define OMAP5430_GPU_HYST_VAL 5000 + +#define OMAP5430_CORE_TSHUT_HOT 915 +#define OMAP5430_CORE_TSHUT_COLD 900 +#define OMAP5430_CORE_T_HOT 800 +#define OMAP5430_CORE_T_COLD 795 +#define OMAP5430_CORE_MAX_FREQ 1500000 +#define OMAP5430_CORE_MIN_FREQ 1000000 +#define OMAP5430_CORE_MIN_TEMP -40000 +#define OMAP5430_CORE_MAX_TEMP 125000 +#define OMAP5430_CORE_HYST_VAL 5000 + +/** + * The register offsets and bit fields might change across + * OMAP versions hence populating them in this structure. + */ + +struct temp_sensor_registers { + u32 temp_sensor_ctrl; + u32 bgap_tempsoff_mask; + u32 bgap_soc_mask; + u32 bgap_eocz_mask; + u32 bgap_dtemp_mask; + + u32 bgap_mask_ctrl; + u32 mask_hot_mask; + u32 mask_cold_mask; + + u32 bgap_mode_ctrl; + u32 mode_ctrl_mask; + + u32 bgap_counter; + u32 counter_mask; + + u32 bgap_threshold; + u32 threshold_thot_mask; + u32 threshold_tcold_mask; + + u32 tshut_threshold; + u32 tshut_hot_mask; + u32 tshut_cold_mask; + + u32 bgap_status; + u32 status_clean_stop_mask; + u32 status_bgap_alert_mask; + u32 status_hot_mask; + u32 status_cold_mask; + + u32 bgap_efuse; +}; + +/** + * The thresholds and limits for temperature sensors. + */ +struct temp_sensor_data { + u32 tshut_hot; + u32 tshut_cold; + u32 t_hot; + u32 t_cold; + u32 min_freq; + u32 max_freq; + int max_temp; + int min_temp; + int hyst_val; + u32 adc_start_val; + u32 adc_end_val; + u32 update_int1; + u32 update_int2; +}; + +struct omap_bandgap_data; + +/** + * struct omap_bandgap - bandgap device structure + * @dev: device pointer + * @conf: platform data with sensor data + * @fclock: pointer to functional clock of temperature sensor + * @div_clk: pointer to parent clock of temperature sensor fclk + * @conv_table: Pointer to adc to temperature conversion table + * @bg_mutex: Mutex for sysfs, irq and PM + * @irq: MPU Irq number for thermal alert + * @tshut_gpio: GPIO where Tshut signal is routed + * @clk_rate: Holds current clock rate + */ +struct omap_bandgap { + struct device *dev; + void __iomem *base; + struct omap_bandgap_data *conf; + struct clk *fclock; + struct clk *div_clk; + const int *conv_table; + struct mutex bg_mutex; /* Mutex for irq and PM */ + int irq; + int tshut_gpio; + u32 clk_rate; +}; + +/** + * struct temp_sensor_regval - temperature sensor register values + * @bg_mode_ctrl: temp sensor control register value + * @bg_ctrl: bandgap ctrl register value + * @bg_counter: bandgap counter value + * @bg_threshold: bandgap threshold register value + * @tshut_threshold: bandgap tshut register value + */ +struct temp_sensor_regval { + u32 bg_mode_ctrl; + u32 bg_ctrl; + u32 bg_counter; + u32 bg_threshold; + u32 tshut_threshold; +}; + +/** + * struct thermal_cooling_conf - description on how to cool a thermal zone + * @freq_clip_count: size of freq_data + */ +struct thermal_cooling_conf { + int freq_clip_count; +}; + +/** + * struct omap_temp_sensor - bandgap temperature sensor platform data + * @ts_data: pointer to struct with thresholds, limits of temperature sensor + * @registers: pointer to the list of register offsets and bitfields + * @regval: temperature sensor register values + * @domain: the name of the domain where the sensor is located + * @cooling_data: description on how the zone should be cooled off. + * @slope: sensor gradient slope info for hotspot extrapolation + * @const: sensor gradient const info for hotspot extrapolation + * @slope_pcb: sensor gradient slope info for hotspot extrapolation + * with no external influence + * @const_pcb: sensor gradient const info for hotspot extrapolation + * with no external influence + * @data: private data + * @register_cooling: function to describe how this sensor is going to be cooled + * @unregister_cooling: function to release cooling data + */ +struct omap_temp_sensor { + struct temp_sensor_data *ts_data; + struct temp_sensor_registers *registers; + struct temp_sensor_regval regval; + char *domain; + struct thermal_cooling_conf cooling_data; + /* for hotspot extrapolation */ + const int slope; + const int constant; + const int slope_pcb; + const int constant_pcb; + void *data; + int (*register_cooling)(struct omap_bandgap *bg_ptr, int id); + int (*unregister_cooling)(struct omap_bandgap *bg_ptr, int id); +}; + +/** + * struct omap_bandgap_data - bandgap platform data structure + * @features: a bitwise flag set to describe the device features + * @conv_table: Pointer to adc to temperature conversion table + * @fclock_name: clock name of the functional clock + * @div_ck_nme: clock name of the clock divisor + * @sensor_count: count of temperature sensor device in scm + * @sensors: array of sensors present in this bandgap instance + * @expose_sensor: callback to export sensor to thermal API + */ +struct omap_bandgap_data { +#define OMAP_BANDGAP_FEATURE_TSHUT (1 << 0) +#define OMAP_BANDGAP_FEATURE_TSHUT_CONFIG (1 << 1) +#define OMAP_BANDGAP_FEATURE_TALERT (1 << 2) +#define OMAP_BANDGAP_FEATURE_MODE_CONFIG (1 << 3) +#define OMAP_BANDGAP_FEATURE_COUNTER (1 << 4) +#define OMAP_BANDGAP_FEATURE_POWER_SWITCH (1 << 5) +#define OMAP_BANDGAP_HAS(b, f) \ + ((b)->conf->features & OMAP_BANDGAP_FEATURE_ ## f) + unsigned int features; + const int *conv_table; + char *fclock_name; + char *div_ck_name; + int sensor_count; + int (*report_temperature)(struct omap_bandgap *bg_ptr, int id); + int (*expose_sensor)(struct omap_bandgap *bg_ptr, int id, char *domain); + int (*remove_sensor)(struct omap_bandgap *bg_ptr, int id); + + /* this needs to be at the end */ + struct omap_temp_sensor sensors[]; +}; + +int omap_bandgap_read_thot(struct omap_bandgap *bg_ptr, int id, int *thot); +int omap_bandgap_write_thot(struct omap_bandgap *bg_ptr, int id, int val); +int omap_bandgap_read_tcold(struct omap_bandgap *bg_ptr, int id, int *tcold); +int omap_bandgap_write_tcold(struct omap_bandgap *bg_ptr, int id, int val); +int omap_bandgap_read_update_interval(struct omap_bandgap *bg_ptr, int id, + int *interval); +int omap_bandgap_write_update_interval(struct omap_bandgap *bg_ptr, int id, + u32 interval); +int omap_bandgap_read_temperature(struct omap_bandgap *bg_ptr, int id, + int *temperature); +int omap_bandgap_set_sensor_data(struct omap_bandgap *bg_ptr, int id, + void *data); +void *omap_bandgap_get_sensor_data(struct omap_bandgap *bg_ptr, int id); + +#ifdef CONFIG_OMAP4_THERMAL +extern const struct omap_bandgap_data omap4430_data; +extern const struct omap_bandgap_data omap4460_data; +extern const struct omap_bandgap_data omap4470_data; +#else +#define omap4430_data NULL +#define omap4460_data NULL +#define omap4470_data NULL +#endif + +#ifdef CONFIG_OMAP5_THERMAL +extern const struct omap_bandgap_data omap5430_data; +#else +#define omap5430_data NULL +#endif + +#endif diff --git a/drivers/staging/omap-thermal/omap-thermal-common.c b/drivers/staging/omap-thermal/omap-thermal-common.c new file mode 100644 index 0000000..0675a5e --- /dev/null +++ b/drivers/staging/omap-thermal/omap-thermal-common.c @@ -0,0 +1,364 @@ +/* + * OMAP thermal driver interface + * + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * Contact: + * Eduardo Valentin <eduardo.valentin@ti.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include <linux/device.h> +#include <linux/err.h> +#include <linux/mutex.h> +#include <linux/gfp.h> +#include <linux/kernel.h> +#include <linux/workqueue.h> +#include <linux/thermal.h> +#include <linux/cpufreq.h> +#include <linux/cpu_cooling.h> + +#include "omap-thermal.h" +#include "omap-bandgap.h" + +/* common data structures */ +struct omap_thermal_data { + struct thermal_zone_device *omap_thermal; + struct thermal_cooling_device *cool_dev; + struct omap_bandgap *bg_ptr; + enum thermal_device_mode mode; + struct work_struct thermal_wq; + int sensor_id; +}; + +static void omap_thermal_work(struct work_struct *work) +{ + struct omap_thermal_data *data = container_of(work, + struct omap_thermal_data, thermal_wq); + + thermal_zone_device_update(data->omap_thermal); + + dev_dbg(&data->omap_thermal->device, "updated thermal zone %s\n", + data->omap_thermal->type); +} + +/** + * omap_thermal_hotspot_temperature - returns sensor extrapolated temperature + * @t: omap sensor temperature + * @s: omap sensor slope value + * @c: omap sensor const value + */ +static inline int omap_thermal_hotspot_temperature(int t, int s, int c) +{ + int delta = t * s / 1000 + c; + + if (delta < 0) + delta = 0; + + return t + delta; +} + +/* thermal zone ops */ +/* Get temperature callback function for thermal zone*/ +static inline int omap_thermal_get_temp(struct thermal_zone_device *thermal, + unsigned long *temp) +{ + struct omap_thermal_data *data = thermal->devdata; + struct omap_bandgap *bg_ptr = data->bg_ptr; + struct omap_temp_sensor *s = &bg_ptr->conf->sensors[data->sensor_id]; + int ret, tmp, pcb_temp, slope, constant; + + ret = omap_bandgap_read_temperature(bg_ptr, data->sensor_id, &tmp); + if (ret) + return ret; + + pcb_temp = 0; + /* TODO: Introduce pcb temperature lookup */ + /* In case pcb zone is available, use the extrapolation rule with it */ + if (pcb_temp) { + tmp -= pcb_temp; + slope = s->slope_pcb; + constant = s->constant_pcb; + } else { + slope = s->slope; + constant = s->constant; + } + *temp = omap_thermal_hotspot_temperature(tmp, slope, constant); + + return ret; +} + +/* Bind callback functions for thermal zone */ +static int omap_thermal_bind(struct thermal_zone_device *thermal, + struct thermal_cooling_device *cdev) +{ + struct omap_thermal_data *data = thermal->devdata; + int max, id; + + if (IS_ERR_OR_NULL(data)) + return -ENODEV; + + /* check if this is the cooling device we registered */ + if (data->cool_dev != cdev) + return 0; + + id = data->sensor_id; + max = data->bg_ptr->conf->sensors[id].cooling_data.freq_clip_count; + + /* TODO: bind with min and max states */ + /* Simple thing, two trips, one passive another critical */ + return thermal_zone_bind_cooling_device(thermal, 0, cdev); +} + +/* Unbind callback functions for thermal zone */ +static int omap_thermal_unbind(struct thermal_zone_device *thermal, + struct thermal_cooling_device *cdev) +{ + struct omap_thermal_data *data = thermal->devdata; + + if (IS_ERR_OR_NULL(data)) + return -ENODEV; + + /* check if this is the cooling device we registered */ + if (data->cool_dev != cdev) + return 0; + + /* Simple thing, two trips, one passive another critical */ + return thermal_zone_unbind_cooling_device(thermal, 0, cdev); +} + +/* Get mode callback functions for thermal zone */ +static int omap_thermal_get_mode(struct thermal_zone_device *thermal, + enum thermal_device_mode *mode) +{ + struct omap_thermal_data *data = thermal->devdata; + + if (data) + *mode = data->mode; + + return 0; +} + +/* Set mode callback functions for thermal zone */ +static int omap_thermal_set_mode(struct thermal_zone_device *thermal, + enum thermal_device_mode mode) +{ + struct omap_thermal_data *data = thermal->devdata; + + if (!data->omap_thermal) { + dev_notice(&thermal->device, "thermal zone not registered\n"); + return 0; + } + + mutex_lock(&data->omap_thermal->lock); + + if (mode == THERMAL_DEVICE_ENABLED) + data->omap_thermal->polling_delay = FAST_TEMP_MONITORING_RATE; + else + data->omap_thermal->polling_delay = 0; + + mutex_unlock(&data->omap_thermal->lock); + + data->mode = mode; + thermal_zone_device_update(data->omap_thermal); + dev_dbg(&thermal->device, "thermal polling set for duration=%d msec\n", + data->omap_thermal->polling_delay); + + return 0; +} + +/* Get trip type callback functions for thermal zone */ +static int omap_thermal_get_trip_type(struct thermal_zone_device *thermal, + int trip, enum thermal_trip_type *type) +{ + if (!omap_thermal_is_valid_trip(trip)) + return -EINVAL; + + if (trip + 1 == OMAP_TRIP_NUMBER) + *type = THERMAL_TRIP_CRITICAL; + else + *type = THERMAL_TRIP_PASSIVE; + + return 0; +} + +/* Get trip temperature callback functions for thermal zone */ +static int omap_thermal_get_trip_temp(struct thermal_zone_device *thermal, + int trip, unsigned long *temp) +{ + if (!omap_thermal_is_valid_trip(trip)) + return -EINVAL; + + *temp = omap_thermal_get_trip_value(trip); + + return 0; +} + +/* Get critical temperature callback functions for thermal zone */ +static int omap_thermal_get_crit_temp(struct thermal_zone_device *thermal, + unsigned long *temp) +{ + /* shutdown zone */ + return omap_thermal_get_trip_temp(thermal, OMAP_TRIP_NUMBER - 1, temp); +} + +static struct thermal_zone_device_ops omap_thermal_ops = { + .get_temp = omap_thermal_get_temp, + /* TODO: add .get_trend */ + .bind = omap_thermal_bind, + .unbind = omap_thermal_unbind, + .get_mode = omap_thermal_get_mode, + .set_mode = omap_thermal_set_mode, + .get_trip_type = omap_thermal_get_trip_type, + .get_trip_temp = omap_thermal_get_trip_temp, + .get_crit_temp = omap_thermal_get_crit_temp, +}; + +int omap_thermal_expose_sensor(struct omap_bandgap *bg_ptr, int id, + char *domain) +{ + struct omap_thermal_data *data; + + data = devm_kzalloc(bg_ptr->dev, sizeof(*data), GFP_KERNEL); + if (!data) { + dev_err(bg_ptr->dev, "kzalloc fail\n"); + return -ENOMEM; + } + data->sensor_id = id; + data->bg_ptr = bg_ptr; + data->mode = THERMAL_DEVICE_ENABLED; + INIT_WORK(&data->thermal_wq, omap_thermal_work); + + /* TODO: remove TC1 TC2 */ + /* Create thermal zone */ + data->omap_thermal = thermal_zone_device_register(domain, + OMAP_TRIP_NUMBER, 0, data, &omap_thermal_ops, + 0, FAST_TEMP_MONITORING_RATE, 0, 0); + if (IS_ERR_OR_NULL(data->omap_thermal)) { + dev_err(bg_ptr->dev, "thermal zone device is NULL\n"); + return PTR_ERR(data->omap_thermal); + } + data->omap_thermal->polling_delay = FAST_TEMP_MONITORING_RATE; + omap_bandgap_set_sensor_data(bg_ptr, id, data); + + return 0; +} + +int omap_thermal_remove_sensor(struct omap_bandgap *bg_ptr, int id) +{ + struct omap_thermal_data *data; + + data = omap_bandgap_get_sensor_data(bg_ptr, id); + + thermal_zone_device_unregister(data->omap_thermal); + + return 0; +} + +int omap_thermal_report_sensor_temperature(struct omap_bandgap *bg_ptr, int id) +{ + struct omap_thermal_data *data; + + data = omap_bandgap_get_sensor_data(bg_ptr, id); + + schedule_work(&data->thermal_wq); + + return 0; +} + +static int omap_thermal_build_cpufreq_clip(struct omap_bandgap *bg_ptr, + struct freq_clip_table **tab_ptr, + int *tab_size) +{ + struct cpufreq_frequency_table *freq_table; + struct freq_clip_table *tab; + int i, count = 0; + + freq_table = cpufreq_frequency_get_table(0); + if (IS_ERR_OR_NULL(freq_table)) { + dev_err(bg_ptr->dev, + "%s: failed to get cpufreq table (%p)\n", + __func__, freq_table); + return -EINVAL; + } + + for (i = 0; freq_table[i].frequency != CPUFREQ_TABLE_END; i++) { + unsigned int freq = freq_table[i].frequency; + if (freq == CPUFREQ_ENTRY_INVALID) + continue; + count++; + } + + tab = devm_kzalloc(bg_ptr->dev, sizeof(*tab) * count, GFP_KERNEL); + if (!tab) { + dev_err(bg_ptr->dev, + "%s: no memory available\n", __func__); + return -ENOMEM; + } + + for (i = 0; freq_table[i].frequency != CPUFREQ_TABLE_END; i++) { + unsigned int freq = freq_table[i].frequency; + + if (freq == CPUFREQ_ENTRY_INVALID) + continue; + + tab[count - i - 1].freq_clip_max = freq; + tab[count - i - 1].temp_level = OMAP_TRIP_HOT; + tab[count - i - 1].mask_val = cpumask_of(0); + } + + *tab_ptr = tab; + *tab_size = count; + + return 0; +} + +int omap_thermal_register_cpu_cooling(struct omap_bandgap *bg_ptr, int id) +{ + struct omap_thermal_data *data; + struct freq_clip_table *tab_ptr; + int tab_size, ret; + + data = omap_bandgap_get_sensor_data(bg_ptr, id); + + ret = omap_thermal_build_cpufreq_clip(bg_ptr, &tab_ptr, &tab_size); + if (ret < 0) { + dev_err(bg_ptr->dev, + "%s: failed to build cpufreq clip table\n", __func__); + return ret; + } + + /* Register cooling device */ + data->cool_dev = cpufreq_cooling_register(tab_ptr, tab_size); + if (IS_ERR_OR_NULL(data->cool_dev)) { + dev_err(bg_ptr->dev, + "Failed to register cpufreq cooling device\n"); + return PTR_ERR(data->cool_dev); + } + bg_ptr->conf->sensors[id].cooling_data.freq_clip_count = tab_size; + + return 0; +} + +int omap_thermal_unregister_cpu_cooling(struct omap_bandgap *bg_ptr, int id) +{ + struct omap_thermal_data *data; + + data = omap_bandgap_get_sensor_data(bg_ptr, id); + cpufreq_cooling_unregister(data->cool_dev); + + return 0; +} diff --git a/drivers/staging/omap-thermal/omap-thermal.h b/drivers/staging/omap-thermal/omap-thermal.h new file mode 100644 index 0000000..0dd2184 --- /dev/null +++ b/drivers/staging/omap-thermal/omap-thermal.h @@ -0,0 +1,108 @@ +/* + * OMAP thermal definitions + * + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * Contact: + * Eduardo Valentin <eduardo.valentin@ti.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ +#ifndef __OMAP_THERMAL_H +#define __OMAP_THERMAL_H + +#include "omap-bandgap.h" + +/* sensors gradient and offsets */ +#define OMAP_GRADIENT_SLOPE_4460 348 +#define OMAP_GRADIENT_CONST_4460 -9301 +#define OMAP_GRADIENT_SLOPE_4470 308 +#define OMAP_GRADIENT_CONST_4470 -7896 + +#define OMAP_GRADIENT_SLOPE_5430_CPU 196 +#define OMAP_GRADIENT_CONST_5430_CPU -6822 +#define OMAP_GRADIENT_SLOPE_5430_GPU 64 +#define OMAP_GRADIENT_CONST_5430_GPU 978 + +/* PCB sensor calculation constants */ +#define OMAP_GRADIENT_SLOPE_W_PCB_4460 1142 +#define OMAP_GRADIENT_CONST_W_PCB_4460 -393 +#define OMAP_GRADIENT_SLOPE_W_PCB_4470 1063 +#define OMAP_GRADIENT_CONST_W_PCB_4470 -477 + +#define OMAP_GRADIENT_SLOPE_W_PCB_5430_CPU 469 +#define OMAP_GRADIENT_CONST_W_PCB_5430_CPU -1272 +#define OMAP_GRADIENT_SLOPE_W_PCB_5430_GPU 378 +#define OMAP_GRADIENT_CONST_W_PCB_5430_GPU 154 + +/* trip points of interest in milicelsius (at hotspot level) */ +#define OMAP_TRIP_COLD 100000 +#define OMAP_TRIP_HOT 110000 +#define OMAP_TRIP_SHUTDOWN 125000 +#define OMAP_TRIP_NUMBER 2 +#define OMAP_TRIP_STEP \ + ((OMAP_TRIP_SHUTDOWN - OMAP_TRIP_HOT) / (OMAP_TRIP_NUMBER - 1)) + +/* Update rates */ +#define FAST_TEMP_MONITORING_RATE 250 + +/* helper macros */ +/** + * omap_thermal_get_trip_value - returns trip temperature based on index + * @i: trip index + */ +#define omap_thermal_get_trip_value(i) \ + (OMAP_TRIP_HOT + ((i) * OMAP_TRIP_STEP)) + +/** + * omap_thermal_is_valid_trip - check for trip index + * @i: trip index + */ +#define omap_thermal_is_valid_trip(trip) \ + ((trip) >= 0 && (trip) < OMAP_TRIP_NUMBER) + +#ifdef CONFIG_OMAP_THERMAL +int omap_thermal_expose_sensor(struct omap_bandgap *bg_ptr, int id, + char *domain); +int omap_thermal_remove_sensor(struct omap_bandgap *bg_ptr, int id); +int omap_thermal_register_cpu_cooling(struct omap_bandgap *bg_ptr, int id); +int omap_thermal_unregister_cpu_cooling(struct omap_bandgap *bg_ptr, int id); +#else +static inline +int omap_thermal_expose_sensor(struct omap_bandgap *bg_ptr, int id, + char *domain) +{ + return 0; +} + +static inline +int omap_thermal_remove_sensor(struct omap_bandgap *bg_ptr, int id) +{ + return 0; +} + +static inline +int omap_thermal_register_cpu_cooling(struct omap_bandgap *bg_ptr, int id) +{ + return 0; +} + +static inline +int omap_thermal_unregister_cpu_cooling(struct omap_bandgap *bg_ptr, int id) +{ + return 0; +} +#endif +#endif diff --git a/drivers/staging/omap-thermal/omap4-thermal.c b/drivers/staging/omap-thermal/omap4-thermal.c new file mode 100644 index 0000000..fa9dbcd --- /dev/null +++ b/drivers/staging/omap-thermal/omap4-thermal.c @@ -0,0 +1,259 @@ +/* + * OMAP4 thermal driver. + * + * Copyright (C) 2011-2012 Texas Instruments Inc. + * Contact: + * Eduardo Valentin <eduardo.valentin@ti.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "omap-thermal.h" +#include "omap-bandgap.h" + +/* + * OMAP4430 has one instance of thermal sensor for MPU + * need to describe the individual bit fields + */ +static struct temp_sensor_registers +omap4430_mpu_temp_sensor_registers = { + .temp_sensor_ctrl = OMAP4430_TEMP_SENSOR_CTRL_OFFSET, + .bgap_tempsoff_mask = OMAP4430_BGAP_TEMPSOFF_MASK, + .bgap_soc_mask = OMAP4430_BGAP_TEMP_SENSOR_SOC_MASK, + .bgap_eocz_mask = OMAP4430_BGAP_TEMP_SENSOR_EOCZ_MASK, + .bgap_dtemp_mask = OMAP4430_BGAP_TEMP_SENSOR_DTEMP_MASK, + + .bgap_mode_ctrl = OMAP4430_TEMP_SENSOR_CTRL_OFFSET, + .mode_ctrl_mask = OMAP4430_SINGLE_MODE_MASK, + + .bgap_efuse = OMAP4430_FUSE_OPP_BGAP, +}; + +/* Thresholds and limits for OMAP4430 MPU temperature sensor */ +static struct temp_sensor_data omap4430_mpu_temp_sensor_data = { + .min_freq = OMAP4430_MIN_FREQ, + .max_freq = OMAP4430_MAX_FREQ, + .max_temp = OMAP4430_MAX_TEMP, + .min_temp = OMAP4430_MIN_TEMP, + .hyst_val = OMAP4430_HYST_VAL, + .adc_start_val = OMAP4430_ADC_START_VALUE, + .adc_end_val = OMAP4430_ADC_END_VALUE, +}; + +/* + * Temperature values in milli degree celsius + * ADC code values from 530 to 923 + */ +static const int +omap4430_adc_to_temp[OMAP4430_ADC_END_VALUE - OMAP4430_ADC_START_VALUE + 1] = { + -38000, -35000, -34000, -32000, -30000, -28000, -26000, -24000, -22000, + -20000, -18000, -17000, -15000, -13000, -12000, -10000, -8000, -6000, + -5000, -3000, -1000, 0, 2000, 3000, 5000, 6000, 8000, 10000, 12000, + 13000, 15000, 17000, 19000, 21000, 23000, 25000, 27000, 28000, 30000, + 32000, 33000, 35000, 37000, 38000, 40000, 42000, 43000, 45000, 47000, + 48000, 50000, 52000, 53000, 55000, 57000, 58000, 60000, 62000, 64000, + 66000, 68000, 70000, 71000, 73000, 75000, 77000, 78000, 80000, 82000, + 83000, 85000, 87000, 88000, 90000, 92000, 93000, 95000, 97000, 98000, + 100000, 102000, 103000, 105000, 107000, 109000, 111000, 113000, 115000, + 117000, 118000, 120000, 122000, 123000, +}; + +/* OMAP4430 data */ +const struct omap_bandgap_data omap4430_data = { + .features = OMAP_BANDGAP_FEATURE_MODE_CONFIG | + OMAP_BANDGAP_FEATURE_POWER_SWITCH, + .fclock_name = "bandgap_fclk", + .div_ck_name = "bandgap_fclk", + .conv_table = omap4430_adc_to_temp, + .expose_sensor = omap_thermal_expose_sensor, + .remove_sensor = omap_thermal_remove_sensor, + .sensors = { + { + .registers = &omap4430_mpu_temp_sensor_registers, + .ts_data = &omap4430_mpu_temp_sensor_data, + .domain = "cpu", + .slope = 0, + .constant = 20000, + .slope_pcb = 0, + .constant_pcb = 20000, + .register_cooling = omap_thermal_register_cpu_cooling, + .unregister_cooling = omap_thermal_unregister_cpu_cooling, + }, + }, + .sensor_count = 1, +}; +/* + * OMAP4460 has one instance of thermal sensor for MPU + * need to describe the individual bit fields + */ +static struct temp_sensor_registers +omap4460_mpu_temp_sensor_registers = { + .temp_sensor_ctrl = OMAP4460_TEMP_SENSOR_CTRL_OFFSET, + .bgap_tempsoff_mask = OMAP4460_BGAP_TEMPSOFF_MASK, + .bgap_soc_mask = OMAP4460_BGAP_TEMP_SENSOR_SOC_MASK, + .bgap_eocz_mask = OMAP4460_BGAP_TEMP_SENSOR_EOCZ_MASK, + .bgap_dtemp_mask = OMAP4460_BGAP_TEMP_SENSOR_DTEMP_MASK, + + .bgap_mask_ctrl = OMAP4460_BGAP_CTRL_OFFSET, + .mask_hot_mask = OMAP4460_MASK_HOT_MASK, + .mask_cold_mask = OMAP4460_MASK_COLD_MASK, + + .bgap_mode_ctrl = OMAP4460_BGAP_CTRL_OFFSET, + .mode_ctrl_mask = OMAP4460_SINGLE_MODE_MASK, + + .bgap_counter = OMAP4460_BGAP_COUNTER_OFFSET, + .counter_mask = OMAP4460_COUNTER_MASK, + + .bgap_threshold = OMAP4460_BGAP_THRESHOLD_OFFSET, + .threshold_thot_mask = OMAP4460_T_HOT_MASK, + .threshold_tcold_mask = OMAP4460_T_COLD_MASK, + + .tshut_threshold = OMAP4460_BGAP_TSHUT_OFFSET, + .tshut_hot_mask = OMAP4460_TSHUT_HOT_MASK, + .tshut_cold_mask = OMAP4460_TSHUT_COLD_MASK, + + .bgap_status = OMAP4460_BGAP_STATUS_OFFSET, + .status_clean_stop_mask = OMAP4460_CLEAN_STOP_MASK, + .status_bgap_alert_mask = OMAP4460_BGAP_ALERT_MASK, + .status_hot_mask = OMAP4460_HOT_FLAG_MASK, + .status_cold_mask = OMAP4460_COLD_FLAG_MASK, + + .bgap_efuse = OMAP4460_FUSE_OPP_BGAP, +}; + +/* Thresholds and limits for OMAP4460 MPU temperature sensor */ +static struct temp_sensor_data omap4460_mpu_temp_sensor_data = { + .tshut_hot = OMAP4460_TSHUT_HOT, + .tshut_cold = OMAP4460_TSHUT_COLD, + .t_hot = OMAP4460_T_HOT, + .t_cold = OMAP4460_T_COLD, + .min_freq = OMAP4460_MIN_FREQ, + .max_freq = OMAP4460_MAX_FREQ, + .max_temp = OMAP4460_MAX_TEMP, + .min_temp = OMAP4460_MIN_TEMP, + .hyst_val = OMAP4460_HYST_VAL, + .adc_start_val = OMAP4460_ADC_START_VALUE, + .adc_end_val = OMAP4460_ADC_END_VALUE, + .update_int1 = 1000, + .update_int2 = 2000, +}; + +/* + * Temperature values in milli degree celsius + * ADC code values from 530 to 923 + */ +static const int +omap4460_adc_to_temp[OMAP4460_ADC_END_VALUE - OMAP4460_ADC_START_VALUE + 1] = { + -40000, -40000, -40000, -40000, -39800, -39400, -39000, -38600, -38200, + -37800, -37300, -36800, -36400, -36000, -35600, -35200, -34800, + -34300, -33800, -33400, -33000, -32600, -32200, -31800, -31300, + -30800, -30400, -30000, -29600, -29200, -28700, -28200, -27800, + -27400, -27000, -26600, -26200, -25700, -25200, -24800, -24400, + -24000, -23600, -23200, -22700, -22200, -21800, -21400, -21000, + -20600, -20200, -19700, -19200, -18800, -18400, -18000, -17600, + -17200, -16700, -16200, -15800, -15400, -15000, -14600, -14200, + -13700, -13200, -12800, -12400, -12000, -11600, -11200, -10700, + -10200, -9800, -9400, -9000, -8600, -8200, -7700, -7200, -6800, + -6400, -6000, -5600, -5200, -4800, -4300, -3800, -3400, -3000, + -2600, -2200, -1800, -1300, -800, -400, 0, 400, 800, 1200, 1600, + 2100, 2600, 3000, 3400, 3800, 4200, 4600, 5100, 5600, 6000, 6400, + 6800, 7200, 7600, 8000, 8500, 9000, 9400, 9800, 10200, 10600, 11000, + 11400, 11900, 12400, 12800, 13200, 13600, 14000, 14400, 14800, + 15300, 15800, 16200, 16600, 17000, 17400, 17800, 18200, 18700, + 19200, 19600, 20000, 20400, 20800, 21200, 21600, 22100, 22600, + 23000, 23400, 23800, 24200, 24600, 25000, 25400, 25900, 26400, + 26800, 27200, 27600, 28000, 28400, 28800, 29300, 29800, 30200, + 30600, 31000, 31400, 31800, 32200, 32600, 33100, 33600, 34000, + 34400, 34800, 35200, 35600, 36000, 36400, 36800, 37300, 37800, + 38200, 38600, 39000, 39400, 39800, 40200, 40600, 41100, 41600, + 42000, 42400, 42800, 43200, 43600, 44000, 44400, 44800, 45300, + 45800, 46200, 46600, 47000, 47400, 47800, 48200, 48600, 49000, + 49500, 50000, 50400, 50800, 51200, 51600, 52000, 52400, 52800, + 53200, 53700, 54200, 54600, 55000, 55400, 55800, 56200, 56600, + 57000, 57400, 57800, 58200, 58700, 59200, 59600, 60000, 60400, + 60800, 61200, 61600, 62000, 62400, 62800, 63300, 63800, 64200, + 64600, 65000, 65400, 65800, 66200, 66600, 67000, 67400, 67800, + 68200, 68700, 69200, 69600, 70000, 70400, 70800, 71200, 71600, + 72000, 72400, 72800, 73200, 73600, 74100, 74600, 75000, 75400, + 75800, 76200, 76600, 77000, 77400, 77800, 78200, 78600, 79000, + 79400, 79800, 80300, 80800, 81200, 81600, 82000, 82400, 82800, + 83200, 83600, 84000, 84400, 84800, 85200, 85600, 86000, 86400, + 86800, 87300, 87800, 88200, 88600, 89000, 89400, 89800, 90200, + 90600, 91000, 91400, 91800, 92200, 92600, 93000, 93400, 93800, + 94200, 94600, 95000, 95500, 96000, 96400, 96800, 97200, 97600, + 98000, 98400, 98800, 99200, 99600, 100000, 100400, 100800, 101200, + 101600, 102000, 102400, 102800, 103200, 103600, 104000, 104400, + 104800, 105200, 105600, 106100, 106600, 107000, 107400, 107800, + 108200, 108600, 109000, 109400, 109800, 110200, 110600, 111000, + 111400, 111800, 112200, 112600, 113000, 113400, 113800, 114200, + 114600, 115000, 115400, 115800, 116200, 116600, 117000, 117400, + 117800, 118200, 118600, 119000, 119400, 119800, 120200, 120600, + 121000, 121400, 121800, 122200, 122600, 123000, 123400, 123800, 124200, + 124600, 124900, 125000, 125000, 125000, 125000 +}; + +/* OMAP4460 data */ +const struct omap_bandgap_data omap4460_data = { + .features = OMAP_BANDGAP_FEATURE_TSHUT | + OMAP_BANDGAP_FEATURE_TSHUT_CONFIG | + OMAP_BANDGAP_FEATURE_TALERT | + OMAP_BANDGAP_FEATURE_MODE_CONFIG | + OMAP_BANDGAP_FEATURE_POWER_SWITCH | + OMAP_BANDGAP_FEATURE_COUNTER, + .fclock_name = "bandgap_ts_fclk", + .div_ck_name = "div_ts_ck", + .conv_table = omap4460_adc_to_temp, + .expose_sensor = omap_thermal_expose_sensor, + .remove_sensor = omap_thermal_remove_sensor, + .sensors = { + { + .registers = &omap4460_mpu_temp_sensor_registers, + .ts_data = &omap4460_mpu_temp_sensor_data, + .domain = "cpu", + .slope = OMAP_GRADIENT_SLOPE_4460, + .constant = OMAP_GRADIENT_CONST_4460, + .slope_pcb = OMAP_GRADIENT_SLOPE_W_PCB_4460, + .constant_pcb = OMAP_GRADIENT_CONST_W_PCB_4460, + .register_cooling = omap_thermal_register_cpu_cooling, + .unregister_cooling = omap_thermal_unregister_cpu_cooling, + }, + }, + .sensor_count = 1, +}; + +/* OMAP4470 data */ +const struct omap_bandgap_data omap4470_data = { + .features = OMAP_BANDGAP_FEATURE_TSHUT | + OMAP_BANDGAP_FEATURE_TSHUT_CONFIG | + OMAP_BANDGAP_FEATURE_TALERT | + OMAP_BANDGAP_FEATURE_MODE_CONFIG | + OMAP_BANDGAP_FEATURE_POWER_SWITCH | + OMAP_BANDGAP_FEATURE_COUNTER, + .fclock_name = "bandgap_ts_fclk", + .div_ck_name = "div_ts_ck", + .conv_table = omap4460_adc_to_temp, + .expose_sensor = omap_thermal_expose_sensor, + .remove_sensor = omap_thermal_remove_sensor, + .sensors = { + { + .registers = &omap4460_mpu_temp_sensor_registers, + .ts_data = &omap4460_mpu_temp_sensor_data, + .domain = "cpu", + .slope = OMAP_GRADIENT_SLOPE_4470, + .constant = OMAP_GRADIENT_CONST_4470, + .slope_pcb = OMAP_GRADIENT_SLOPE_W_PCB_4470, + .constant_pcb = OMAP_GRADIENT_CONST_W_PCB_4470, + .register_cooling = omap_thermal_register_cpu_cooling, + .unregister_cooling = omap_thermal_unregister_cpu_cooling, + }, + }, + .sensor_count = 1, +}; diff --git a/drivers/staging/omap-thermal/omap5-thermal.c b/drivers/staging/omap-thermal/omap5-thermal.c new file mode 100644 index 0000000..0658af2 --- /dev/null +++ b/drivers/staging/omap-thermal/omap5-thermal.c @@ -0,0 +1,297 @@ +/* + * OMAP5 thermal driver. + * + * Copyright (C) 2011-2012 Texas Instruments Inc. + * Contact: + * Eduardo Valentin <eduardo.valentin@ti.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "omap-bandgap.h" +#include "omap-thermal.h" + +/* + * omap5430 has one instance of thermal sensor for MPU + * need to describe the individual bit fields + */ +static struct temp_sensor_registers +omap5430_mpu_temp_sensor_registers = { + .temp_sensor_ctrl = OMAP5430_TEMP_SENSOR_MPU_OFFSET, + .bgap_tempsoff_mask = OMAP5430_BGAP_TEMPSOFF_MASK, + .bgap_soc_mask = OMAP5430_BGAP_TEMP_SENSOR_SOC_MASK, + .bgap_eocz_mask = OMAP5430_BGAP_TEMP_SENSOR_EOCZ_MASK, + .bgap_dtemp_mask = OMAP5430_BGAP_TEMP_SENSOR_DTEMP_MASK, + + .bgap_mask_ctrl = OMAP5430_BGAP_CTRL_OFFSET, + .mask_hot_mask = OMAP5430_MASK_HOT_MPU_MASK, + .mask_cold_mask = OMAP5430_MASK_COLD_MPU_MASK, + + .bgap_mode_ctrl = OMAP5430_BGAP_COUNTER_MPU_OFFSET, + .mode_ctrl_mask = OMAP5430_REPEAT_MODE_MASK, + + .bgap_counter = OMAP5430_BGAP_COUNTER_MPU_OFFSET, + .counter_mask = OMAP5430_COUNTER_MASK, + + .bgap_threshold = OMAP5430_BGAP_THRESHOLD_MPU_OFFSET, + .threshold_thot_mask = OMAP5430_T_HOT_MASK, + .threshold_tcold_mask = OMAP5430_T_COLD_MASK, + + .tshut_threshold = OMAP5430_BGAP_TSHUT_MPU_OFFSET, + .tshut_hot_mask = OMAP5430_TSHUT_HOT_MASK, + .tshut_cold_mask = OMAP5430_TSHUT_COLD_MASK, + + .bgap_status = OMAP5430_BGAP_STATUS_OFFSET, + .status_clean_stop_mask = 0x0, + .status_bgap_alert_mask = OMAP5430_BGAP_ALERT_MASK, + .status_hot_mask = OMAP5430_HOT_MPU_FLAG_MASK, + .status_cold_mask = OMAP5430_COLD_MPU_FLAG_MASK, + + .bgap_efuse = OMAP5430_FUSE_OPP_BGAP_MPU, +}; + +/* + * omap5430 has one instance of thermal sensor for GPU + * need to describe the individual bit fields + */ +static struct temp_sensor_registers +omap5430_gpu_temp_sensor_registers = { + .temp_sensor_ctrl = OMAP5430_TEMP_SENSOR_GPU_OFFSET, + .bgap_tempsoff_mask = OMAP5430_BGAP_TEMPSOFF_MASK, + .bgap_soc_mask = OMAP5430_BGAP_TEMP_SENSOR_SOC_MASK, + .bgap_eocz_mask = OMAP5430_BGAP_TEMP_SENSOR_EOCZ_MASK, + .bgap_dtemp_mask = OMAP5430_BGAP_TEMP_SENSOR_DTEMP_MASK, + + .bgap_mask_ctrl = OMAP5430_BGAP_CTRL_OFFSET, + .mask_hot_mask = OMAP5430_MASK_HOT_MM_MASK, + .mask_cold_mask = OMAP5430_MASK_COLD_MM_MASK, + + .bgap_mode_ctrl = OMAP5430_BGAP_COUNTER_GPU_OFFSET, + .mode_ctrl_mask = OMAP5430_REPEAT_MODE_MASK, + + .bgap_counter = OMAP5430_BGAP_COUNTER_GPU_OFFSET, + .counter_mask = OMAP5430_COUNTER_MASK, + + .bgap_threshold = OMAP5430_BGAP_THRESHOLD_GPU_OFFSET, + .threshold_thot_mask = OMAP5430_T_HOT_MASK, + .threshold_tcold_mask = OMAP5430_T_COLD_MASK, + + .tshut_threshold = OMAP5430_BGAP_TSHUT_GPU_OFFSET, + .tshut_hot_mask = OMAP5430_TSHUT_HOT_MASK, + .tshut_cold_mask = OMAP5430_TSHUT_COLD_MASK, + + .bgap_status = OMAP5430_BGAP_STATUS_OFFSET, + .status_clean_stop_mask = 0x0, + .status_bgap_alert_mask = OMAP5430_BGAP_ALERT_MASK, + .status_hot_mask = OMAP5430_HOT_MM_FLAG_MASK, + .status_cold_mask = OMAP5430_COLD_MM_FLAG_MASK, + + .bgap_efuse = OMAP5430_FUSE_OPP_BGAP_GPU, +}; + +/* + * omap5430 has one instance of thermal sensor for CORE + * need to describe the individual bit fields + */ +static struct temp_sensor_registers +omap5430_core_temp_sensor_registers = { + .temp_sensor_ctrl = OMAP5430_TEMP_SENSOR_CORE_OFFSET, + .bgap_tempsoff_mask = OMAP5430_BGAP_TEMPSOFF_MASK, + .bgap_soc_mask = OMAP5430_BGAP_TEMP_SENSOR_SOC_MASK, + .bgap_eocz_mask = OMAP5430_BGAP_TEMP_SENSOR_EOCZ_MASK, + .bgap_dtemp_mask = OMAP5430_BGAP_TEMP_SENSOR_DTEMP_MASK, + + .bgap_mask_ctrl = OMAP5430_BGAP_CTRL_OFFSET, + .mask_hot_mask = OMAP5430_MASK_HOT_CORE_MASK, + .mask_cold_mask = OMAP5430_MASK_COLD_CORE_MASK, + + .bgap_mode_ctrl = OMAP5430_BGAP_COUNTER_CORE_OFFSET, + .mode_ctrl_mask = OMAP5430_REPEAT_MODE_MASK, + + .bgap_counter = OMAP5430_BGAP_COUNTER_CORE_OFFSET, + .counter_mask = OMAP5430_COUNTER_MASK, + + .bgap_threshold = OMAP5430_BGAP_THRESHOLD_CORE_OFFSET, + .threshold_thot_mask = OMAP5430_T_HOT_MASK, + .threshold_tcold_mask = OMAP5430_T_COLD_MASK, + + .tshut_threshold = OMAP5430_BGAP_TSHUT_CORE_OFFSET, + .tshut_hot_mask = OMAP5430_TSHUT_HOT_MASK, + .tshut_cold_mask = OMAP5430_TSHUT_COLD_MASK, + + .bgap_status = OMAP5430_BGAP_STATUS_OFFSET, + .status_clean_stop_mask = 0x0, + .status_bgap_alert_mask = OMAP5430_BGAP_ALERT_MASK, + .status_hot_mask = OMAP5430_HOT_CORE_FLAG_MASK, + .status_cold_mask = OMAP5430_COLD_CORE_FLAG_MASK, + + .bgap_efuse = OMAP5430_FUSE_OPP_BGAP_CORE, +}; + +/* Thresholds and limits for OMAP5430 MPU temperature sensor */ +static struct temp_sensor_data omap5430_mpu_temp_sensor_data = { + .tshut_hot = OMAP5430_MPU_TSHUT_HOT, + .tshut_cold = OMAP5430_MPU_TSHUT_COLD, + .t_hot = OMAP5430_MPU_T_HOT, + .t_cold = OMAP5430_MPU_T_COLD, + .min_freq = OMAP5430_MPU_MIN_FREQ, + .max_freq = OMAP5430_MPU_MAX_FREQ, + .max_temp = OMAP5430_MPU_MAX_TEMP, + .min_temp = OMAP5430_MPU_MIN_TEMP, + .hyst_val = OMAP5430_MPU_HYST_VAL, + .adc_start_val = OMAP5430_ADC_START_VALUE, + .adc_end_val = OMAP5430_ADC_END_VALUE, + .update_int1 = 1000, + .update_int2 = 2000, +}; + +/* Thresholds and limits for OMAP5430 GPU temperature sensor */ +static struct temp_sensor_data omap5430_gpu_temp_sensor_data = { + .tshut_hot = OMAP5430_GPU_TSHUT_HOT, + .tshut_cold = OMAP5430_GPU_TSHUT_COLD, + .t_hot = OMAP5430_GPU_T_HOT, + .t_cold = OMAP5430_GPU_T_COLD, + .min_freq = OMAP5430_GPU_MIN_FREQ, + .max_freq = OMAP5430_GPU_MAX_FREQ, + .max_temp = OMAP5430_GPU_MAX_TEMP, + .min_temp = OMAP5430_GPU_MIN_TEMP, + .hyst_val = OMAP5430_GPU_HYST_VAL, + .adc_start_val = OMAP5430_ADC_START_VALUE, + .adc_end_val = OMAP5430_ADC_END_VALUE, + .update_int1 = 1000, + .update_int2 = 2000, +}; + +/* Thresholds and limits for OMAP5430 CORE temperature sensor */ +static struct temp_sensor_data omap5430_core_temp_sensor_data = { + .tshut_hot = OMAP5430_CORE_TSHUT_HOT, + .tshut_cold = OMAP5430_CORE_TSHUT_COLD, + .t_hot = OMAP5430_CORE_T_HOT, + .t_cold = OMAP5430_CORE_T_COLD, + .min_freq = OMAP5430_CORE_MIN_FREQ, + .max_freq = OMAP5430_CORE_MAX_FREQ, + .max_temp = OMAP5430_CORE_MAX_TEMP, + .min_temp = OMAP5430_CORE_MIN_TEMP, + .hyst_val = OMAP5430_CORE_HYST_VAL, + .adc_start_val = OMAP5430_ADC_START_VALUE, + .adc_end_val = OMAP5430_ADC_END_VALUE, + .update_int1 = 1000, + .update_int2 = 2000, +}; + +static const int +omap5430_adc_to_temp[OMAP5430_ADC_END_VALUE - OMAP5430_ADC_START_VALUE + 1] = { + -40000, -40000, -40000, -40000, -39800, -39400, -39000, -38600, + -38200, -37800, -37300, -36800, + -36400, -36000, -35600, -35200, -34800, -34300, -33800, -33400, -33000, + -32600, + -32200, -31800, -31300, -30800, -30400, -30000, -29600, -29200, -28700, + -28200, -27800, -27400, -27000, -26600, -26200, -25700, -25200, -24800, + -24400, -24000, -23600, -23200, -22700, -22200, -21800, -21400, -21000, + -20600, -20200, -19700, -19200, -9300, -18400, -18000, -17600, -17200, + -16700, -16200, -15800, -15400, -15000, -14600, -14200, -13700, -13200, + -12800, -12400, -12000, -11600, -11200, -10700, -10200, -9800, -9400, + -9000, + -8600, -8200, -7700, -7200, -6800, -6400, -6000, -5600, -5200, -4800, + -4300, + -3800, -3400, -3000, -2600, -2200, -1800, -1300, -800, -400, 0, 400, + 800, + 1200, 1600, 2100, 2600, 3000, 3400, 3800, 4200, 4600, 5100, 5600, 6000, + 6400, 6800, 7200, 7600, 8000, 8500, 9000, 9400, 9800, 10200, 10800, + 11100, + 11400, 11900, 12400, 12800, 13200, 13600, 14000, 14400, 14800, 15300, + 15800, + 16200, 16600, 17000, 17400, 17800, 18200, 18700, 19200, 19600, 20000, + 20400, + 20800, 21200, 21600, 22100, 22600, 23000, 23400, 23800, 24200, 24600, + 25000, + 25400, 25900, 26400, 26800, 27200, 27600, 28000, 28400, 28800, 29300, + 29800, + 30200, 30600, 31000, 31400, 31800, 32200, 32600, 33100, 33600, 34000, + 34400, + 34800, 35200, 35600, 36000, 36400, 36800, 37300, 37800, 38200, 38600, + 39000, + 39400, 39800, 40200, 40600, 41100, 41600, 42000, 42400, 42800, 43200, + 43600, + 44000, 44400, 44800, 45300, 45800, 46200, 46600, 47000, 47400, 47800, + 48200, + 48600, 49000, 49500, 50000, 50400, 50800, 51200, 51600, 52000, 52400, + 52800, + 53200, 53700, 54200, 54600, 55000, 55400, 55800, 56200, 56600, 57000, + 57400, + 57800, 58200, 58700, 59200, 59600, 60000, 60400, 60800, 61200, 61600, + 62000, + 62400, 62800, 63300, 63800, 64200, 64600, 65000, 65400, 65800, 66200, + 66600, + 67000, 67400, 67800, 68200, 68700, 69200, 69600, 70000, 70400, 70800, + 71200, + 71600, 72000, 72400, 72800, 73200, 73600, 74100, 74600, 75000, 75400, + 75800, + 76200, 76600, 77000, 77400, 77800, 78200, 78600, 79000, 79400, 79800, + 80300, + 80800, 81200, 81600, 82000, 82400, 82800, 83200, 83600, 84000, 84400, + 84800, + 85200, 85600, 86000, 86400, 86800, 87300, 87800, 88200, 88600, 89000, + 89400, + 89800, 90200, 90600, 91000, 91400, 91800, 92200, 92600, 93000, 93400, + 93800, + 94200, 94600, 95000, 95500, 96000, 96400, 96800, 97200, 97600, 98000, + 98400, + 98800, 99200, 99600, 100000, 100400, 100800, 101200, 101600, 102000, + 102400, + 102800, 103200, 103600, 104000, 104400, 104800, 105200, 105600, 106100, + 106600, 107000, 107400, 107800, 108200, 108600, 109000, 109400, 109800, + 110200, 110600, 111000, 111400, 111800, 112200, 112600, 113000, 113400, + 113800, 114200, 114600, 115000, 115400, 115800, 116200, 116600, 117000, + 117400, 117800, 118200, 118600, 119000, 119400, 119800, 120200, 120600, + 121000, 121400, 121800, 122200, 122600, 123000, 123400, 123800, 124200, + 124600, 124900, 125000, 125000, 125000, 125000, +}; + +const struct omap_bandgap_data omap5430_data = { + .features = OMAP_BANDGAP_FEATURE_TSHUT_CONFIG | + OMAP_BANDGAP_FEATURE_TALERT | + OMAP_BANDGAP_FEATURE_MODE_CONFIG | + OMAP_BANDGAP_FEATURE_COUNTER, + .fclock_name = "ts_clk_div_ck", + .div_ck_name = "ts_clk_div_ck", + .conv_table = omap5430_adc_to_temp, + .expose_sensor = omap_thermal_expose_sensor, + .remove_sensor = omap_thermal_remove_sensor, + .sensors = { + { + .registers = &omap5430_mpu_temp_sensor_registers, + .ts_data = &omap5430_mpu_temp_sensor_data, + .domain = "cpu", + .register_cooling = omap_thermal_register_cpu_cooling, + .unregister_cooling = omap_thermal_unregister_cpu_cooling, + .slope = OMAP_GRADIENT_SLOPE_5430_CPU, + .constant = OMAP_GRADIENT_CONST_5430_CPU, + .slope_pcb = OMAP_GRADIENT_SLOPE_W_PCB_5430_CPU, + .constant_pcb = OMAP_GRADIENT_CONST_W_PCB_5430_CPU, + }, + { + .registers = &omap5430_gpu_temp_sensor_registers, + .ts_data = &omap5430_gpu_temp_sensor_data, + .domain = "gpu", + .slope = OMAP_GRADIENT_SLOPE_5430_GPU, + .constant = OMAP_GRADIENT_CONST_5430_GPU, + .slope_pcb = OMAP_GRADIENT_SLOPE_W_PCB_5430_GPU, + .constant_pcb = OMAP_GRADIENT_CONST_W_PCB_5430_GPU, + }, + { + .registers = &omap5430_core_temp_sensor_registers, + .ts_data = &omap5430_core_temp_sensor_data, + .domain = "core", + }, + }, + .sensor_count = 3, +}; diff --git a/drivers/staging/omap-thermal/omap_bandgap.txt b/drivers/staging/omap-thermal/omap_bandgap.txt new file mode 100644 index 0000000..6008a14 --- /dev/null +++ b/drivers/staging/omap-thermal/omap_bandgap.txt @@ -0,0 +1,30 @@ +* Texas Instrument OMAP SCM bandgap bindings + +In the System Control Module, OMAP supplies a voltage reference +and a temperature sensor feature that are gathered in the band +gap voltage and temperature sensor (VBGAPTS) module. The band +gap provides current and voltage reference for its internal +circuits and other analog IP blocks. The analog-to-digital +converter (ADC) produces an output value that is proportional +to the silicon temperature. + +Required properties: +- compatible : Should be: + - "ti,omap4460-control-bandgap" : for OMAP4460 bandgap + - "ti,omap5430-control-bandgap" : for OMAP5430 bandgap +- interrupts : this entry should indicate which interrupt line +the talert signal is routed to; +Specific: +- ti,tshut-gpio : this entry should be used to inform which GPIO +line the tshut signal is routed to; + +Example: + +bandgap { + reg = <0x4a002260 0x4 + 0x4a00232C 0x4 + 0x4a002378 0x18>; + compatible = "ti,omap4460-control-bandgap"; + interrupts = <0 126 4>; /* talert */ + ti,tshut-gpio = <86>; +}; |