X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=drivers%2Fstaging%2Fomap-thermal%2Fomap-bandgap.c;h=065f3716c540f761275f6181dfbc4116f6731e06;hb=31102a7202dec13296a7de8e72427966813da75e;hp=dcc1448dbf8e8b2f7d74ab3d4a4735c8878a246c;hpb=7bf2fbcdf5300d8b19fa36e37b5bcd8326c95c1d;p=~andy%2Flinux diff --git a/drivers/staging/omap-thermal/omap-bandgap.c b/drivers/staging/omap-thermal/omap-bandgap.c index dcc1448dbf8..065f3716c54 100644 --- a/drivers/staging/omap-thermal/omap-bandgap.c +++ b/drivers/staging/omap-thermal/omap-bandgap.c @@ -42,57 +42,143 @@ #include "omap-bandgap.h" +/*** Helper functions to access registers and their bitfields ***/ + +/** + * omap_bandgap_readl() - simple read helper function + * @bg_ptr: pointer to omap_bandgap structure + * @reg: desired register (offset) to be read + * + * Helper function to read bandgap registers. It uses the io remapped area. + * Returns the register value. + */ static u32 omap_bandgap_readl(struct omap_bandgap *bg_ptr, u32 reg) { return readl(bg_ptr->base + reg); } +/** + * omap_bandgap_writel() - simple write helper function + * @bg_ptr: pointer to omap_bandgap structure + * @val: desired register value to be written + * @reg: desired register (offset) to be written + * + * Helper function to write bandgap registers. It uses the io remapped area. + */ static void omap_bandgap_writel(struct omap_bandgap *bg_ptr, u32 val, u32 reg) { writel(val, bg_ptr->base + reg); } +/** + * DOC: macro to update bits. + * + * RMW_BITS() - used to read, modify and update bandgap bitfields. + * The value passed will be shifted. + */ +#define RMW_BITS(bg_ptr, id, reg, mask, val) \ +do { \ + struct temp_sensor_registers *t; \ + u32 r; \ + \ + t = bg_ptr->conf->sensors[(id)].registers; \ + r = omap_bandgap_readl(bg_ptr, t->reg); \ + r &= ~t->mask; \ + r |= (val) << __ffs(t->mask); \ + omap_bandgap_writel(bg_ptr, r, t->reg); \ +} while (0) + +/*** Basic helper functions ***/ + +/** + * omap_bandgap_power() - controls the power state of a bandgap device + * @bg_ptr: pointer to omap_bandgap structure + * @on: desired power state (1 - on, 0 - off) + * + * Used to power on/off a bandgap device instance. Only used on those + * that features tempsoff bit. + */ 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; + goto exit; - 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; + for (i = 0; i < bg_ptr->conf->sensor_count; i++) /* active on 0 */ - ctrl |= !on << __ffs(tsr->bgap_tempsoff_mask); + RMW_BITS(bg_ptr, i, temp_sensor_ctrl, bgap_tempsoff_mask, !on); + +exit: + return 0; +} + +/** + * omap_bandgap_read_temp() - helper function to read sensor temperature + * @bg_ptr: pointer to omap_bandgap structure + * @id: bandgap sensor id + * + * Function to concentrate the steps to read sensor temperature register. + * This function is desired because, depending on bandgap device version, + * it might be needed to freeze the bandgap state machine, before fetching + * the register value. + */ +static u32 omap_bandgap_read_temp(struct omap_bandgap *bg_ptr, int id) +{ + struct temp_sensor_registers *tsr; + u32 temp, reg; + + tsr = bg_ptr->conf->sensors[id].registers; + reg = tsr->temp_sensor_ctrl; - /* write BGAP_TEMPSOFF should be reset to 0 */ - omap_bandgap_writel(bg_ptr, ctrl, tsr->temp_sensor_ctrl); + if (OMAP_BANDGAP_HAS(bg_ptr, FREEZE_BIT)) { + RMW_BITS(bg_ptr, id, bgap_mask_ctrl, mask_freeze_mask, 1); + /* + * In case we cannot read from cur_dtemp / dtemp_0, + * then we read from the last valid temp read + */ + reg = tsr->ctrl_dtemp_1; } - return 0; + /* read temperature */ + temp = omap_bandgap_readl(bg_ptr, reg); + temp &= tsr->bgap_dtemp_mask; + + if (OMAP_BANDGAP_HAS(bg_ptr, FREEZE_BIT)) + RMW_BITS(bg_ptr, id, bgap_mask_ctrl, mask_freeze_mask, 0); + + return temp; } -/* This is the Talert handler. Call it only if HAS(TALERT) is set */ -static irqreturn_t talert_irq_handler(int irq, void *data) +/*** IRQ handlers ***/ + +/** + * omap_bandgap_talert_irq_handler() - handles Temperature alert IRQs + * @irq: IRQ number + * @data: private data (struct omap_bandgap *) + * + * This is the Talert handler. Use it only if bandgap device features + * HAS(TALERT). This handler goes over all sensors and checks their + * conditions and acts accordingly. In case there are events pending, + * it will reset the event mask to wait for the opposite event (next event). + * Every time there is a new event, it will be reported to thermal layer. + */ +static irqreturn_t omap_bandgap_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; + u32 t_hot = 0, t_cold = 0, 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; + ctrl = omap_bandgap_readl(bg_ptr, tsr->bgap_status); + + /* Read the status of t_hot */ + t_hot = ctrl & 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; + t_cold = ctrl & tsr->status_cold_mask; if (!t_cold && !t_hot) continue; @@ -118,10 +204,6 @@ static irqreturn_t talert_irq_handler(int irq, void *data) __func__, bg_ptr->conf->sensors[i].domain, t_hot, t_cold); - /* 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); @@ -130,68 +212,152 @@ static irqreturn_t talert_irq_handler(int irq, void *data) return IRQ_HANDLED; } -/* This is the Tshut handler. Call it only if HAS(TSHUT) is set */ +/** + * omap_bandgap_tshut_irq_handler() - handles Temperature shutdown signal + * @irq: IRQ number + * @data: private data (unused) + * + * This is the Tshut handler. Use it only if bandgap device features + * HAS(TSHUT). If any sensor fires the Tshut signal, we simply shutdown + * the system. + */ static irqreturn_t omap_bandgap_tshut_irq_handler(int irq, void *data) { + pr_emerg("%s: TSHUT temperature reached. Needs shut down...\n", + __func__); + orderly_poweroff(true); return IRQ_HANDLED; } +/*** Helper functions which manipulate conversion ADC <-> mi Celsius ***/ + +/** + * omap_bandgap_adc_to_mcelsius() - converts an ADC value to mCelsius scale + * @bg_ptr: struct omap_bandgap pointer + * @adc_val: value in ADC representation + * @t: address where to write the resulting temperature in mCelsius + * + * Simple conversion from ADC representation to mCelsius. In case the ADC value + * is out of the ADC conv table range, it returns -ERANGE, 0 on success. + * The conversion table is indexed by the ADC values. + */ static -int adc_to_temp_conversion(struct omap_bandgap *bg_ptr, int id, int adc_val, - int *t) +int omap_bandgap_adc_to_mcelsius(struct omap_bandgap *bg_ptr, + int adc_val, int *t) { - struct temp_sensor_data *ts_data = bg_ptr->conf->sensors[id].ts_data; + struct omap_bandgap_data *conf = bg_ptr->conf; + int ret = 0; /* 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; + if (adc_val < conf->adc_start_val || adc_val > conf->adc_end_val) { + ret = -ERANGE; + goto exit; + } - *t = bg_ptr->conv_table[adc_val - ts_data->adc_start_val]; + *t = bg_ptr->conf->conv_table[adc_val - conf->adc_start_val]; - return 0; +exit: + return ret; } -static int temp_to_adc_conversion(long temp, struct omap_bandgap *bg_ptr, int i, - int *adc) +/** + * omap_bandgap_mcelsius_to_adc() - converts a mCelsius value to ADC scale + * @bg_ptr: struct omap_bandgap pointer + * @temp: value in mCelsius + * @adc: address where to write the resulting temperature in ADC representation + * + * Simple conversion from mCelsius to ADC values. In case the temp value + * is out of the ADC conv table range, it returns -ERANGE, 0 on success. + * The conversion table is indexed by the ADC values. + */ +static +int omap_bandgap_mcelsius_to_adc(struct omap_bandgap *bg_ptr, long temp, + int *adc) { - struct temp_sensor_data *ts_data = bg_ptr->conf->sensors[i].ts_data; - int high, low, mid; + struct omap_bandgap_data *conf = bg_ptr->conf; + const int *conv_table = bg_ptr->conf->conv_table; + int high, low, mid, ret = 0; low = 0; - high = ts_data->adc_end_val - ts_data->adc_start_val; + high = conf->adc_end_val - conf->adc_start_val; mid = (high + low) / 2; - if (temp < bg_ptr->conv_table[low] || temp > bg_ptr->conv_table[high]) - return -EINVAL; + if (temp < conv_table[low] || temp > conv_table[high]) { + ret = -ERANGE; + goto exit; + } while (low < high) { - if (temp < bg_ptr->conv_table[mid]) + if (temp < conv_table[mid]) high = mid - 1; else low = mid + 1; mid = (low + high) / 2; } - *adc = ts_data->adc_start_val + low; + *adc = conf->adc_start_val + low; - return 0; +exit: + return ret; } -/* 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) +/** + * omap_bandgap_add_hyst() - add hysteresis (in mCelsius) to an ADC value + * @bg_ptr: struct omap_bandgap pointer + * @adc_val: temperature value in ADC representation + * @hyst_val: hysteresis value in mCelsius + * @sum: address where to write the resulting temperature (in ADC scale) + * + * Adds an hysteresis value (in mCelsius) to a ADC temperature value. + * Returns 0 on success, -ERANGE otherwise. + */ +static +int omap_bandgap_add_hyst(struct omap_bandgap *bg_ptr, int adc_val, + int hyst_val, u32 *sum) +{ + int temp, ret; + + /* + * Need to add in the mcelsius domain, so we have a temperature + * the conv_table range + */ + ret = omap_bandgap_adc_to_mcelsius(bg_ptr, adc_val, &temp); + if (ret < 0) + goto exit; + + temp += hyst_val; + + ret = omap_bandgap_mcelsius_to_adc(bg_ptr, temp, sum); + +exit: + return ret; +} + +/*** Helper functions handling device Alert/Shutdown signals ***/ + +/** + * omap_bandgap_unmask_interrupts() - unmasks the events of thot & tcold + * @bg_ptr: struct omap_bandgap pointer + * @t_hot: hot temperature value to trigger alert signal + * @t_cold: cold temperature value to trigger alert signal + * + * Checks the requested t_hot and t_cold values and configures the IRQ event + * masks accordingly. Call this function only if bandgap features HAS(TALERT). + */ +static void omap_bandgap_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; + temp = omap_bandgap_read_temp(bg_ptr, id); + tsr = bg_ptr->conf->sensors[id].registers; reg_val = omap_bandgap_readl(bg_ptr, tsr->bgap_mask_ctrl); + if (temp < t_hot) reg_val |= tsr->mask_hot_mask; else @@ -202,200 +368,160 @@ static int temp_sensor_unmask_interrupts(struct omap_bandgap *bg_ptr, int id, 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) +int omap_bandgap_update_alert_threshold(struct omap_bandgap *bg_ptr, int id, + int val, bool 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; + u32 thresh_val, reg_val, t_hot, t_cold; + int err = 0; tsr = bg_ptr->conf->sensors[id].registers; - /* obtain the T cold value */ + /* obtain the current 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; + t_cold = (thresh_val & tsr->threshold_tcold_mask) >> + __ffs(tsr->threshold_tcold_mask); + t_hot = (thresh_val & tsr->threshold_thot_mask) >> + __ffs(tsr->threshold_thot_mask); + if (hot) + t_hot = val; + else + t_cold = val; + + if (t_cold < t_hot) { + if (hot) + err = omap_bandgap_add_hyst(bg_ptr, t_hot, + -ts_data->hyst_val, + &t_cold); + else + err = omap_bandgap_add_hyst(bg_ptr, t_cold, + ts_data->hyst_val, + &t_hot); } - /* write the new t_hot value */ + /* write the new threshold values */ reg_val = thresh_val & ~tsr->threshold_thot_mask; reg_val |= (t_hot << __ffs(tsr->threshold_thot_mask)); + 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 thot threshold\n"); - return -EIO; + err = -EIO; + goto exit; } - return temp_sensor_unmask_interrupts(bg_ptr, id, t_hot, cold); + omap_bandgap_unmask_interrupts(bg_ptr, id, t_hot, t_cold); +exit: + return err; } -/* 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) +static inline int omap_bandgap_validate(struct omap_bandgap *bg_ptr, int id) { - 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); + int ret = 0; - /* 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 (IS_ERR_OR_NULL(bg_ptr)) { + pr_err("%s: invalid bandgap pointer\n", __func__); + ret = -EINVAL; + goto exit; + } - 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); + if ((id < 0) || (id >= bg_ptr->conf->sensor_count)) { + dev_err(bg_ptr->dev, "%s: sensor id out of range (%d)\n", + __func__, id); + ret = -ERANGE; + } - return 0; +exit: + return ret; } -/* 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) +int _omap_bandgap_write_threshold(struct omap_bandgap *bg_ptr, int id, int val, + bool hot) { - struct temp_sensor_data *ts_data = bg_ptr->conf->sensors[id].ts_data; + struct temp_sensor_data *ts_data; struct temp_sensor_registers *tsr; - u32 thresh_val, reg_val; - int hot, err = 0; + u32 adc_val; + int ret; - 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; + ret = omap_bandgap_validate(bg_ptr, id); + if (ret) + goto exit; + + if (!OMAP_BANDGAP_HAS(bg_ptr, TALERT)) { + ret = -ENOTSUPP; + goto exit; } - /* 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; + ts_data = bg_ptr->conf->sensors[id].ts_data; + tsr = bg_ptr->conf->sensors[id].registers; + if (hot) { + if (val < ts_data->min_temp + ts_data->hyst_val) + ret = -EINVAL; + } else { + if (val > ts_data->max_temp + ts_data->hyst_val) + ret = -EINVAL; } - return temp_sensor_unmask_interrupts(bg_ptr, id, hot, t_cold); -} + if (ret) + goto exit; -/* 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; + ret = omap_bandgap_mcelsius_to_adc(bg_ptr, val, &adc_val); + if (ret < 0) + goto exit; - 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); + mutex_lock(&bg_ptr->bg_mutex); + omap_bandgap_update_alert_threshold(bg_ptr, id, adc_val, hot); + mutex_unlock(&bg_ptr->bg_mutex); - return 0; +exit: + return ret; } -/* 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) +int _omap_bandgap_read_threshold(struct omap_bandgap *bg_ptr, int id, + int *val, bool hot) { struct temp_sensor_registers *tsr; - u32 reg_val; + u32 temp, mask; + int ret = 0; - 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; -} + ret = omap_bandgap_validate(bg_ptr, id); + if (ret) + goto exit; -/* 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; + if (!OMAP_BANDGAP_HAS(bg_ptr, TALERT)) { + ret = -ENOTSUPP; + goto exit; + } 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; -} + if (hot) + mask = tsr->threshold_thot_mask; + else + mask = tsr->threshold_tcold_mask; -#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; + temp = omap_bandgap_readl(bg_ptr, tsr->bgap_threshold); + temp = (temp & mask) >> __ffs(mask); + ret |= omap_bandgap_adc_to_mcelsius(bg_ptr, temp, &temp); + if (ret) { + dev_err(bg_ptr->dev, "failed to read thot\n"); + ret = -EIO; + goto exit; } - 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; - } + *val = temp; +exit: return 0; } -/* Exposed APIs */ +/*** Exposed APIs ***/ + /** * omap_bandgap_read_thot() - reads sensor current thot * @bg_ptr - pointer to bandgap instance @@ -405,32 +531,9 @@ static inline int omap_bandgap_validate(struct omap_bandgap *bg_ptr, int id) * returns 0 on success or the proper error code */ int omap_bandgap_read_thot(struct omap_bandgap *bg_ptr, int id, - int *thot) + 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; + return _omap_bandgap_read_threshold(bg_ptr, id, thot, true); } /** @@ -443,32 +546,7 @@ int omap_bandgap_read_thot(struct omap_bandgap *bg_ptr, int id, */ 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; + return _omap_bandgap_write_threshold(bg_ptr, id, val, true); } /** @@ -480,30 +558,9 @@ int omap_bandgap_write_thot(struct omap_bandgap *bg_ptr, int id, int val) * returns 0 on success or the proper error code */ int omap_bandgap_read_tcold(struct omap_bandgap *bg_ptr, int id, - int *tcold) + 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; + return _omap_bandgap_read_threshold(bg_ptr, id, tcold, false); } /** @@ -516,32 +573,7 @@ int omap_bandgap_read_tcold(struct omap_bandgap *bg_ptr, int id, */ 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; + return _omap_bandgap_write_threshold(bg_ptr, id, val, false); } /** @@ -585,7 +617,7 @@ int omap_bandgap_read_update_interval(struct omap_bandgap *bg_ptr, int id, * 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 id, u32 interval) { int ret = omap_bandgap_validate(bg_ptr, id); if (ret) @@ -596,7 +628,7 @@ int omap_bandgap_write_update_interval(struct omap_bandgap *bg_ptr, interval = interval * bg_ptr->clk_rate / 1000; mutex_lock(&bg_ptr->bg_mutex); - configure_temp_sensor_counter(bg_ptr, id, interval); + RMW_BITS(bg_ptr, id, bgap_counter, counter_mask, interval); mutex_unlock(&bg_ptr->bg_mutex); return 0; @@ -611,9 +643,8 @@ int omap_bandgap_write_update_interval(struct omap_bandgap *bg_ptr, * returns 0 on success or the proper error code */ int omap_bandgap_read_temperature(struct omap_bandgap *bg_ptr, int id, - int *temperature) + int *temperature) { - struct temp_sensor_registers *tsr; u32 temp; int ret; @@ -621,11 +652,11 @@ int omap_bandgap_read_temperature(struct omap_bandgap *bg_ptr, int 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; + mutex_lock(&bg_ptr->bg_mutex); + temp = omap_bandgap_read_temp(bg_ptr, id); + mutex_unlock(&bg_ptr->bg_mutex); - ret |= adc_to_temp_conversion(bg_ptr, id, temp, &temp); + ret |= omap_bandgap_adc_to_mcelsius(bg_ptr, temp, &temp); if (ret) return -EIO; @@ -644,7 +675,7 @@ int omap_bandgap_read_temperature(struct omap_bandgap *bg_ptr, int id, * returns 0 on success or the proper error code */ int omap_bandgap_set_sensor_data(struct omap_bandgap *bg_ptr, int id, - void *data) + void *data) { int ret = omap_bandgap_validate(bg_ptr, id); if (ret) @@ -672,65 +703,64 @@ void *omap_bandgap_get_sensor_data(struct omap_bandgap *bg_ptr, int id) return bg_ptr->conf->sensors[id].data; } +/*** Helper functions used during device initialization ***/ + +/** + * omap_bandgap_force_single_read() - executes 1 single ADC conversion + * @bg_ptr: pointer to struct omap_bandgap + * @id: sensor id which it is desired to read 1 temperature + * + * Used to initialize the conversion state machine and set it to a valid + * state. Called during device initialization and context restore events. + */ 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); - } + if (OMAP_BANDGAP_HAS(bg_ptr, MODE_CONFIG)) + RMW_BITS(bg_ptr, id, bgap_mode_ctrl, mode_ctrl_mask, 0); /* 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); + RMW_BITS(bg_ptr, id, temp_sensor_ctrl, bgap_soc_mask, 1); /* 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); - } + temp = omap_bandgap_read_temp(bg_ptr, id); + + while ((temp == 0) && --counter) + temp = omap_bandgap_read_temp(bg_ptr, id); + /* REVISIT: Check correct condition for end of conversion */ + /* 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); + RMW_BITS(bg_ptr, id, temp_sensor_ctrl, bgap_soc_mask, 0); return 0; } /** - * enable_continuous_mode() - One time enabling of continuous conversion mode - * @bg_ptr - pointer to scm instance + * omap_bandgap_set_continous_mode() - One time enabling of continuous mode + * @bg_ptr: pointer to struct omap_bandgap * - * Call this function only if HAS(MODE_CONFIG) is set + * Call this function only if HAS(MODE_CONFIG) is set. As this driver may + * be used for junction temperature monitoring, it is desirable that the + * sensors are operational all the time, so that alerts are generated + * properly. */ -static int enable_continuous_mode(struct omap_bandgap *bg_ptr) +static int omap_bandgap_set_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); + RMW_BITS(bg_ptr, i, bgap_mode_ctrl, mode_ctrl_mask, 1); } return 0; } static int omap_bandgap_tshut_init(struct omap_bandgap *bg_ptr, - struct platform_device *pdev) + struct platform_device *pdev) { int gpio_nr = bg_ptr->tshut_gpio; int status; @@ -763,7 +793,7 @@ static int omap_bandgap_tshut_init(struct omap_bandgap *bg_ptr, /* 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) + struct platform_device *pdev) { int ret; @@ -773,7 +803,7 @@ static int omap_bandgap_talert_init(struct omap_bandgap *bg_ptr, return bg_ptr->irq; } ret = request_threaded_irq(bg_ptr->irq, NULL, - talert_irq_handler, + omap_bandgap_talert_irq_handler, IRQF_TRIGGER_HIGH | IRQF_ONESHOT, "talert", bg_ptr); if (ret) { @@ -823,7 +853,7 @@ static struct omap_bandgap *omap_bandgap_build(struct platform_device *pdev) bg_ptr->base = chunk; if (IS_ERR(chunk)) return ERR_CAST(chunk); - + i++; } while (res); @@ -843,6 +873,8 @@ static struct omap_bandgap *omap_bandgap_build(struct platform_device *pdev) return bg_ptr; } +/*** Device driver call backs ***/ + static int omap_bandgap_probe(struct platform_device *pdev) { @@ -880,7 +912,6 @@ int omap_bandgap_probe(struct platform_device *pdev) 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; @@ -911,7 +942,9 @@ int omap_bandgap_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Cannot re-set clock rate. Continuing\n"); bg_ptr->clk_rate = clk_rate; - clk_enable(bg_ptr->fclock); + if (OMAP_BANDGAP_HAS(bg_ptr, CLK_CTRL)) + clk_prepare_enable(bg_ptr->fclock); + mutex_init(&bg_ptr->bg_mutex); bg_ptr->dev = &pdev->dev; @@ -922,33 +955,42 @@ int omap_bandgap_probe(struct platform_device *pdev) /* 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); + RMW_BITS(bg_ptr, i, bgap_counter, counter_mask, 1); + /* Set default thresholds for alert and shutdown */ 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, TALERT)) { + /* Set initial Talert thresholds */ + RMW_BITS(bg_ptr, i, bgap_threshold, + threshold_tcold_mask, ts_data->t_cold); + RMW_BITS(bg_ptr, i, bgap_threshold, + threshold_thot_mask, ts_data->t_hot); + /* Enable the alert events */ + RMW_BITS(bg_ptr, i, bgap_mask_ctrl, mask_hot_mask, 1); + RMW_BITS(bg_ptr, i, bgap_mask_ctrl, mask_cold_mask, 1); + } + 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); + /* Set initial Tshut thresholds */ + RMW_BITS(bg_ptr, i, tshut_threshold, + tshut_hot_mask, ts_data->tshut_hot); + RMW_BITS(bg_ptr, i, tshut_threshold, + tshut_cold_mask, ts_data->tshut_cold); } } if (OMAP_BANDGAP_HAS(bg_ptr, MODE_CONFIG)) - enable_continuous_mode(bg_ptr); + omap_bandgap_set_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); + RMW_BITS(bg_ptr, i, bgap_counter, counter_mask, + bg_ptr->clk_rate / 4); /* Every thing is good? Then expose the sensors */ for (i = 0; i < bg_ptr->conf->sensor_count; i++) { @@ -979,7 +1021,8 @@ int omap_bandgap_probe(struct platform_device *pdev) return 0; disable_clk: - clk_disable(bg_ptr->fclock); + if (OMAP_BANDGAP_HAS(bg_ptr, CLK_CTRL)) + clk_disable_unprepare(bg_ptr->fclock); put_clks: clk_put(bg_ptr->fclock); clk_put(bg_ptr->div_clk); @@ -1009,7 +1052,8 @@ int omap_bandgap_remove(struct platform_device *pdev) omap_bandgap_power(bg_ptr, false); - clk_disable(bg_ptr->fclock); + if (OMAP_BANDGAP_HAS(bg_ptr, CLK_CTRL)) + clk_disable_unprepare(bg_ptr->fclock); clk_put(bg_ptr->fclock); clk_put(bg_ptr->div_clk); @@ -1073,9 +1117,8 @@ static int omap_bandgap_restore_ctxt(struct omap_bandgap *bg_ptr) val = omap_bandgap_readl(bg_ptr, tsr->bgap_counter); if (OMAP_BANDGAP_HAS(bg_ptr, TSHUT_CONFIG)) - omap_bandgap_writel(bg_ptr, - rval->tshut_threshold, - tsr->tshut_threshold); + omap_bandgap_writel(bg_ptr, rval->tshut_threshold, + tsr->tshut_threshold); /* Force immediate temperature measurement and update * of the DTEMP field */ @@ -1083,16 +1126,15 @@ static int omap_bandgap_restore_ctxt(struct omap_bandgap *bg_ptr) if (OMAP_BANDGAP_HAS(bg_ptr, COUNTER)) omap_bandgap_writel(bg_ptr, rval->bg_counter, - tsr->bgap_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); + 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_threshold, + tsr->bgap_threshold); omap_bandgap_writel(bg_ptr, rval->bg_ctrl, - tsr->bgap_mask_ctrl); + tsr->bgap_mask_ctrl); } } @@ -1106,7 +1148,9 @@ static int omap_bandgap_suspend(struct device *dev) err = omap_bandgap_save_ctxt(bg_ptr); omap_bandgap_power(bg_ptr, false); - clk_disable(bg_ptr->fclock); + + if (OMAP_BANDGAP_HAS(bg_ptr, CLK_CTRL)) + clk_disable_unprepare(bg_ptr->fclock); return err; } @@ -1115,7 +1159,9 @@ static int omap_bandgap_resume(struct device *dev) { struct omap_bandgap *bg_ptr = dev_get_drvdata(dev); - clk_enable(bg_ptr->fclock); + if (OMAP_BANDGAP_HAS(bg_ptr, CLK_CTRL)) + clk_prepare_enable(bg_ptr->fclock); + omap_bandgap_power(bg_ptr, true); return omap_bandgap_restore_ctxt(bg_ptr);