/* ----------------------------------------------------------------------------
 *         SAM Software Package License
 * ----------------------------------------------------------------------------
 * Copyright (c) 2015, Atmel Corporation
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * - Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the disclaimer below.
 *
 * Atmel's name may not be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
 * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * ----------------------------------------------------------------------------
 */

/** \addtogroup pwm_module Working with PWM
 * \section Purpose
 * The PWM driver provides the interface to configure and use the PWM
 * peripheral.
 *
 * The PWM macrocell controls square output waveforms of 4 channels.
 * Characteristics of output waveforms such as period, duty-cycle can be configured.\n
 *
 * Before enabling the channels, they must have been configured first.
 * The main settings include:
 * <ul>
 * <li>Configuration of the clock generator.</li>
 * <li>Selection of the clock for each channel.</li>
 * <li>Configuration of output waveform characteristics, such as period, duty-cycle etc.</li>
 * </ul>
 *
 * After the channels is enabled, the user must use respective update registers
 * to change the wave characteristics to prevent unexpected output waveform.
 * i.e. PWM_CUPDx register should be used if user want to change duty-cycle
 * when the channel is enabled.
 *
 * \section Usage
 * <ul>
 * <li>  Configure PWM clock using pwmc_configure_clocks().
 * <li>  Enable & disable given PWM channel using pwmc_enable_channel() and pwmc_disable_channel().
 * <li>  Enable & disable interrupt of given PWM channel using pwmc_enable_channel_it()
 * and pwmc_disable_channel_it().
 * <li>  Set feature of the given PWM channel's output signal using pwmc_set_period()
 * and pwmc_set_duty_cycle().
 * </li>
 * </ul>
 *
 * For more accurate information, please look at the PWM section of the
 * Datasheet.
 *
 * Related files :\n
 * \ref pwmc.c\n
 * \ref pwmc.h.\n
 */
/*@{*/
/*@}*/

/**
 * \file
 *
 * Implementation of the Pulse Width Modulation Controller (PWM) peripheral.
 *
 */

/*----------------------------------------------------------------------------
 *        Headers
 *----------------------------------------------------------------------------*/

#include "chip.h"
#include "peripherals/pwmc.h"

#include <stdint.h>
#include <assert.h>

/*----------------------------------------------------------------------------
 *        Exported functions
 *----------------------------------------------------------------------------*/

void pwmc_configure_clocks(Pwm * p_pwm, uint32_t mode)
{
	p_pwm->PWM_CLK = mode;
}

void pwmc_enable_channel(Pwm * p_pwm, uint8_t channel)
{
	p_pwm->PWM_ENA = 0x1ul << channel;
}

void pwmc_disable_channel(Pwm * p_pwm, uint8_t channel)
{
	p_pwm->PWM_DIS = 0x1ul << channel;
}

void pwmc_enable_channel_it(Pwm * p_pwm, uint8_t channel)
{
	p_pwm->PWM_IER1 = 0x1ul << channel;
}

void pwmc_disable_channel_it(Pwm * p_pwm, uint8_t channel)
{
	p_pwm->PWM_IDR1 = 0x1ul << channel;
}

void pwmc_configure_channel(Pwm * p_pwm, uint8_t channel, uint32_t mode)
{
	p_pwm->PWM_CH_NUM[channel].PWM_CMR = mode;
}

void pwmc_set_period(Pwm * p_pwm, uint8_t channel, uint16_t period)
{
	/* If channel is disabled, write to CPRD */
	if ((p_pwm->PWM_SR & (1 << channel)) == 0) {
		p_pwm->PWM_CH_NUM[channel].PWM_CPRD = period;
	}
	/* Otherwise use update register */
	else {
		p_pwm->PWM_CH_NUM[channel].PWM_CPRDUPD = period;
	}
}

void pwmc_set_duty_cycle(Pwm * p_pwm, uint8_t channel, uint16_t duty)
{
	assert(duty <= p_pwm->PWM_CH_NUM[channel].PWM_CPRD);

	/* If channel is disabled, write to CDTY */
	if ((p_pwm->PWM_SR & (1 << channel)) == 0) {
		p_pwm->PWM_CH_NUM[channel].PWM_CDTY = duty;
	}
	/* Otherwise use update register */
	else {
		p_pwm->PWM_CH_NUM[channel].PWM_CDTYUPD = duty;
	}
}
