1 /**
2  * \file
3  *
4  * \brief SAM PWM Driver for SAMB11
5  *
6  * Copyright (C) 2015-2016 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 #include "pwm.h"
47 #include "system_sam_b.h"
48 
49 /**
50  * \internal Get the register configuration values by PWM device
51  */
_pwm_reg_output_polarity(enum pwm_device_select device_select)52 static uint32_t _pwm_reg_output_polarity(enum pwm_device_select device_select)
53 {
54 	switch (device_select) {
55 		case PWM0:
56 			return LPMCU_MISC_REGS_PWM0_CTRL_OUTPUT_POLARITY;
57 		case PWM1:
58 			return LPMCU_MISC_REGS_PWM1_CTRL_OUTPUT_POLARITY;
59 		case PWM2:
60 			return LPMCU_MISC_REGS_PWM2_CTRL_OUTPUT_POLARITY;
61 		case PWM3:
62 			return LPMCU_MISC_REGS_PWM3_CTRL_OUTPUT_POLARITY;
63 
64 		default:
65 			return 0;
66 	}
67 }
68 
69 /**
70  * \internal Get the register configuration values by PWM device
71  */
_pwm_reg_agcdata_fmt(enum pwm_device_select device_select)72 static uint32_t _pwm_reg_agcdata_fmt(enum pwm_device_select device_select)
73 {
74 	switch (device_select) {
75 		case PWM0:
76 			return LPMCU_MISC_REGS_PWM0_CTRL_AGCDATA_FMT;
77 		case PWM1:
78 			return LPMCU_MISC_REGS_PWM1_CTRL_AGCDATA_FMT;
79 		case PWM2:
80 			return LPMCU_MISC_REGS_PWM2_CTRL_AGCDATA_FMT;
81 		case PWM3:
82 			return LPMCU_MISC_REGS_PWM3_CTRL_AGCDATA_FMT;
83 
84 		default:
85 			return 0;
86 	}
87 }
88 
89 /**
90  * \internal Get the register configuration values by PWM device
91  */
_pwm_reg_sample_method(enum pwm_device_select device_select,bool value)92 static uint32_t _pwm_reg_sample_method(enum pwm_device_select device_select, bool value)
93 {
94 	switch (device_select) {
95 		case PWM0:
96 			return (LPMCU_MISC_REGS_PWM0_CTRL_SAMPLE_METHOD &
97 					((value) << LPMCU_MISC_REGS_PWM0_CTRL_SAMPLE_METHOD_Pos));
98 		case PWM1:
99 			return (LPMCU_MISC_REGS_PWM1_CTRL_SAMPLE_METHOD &
100 					((value) << LPMCU_MISC_REGS_PWM1_CTRL_SAMPLE_METHOD_Pos));
101 		case PWM2:
102 			return (LPMCU_MISC_REGS_PWM2_CTRL_SAMPLE_METHOD &
103 					((value) << LPMCU_MISC_REGS_PWM2_CTRL_SAMPLE_METHOD_Pos));
104 		case PWM3:
105 			return (LPMCU_MISC_REGS_PWM3_CTRL_SAMPLE_METHOD &
106 					((value) << LPMCU_MISC_REGS_PWM3_CTRL_SAMPLE_METHOD_Pos));
107 
108 		default:
109 			return 0;
110 	}
111 }
112 
113 /**
114  * \internal Get the register configuration values by PWM device
115  */
_pwm_reg_period(enum pwm_device_select device_select,enum pwm_period value)116 static uint32_t _pwm_reg_period(enum pwm_device_select device_select, enum pwm_period value)
117 {
118 	switch (device_select) {
119 		case PWM0:
120 			return LPMCU_MISC_REGS_PWM0_CTRL_PWM_PERIOD(value);
121 		case PWM1:
122 			return LPMCU_MISC_REGS_PWM1_CTRL_PWM_PERIOD(value);
123 		case PWM2:
124 			return LPMCU_MISC_REGS_PWM2_CTRL_PWM_PERIOD(value);
125 		case PWM3:
126 			return LPMCU_MISC_REGS_PWM3_CTRL_PWM_PERIOD(value);
127 
128 		default:
129 			return 0;
130 	}
131 }
132 
133 /**
134  * \internal Get the register configuration values by PWM device
135  */
_pwm_reg_agcdata_in(enum pwm_device_select device_select,bool agcdata_format,uint8_t duty_cycle)136 static uint32_t _pwm_reg_agcdata_in(
137 				enum pwm_device_select device_select, \
138 				bool agcdata_format, \
139 				uint8_t duty_cycle)
140 {
141 	int32_t agcdata_in;
142 
143 	if (agcdata_format) {
144 		agcdata_in = (uint16_t)((1024 * duty_cycle) / 100);
145 	} else {
146 		agcdata_in = (int16_t)((1024 * duty_cycle) / 100 - 512);
147 	}
148 	switch (device_select) {
149 		case PWM0:
150 			return LPMCU_MISC_REGS_PWM0_CTRL_AGCDATA_IN(agcdata_in);
151 		case PWM1:
152 			return LPMCU_MISC_REGS_PWM1_CTRL_AGCDATA_IN(agcdata_in);
153 		case PWM2:
154 			return LPMCU_MISC_REGS_PWM2_CTRL_AGCDATA_IN(agcdata_in);
155 		case PWM3:
156 			return LPMCU_MISC_REGS_PWM3_CTRL_AGCDATA_IN(agcdata_in);
157 
158 		default:
159 			return 0;
160 	}
161 }
162 
163 /**
164  * \internal Get the register configuration values by PWM device
165  */
_pwm_reg_clock_sel(enum pwm_device_select device_select,enum pwm_clock_select value)166 static uint32_t _pwm_reg_clock_sel(enum pwm_device_select device_select, enum pwm_clock_select value)
167 {
168 	switch (device_select) {
169 		case PWM0:
170 			return LPMCU_MISC_REGS_PWM0_CTRL_CLOCK_SEL(value);
171 		case PWM1:
172 			return LPMCU_MISC_REGS_PWM1_CTRL_CLOCK_SEL(value);
173 		case PWM2:
174 			return LPMCU_MISC_REGS_PWM2_CTRL_CLOCK_SEL(value);
175 		case PWM3:
176 			return LPMCU_MISC_REGS_PWM3_CTRL_CLOCK_SEL(value);
177 
178 		default:
179 			return 0;
180 	}
181 }
182 
183 /**
184  *  \brief Initializes a pwm configuration structure to defaults.
185  *
186  *  Initializes a given pwm configuration structure to a set of
187  *  known default values. This function should be called on all new
188  *  instances of these configuration structures before being modified by the
189  *  user application.
190  *
191  *  The default configuration is as follows:
192  *   \li Not to inverse the polarity
193  *   \li Sample method 0
194  *   \li PWM period is 4
195  *   \li Duty cycle is 50%
196  *   \li Clock is 26MHz
197  *   \li Output frequency is 25.4KHz
198  *   \li Pinmux pad
199  *
200  *  \param[out] config  Configuration structure to initialize to default values
201  */
pwm_get_config_defaults(struct pwm_config * const config)202 void pwm_get_config_defaults(struct pwm_config *const config)
203 {
204 	config->output_polarity = false;
205 	config->agcdata_format = false;
206 	config->sample_method = PWM_SAMPLE_METHOD_0;
207 	config->period = PWM_PERIOD_4;
208 	config->duty_cycle = 50;
209 	config->clock_select = PWM_CLOCK_SELECT_26_0;
210 	config->pin_number_pad = 0;
211 	config->pinmux_sel_pad = 0;
212 }
213 
214 /**
215  * \brief Set the duty cycle of the PWM module.
216  *
217  * This function will set the duty cycle of the PWM module, based on the values
218  * of setting.
219  *
220  * \param[in]   device_select   PWM device
221  * \param[in]   duty_cycle      This value specifies the duty cycle(%)
222  */
pwm_set_duty_cycle(enum pwm_device_select device_select,uint8_t duty_cycle)223 void pwm_set_duty_cycle(enum pwm_device_select device_select, \
224 					uint8_t duty_cycle)
225 {
226 	bool agcdata_format;
227 	uint32_t temp;
228 
229 	switch(device_select) {
230 		case PWM0:
231 			agcdata_format = LPMCU_MISC_REGS0->PWM0_CTRL.bit.AGCDATA_FMT;
232 			temp = LPMCU_MISC_REGS0->PWM0_CTRL.reg;
233 			temp &= ~LPMCU_MISC_REGS_PWM0_CTRL_AGCDATA_IN_Msk;
234 			temp |= _pwm_reg_agcdata_in(device_select, agcdata_format, duty_cycle);
235 			LPMCU_MISC_REGS0->PWM0_CTRL.reg = temp;
236 			break;
237 
238 		case PWM1:
239 			agcdata_format = LPMCU_MISC_REGS0->PWM1_CTRL.bit.AGCDATA_FMT;
240 			temp = LPMCU_MISC_REGS0->PWM1_CTRL.reg;
241 			temp &= ~LPMCU_MISC_REGS_PWM1_CTRL_AGCDATA_IN_Msk;
242 			temp |= _pwm_reg_agcdata_in(device_select, agcdata_format, duty_cycle);
243 			LPMCU_MISC_REGS0->PWM1_CTRL.reg = temp;
244 			break;
245 
246 		case PWM2:
247 			agcdata_format = LPMCU_MISC_REGS0->PWM2_CTRL.bit.AGCDATA_FMT;
248 			temp = LPMCU_MISC_REGS0->PWM2_CTRL.reg;
249 			temp &= ~LPMCU_MISC_REGS_PWM2_CTRL_AGCDATA_IN_Msk;
250 			temp |= _pwm_reg_agcdata_in(device_select, agcdata_format, duty_cycle);
251 			LPMCU_MISC_REGS0->PWM2_CTRL.reg = temp;
252 			break;
253 
254 		case PWM3:
255 			agcdata_format = LPMCU_MISC_REGS0->PWM3_CTRL.bit.AGCDATA_FMT;
256 			temp = LPMCU_MISC_REGS0->PWM3_CTRL.reg;
257 			temp &= ~LPMCU_MISC_REGS_PWM3_CTRL_AGCDATA_IN_Msk;
258 			temp |= _pwm_reg_agcdata_in(device_select, agcdata_format, duty_cycle);
259 			LPMCU_MISC_REGS0->PWM3_CTRL.reg = temp;
260 			break;
261 	}
262 }
263 
264 /**
265  * \brief Set the period of the PWM module.
266  *
267  * This function will set the frequence of the PWM module, based on the values
268  * of setting period.
269  *
270  * \param[in]   device_select   PWM device
271  * \param[in]   period          Programmable PWM update period
272  */
pwm_set_period(enum pwm_device_select device_select,enum pwm_period period)273 void pwm_set_period(enum pwm_device_select device_select, \
274 					enum pwm_period period)
275 {
276 	uint32_t reg_value;
277 	uint32_t temp;
278 
279 	if (period > PWM_PERIOD_8) {
280 		reg_value = PWM_PERIOD_4;
281 	}
282 	reg_value = _pwm_reg_period(device_select, period);
283 
284 	switch(device_select) {
285 		case PWM0:
286 			temp = LPMCU_MISC_REGS0->PWM0_CTRL.reg;
287 			temp &= ~LPMCU_MISC_REGS_PWM0_CTRL_PWM_PERIOD_Msk;
288 			temp |= reg_value;
289 			LPMCU_MISC_REGS0->PWM0_CTRL.reg = temp;
290 			break;
291 
292 		case PWM1:
293 			temp = LPMCU_MISC_REGS0->PWM1_CTRL.reg;
294 			temp &= ~LPMCU_MISC_REGS_PWM1_CTRL_PWM_PERIOD_Msk;
295 			temp |= reg_value;
296 			LPMCU_MISC_REGS0->PWM1_CTRL.reg = temp;
297 			break;
298 
299 		case PWM2:
300 			temp = LPMCU_MISC_REGS0->PWM2_CTRL.reg;
301 			temp &= ~LPMCU_MISC_REGS_PWM2_CTRL_PWM_PERIOD_Msk;
302 			temp |= reg_value;
303 			LPMCU_MISC_REGS0->PWM2_CTRL.reg = temp;
304 			break;
305 
306 		case PWM3:
307 			temp = LPMCU_MISC_REGS0->PWM3_CTRL.reg;
308 			temp &= ~LPMCU_MISC_REGS_PWM3_CTRL_PWM_PERIOD_Msk;
309 			temp |= reg_value;
310 			LPMCU_MISC_REGS0->PWM3_CTRL.reg = temp;
311 			break;
312 	}
313 }
314 
315 /**
316  * \brief Initializes the PWM module
317  *
318  * This function will initialize the PWM module, based on the values
319  * of the config struct.
320  *
321  * \param[in]   device_select   PWM device
322  * \param[in]   config          Pointer to the config struct
323  *
324  * \return The status of the configuration.
325  * \retval STATUS_ERR_UNSUPPORTED_DEV  If unsupported device were provided
326  * \retval STATUS_OK                   If the configuration was written
327  */
pwm_init(enum pwm_device_select device_select,const struct pwm_config * const config)328 enum status_code pwm_init(enum pwm_device_select device_select, \
329 		const struct pwm_config *const config)
330 {
331 	uint32_t reg_value = 0;
332 
333 	if (device_select > PWM3) {
334 		return STATUS_ERR_UNSUPPORTED_DEV;
335 	}
336 
337 	if (config->output_polarity) {
338 		reg_value |= _pwm_reg_output_polarity(device_select);
339 	}
340 
341 	if (config->agcdata_format) {
342 		reg_value |= _pwm_reg_agcdata_fmt(device_select);
343 	}
344 
345 	reg_value |= _pwm_reg_sample_method(device_select, config->sample_method);
346 
347 	/* If period > 8 will be set to 4 as default. */
348 	if (config->period > PWM_PERIOD_8) {
349 		reg_value |= _pwm_reg_period(device_select, PWM_PERIOD_4);
350 	} else {
351 		reg_value |= _pwm_reg_period(device_select, config->period);
352 	}
353 	reg_value |= _pwm_reg_agcdata_in(device_select, config->agcdata_format, \
354 									config->duty_cycle);
355 
356 	reg_value |= _pwm_reg_clock_sel(device_select, config->clock_select);
357 
358 	switch(device_select) {
359 		case PWM0:
360 			LPMCU_MISC_REGS0->PWM0_CTRL.reg = reg_value;
361 			break;
362 
363 		case PWM1:
364 			LPMCU_MISC_REGS0->PWM1_CTRL.reg = reg_value;
365 			break;
366 
367 		case PWM2:
368 			LPMCU_MISC_REGS0->PWM2_CTRL.reg = reg_value;
369 			break;
370 
371 		case PWM3:
372 			LPMCU_MISC_REGS0->PWM3_CTRL.reg = reg_value;
373 			break;
374 	}
375 
376 	struct gpio_config config_gpio;
377 	gpio_get_config_defaults(&config_gpio);
378 	config_gpio.direction = GPIO_PIN_DIR_OUTPUT;
379 	gpio_pin_set_config(config->pin_number_pad, &config_gpio);
380 	gpio_pinmux_cofiguration(config->pin_number_pad, \
381 							(uint16_t)(config->pinmux_sel_pad));
382 
383 	return STATUS_OK;
384 }
385 
386 /**
387  * \brief Enables the PWM module
388  *
389  * This function will enable the PWM module.
390  *
391  * \param[in] device_select  PWM device
392  */
pwm_enable(enum pwm_device_select device_select)393 void pwm_enable(enum pwm_device_select device_select)
394 {
395 	switch (device_select) {
396 		case PWM0:
397 			system_clock_peripheral_enable(PERIPHERAL_PWM0);
398 			LPMCU_MISC_REGS0->PWM0_CTRL.reg |= LPMCU_MISC_REGS_PWM0_CTRL_PWM_EN;
399 			break;
400 
401 		case PWM1:
402 			system_clock_peripheral_enable(PERIPHERAL_PWM1);
403 			LPMCU_MISC_REGS0->PWM1_CTRL.reg |= LPMCU_MISC_REGS_PWM1_CTRL_PWM_EN;
404 			break;
405 
406 		case PWM2:
407 			system_clock_peripheral_enable(PERIPHERAL_PWM2);
408 			LPMCU_MISC_REGS0->PWM2_CTRL.reg |= LPMCU_MISC_REGS_PWM2_CTRL_PWM_EN;
409 			break;
410 
411 		case PWM3:
412 			system_clock_peripheral_enable(PERIPHERAL_PWM3);
413 			LPMCU_MISC_REGS0->PWM3_CTRL.reg |= LPMCU_MISC_REGS_PWM3_CTRL_PWM_EN;
414 			break;
415 	}
416 }
417 
418 /**
419  * \brief Disable the PWM module
420  *
421  * This function will disable the PWM module.
422  *
423  * \param[in] device_select  PWM device
424  */
pwm_disable(enum pwm_device_select device_select)425 void pwm_disable(enum pwm_device_select device_select)
426 {
427 	switch (device_select) {
428 		case PWM0:
429 			system_clock_peripheral_disable(PERIPHERAL_PWM0);
430 			LPMCU_MISC_REGS0->PWM0_CTRL.reg &= ~LPMCU_MISC_REGS_PWM0_CTRL_PWM_EN;
431 			break;
432 
433 		case PWM1:
434 			system_clock_peripheral_disable(PERIPHERAL_PWM1);
435 			LPMCU_MISC_REGS0->PWM1_CTRL.reg &= ~LPMCU_MISC_REGS_PWM1_CTRL_PWM_EN;
436 			break;
437 
438 		case PWM2:
439 			system_clock_peripheral_disable(PERIPHERAL_PWM2);
440 			LPMCU_MISC_REGS0->PWM2_CTRL.reg &= ~LPMCU_MISC_REGS_PWM2_CTRL_PWM_EN;
441 			break;
442 
443 		case PWM3:
444 			system_clock_peripheral_disable(PERIPHERAL_PWM3);
445 			LPMCU_MISC_REGS0->PWM3_CTRL.reg &= ~LPMCU_MISC_REGS_PWM3_CTRL_PWM_EN;
446 			break;
447 	}
448 }
449 
450 /**
451  * \brief Reset the PWM module
452  *
453  * This function will reset the PWM module.
454  *
455  * \param[in] device_select  PWM device
456  */
pwm_reset(enum pwm_device_select device_select)457 void pwm_reset(enum pwm_device_select device_select)
458 {
459 	switch (device_select) {
460 		case PWM0:
461 			system_peripheral_reset(PERIPHERAL_PWM0);
462 			break;
463 
464 		case PWM1:
465 			system_peripheral_reset(PERIPHERAL_PWM1);
466 			break;
467 
468 		case PWM2:
469 			system_peripheral_reset(PERIPHERAL_PWM2);
470 			break;
471 
472 		case PWM3:
473 			system_peripheral_reset(PERIPHERAL_PWM3);
474 			break;
475 	}
476 }