1 /**
2  * \file
3  *
4  * \brief SAM Temperature Sensor (TSENS) Driver
5  *
6  * Copyright (C) 2015 Atmel Corporation. All rights reserved.
7  *
8  * \asf_license_start
9  *
10  * \page License
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions are met:
14  *
15  * 1. Redistributions of source code must retain the above copyright notice,
16  *    this list of conditions and the following disclaimer.
17  *
18  * 2. Redistributions in binary form must reproduce the above copyright notice,
19  *    this list of conditions and the following disclaimer in the documentation
20  *    and/or other materials provided with the distribution.
21  *
22  * 3. The name of Atmel may not be used to endorse or promote products derived
23  *    from this software without specific prior written permission.
24  *
25  * 4. This software may only be redistributed and used in connection with an
26  *    Atmel microcontroller product.
27  *
28  * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
29  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
30  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
31  * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR
32  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
36  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
37  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
38  * POSSIBILITY OF SUCH DAMAGE.
39  *
40  * \asf_license_stop
41  *
42  */
43 /*
44  * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a>
45  */
46 
47 #include "tsens.h"
48 
49 #define WINDOW_MIN_VALUE    -40
50 #define WINDOW_MAX_VALUE    105
51 
52 /**
53  * \internal Writes an TSENS configuration to the hardware module
54  *
55  * Writes out a given TSENS module configuration to the hardware module.
56  *
57  * \param[in]  config       Pointer to configuration struct
58  *
59  * \return Status of the configuration procedure.
60  * \retval STATUS_OK               The configuration was successful
61  * \retval STATUS_ERR_INVALID_ARG  Invalid argument(s) were provided
62  */
_tsens_set_config(struct tsens_config * const config)63 static enum status_code _tsens_set_config(struct tsens_config *const config)
64 {
65 	/* Configure GCLK channel and enable clock */
66 	struct system_gclk_chan_config gclk_chan_conf;
67 	system_gclk_chan_get_config_defaults(&gclk_chan_conf);
68 	gclk_chan_conf.source_generator = config->clock_source;
69 	system_gclk_chan_set_config(TSENS_GCLK_ID, &gclk_chan_conf);
70 	system_gclk_chan_enable(TSENS_GCLK_ID);
71 
72 	/* Configure run in standby */
73 	TSENS->CTRLA.reg = (config->run_in_standby << TSENS_CTRLA_RUNSTDBY_Pos);
74 
75 	/* Check validity of window thresholds */
76 	if (config->window.window_mode != TSENS_WINDOW_MODE_DISABLE) {
77 		if((config->window.window_lower_value < WINDOW_MIN_VALUE) || \
78 			(config->window.window_upper_value > WINDOW_MAX_VALUE)) {
79 				return STATUS_ERR_INVALID_ARG;
80 			}
81 	}
82 
83 	/* Configure CTRLC */
84 	TSENS->CTRLC.reg =
85 			(config->free_running << TSENS_CTRLC_FREERUN_Pos) | \
86 			(config->window.window_mode);
87 
88 #if ERRATA_14476
89 	/* Configure lower threshold */
90 	TSENS->WINLT.reg = TSENS_WINLT_WINLT(config->window.window_upper_value);
91 
92 	/* Configure upper threshold */
93 	TSENS->WINUT.reg = TSENS_WINLT_WINLT(config->window.window_lower_value);
94 #else
95 	/* Configure lower threshold */
96 	TSENS->WINLT.reg = TSENS_WINLT_WINLT(config->window.window_lower_value);
97 
98 	/* Configure upper threshold */
99 	TSENS->WINUT.reg = TSENS_WINLT_WINLT(config->window.window_upper_value);
100 #endif
101 
102 	/* Configure events */
103 	TSENS->EVCTRL.reg = config->event_action;
104 
105 	/* Disable all interrupts */
106 	TSENS->INTENCLR.reg =
107 			(1 << TSENS_INTENCLR_OVF_Pos) | (1 << TSENS_INTENCLR_WINMON_Pos) | \
108 			(1 << TSENS_INTENCLR_OVERRUN_Pos) | (1 << TSENS_INTENCLR_RESRDY_Pos);
109 
110 	/* Read calibration from NVM */
111 	uint32_t tsens_bits = *((uint32_t *)NVMCTRL_TEMP_LOG);
112 	uint32_t tsens_tcal = \
113 				((tsens_bits & TSENS_FUSES_TCAL_Msk) >> TSENS_FUSES_TCAL_Pos);
114 	uint32_t tsens_fcal = \
115 				((tsens_bits & TSENS_FUSES_FCAL_Msk) >> TSENS_FUSES_FCAL_Pos);
116 
117 	TSENS->CAL.reg = TSENS_CAL_TCAL(tsens_tcal) | TSENS_CAL_FCAL(tsens_fcal);
118 	TSENS->GAIN.reg = TSENS_GAIN_GAIN(config->calibration.gain);
119 	TSENS->OFFSET.reg = TSENS_OFFSET_OFFSETC(config->calibration.offset);
120 
121 	return STATUS_OK;
122 }
123 
124 /**
125  * \brief Initializes the TSENS.
126  *
127  * Initializes the TSENS device struct and the hardware module based on the
128  * given configuration struct values.
129  *
130  * \param[in]  config      Pointer to the configuration struct
131  *
132  * \return Status of the initialization procedure.
133  * \retval STATUS_OK                The initialization was successful
134  * \retval STATUS_ERR_INVALID_ARG   Invalid argument(s) were provided
135  * \retval STATUS_BUSY              The module is busy with a reset operation
136  * \retval STATUS_ERR_DENIED        The module is enabled
137  */
tsens_init(struct tsens_config * config)138 enum status_code tsens_init(struct tsens_config *config)
139 {
140 	/* Sanity check arguments */
141 	Assert(config);
142 
143 	/* Turn on the digital interface clock */
144 	system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBA, MCLK_APBAMASK_TSENS);
145 
146 	if (TSENS->CTRLA.reg & TSENS_CTRLA_SWRST) {
147 		/* We are in the middle of a reset. Abort. */
148 		return STATUS_BUSY;
149 	}
150 
151 	if (TSENS->CTRLA.reg & TSENS_CTRLA_ENABLE) {
152 		/* Module must be disabled before initialization. Abort. */
153 		return STATUS_ERR_DENIED;
154 	}
155 
156 	/* Write configuration to module */
157 	return _tsens_set_config(config);
158 }
159 
160 /**
161  * \brief Initializes an TSENS configuration structure to defaults.
162  *
163  * Initializes a given TSENS configuration struct to a set of known default
164  * values. This function should be called on any new instance of the
165  * configuration struct before being modified by the user application.
166  *
167  * The default configuration is as follows:
168  *  \li GCLK generator 0 (GCLK main) clock source
169  *  \li All events (input and generation) disabled
170  *  \li Free running disabled
171  *  \li Run in standby disabled
172  *  \li Window monitor disabled
173  *  \li Register GAIN value
174  *  \li Register OFFSET value
175  *
176  * \note Register GAIN and OFFSET is loaded from NVM, or can also be fixed.
177  * If this bitfield is to be fixed, pay attention to the relationship between GCLK
178  * frequency, GAIN, and resolution. See \ref asfdoc_sam0_tsens_module_overview
179  * "Chapter Module Overview".
180  *
181  * \param[out] config  Pointer to configuration struct to initialize to
182  *                     default values
183  */
tsens_get_config_defaults(struct tsens_config * const config)184 void tsens_get_config_defaults(struct tsens_config *const config)
185 {
186 	Assert(config);
187 	config->clock_source                  = GCLK_GENERATOR_0;
188 	config->free_running                  = false;
189 	config->run_in_standby                = false;
190 	config->window.window_mode            = TSENS_WINDOW_MODE_DISABLE;
191 	config->window.window_upper_value     = 0;
192 	config->window.window_lower_value     = 0;
193 	config->event_action                  = TSENS_EVENT_ACTION_DISABLED;
194 
195 	uint32_t tsens_bits[2];
196 	tsens_bits[0] = *((uint32_t *)NVMCTRL_TEMP_LOG);
197 	tsens_bits[1] = *(((uint32_t *)NVMCTRL_TEMP_LOG) + 1);
198 	config->calibration.offset   = \
199 		((tsens_bits[0] & TSENS_FUSES_OFFSET_Msk) >> TSENS_FUSES_OFFSET_Pos);
200 	config->calibration.gain     = \
201 		((tsens_bits[0] & TSENS_FUSES_GAIN_0_Msk) >> TSENS_FUSES_GAIN_0_Pos) | \
202 		((tsens_bits[1] & TSENS_FUSES_GAIN_1_Msk) >> TSENS_FUSES_GAIN_1_Pos);
203 }
204 
205 /**
206  * \brief Reads the TSENS result.
207  *
208  * Reads the result from a TSENS conversion that was previously started.
209  *
210  * \param[out] result       Pointer to store the result value in
211  *
212  * \return Status of the TSENS read request.
213  * \retval STATUS_OK           The result was retrieved successfully
214  * \retval STATUS_BUSY         A conversion result was not ready
215  * \retval STATUS_ERR_OVERFLOW The result register has been overwritten by the
216  *                             TSENS module before the result was read by the software
217  */
tsens_read(int32_t * result)218 enum status_code tsens_read(int32_t *result)
219 {
220 	Assert(result);
221 
222 	if (!(tsens_get_status() & TSENS_STATUS_RESULT_READY)) {
223 		/* Result not ready */
224 		return STATUS_BUSY;
225 	}
226 
227 	if (TSENS->STATUS.reg & TSENS_STATUS_OVF) {
228 		/* The result is not valid */
229 		return STATUS_ERR_BAD_DATA;
230 	}
231 
232 	/* Get TSENS result */
233 	uint32_t temp = TSENS->VALUE.reg;
234 	if(temp & 0x00800000) {
235 		temp |= ~TSENS_VALUE_MASK;
236 	}
237 #if (ERRATA_14476)
238 	*result = temp * (-1);
239 #endif
240 
241 	/* Reset ready flag */
242 	tsens_clear_status(TSENS_STATUS_RESULT_READY);
243 
244 	if (tsens_get_status() & TSENS_STATUS_OVERRUN) {
245 		tsens_clear_status(TSENS_STATUS_OVERRUN);
246 		return STATUS_ERR_OVERFLOW;
247 	}
248 
249 	return STATUS_OK;
250 }
251