diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/staging/Kconfig | 2 | ||||
-rw-r--r-- | drivers/staging/Makefile | 1 | ||||
-rw-r--r-- | drivers/staging/omap-thermal/Kconfig | 11 | ||||
-rw-r--r-- | drivers/staging/omap-thermal/Makefile | 2 | ||||
-rw-r--r-- | drivers/staging/omap-thermal/TODO | 28 | ||||
-rw-r--r-- | drivers/staging/omap-thermal/omap-bandgap.c | 1167 | ||||
-rw-r--r-- | drivers/staging/omap-thermal/omap-bandgap.h | 425 | ||||
-rw-r--r-- | drivers/staging/omap-thermal/omap_bandgap.txt | 30 |
8 files changed, 1666 insertions, 0 deletions
diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index d3934d79524a..e3402d5644dd 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -134,4 +134,6 @@ source "drivers/staging/gdm72xx/Kconfig" source "drivers/staging/csr/Kconfig" +source "drivers/staging/omap-thermal/Kconfig" + endif # STAGING diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index 5b2219ac5207..3be59d02cae4 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -59,3 +59,4 @@ obj-$(CONFIG_USB_WPAN_HCD) += ozwpan/ obj-$(CONFIG_USB_G_CCG) += ccg/ obj-$(CONFIG_WIMAX_GDM72XX) += gdm72xx/ obj-$(CONFIG_CSR_WIFI) += csr/ +obj-$(CONFIG_OMAP_BANDGAP) += omap-thermal/ diff --git a/drivers/staging/omap-thermal/Kconfig b/drivers/staging/omap-thermal/Kconfig new file mode 100644 index 000000000000..8c9979dfaa69 --- /dev/null +++ b/drivers/staging/omap-thermal/Kconfig @@ -0,0 +1,11 @@ +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. diff --git a/drivers/staging/omap-thermal/Makefile b/drivers/staging/omap-thermal/Makefile new file mode 100644 index 000000000000..c92a854e65f1 --- /dev/null +++ b/drivers/staging/omap-thermal/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_OMAP_BANDGAP) += omap-thermal.o +omap-thermal-y := omap-bandgap.o diff --git a/drivers/staging/omap-thermal/TODO b/drivers/staging/omap-thermal/TODO new file mode 100644 index 000000000000..9e23cc4d551b --- /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 000000000000..8ea626406a34 --- /dev/null +++ b/drivers/staging/omap-thermal/omap-bandgap.c @@ -0,0 +1,1167 @@ +/* + * 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[] = { + /* 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 000000000000..8b9883d0e1ed --- /dev/null +++ b/drivers/staging/omap-thermal/omap-bandgap.h @@ -0,0 +1,425 @@ +/* + * 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); + +#endif diff --git a/drivers/staging/omap-thermal/omap_bandgap.txt b/drivers/staging/omap-thermal/omap_bandgap.txt new file mode 100644 index 000000000000..6008a1452fde --- /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>; +}; |