2013-11-06 10:55:12 -06:00
|
|
|
/****************************************************************************
|
|
|
|
* arch/arm/src/sama5/sam_pwm.c
|
|
|
|
*
|
|
|
|
* Copyright (C) 2013 Gregory Nutt. All rights reserved.
|
|
|
|
* Author: Gregory Nutt <gnutt@nuttx.org>
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
*
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in
|
|
|
|
* the documentation and/or other materials provided with the
|
|
|
|
* distribution.
|
|
|
|
* 3. Neither the name NuttX nor the names of its contributors may be
|
|
|
|
* used to endorse or promote products derived from this software
|
|
|
|
* without specific prior written permission.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
|
|
|
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
|
|
|
* COPYRIGHT OWNER OR CONTRIBUTORS 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.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Included Files
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
#include <nuttx/config.h>
|
|
|
|
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <assert.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <fixedmath.h>
|
|
|
|
#include <debug.h>
|
|
|
|
|
|
|
|
#include <nuttx/arch.h>
|
|
|
|
#include <nuttx/pwm.h>
|
2013-11-06 12:24:51 -06:00
|
|
|
|
|
|
|
#include "chip/sam_pinmap.h"
|
2013-11-06 10:55:12 -06:00
|
|
|
#include <arch/board/board.h>
|
|
|
|
|
|
|
|
#include "up_internal.h"
|
|
|
|
#include "up_arch.h"
|
|
|
|
|
|
|
|
#include "sam_periphclks.h"
|
|
|
|
#include "sam_pio.h"
|
|
|
|
#include "sam_pwm.h"
|
|
|
|
|
|
|
|
#ifdef CONFIG_SAMA5_PWM
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Pre-processor Definitions
|
|
|
|
****************************************************************************/
|
|
|
|
/* Configuration ************************************************************/
|
|
|
|
/* Currently, we support only a single PWM peripheral. However, the hooks
|
|
|
|
* are in place to support multiple PWM peripherals.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define PWM_SINGLE 1
|
|
|
|
|
|
|
|
/* Pulse counting is not supported by this driver */
|
|
|
|
|
|
|
|
#ifdef CONFIG_PWM_PULSECOUNT
|
|
|
|
# warning CONFIG_PWM_PULSECOUNT no supported by this driver.
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* Are we using CLKA? CLKB? If so, what frequency? Select the prescaler
|
|
|
|
* value that allows the largest, valid divider value. This may not be
|
|
|
|
* optimal in all cases, but in general should provide a reasonable frequency
|
|
|
|
* value.
|
|
|
|
*
|
|
|
|
* frequency = MCK / prescaler / div
|
|
|
|
*
|
|
|
|
* Pick smallest prescaler such that:
|
|
|
|
*
|
|
|
|
* prescaler = MCK / frequency / div < 256
|
|
|
|
*
|
|
|
|
* Then:
|
|
|
|
*
|
|
|
|
* div = MCK / prescaler / frequency
|
|
|
|
*
|
|
|
|
* Calulcated Values
|
|
|
|
*
|
|
|
|
* CLKn_PRE = CLKn prescaler value
|
|
|
|
* PWM_CLK_PREn = CLKn prescaler register setting
|
|
|
|
* CLKn_DIV = CLKn divider value
|
|
|
|
* PWM_CLK_DIVn = CLKn divider register setting
|
|
|
|
* CLKn_FREQUENCY = Actual resulting CLKn frequency
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifdef CONFIG_SAMA5_PWM_CLKA
|
|
|
|
|
|
|
|
# if !defined(CONFIG_SAMA5_PWM_CLKA_FREQUENCY)
|
|
|
|
# error CONFIG_SAMA5_PWM_CLKA_FREQUENCY is not defined
|
|
|
|
|
|
|
|
# elif (BOARD_MCK_FREQUENCY / CONFIG_SAMA5_PWM_CLKA_FREQUENCY) < 256
|
2013-11-06 12:24:51 -06:00
|
|
|
# define CLKA_PRE_BITS PWM_CLK_PREA_DIV1
|
|
|
|
# define CLKA_PRE 1
|
2013-11-06 10:55:12 -06:00
|
|
|
|
|
|
|
# elif (BOARD_MCK_FREQUENCY / 2 / CONFIG_SAMA5_PWM_CLKA_FREQUENCY) < 256
|
2013-11-06 12:24:51 -06:00
|
|
|
# define CLKA_PRE_BITS PWM_CLK_PREA_DIV2
|
|
|
|
# define CLKA_PRE 2
|
2013-11-06 10:55:12 -06:00
|
|
|
|
|
|
|
# elif (BOARD_MCK_FREQUENCY / 4 / CONFIG_SAMA5_PWM_CLKA_FREQUENCY) < 256
|
2013-11-06 12:24:51 -06:00
|
|
|
# define CLKA_PRE_BITS PWM_CLK_PREA_DIV4
|
|
|
|
# define CLKA_PRE 4
|
2013-11-06 10:55:12 -06:00
|
|
|
|
|
|
|
# elif (BOARD_MCK_FREQUENCY / 8 / CONFIG_SAMA5_PWM_CLKA_FREQUENCY) < 256
|
2013-11-06 12:24:51 -06:00
|
|
|
# define CLKA_PRE_BITS PWM_CLK_PREA_DIV8
|
|
|
|
# define CLKA_PRE 8
|
2013-11-06 10:55:12 -06:00
|
|
|
|
|
|
|
# elif (BOARD_MCK_FREQUENCY / 16 / CONFIG_SAMA5_PWM_CLKA_FREQUENCY) < 256
|
2013-11-06 12:24:51 -06:00
|
|
|
# define CLKA_PRE_BITS PWM_CLK_PREA_DIV16
|
|
|
|
# define CLKA_PRE 16
|
2013-11-06 10:55:12 -06:00
|
|
|
|
|
|
|
# elif (BOARD_MCK_FREQUENCY / 32 / CONFIG_SAMA5_PWM_CLKA_FREQUENCY) < 256
|
2013-11-06 12:24:51 -06:00
|
|
|
# define CLKA_PRE_BITS PWM_CLK_PREA_DIV32
|
|
|
|
# define CLKA_PRE 32
|
2013-11-06 10:55:12 -06:00
|
|
|
|
|
|
|
# elif (BOARD_MCK_FREQUENCY / 64 / CONFIG_SAMA5_PWM_CLKA_FREQUENCY) < 256
|
2013-11-06 12:24:51 -06:00
|
|
|
# define CLKA_PRE_BITS PWM_CLK_PREA_DIV64
|
|
|
|
# define CLKA_PRE 64
|
2013-11-06 10:55:12 -06:00
|
|
|
|
|
|
|
# elif (BOARD_MCK_FREQUENCY / 128 / CONFIG_SAMA5_PWM_CLKA_FREQUENCY) < 256
|
2013-11-06 12:24:51 -06:00
|
|
|
# define CLKA_PRE_BITS PWM_CLK_PREA_DIV128
|
|
|
|
# define CLKA_PRE 128
|
2013-11-06 10:55:12 -06:00
|
|
|
|
|
|
|
# elif (BOARD_MCK_FREQUENCY / 256 / CONFIG_SAMA5_PWM_CLKA_FREQUENCY) < 256
|
2013-11-06 12:24:51 -06:00
|
|
|
# define CLKA_PRE_BITS PWM_CLK_PREA_DIV256
|
|
|
|
# define CLKA_PRE 256
|
2013-11-06 10:55:12 -06:00
|
|
|
|
|
|
|
# elif (BOARD_MCK_FREQUENCY / 512 / CONFIG_SAMA5_PWM_CLKA_FREQUENCY) < 256
|
2013-11-06 12:24:51 -06:00
|
|
|
# define CLKA_PRE_BITS PWM_CLK_PREA_DIV512
|
|
|
|
# define CLKA_PRE 512
|
2013-11-06 10:55:12 -06:00
|
|
|
|
|
|
|
# elif (BOARD_MCK_FREQUENCY / 1024 / CONFIG_SAMA5_PWM_CLKA_FREQUENCY) < 256
|
2013-11-06 12:24:51 -06:00
|
|
|
# define CLKA_PRE_BITS PWM_CLK_PREA_DIV1024
|
|
|
|
# define CLKA_PRE 1024
|
2013-11-06 10:55:12 -06:00
|
|
|
|
|
|
|
# else
|
|
|
|
# error Cannot realize CONFIG_SAMA5_PWM_CLKA_FREQUENCY
|
|
|
|
# endif
|
|
|
|
|
|
|
|
# define CLKA_DIV (BOARD_MCK_FREQUENCY / CLKA_PRE / CONFIG_SAMA5_PWM_CLKA_FREQUENCY)
|
|
|
|
# define CLKA_FREQUENCY (BOARD_MCK_FREQUENCY / CLKA_PRE / CLKA_DIV)
|
2013-11-06 12:24:51 -06:00
|
|
|
# define CLKA_DIV_BITS PWM_CLK_DIVA(CLKA_DIV)
|
2013-11-06 10:55:12 -06:00
|
|
|
|
|
|
|
#else
|
2013-11-06 12:24:51 -06:00
|
|
|
# undef CONFIG_SAMA5_PWM_CLKA_FREQUENCY
|
|
|
|
# define CLKA_PRE_BITS PWM_CLK_PREA_DIV1
|
|
|
|
# define CLKA_DIV_BITS PWM_CLK_DIVA_OFF
|
2013-11-06 10:55:12 -06:00
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef CONFIG_SAMA5_PWM_CLKB
|
|
|
|
|
|
|
|
# if !defined(CONFIG_SAMA5_PWM_CLKB_FREQUENCY)
|
|
|
|
# error CONFIG_SAMA5_PWM_CLKB_FREQUENCY is not defined
|
|
|
|
|
|
|
|
# elif (BOARD_MCK_FREQUENCY / CONFIG_SAMA5_PWM_CLKB_FREQUENCY) < 256
|
2013-11-06 12:24:51 -06:00
|
|
|
# define CLKB_PRE_BITS PWM_CLK_PREB_DIV1
|
|
|
|
# define CLKB_PRE 1
|
2013-11-06 10:55:12 -06:00
|
|
|
|
|
|
|
# elif (BOARD_MCK_FREQUENCY / 2 / CONFIG_SAMA5_PWM_CLKB_FREQUENCY) < 256
|
2013-11-06 12:24:51 -06:00
|
|
|
# define CLKB_PRE_BITS PWM_CLK_PREB_DIV2
|
|
|
|
# define CLKB_PRE 2
|
2013-11-06 10:55:12 -06:00
|
|
|
|
|
|
|
# elif (BOARD_MCK_FREQUENCY / 4 / CONFIG_SAMA5_PWM_CLKB_FREQUENCY) < 256
|
2013-11-06 12:24:51 -06:00
|
|
|
# define CLKB_PRE_BITS PWM_CLK_PREB_DIV4
|
|
|
|
# define CLKB_PRE 4
|
2013-11-06 10:55:12 -06:00
|
|
|
|
|
|
|
# elif (BOARD_MCK_FREQUENCY / 8 / CONFIG_SAMA5_PWM_CLKB_FREQUENCY) < 256
|
2013-11-06 12:24:51 -06:00
|
|
|
# define CLKB_PRE_BITS PWM_CLK_PREB_DIV8
|
|
|
|
# define CLKB_PRE 8
|
2013-11-06 10:55:12 -06:00
|
|
|
|
|
|
|
# elif (BOARD_MCK_FREQUENCY / 16 / CONFIG_SAMA5_PWM_CLKB_FREQUENCY) < 256
|
2013-11-06 12:24:51 -06:00
|
|
|
# define CLKB_PRE_BITS PWM_CLK_PREB_DIV16
|
|
|
|
# define CLKB_PRE 16
|
2013-11-06 10:55:12 -06:00
|
|
|
|
|
|
|
# elif (BOARD_MCK_FREQUENCY / 32 / CONFIG_SAMA5_PWM_CLKB_FREQUENCY) < 256
|
2013-11-06 12:24:51 -06:00
|
|
|
# define CLKB_PRE_BITS PWM_CLK_PREB_DIV32
|
|
|
|
# define CLKB_PRE 32
|
2013-11-06 10:55:12 -06:00
|
|
|
|
|
|
|
# elif (BOARD_MCK_FREQUENCY / 64 / CONFIG_SAMA5_PWM_CLKB_FREQUENCY) < 256
|
2013-11-06 12:24:51 -06:00
|
|
|
# define CLKB_PRE_BITS PWM_CLK_PREB_DIV64
|
|
|
|
# define CLKB_PRE 64
|
2013-11-06 10:55:12 -06:00
|
|
|
|
|
|
|
# elif (BOARD_MCK_FREQUENCY / 128 / CONFIG_SAMA5_PWM_CLKB_FREQUENCY) < 256
|
2013-11-06 12:24:51 -06:00
|
|
|
# define CLKB_PRE_BITS PWM_CLK_PREB_DIV128
|
|
|
|
# define CLKB_PRE 128
|
2013-11-06 10:55:12 -06:00
|
|
|
|
|
|
|
# elif (BOARD_MCK_FREQUENCY / 256 / CONFIG_SAMA5_PWM_CLKB_FREQUENCY) < 256
|
2013-11-06 12:24:51 -06:00
|
|
|
# define CLKB_PRE_BITS PWM_CLK_PREB_DIV256
|
|
|
|
# define CLKB_PRE 256
|
2013-11-06 10:55:12 -06:00
|
|
|
|
|
|
|
# elif (BOARD_MCK_FREQUENCY / 512 / CONFIG_SAMA5_PWM_CLKB_FREQUENCY) < 256
|
2013-11-06 12:24:51 -06:00
|
|
|
# define CLKB_PRE_BITS PWM_CLK_PREB_DIV512
|
|
|
|
# define CLKB_PRE 512
|
2013-11-06 10:55:12 -06:00
|
|
|
|
|
|
|
# elif (BOARD_MCK_FREQUENCY / 1024 / CONFIG_SAMA5_PWM_CLKB_FREQUENCY) < 256
|
2013-11-06 12:24:51 -06:00
|
|
|
# define CLKB_PRE_BITS PWM_CLK_PREB_DIV1024
|
|
|
|
# define CLKB_PRE 1024
|
2013-11-06 10:55:12 -06:00
|
|
|
|
|
|
|
# else
|
|
|
|
# error Cannot realize CONFIG_SAMA5_PWM_CLKB_FREQUENCY
|
|
|
|
# endif
|
|
|
|
|
2013-11-06 12:24:51 -06:00
|
|
|
# define CLKB_DIV (BOARD_MCK_FREQUENCY / CLKB_PRE / CONFIG_SAMA5_PWM_CLKB_FREQUENCY)
|
2013-11-06 10:55:12 -06:00
|
|
|
# define CLKB_FREQUENCY (BOARD_MCK_FREQUENCY / CLKB_PRE / CLKB_DIV)
|
2013-11-06 12:24:51 -06:00
|
|
|
# define CLKB_DIV_BITS PWM_CLK_DIVB(CLKB_DIV)
|
2013-11-06 10:55:12 -06:00
|
|
|
|
|
|
|
#else
|
2013-11-06 12:24:51 -06:00
|
|
|
# undef CONFIG_SAMA5_PWM_CLKB_FREQUENCY
|
|
|
|
# define CLKB_PRE_BITS PWM_CLK_PREB_DIV1
|
|
|
|
# define CLKB_DIV_BITS PWM_CLK_DIVB_OFF
|
2013-11-06 10:55:12 -06:00
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef CONFIG_SAMA5_PWM_CHAN0
|
|
|
|
# if defined(CONFIG_SAMA5_PWM_CHAN0_MCK)
|
|
|
|
# undef CONFIG_SAMA5_PWM_CHAN0_CLKA
|
|
|
|
# undef CONFIG_SAMA5_PWM_CHAN0_CLKB
|
|
|
|
# if CONFIG_SAMA5_PWM_CHAN0_MCKDIV == 1
|
2013-11-06 12:24:51 -06:00
|
|
|
# define SAMA5_PWM_CHAN0_MCKDIV_LOG2 = 0
|
2013-11-06 10:55:12 -06:00
|
|
|
# elif CONFIG_SAMA5_PWM_CHAN0_MCKDIV == 2
|
2013-11-06 12:24:51 -06:00
|
|
|
# define SAMA5_PWM_CHAN0_MCKDIV_LOG2 = 1
|
2013-11-06 10:55:12 -06:00
|
|
|
# elif CONFIG_SAMA5_PWM_CHAN0_MCKDIV == 4
|
2013-11-06 12:24:51 -06:00
|
|
|
# define SAMA5_PWM_CHAN0_MCKDIV_LOG2 = 2
|
2013-11-06 10:55:12 -06:00
|
|
|
# elif CONFIG_SAMA5_PWM_CHAN0_MCKDIV == 8
|
2013-11-06 12:24:51 -06:00
|
|
|
# define SAMA5_PWM_CHAN0_MCKDIV_LOG2 = 3
|
2013-11-06 10:55:12 -06:00
|
|
|
# elif CONFIG_SAMA5_PWM_CHAN0_MCKDIV == 16
|
2013-11-06 12:24:51 -06:00
|
|
|
# define SAMA5_PWM_CHAN0_MCKDIV_LOG2 = 4
|
2013-11-06 10:55:12 -06:00
|
|
|
# elif CONFIG_SAMA5_PWM_CHAN0_MCKDIV == 32
|
2013-11-06 12:24:51 -06:00
|
|
|
# define SAMA5_PWM_CHAN0_MCKDIV_LOG2 = 5
|
2013-11-06 10:55:12 -06:00
|
|
|
# elif CONFIG_SAMA5_PWM_CHAN0_MCKDIV == 64
|
2013-11-06 12:24:51 -06:00
|
|
|
# define SAMA5_PWM_CHAN0_MCKDIV_LOG2 = 6
|
2013-11-06 10:55:12 -06:00
|
|
|
# elif CONFIG_SAMA5_PWM_CHAN0_MCKDIV == 128
|
2013-11-06 12:24:51 -06:00
|
|
|
# define SAMA5_PWM_CHAN0_MCKDIV_LOG2 = 7
|
2013-11-06 10:55:12 -06:00
|
|
|
# elif CONFIG_SAMA5_PWM_CHAN0_MCKDIV == 256
|
2013-11-06 12:24:51 -06:00
|
|
|
# define SAMA5_PWM_CHAN0_MCKDIV_LOG2 = 8
|
2013-11-06 10:55:12 -06:00
|
|
|
# elif CONFIG_SAMA5_PWM_CHAN0_MCKDIV == 512
|
2013-11-06 12:24:51 -06:00
|
|
|
# define SAMA5_PWM_CHAN0_MCKDIV_LOG2 = 9
|
2013-11-06 10:55:12 -06:00
|
|
|
# elif CONFIG_SAMA5_PWM_CHAN0_MCKDIV == 1024
|
2013-11-06 12:24:51 -06:00
|
|
|
# define SAMA5_PWM_CHAN0_MCKDIV_LOG2 = 10
|
2013-11-06 10:55:12 -06:00
|
|
|
# else
|
|
|
|
# error Unsupported MCK divider value
|
|
|
|
# endif
|
|
|
|
|
|
|
|
# elif defined(CONFIG_SAMA5_PWM_CHAN0_CLKA)
|
|
|
|
# undef CONFIG_SAMA5_PWM_CHAN0_CLKB
|
|
|
|
|
|
|
|
# elif !defined(CONFIG_SAMA5_PWM_CHAN0_CLKB)
|
|
|
|
# error CHAN0 clock source not defined
|
|
|
|
|
|
|
|
# endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef CONFIG_SAMA5_PWM_CHAN1
|
|
|
|
# if defined(CONFIG_SAMA5_PWM_CHAN1_MCK)
|
|
|
|
# undef CONFIG_SAMA5_PWM_CHAN1_CLKA
|
|
|
|
# undef CONFIG_SAMA5_PWM_CHAN1_CLKB
|
|
|
|
# if CONFIG_SAMA5_PWM_CHAN1_MCKDIV == 1
|
2013-11-06 12:24:51 -06:00
|
|
|
# define SAMA5_PWM_CHAN1_MCKDIV_LOG2 0
|
2013-11-06 10:55:12 -06:00
|
|
|
# elif CONFIG_SAMA5_PWM_CHAN1_MCKDIV == 2
|
2013-11-06 12:24:51 -06:00
|
|
|
# define SAMA5_PWM_CHAN1_MCKDIV_LOG2 1
|
2013-11-06 10:55:12 -06:00
|
|
|
# elif CONFIG_SAMA5_PWM_CHAN1_MCKDIV == 4
|
2013-11-06 12:24:51 -06:00
|
|
|
# define SAMA5_PWM_CHAN1_MCKDIV_LOG2 2
|
2013-11-06 10:55:12 -06:00
|
|
|
# elif CONFIG_SAMA5_PWM_CHAN1_MCKDIV == 8
|
2013-11-06 12:24:51 -06:00
|
|
|
# define SAMA5_PWM_CHAN1_MCKDIV_LOG2 3
|
2013-11-06 10:55:12 -06:00
|
|
|
# elif CONFIG_SAMA5_PWM_CHAN1_MCKDIV == 16
|
2013-11-06 12:24:51 -06:00
|
|
|
# define SAMA5_PWM_CHAN1_MCKDIV_LOG2 4
|
2013-11-06 10:55:12 -06:00
|
|
|
# elif CONFIG_SAMA5_PWM_CHAN1_MCKDIV == 32
|
2013-11-06 12:24:51 -06:00
|
|
|
# define SAMA5_PWM_CHAN1_MCKDIV_LOG2 5
|
2013-11-06 10:55:12 -06:00
|
|
|
# elif CONFIG_SAMA5_PWM_CHAN1_MCKDIV == 64
|
2013-11-06 12:24:51 -06:00
|
|
|
# define SAMA5_PWM_CHAN1_MCKDIV_LOG2 6
|
2013-11-06 10:55:12 -06:00
|
|
|
# elif CONFIG_SAMA5_PWM_CHAN1_MCKDIV == 128
|
2013-11-06 12:24:51 -06:00
|
|
|
# define SAMA5_PWM_CHAN1_MCKDIV_LOG2 7
|
2013-11-06 10:55:12 -06:00
|
|
|
# elif CONFIG_SAMA5_PWM_CHAN1_MCKDIV == 256
|
2013-11-06 12:24:51 -06:00
|
|
|
# define SAMA5_PWM_CHAN1_MCKDIV_LOG2 8
|
2013-11-06 10:55:12 -06:00
|
|
|
# elif CONFIG_SAMA5_PWM_CHAN1_MCKDIV == 512
|
2013-11-06 12:24:51 -06:00
|
|
|
# define SAMA5_PWM_CHAN1_MCKDIV_LOG2 9
|
2013-11-06 10:55:12 -06:00
|
|
|
# elif CONFIG_SAMA5_PWM_CHAN1_MCKDIV == 1024
|
2013-11-06 12:24:51 -06:00
|
|
|
# define SAMA5_PWM_CHAN1_MCKDIV_LOG2 10
|
2013-11-06 10:55:12 -06:00
|
|
|
# else
|
|
|
|
# error Unsupported MCK divider value
|
|
|
|
# endif
|
|
|
|
|
|
|
|
# elif defined(CONFIG_SAMA5_PWM_CHAN1_CLKA)
|
|
|
|
# undef CONFIG_SAMA5_PWM_CHAN1_CLKB
|
|
|
|
|
|
|
|
# elif !defined(CONFIG_SAMA5_PWM_CHAN1_CLKB)
|
|
|
|
# error CHAN1 clock source not defined
|
|
|
|
|
|
|
|
# endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef CONFIG_SAMA5_PWM_CHAN2
|
|
|
|
# if defined(CONFIG_SAMA5_PWM_CHAN2_MCK)
|
|
|
|
# undef CONFIG_SAMA5_PWM_CHAN2_CLKA
|
|
|
|
# undef CONFIG_SAMA5_PWM_CHAN2_CLKB
|
|
|
|
# if CONFIG_SAMA5_PWM_CHAN2_MCKDIV == 1
|
2013-11-06 12:24:51 -06:00
|
|
|
# define SAMA5_PWM_CHAN2_MCKDIV_LOG2 0
|
2013-11-06 10:55:12 -06:00
|
|
|
# elif CONFIG_SAMA5_PWM_CHAN2_MCKDIV == 2
|
2013-11-06 12:24:51 -06:00
|
|
|
# define SAMA5_PWM_CHAN2_MCKDIV_LOG2 1
|
2013-11-06 10:55:12 -06:00
|
|
|
# elif CONFIG_SAMA5_PWM_CHAN2_MCKDIV == 4
|
2013-11-06 12:24:51 -06:00
|
|
|
# define SAMA5_PWM_CHAN2_MCKDIV_LOG2 2
|
2013-11-06 10:55:12 -06:00
|
|
|
# elif CONFIG_SAMA5_PWM_CHAN2_MCKDIV == 8
|
2013-11-06 12:24:51 -06:00
|
|
|
# define SAMA5_PWM_CHAN2_MCKDIV_LOG2 3
|
2013-11-06 10:55:12 -06:00
|
|
|
# elif CONFIG_SAMA5_PWM_CHAN2_MCKDIV == 16
|
2013-11-06 12:24:51 -06:00
|
|
|
# define SAMA5_PWM_CHAN2_MCKDIV_LOG2 4
|
2013-11-06 10:55:12 -06:00
|
|
|
# elif CONFIG_SAMA5_PWM_CHAN2_MCKDIV == 32
|
2013-11-06 12:24:51 -06:00
|
|
|
# define SAMA5_PWM_CHAN2_MCKDIV_LOG2 5
|
2013-11-06 10:55:12 -06:00
|
|
|
# elif CONFIG_SAMA5_PWM_CHAN2_MCKDIV == 64
|
2013-11-06 12:24:51 -06:00
|
|
|
# define SAMA5_PWM_CHAN2_MCKDIV_LOG2 6
|
2013-11-06 10:55:12 -06:00
|
|
|
# elif CONFIG_SAMA5_PWM_CHAN2_MCKDIV == 128
|
2013-11-06 12:24:51 -06:00
|
|
|
# define SAMA5_PWM_CHAN2_MCKDIV_LOG2 7
|
2013-11-06 10:55:12 -06:00
|
|
|
# elif CONFIG_SAMA5_PWM_CHAN2_MCKDIV == 256
|
2013-11-06 12:24:51 -06:00
|
|
|
# define SAMA5_PWM_CHAN2_MCKDIV_LOG2 8
|
2013-11-06 10:55:12 -06:00
|
|
|
# elif CONFIG_SAMA5_PWM_CHAN2_MCKDIV == 512
|
2013-11-06 12:24:51 -06:00
|
|
|
# define SAMA5_PWM_CHAN2_MCKDIV_LOG2 9
|
2013-11-06 10:55:12 -06:00
|
|
|
# elif CONFIG_SAMA5_PWM_CHAN2_MCKDIV == 1024
|
2013-11-06 12:24:51 -06:00
|
|
|
# define SAMA5_PWM_CHAN2_MCKDIV_LOG2 10
|
2013-11-06 10:55:12 -06:00
|
|
|
# else
|
|
|
|
# error Unsupported MCK divider value
|
|
|
|
# endif
|
|
|
|
|
|
|
|
# elif defined(CONFIG_SAMA5_PWM_CHAN2_CLKA)
|
|
|
|
# undef CONFIG_SAMA5_PWM_CHAN2_CLKB
|
|
|
|
|
|
|
|
# elif !defined(CONFIG_SAMA5_PWM_CHAN2_CLKB)
|
|
|
|
# error CHAN2 clock source not defined
|
|
|
|
|
|
|
|
# endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef CONFIG_SAMA5_PWM_CHAN3
|
|
|
|
# if defined(CONFIG_SAMA5_PWM_CHAN3_MCK)
|
|
|
|
# undef CONFIG_SAMA5_PWM_CHAN3_CLKA
|
|
|
|
# undef CONFIG_SAMA5_PWM_CHAN3_CLKB
|
|
|
|
# if CONFIG_SAMA5_PWM_CHAN3_MCKDIV == 1
|
2013-11-06 12:24:51 -06:00
|
|
|
# define SAMA5_PWM_CHAN3_MCKDIV_LOG2 0
|
2013-11-06 10:55:12 -06:00
|
|
|
# elif CONFIG_SAMA5_PWM_CHAN3_MCKDIV == 2
|
2013-11-06 12:24:51 -06:00
|
|
|
# define SAMA5_PWM_CHAN3_MCKDIV_LOG2 1
|
2013-11-06 10:55:12 -06:00
|
|
|
# elif CONFIG_SAMA5_PWM_CHAN3_MCKDIV == 4
|
2013-11-06 12:24:51 -06:00
|
|
|
# define SAMA5_PWM_CHAN3_MCKDIV_LOG2 2
|
2013-11-06 10:55:12 -06:00
|
|
|
# elif CONFIG_SAMA5_PWM_CHAN3_MCKDIV == 8
|
2013-11-06 12:24:51 -06:00
|
|
|
# define SAMA5_PWM_CHAN3_MCKDIV_LOG2 3
|
2013-11-06 10:55:12 -06:00
|
|
|
# elif CONFIG_SAMA5_PWM_CHAN3_MCKDIV == 16
|
2013-11-06 12:24:51 -06:00
|
|
|
# define SAMA5_PWM_CHAN3_MCKDIV_LOG2 4
|
2013-11-06 10:55:12 -06:00
|
|
|
# elif CONFIG_SAMA5_PWM_CHAN3_MCKDIV == 32
|
2013-11-06 12:24:51 -06:00
|
|
|
# define SAMA5_PWM_CHAN3_MCKDIV_LOG2 5
|
2013-11-06 10:55:12 -06:00
|
|
|
# elif CONFIG_SAMA5_PWM_CHAN3_MCKDIV == 64
|
2013-11-06 12:24:51 -06:00
|
|
|
# define SAMA5_PWM_CHAN3_MCKDIV_LOG2 6
|
2013-11-06 10:55:12 -06:00
|
|
|
# elif CONFIG_SAMA5_PWM_CHAN3_MCKDIV == 128
|
2013-11-06 12:24:51 -06:00
|
|
|
# define SAMA5_PWM_CHAN3_MCKDIV_LOG2 7
|
2013-11-06 10:55:12 -06:00
|
|
|
# elif CONFIG_SAMA5_PWM_CHAN3_MCKDIV == 256
|
2013-11-06 12:24:51 -06:00
|
|
|
# define SAMA5_PWM_CHAN3_MCKDIV_LOG2 8
|
2013-11-06 10:55:12 -06:00
|
|
|
# elif CONFIG_SAMA5_PWM_CHAN3_MCKDIV == 512
|
2013-11-06 12:24:51 -06:00
|
|
|
# define SAMA5_PWM_CHAN3_MCKDIV_LOG2 9
|
2013-11-06 10:55:12 -06:00
|
|
|
# elif CONFIG_SAMA5_PWM_CHAN3_MCKDIV == 1024
|
2013-11-06 12:24:51 -06:00
|
|
|
# define SAMA5_PWM_CHAN3_MCKDIV_LOG2 10
|
2013-11-06 10:55:12 -06:00
|
|
|
# else
|
|
|
|
# error Unsupported MCK divider value
|
|
|
|
# endif
|
|
|
|
|
|
|
|
# elif defined(CONFIG_SAMA5_PWM_CHAN3_CLKA)
|
|
|
|
# undef CONFIG_SAMA5_PWM_CHAN3_CLKB
|
|
|
|
|
|
|
|
# elif !defined(CONFIG_SAMA5_PWM_CHAN3_CLKB)
|
|
|
|
# error CHAN3 clock source not defined
|
|
|
|
|
|
|
|
# endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* The current design does not use any PWM interrupts */
|
|
|
|
|
|
|
|
#undef PWM_INTERRUPTS
|
|
|
|
|
|
|
|
/* Pin configuration ********************************************************/
|
|
|
|
|
|
|
|
#define PWM_INPUTCFG (PIO_INPUT | PIO_CFG_DEFAULT | PIO_DRIVE_LOW)
|
|
|
|
#define PWM_PINMASK (PIO_PORT_MASK | PIO_PIN_MASK)
|
|
|
|
#define PWM_MKINPUT(cfg) (((cfg) & PWM_PINMASK) | PWM_INPUTCFG)
|
|
|
|
|
|
|
|
/* Debug ********************************************************************/
|
|
|
|
/* Non-standard debug that may be enabled just for testing PWM */
|
|
|
|
|
|
|
|
#ifndef CONFIG_DEBUG
|
|
|
|
# undef CONFIG_DEBUG_PWM
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef CONFIG_DEBUG_PWM
|
|
|
|
# define pwmdbg dbg
|
|
|
|
# define pwmlldbg lldbg
|
|
|
|
# ifdef CONFIG_DEBUG_VERBOSE
|
|
|
|
# define pwmvdbg vdbg
|
|
|
|
# define pwmllvdbg llvdbg
|
|
|
|
# else
|
|
|
|
# define pwmlldbg(x...)
|
|
|
|
# define pwmllvdbg(x...)
|
|
|
|
# endif
|
|
|
|
#else
|
|
|
|
# define pwmdbg(x...)
|
|
|
|
# define pwmlldbg(x...)
|
|
|
|
# define pwmvdbg(x...)
|
|
|
|
# define pwmllvdbg(x...)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Private Types
|
|
|
|
****************************************************************************/
|
|
|
|
/* Channel clock sources */
|
|
|
|
|
|
|
|
enum pwm_clksrc_e
|
|
|
|
{
|
|
|
|
PWM_CLKSRC_MCK = 1, /* Source = Divided MCK */
|
|
|
|
PWM_CLKSRC_CLKA, /* Source = CLKA */
|
|
|
|
PWM_CLKSRC_CLKB, /* Source = CLKB */
|
|
|
|
};
|
|
|
|
|
|
|
|
/* This structure represents the state of one PWM channel */
|
|
|
|
|
|
|
|
struct sam_pwm_s;
|
|
|
|
struct sam_pwm_chan_s
|
|
|
|
{
|
|
|
|
const struct pwm_ops_s *ops; /* PWM operations */
|
|
|
|
#ifndef PWM_SINGLE
|
|
|
|
struct sam_pwm_s *pwm; /* Parent PWM peripheral */
|
|
|
|
#endif
|
|
|
|
uintptr_t base; /* Base address of channel registers */
|
|
|
|
uint8_t channel; /* PWM channel: {0,..3} */
|
|
|
|
uint8_t clksrc; /* 0=MCK; 1=CLKA; 2=CLKB */
|
|
|
|
uint8_t divlog2; /* Log2 MCK divisor: 0->1, 1->2, 2->4, ... 10->1024 */
|
|
|
|
pio_pinset_t ohpincfg; /* Output high pin configuration */
|
|
|
|
pio_pinset_t olpincfg; /* Output low pin configuration */
|
|
|
|
pio_pinset_t fipincfg; /* Fault input pin configuration */
|
|
|
|
};
|
|
|
|
|
|
|
|
/* This structure represents the overall state of the PWM peripheral */
|
|
|
|
|
|
|
|
struct sam_pwm_s
|
|
|
|
{
|
|
|
|
bool initialized; /* True: one time initialization has been performed */
|
|
|
|
#ifndef PWM_SINGLE
|
|
|
|
uintptr_t base; /* Base address of peripheral registers */
|
|
|
|
#endif
|
|
|
|
/* Debug stuff */
|
|
|
|
|
|
|
|
#ifdef CONFIG_SAMA5_PWM_REGDEBUG
|
|
|
|
bool wr; /* Last was a write */
|
|
|
|
uint32_t regaddr; /* Last address */
|
|
|
|
uint32_t regval; /* Last value */
|
|
|
|
int count; /* Number of times */
|
|
|
|
#endif
|
|
|
|
};
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Static Function Prototypes
|
|
|
|
****************************************************************************/
|
|
|
|
/* Register access */
|
|
|
|
|
|
|
|
#ifdef CONFIG_SAMA5_PWM_REGDEBUG
|
2013-11-07 09:17:46 -06:00
|
|
|
static bool pwm_checkreg(FAR struct sam_pwm_s *chan, bool wr, uint32_t regval,
|
2013-11-06 10:55:12 -06:00
|
|
|
uintptr_t regaddr);
|
|
|
|
#else
|
|
|
|
# define pwm_checkreg(chan,wr,regval,regaddr) (false)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static uint32_t pwm_getreg(FAR struct sam_pwm_chan_s *chan, int offset);
|
|
|
|
static void pwm_putreg(FAR struct sam_pwm_chan_s *chan, int offset, uint32_t regval);
|
|
|
|
|
|
|
|
#if defined(CONFIG_DEBUG_PWM) && defined(CONFIG_DEBUG_VERBOSE)
|
|
|
|
static void pwm_dumpregs(FAR struct sam_pwm_chan_s *chan, FAR const char *msg);
|
|
|
|
#else
|
|
|
|
# define pwm_dumpregs(chan,msg)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* PWM Interrupts */
|
|
|
|
|
|
|
|
#ifdef PWM_INTERRUPTS
|
|
|
|
static int pwm_interrupt(int irq, void *context);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* PWM driver methods */
|
|
|
|
|
|
|
|
static int pwm_setup(FAR struct pwm_lowerhalf_s *dev);
|
|
|
|
static int pwm_shutdown(FAR struct pwm_lowerhalf_s *dev);
|
|
|
|
static int pwm_start(FAR struct pwm_lowerhalf_s *dev,
|
|
|
|
FAR const struct pwm_info_s *info);
|
|
|
|
static int pwm_stop(FAR struct pwm_lowerhalf_s *dev);
|
|
|
|
static int pwm_ioctl(FAR struct pwm_lowerhalf_s *dev,
|
|
|
|
int cmd, unsigned long arg);
|
|
|
|
|
|
|
|
/* Initialization */
|
|
|
|
|
|
|
|
static void pwm_resetpins(FAR struct sam_pwm_chan_s *chan);
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Private Data
|
|
|
|
****************************************************************************/
|
|
|
|
/* This is the list of lower half PWM driver methods used by the upper
|
|
|
|
* half driver
|
|
|
|
*/
|
|
|
|
|
|
|
|
static const struct pwm_ops_s g_pwmops =
|
|
|
|
{
|
|
|
|
.setup = pwm_setup,
|
|
|
|
.shutdown = pwm_shutdown,
|
|
|
|
.start = pwm_start,
|
|
|
|
.stop = pwm_stop,
|
|
|
|
.ioctl = pwm_ioctl,
|
|
|
|
};
|
|
|
|
|
|
|
|
/* This is the overall state of the PWM peripheral */
|
|
|
|
|
2013-11-06 12:24:51 -06:00
|
|
|
static struct sam_pwm_s g_pwm =
|
2013-11-06 10:55:12 -06:00
|
|
|
{
|
|
|
|
.initialized = false,
|
|
|
|
#ifndef PWM_SINGLE
|
|
|
|
.base = SAM_PWMC_VBASE,
|
|
|
|
#endif
|
|
|
|
};
|
|
|
|
|
|
|
|
#ifdef CONFIG_SAMA5_PWM_CHAN0
|
|
|
|
/* This is the state of the PWM channel 0 */
|
|
|
|
|
|
|
|
static struct sam_pwm_chan_s g_pwm_chan0 =
|
|
|
|
{
|
|
|
|
.ops = &g_pwmops,
|
2013-11-06 12:24:51 -06:00
|
|
|
#ifndef PWM_SINGLE
|
2013-11-06 10:55:12 -06:00
|
|
|
.pwm = &g_pwm,
|
2013-11-06 12:24:51 -06:00
|
|
|
#endif
|
2013-11-06 10:55:12 -06:00
|
|
|
.channel = 0,
|
|
|
|
.base = SAM_PWM_CHAN_BASE(0),
|
|
|
|
|
|
|
|
#if defined(CONFIG_SAMA5_PWM_CHAN0_MCK)
|
|
|
|
.clksrc = PWM_CLKSRC_MCK,
|
|
|
|
.divlog2 = SAMA5_PWM_CHAN0_MCKDIV_LOG2,
|
|
|
|
#elif defined(CONFIG_SAMA5_PWM_CHAN0_CLKA)
|
|
|
|
.clksrc = PWM_CLKSRC_CLKA,
|
|
|
|
#elif defined(CONFIG_SAMA5_PWM_CHAN0_CLKB)
|
|
|
|
.clksrc = PWM_CLKSRC_CLKB,
|
|
|
|
#else
|
|
|
|
# error No clock source for channel 0
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef CONFIG_SAMA5_PWM_CHAN0_OUTPUTH
|
|
|
|
.ohpincfg = PIO_PWM0_H,
|
|
|
|
#endif
|
|
|
|
#ifdef CONFIG_SAMA5_PWM_CHAN0_OUTPUTL
|
|
|
|
.olpincfg = PIO_PWM0_L,
|
|
|
|
#endif
|
|
|
|
#ifdef CONFIG_SAMA5_PWM_CHAN0_FAULTINPUT
|
|
|
|
.fipincfg = PIO_PWM0_FI,
|
|
|
|
#endif
|
|
|
|
};
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef CONFIG_SAMA5_PWM_CHAN1
|
|
|
|
/* This is the state of the PWM channel 1 */
|
|
|
|
|
|
|
|
static struct sam_pwm_chan_s g_pwm_chan1 =
|
|
|
|
{
|
|
|
|
.ops = &g_pwmops,
|
2013-11-06 12:24:51 -06:00
|
|
|
#ifndef PWM_SINGLE
|
2013-11-06 10:55:12 -06:00
|
|
|
.pwm = &g_pwm,
|
2013-11-06 12:24:51 -06:00
|
|
|
#endif
|
2013-11-06 10:55:12 -06:00
|
|
|
.channel = 1,
|
|
|
|
.base = SAM_PWM_CHAN_BASE(1),
|
|
|
|
|
|
|
|
#if defined(CONFIG_SAMA5_PWM_CHAN1_MCK)
|
|
|
|
.clksrc = PWM_CLKSRC_MCK,
|
|
|
|
.divlog2 = SAMA5_PWM_CHAN1_MCKDIV_LOG2,
|
|
|
|
#elif defined(CONFIG_SAMA5_PWM_CHAN1_CLKA)
|
|
|
|
.clksrc = PWM_CLKSRC_CLKA,
|
|
|
|
#elif defined(CONFIG_SAMA5_PWM_CHAN1_CLKB)
|
|
|
|
.clksrc = PWM_CLKSRC_CLKB,
|
|
|
|
#else
|
|
|
|
# error No clock source for channel 0
|
|
|
|
#endif
|
|
|
|
|
2013-11-06 12:24:51 -06:00
|
|
|
#ifdef CONFIG_SAMA5_PWM_CHAN1_OUTPUTH
|
2013-11-06 10:55:12 -06:00
|
|
|
.ohpincfg = PIO_PWM1_H,
|
|
|
|
#endif
|
2013-11-06 12:24:51 -06:00
|
|
|
#ifdef CONFIG_SAMA5_PWM_CHAN1_OUTPUTL
|
2013-11-06 10:55:12 -06:00
|
|
|
.olpincfg = PIO_PWM1_L,
|
|
|
|
#endif
|
2013-11-06 12:24:51 -06:00
|
|
|
#ifdef CONFIG_SAMA5_PWM_CHAN1_FAULTINPUT
|
2013-11-06 10:55:12 -06:00
|
|
|
.fipincfg = PIO_PWM1_FI,
|
|
|
|
#endif
|
|
|
|
};
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef CONFIG_SAMA5_PWM_CHAN2
|
|
|
|
/* This is the state of the PWM channel 2 */
|
|
|
|
|
|
|
|
static struct sam_pwm_chan_s g_pwm_chan2 =
|
|
|
|
{
|
|
|
|
.ops = &g_pwmops,
|
2013-11-06 12:24:51 -06:00
|
|
|
#ifndef PWM_SINGLE
|
2013-11-06 10:55:12 -06:00
|
|
|
.pwm = &g_pwm,
|
2013-11-06 12:24:51 -06:00
|
|
|
#endif
|
2013-11-06 10:55:12 -06:00
|
|
|
.channel = 2,
|
|
|
|
.base = SAM_PWM_CHAN_BASE(2),
|
|
|
|
|
|
|
|
#if defined(CONFIG_SAMA5_PWM_CHAN2_MCK)
|
|
|
|
.clksrc = PWM_CLKSRC_MCK,
|
|
|
|
.divlog2 = SAMA5_PWM_CHAN2_MCKDIV_LOG2,
|
|
|
|
#elif defined(CONFIG_SAMA5_PWM_CHAN2_CLKA)
|
|
|
|
.clksrc = PWM_CLKSRC_CLKA,
|
|
|
|
#elif defined(CONFIG_SAMA5_PWM_CHAN2_CLKB)
|
|
|
|
.clksrc = PWM_CLKSRC_CLKB,
|
|
|
|
#else
|
|
|
|
# error No clock source for channel 0
|
|
|
|
#endif
|
|
|
|
|
2013-11-06 12:24:51 -06:00
|
|
|
#ifdef CONFIG_SAMA5_PWM_CHAN2_OUTPUTH
|
2013-11-06 10:55:12 -06:00
|
|
|
.ohpincfg = PIO_PWM2_H,
|
|
|
|
#endif
|
2013-11-06 12:24:51 -06:00
|
|
|
#ifdef CONFIG_SAMA5_PWM_CHAN2_OUTPUTL
|
2013-11-06 10:55:12 -06:00
|
|
|
.olpincfg = PIO_PWM2_L,
|
|
|
|
#endif
|
2013-11-06 12:24:51 -06:00
|
|
|
#ifdef CONFIG_SAMA5_PWM_CHAN2_FAULTINPUT
|
2013-11-06 10:55:12 -06:00
|
|
|
.fipincfg = PIO_PWM2_FI,
|
|
|
|
#endif
|
|
|
|
};
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef CONFIG_SAMA5_PWM_CHAN3
|
|
|
|
/* This is the state of the PWM channel 3 */
|
|
|
|
|
|
|
|
static struct sam_pwm_chan_s g_pwm_chan3 =
|
|
|
|
{
|
|
|
|
.ops = &g_pwmops,
|
2013-11-06 12:24:51 -06:00
|
|
|
#ifndef PWM_SINGLE
|
2013-11-06 10:55:12 -06:00
|
|
|
.pwm = &g_pwm,
|
2013-11-06 12:24:51 -06:00
|
|
|
#endif
|
2013-11-06 10:55:12 -06:00
|
|
|
.channel = 3,
|
2013-11-06 12:24:51 -06:00
|
|
|
.base = SAM_PWM_CHAN_BASE(3),
|
2013-11-06 10:55:12 -06:00
|
|
|
|
|
|
|
#if defined(CONFIG_SAMA5_PWM_CHAN3_MCK)
|
|
|
|
.clksrc = PWM_CLKSRC_MCK,
|
|
|
|
.divlog2 = SAMA5_PWM_CHAN3_MCKDIV_LOG2,
|
|
|
|
#elif defined(CONFIG_SAMA5_PWM_CHAN3_CLKA)
|
|
|
|
.clksrc = PWM_CLKSRC_CLKA,
|
|
|
|
#elif defined(CONFIG_SAMA5_PWM_CHAN3_CLKB)
|
|
|
|
.clksrc = PWM_CLKSRC_CLKB,
|
|
|
|
#else
|
|
|
|
# error No clock source for channel 0
|
|
|
|
#endif
|
|
|
|
|
2013-11-06 12:24:51 -06:00
|
|
|
#ifdef CONFIG_SAMA5_PWM_CHAN3_OUTPUTH
|
2013-11-06 10:55:12 -06:00
|
|
|
.ohpincfg = PIO_PWM3_H,
|
|
|
|
#endif
|
2013-11-06 12:24:51 -06:00
|
|
|
#ifdef CONFIG_SAMA5_PWM_CHAN3_OUTPUTL
|
2013-11-06 10:55:12 -06:00
|
|
|
.olpincfg = PIO_PWM3_L,
|
|
|
|
#endif
|
2013-11-06 12:24:51 -06:00
|
|
|
#ifdef CONFIG_SAMA5_PWM_CHAN3_FAULTINPUT
|
2013-11-06 10:55:12 -06:00
|
|
|
.fipincfg = PIO_PWM3_FI,
|
|
|
|
#endif
|
|
|
|
};
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Private Functions
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Name: pwm_checkreg
|
|
|
|
*
|
|
|
|
* Description:
|
|
|
|
* Check if the current register access is a duplicate of the preceding.
|
|
|
|
*
|
|
|
|
* Input Parameters:
|
|
|
|
* regval - The value to be written
|
|
|
|
* regaddr - The address of the register to write to
|
|
|
|
*
|
|
|
|
* Returned Value:
|
|
|
|
* true: This is the first register access of this type.
|
|
|
|
* flase: This is the same as the preceding register access.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
#ifdef CONFIG_SAMA5_PWM_REGDEBUG
|
2013-11-07 09:17:46 -06:00
|
|
|
static bool pwm_checkreg(FAR struct sam_pwm_s *pwm, bool wr, uint32_t regval,
|
2013-11-06 10:55:12 -06:00
|
|
|
uintptr_t regaddr)
|
|
|
|
{
|
|
|
|
if (wr == pwm->wr && /* Same kind of access? */
|
|
|
|
regval == pwm->regval && /* Same value? */
|
|
|
|
regaddr == pwm->regaddr) /* Same address? */
|
|
|
|
{
|
|
|
|
/* Yes, then just keep a count of the number of times we did this. */
|
|
|
|
|
|
|
|
pwm->count++;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Did we do the previous operation more than once? */
|
|
|
|
|
|
|
|
if (pwm->count > 0)
|
|
|
|
{
|
|
|
|
/* Yes... show how many times we did it */
|
|
|
|
|
|
|
|
lldbg("...[Repeats %d times]...\n", pwm->count);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Save information about the new access */
|
|
|
|
|
|
|
|
pwm->wr = wr;
|
|
|
|
pwm->regval = regval;
|
|
|
|
pwm->regaddr = regaddr;
|
|
|
|
pwm->count = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Return true if this is the first time that we have done this operation */
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Name: pwm_getreg
|
|
|
|
*
|
|
|
|
* Description:
|
|
|
|
* Read the value of a PWM register.
|
|
|
|
*
|
|
|
|
* Input Parameters:
|
|
|
|
* chan - A reference to the PWM channel instance
|
|
|
|
* offset - The offset to the PWM register to read
|
|
|
|
*
|
|
|
|
* Returned Value:
|
|
|
|
* The current contents of the specified register
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
static uint32_t pwm_getreg(struct sam_pwm_chan_s *chan, int offset)
|
|
|
|
{
|
2013-11-07 09:17:46 -06:00
|
|
|
#ifdef PWM_SINGLE
|
2013-11-06 10:55:12 -06:00
|
|
|
uintptr_t regaddr;
|
|
|
|
uint32_t regval;
|
|
|
|
|
2013-11-06 12:24:51 -06:00
|
|
|
regaddr = SAM_PWMC_VBASE + offset;
|
2013-11-07 09:17:46 -06:00
|
|
|
regval = getreg32(regaddr);
|
|
|
|
|
|
|
|
#ifdef CONFIG_SAMA5_PWM_REGDEBUG
|
|
|
|
if (pwm_checkreg(&g_pwm, false, regval, regaddr))
|
|
|
|
{
|
|
|
|
lldbg("%08x->%08x\n", regaddr, regval);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return regval;
|
|
|
|
|
2013-11-06 10:55:12 -06:00
|
|
|
#else
|
|
|
|
struct sam_pwm_chan_s *pwm = chan->pwm;
|
2013-11-07 09:17:46 -06:00
|
|
|
uintptr_t regaddr;
|
|
|
|
uint32_t regval;
|
2013-11-06 10:55:12 -06:00
|
|
|
|
2013-11-07 09:17:46 -06:00
|
|
|
regaddr = pwm->base + offset;
|
|
|
|
regval = getreg32(regaddr);
|
2013-11-06 10:55:12 -06:00
|
|
|
|
|
|
|
#ifdef CONFIG_SAMA5_PWM_REGDEBUG
|
2013-11-07 09:17:46 -06:00
|
|
|
if (pwm_checkreg(pwm, false, regval, regaddr))
|
2013-11-06 10:55:12 -06:00
|
|
|
{
|
|
|
|
lldbg("%08x->%08x\n", regaddr, regval);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return regval;
|
2013-11-07 09:17:46 -06:00
|
|
|
|
|
|
|
#endif
|
2013-11-06 10:55:12 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Name: pwm_chan_getreg
|
|
|
|
*
|
|
|
|
* Description:
|
|
|
|
* Read the value of a PWM channel register.
|
|
|
|
*
|
|
|
|
* Input Parameters:
|
|
|
|
* chan - A reference to the PWM channel instance
|
|
|
|
* offset - The offset to the channel register to read
|
|
|
|
*
|
|
|
|
* Returned Value:
|
|
|
|
* The current contents of the specified register
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
static uint32_t pwm_chan_getreg(struct sam_pwm_chan_s *chan, int offset)
|
|
|
|
{
|
|
|
|
uintptr_t regaddr;
|
|
|
|
uint32_t regval;
|
|
|
|
|
|
|
|
regaddr = chan->base + offset;
|
|
|
|
regval = getreg32(regaddr);
|
|
|
|
|
|
|
|
#ifdef CONFIG_SAMA5_PWM_REGDEBUG
|
2013-11-07 09:17:46 -06:00
|
|
|
#ifdef PWM_SINGLE
|
|
|
|
if (pwm_checkreg(&g_pwm, false, regval, regaddr))
|
|
|
|
#else
|
2013-11-06 10:55:12 -06:00
|
|
|
if (pwm_checkreg(chan->pwm, false, regval, regaddr))
|
2013-11-07 09:17:46 -06:00
|
|
|
#endif
|
2013-11-06 10:55:12 -06:00
|
|
|
{
|
|
|
|
lldbg("%08x->%08x\n", regaddr, regval);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return regval;
|
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Name: pwm_putreg
|
|
|
|
*
|
|
|
|
* Description:
|
|
|
|
* Write a value to a PWM register.
|
|
|
|
*
|
|
|
|
* Input Parameters:
|
|
|
|
* chan - A reference to the PWM channel instance
|
|
|
|
* offset - The offset to the register to read
|
|
|
|
*
|
|
|
|
* Returned Value:
|
|
|
|
* None
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
2013-11-07 09:17:46 -06:00
|
|
|
static void pwm_putreg(struct sam_pwm_chan_s *chan, int offset,
|
|
|
|
uint32_t regval)
|
2013-11-06 10:55:12 -06:00
|
|
|
{
|
|
|
|
#ifdef PWM_SINGLE
|
2013-11-07 09:17:46 -06:00
|
|
|
uintptr_t regaddr = SAM_PWMC_VBASE + offset;
|
|
|
|
|
|
|
|
#ifdef CONFIG_SAMA5_PWM_REGDEBUG
|
|
|
|
if (pwm_checkreg(&g_pwm, true, regval, regaddr))
|
|
|
|
{
|
|
|
|
lldbg("%08x<-%08x\n", regaddr, regval);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
putreg32(regval, regaddr);
|
|
|
|
|
2013-11-06 10:55:12 -06:00
|
|
|
#else
|
|
|
|
struct sam_pwm_chan_s *pwm = chan->pwm;
|
2013-11-07 09:17:46 -06:00
|
|
|
uintptr_t regaddr = pwm->base + offset;
|
2013-11-06 10:55:12 -06:00
|
|
|
|
|
|
|
#ifdef CONFIG_SAMA5_PWM_REGDEBUG
|
2013-11-07 09:17:46 -06:00
|
|
|
if (pwm_checkreg(pwm, true, regval, regaddr))
|
2013-11-06 10:55:12 -06:00
|
|
|
{
|
|
|
|
lldbg("%08x<-%08x\n", regaddr, regval);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
putreg32(regval, regaddr);
|
2013-11-07 09:17:46 -06:00
|
|
|
|
|
|
|
#endif
|
2013-11-06 10:55:12 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Name: pwm_chan_putreg
|
|
|
|
*
|
|
|
|
* Description:
|
|
|
|
* Read the value of an PWM channel register.
|
|
|
|
*
|
|
|
|
* Input Parameters:
|
|
|
|
* chan - A reference to the PWM channel instance
|
|
|
|
* offset - The offset to the channel register to read
|
|
|
|
*
|
|
|
|
* Returned Value:
|
|
|
|
* None
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
2013-11-07 09:17:46 -06:00
|
|
|
static void pwm_chan_putreg(struct sam_pwm_chan_s *chan, int offset,
|
|
|
|
uint32_t regval)
|
2013-11-06 10:55:12 -06:00
|
|
|
{
|
|
|
|
uintptr_t regaddr = chan->base + offset;
|
|
|
|
|
|
|
|
#ifdef CONFIG_SAMA5_PWM_REGDEBUG
|
2013-11-07 09:17:46 -06:00
|
|
|
#ifdef PWM_SINGLE
|
|
|
|
if (pwm_checkreg(&g_pwm, true, regval, regaddr))
|
|
|
|
#else
|
2013-11-06 10:55:12 -06:00
|
|
|
if (pwm_checkreg(chan->pwm, true, regval, regaddr))
|
2013-11-07 09:17:46 -06:00
|
|
|
#endif
|
2013-11-06 10:55:12 -06:00
|
|
|
{
|
|
|
|
lldbg("%08x<-%08x\n", regaddr, regval);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
putreg32(regval, regaddr);
|
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Name: pwm_dumpregs
|
|
|
|
*
|
|
|
|
* Description:
|
|
|
|
* Dump all timer registers.
|
|
|
|
*
|
|
|
|
* Input parameters:
|
|
|
|
* chan - A reference to the PWM channel instance
|
|
|
|
*
|
|
|
|
* Returned Value:
|
|
|
|
* None
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
#if defined(CONFIG_DEBUG_PWM) && defined(CONFIG_DEBUG_VERBOSE)
|
|
|
|
static void pwm_dumpregs(struct sam_pwm_chan_s *chan, FAR const char *msg)
|
|
|
|
{
|
|
|
|
pwmvdbg("PWM: %s\n", msg);
|
2013-11-07 09:17:46 -06:00
|
|
|
pwmvdbg(" CLK: %08x SR: %08x IMR1: %08x ISR1: %08x\n",
|
2013-11-06 10:55:12 -06:00
|
|
|
pwm_getreg(chan, SAM_PWM_CLK_OFFSET),
|
|
|
|
pwm_getreg(chan, SAM_PWM_SR_OFFSET),
|
|
|
|
pwm_getreg(chan, SAM_PWM_IMR1_OFFSET),
|
|
|
|
pwm_getreg(chan, SAM_PWM_ISR1_OFFSET));
|
2013-11-07 09:17:46 -06:00
|
|
|
pwmvdbg(" SCM: %08x SCUC: %08x SCUP: %08x IMR2: %08x\n",
|
2013-11-06 10:55:12 -06:00
|
|
|
pwm_getreg(chan, SAM_PWM_SCM_OFFSET),
|
|
|
|
pwm_getreg(chan, SAM_PWM_SCUC_OFFSET),
|
|
|
|
pwm_getreg(chan, SAM_PWM_SCUP_OFFSET),
|
|
|
|
pwm_getreg(chan, SAM_PWM_IMR2_OFFSET));
|
2013-11-07 09:17:46 -06:00
|
|
|
pwmvdbg(" ISR2: %08x OOV: %08x OS: %08x FMR: %08x\n",
|
2013-11-06 10:55:12 -06:00
|
|
|
pwm_getreg(chan, SAM_PWM_ISR2_OFFSET),
|
|
|
|
pwm_getreg(chan, SAM_PWM_OOV_OFFSET),
|
|
|
|
pwm_getreg(chan, SAM_PWM_OS_OFFSET),
|
|
|
|
pwm_getreg(chan, SAM_PWM_FMR_OFFSET));
|
2013-11-07 09:17:46 -06:00
|
|
|
pwmvdbg(" FSR: %08x FPV: %08x FPE: %08x ELMR0: %08x\n",
|
2013-11-06 10:55:12 -06:00
|
|
|
pwm_getreg(chan, SAM_PWM_FSR_OFFSET),
|
|
|
|
pwm_getreg(chan, SAM_PWM_FPV_OFFSET),
|
|
|
|
pwm_getreg(chan, SAM_PWM_FPE_OFFSET),
|
|
|
|
pwm_getreg(chan, SAM_PWM_ELMR0_OFFSET));
|
2013-11-07 09:17:46 -06:00
|
|
|
pwmvdbg(" ELMR1: %08x SMMR: %08x WPSR: %08x\n",
|
2013-11-06 10:55:12 -06:00
|
|
|
pwm_getreg(chan, SAM_PWM_ELMR1_OFFSET),
|
|
|
|
pwm_getreg(chan, SAM_PWM_SMMR_OFFSET),
|
|
|
|
pwm_getreg(chan, SAM_PWM_WPSR_OFFSET));
|
2013-11-07 09:17:46 -06:00
|
|
|
pwmvdbg(" CMPV0: %08x CMPM0: %08x CMPV1: %08x CMPM1: %08x\n",
|
2013-11-06 10:55:12 -06:00
|
|
|
pwm_getreg(chan, SAM_PWM_CMPV0_OFFSET),
|
|
|
|
pwm_getreg(chan, SAM_PWM_CMPM0_OFFSET),
|
|
|
|
pwm_getreg(chan, SAM_PWM_CMPV1_OFFSET),
|
|
|
|
pwm_getreg(chan, SAM_PWM_CMPM1_OFFSET));
|
2013-11-07 09:17:46 -06:00
|
|
|
pwmvdbg(" CMPV2: %08x CMPM2: %08x CMPV3: %08x CMPM3: %08x\n",
|
2013-11-06 10:55:12 -06:00
|
|
|
pwm_getreg(chan, SAM_PWM_CMPV2_OFFSET),
|
|
|
|
pwm_getreg(chan, SAM_PWM_CMPM2_OFFSET),
|
|
|
|
pwm_getreg(chan, SAM_PWM_CMPV3_OFFSET),
|
|
|
|
pwm_getreg(chan, SAM_PWM_CMPM3_OFFSET));
|
2013-11-07 09:17:46 -06:00
|
|
|
pwmvdbg(" CMPV4: %08x CMPM4: %08x CMPV5: %08x CMPM5: %08x\n",
|
2013-11-06 10:55:12 -06:00
|
|
|
pwm_getreg(chan, SAM_PWM_CMPV4_OFFSET),
|
|
|
|
pwm_getreg(chan, SAM_PWM_CMPM4_OFFSET),
|
|
|
|
pwm_getreg(chan, SAM_PWM_CMPV5_OFFSET),
|
|
|
|
pwm_getreg(chan, SAM_PWM_CMPM5_OFFSET));
|
2013-11-07 09:17:46 -06:00
|
|
|
pwmvdbg(" CMPV6: %08x CMPM6: %08x CMPV7: %08x CMPM7: %08x\n",
|
2013-11-06 10:55:12 -06:00
|
|
|
pwm_getreg(chan, SAM_PWM_CMPV6_OFFSET),
|
|
|
|
pwm_getreg(chan, SAM_PWM_CMPM6_OFFSET),
|
|
|
|
pwm_getreg(chan, SAM_PWM_CMPV7_OFFSET),
|
|
|
|
pwm_getreg(chan, SAM_PWM_CMPM7_OFFSET));
|
|
|
|
pwmvdbg("Channel %d: %s\n", chan->channel, msg);
|
2013-11-07 09:17:46 -06:00
|
|
|
pwmvdbg(" CMR: %08x CDTY: %08x CPRD: %08x CCNT: %08x\n",
|
2013-11-06 10:55:12 -06:00
|
|
|
pwm_chan_getreg(chan, SAM_PWM_CMR_OFFSET),
|
|
|
|
pwm_chan_getreg(chan, SAM_PWM_CDTY_OFFSET),
|
|
|
|
pwm_chan_getreg(chan, SAM_PWM_CPRD_OFFSET),
|
|
|
|
pwm_chan_getreg(chan, SAM_PWM_CCNT_OFFSET));
|
2013-11-07 09:17:46 -06:00
|
|
|
pwmvdbg(" CT: %08x\n",
|
2013-11-06 10:55:12 -06:00
|
|
|
pwm_chan_getreg(chan, SAM_PWM_DT_OFFSET));
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Name: pwm_interrupt
|
|
|
|
*
|
|
|
|
* Description:
|
|
|
|
* Handle timer interrupts.
|
|
|
|
*
|
|
|
|
* Input parameters:
|
|
|
|
* Standard interrupt handler inputs
|
|
|
|
*
|
|
|
|
* Returned Value:
|
|
|
|
* Zero on success; a negated errno value on failure
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
#ifdef PWM_INTERRUPTS
|
|
|
|
static int pwm_interrupt(int irq, void *context)
|
|
|
|
{
|
|
|
|
/* No PWM interrupts are used in the current design */
|
|
|
|
|
|
|
|
#warning Missing logic
|
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Name: pwm_setup
|
|
|
|
*
|
|
|
|
* Description:
|
|
|
|
* This method is called when the driver is opened. The lower half driver
|
|
|
|
* will be configured and initialized the device so that it is ready for
|
|
|
|
* use. It will not, however, output pulses until the start method is
|
|
|
|
* called.
|
|
|
|
*
|
|
|
|
* Input parameters:
|
|
|
|
* dev - A reference to the lower half PWM driver state structure
|
|
|
|
*
|
|
|
|
* Returned Value:
|
|
|
|
* Zero on success; a negated errno value on failure
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
static int pwm_setup(FAR struct pwm_lowerhalf_s *dev)
|
|
|
|
{
|
|
|
|
FAR struct sam_pwm_chan_s *chan = (FAR struct sam_pwm_chan_s *)dev;
|
|
|
|
|
|
|
|
pwmvdbg("Channel %d: H=%08x L=%08x FI=%08x\n",
|
|
|
|
chan->channel, chan->ohpincfg, chan->olpincfg, chan->fipincfg);
|
|
|
|
|
|
|
|
/* Configure selected PWM pins */
|
|
|
|
|
|
|
|
if (chan->ohpincfg)
|
|
|
|
{
|
|
|
|
(void)sam_configpio(chan->ohpincfg);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (chan->olpincfg)
|
|
|
|
{
|
|
|
|
(void)sam_configpio(chan->olpincfg);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (chan->fipincfg)
|
|
|
|
{
|
|
|
|
(void)sam_configpio(chan->fipincfg);
|
|
|
|
}
|
|
|
|
|
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Name: pwm_shutdown
|
|
|
|
*
|
|
|
|
* Description:
|
|
|
|
* This method is called when the driver is closed. The lower half driver
|
|
|
|
* stop pulsed output, free any resources, disable the timer hardware, and
|
|
|
|
* put the system into the lowest possible power usage state
|
|
|
|
*
|
|
|
|
* Input parameters:
|
|
|
|
* dev - A reference to the lower half PWM driver state structure
|
|
|
|
*
|
|
|
|
* Returned Value:
|
|
|
|
* Zero on success; a negated errno value on failure
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
static int pwm_shutdown(FAR struct pwm_lowerhalf_s *dev)
|
|
|
|
{
|
|
|
|
FAR struct sam_pwm_chan_s *chan = (FAR struct sam_pwm_chan_s *)dev;
|
|
|
|
|
2013-11-06 12:24:51 -06:00
|
|
|
pwmvdbg("Channel %d\n", chan->channel);
|
2013-11-06 10:55:12 -06:00
|
|
|
|
|
|
|
/* Make sure that the output has been stopped */
|
|
|
|
|
|
|
|
pwm_stop(dev);
|
|
|
|
|
|
|
|
/* Then put the GPIO pins back to the default, input state */
|
|
|
|
|
|
|
|
pwm_resetpins(chan);
|
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Name: pwm_start
|
|
|
|
*
|
|
|
|
* Description:
|
|
|
|
* (Re-)initialize the timer resources and start the pulsed output
|
|
|
|
*
|
|
|
|
* Input parameters:
|
|
|
|
* dev - A reference to the lower half PWM driver state structure
|
|
|
|
* info - A reference to the characteristics of the pulsed output
|
|
|
|
*
|
|
|
|
* Returned Value:
|
|
|
|
* Zero on success; a negated errno value on failure
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
static int pwm_start(FAR struct pwm_lowerhalf_s *dev,
|
|
|
|
FAR const struct pwm_info_s *info)
|
|
|
|
{
|
|
|
|
FAR struct sam_pwm_chan_s *chan = (FAR struct sam_pwm_chan_s *)dev;
|
|
|
|
uint32_t regval;
|
|
|
|
uint32_t cprd;
|
2013-11-06 12:24:51 -06:00
|
|
|
uint32_t fsrc;
|
2013-11-06 10:55:12 -06:00
|
|
|
|
|
|
|
/* Disable the channel (should already be disabled) */
|
|
|
|
|
2013-11-06 12:24:51 -06:00
|
|
|
pwm_putreg(chan, SAM_PWM_DIS_OFFSET, PWM_CHID(chan->channel));
|
2013-11-06 10:55:12 -06:00
|
|
|
|
|
|
|
/* Determine the clock source */
|
|
|
|
|
|
|
|
switch (chan->clksrc)
|
|
|
|
{
|
|
|
|
case PWM_CLKSRC_MCK:
|
2013-11-06 12:24:51 -06:00
|
|
|
regval = PWM_CMR_CPRE_MCKDIV(chan->divlog2);
|
|
|
|
fsrc = BOARD_MCK_FREQUENCY >> chan->divlog2;
|
2013-11-06 10:55:12 -06:00
|
|
|
break;
|
|
|
|
|
2013-11-06 14:38:19 -06:00
|
|
|
#ifdef CONFIG_SAMA5_PWM_CLKA
|
2013-11-06 10:55:12 -06:00
|
|
|
case PWM_CLKSRC_CLKA:
|
|
|
|
regval = PWM_CMR_CPRE_CLKA;
|
|
|
|
fsrc = CLKA_FREQUENCY;
|
|
|
|
break;
|
2013-11-06 14:38:19 -06:00
|
|
|
#endif
|
2013-11-06 10:55:12 -06:00
|
|
|
|
2013-11-06 14:38:19 -06:00
|
|
|
#ifdef CONFIG_SAMA5_PWM_CLKB
|
2013-11-06 10:55:12 -06:00
|
|
|
case PWM_CLKSRC_CLKB:
|
|
|
|
regval = PWM_CMR_CPRE_CLKB;
|
|
|
|
fsrc = CLKB_FREQUENCY;
|
|
|
|
break;
|
2013-11-06 14:38:19 -06:00
|
|
|
#endif
|
2013-11-06 10:55:12 -06:00
|
|
|
|
|
|
|
default:
|
2013-11-06 14:38:19 -06:00
|
|
|
pwmdbg("ERROR: Invalid or unsupported clock source value: %d\n", chan->clksrc);
|
2013-11-06 10:55:12 -06:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Configure the channel */
|
|
|
|
|
2013-11-06 12:24:51 -06:00
|
|
|
pwm_chan_putreg(chan, SAM_PWM_CMR_OFFSET, PWM_CMR_CPRE_CLKA);
|
2013-11-06 10:55:12 -06:00
|
|
|
|
2014-04-13 16:22:22 -06:00
|
|
|
/* Set the PWM period.
|
2013-11-06 10:55:12 -06:00
|
|
|
*
|
|
|
|
* If the waveform is left-aligned, then the output waveform period
|
|
|
|
* depends on the channel counter source clock and can be calculated
|
|
|
|
* as follows:
|
|
|
|
*
|
|
|
|
* Tchan = cprd / Fsrc
|
|
|
|
* cprd = Fsrc / Fchan
|
|
|
|
*
|
|
|
|
* If the waveform is center-aligned, then the output waveform period
|
|
|
|
* depends on the channel counter source clock and can be calculated:
|
|
|
|
*
|
|
|
|
* Tchan = 2 * cprd / Fsrc
|
|
|
|
* cprd = Fsrc / 2 / Fchan
|
|
|
|
*
|
|
|
|
* Since the PWM is disabled, we can write directly to the CPRD (vs.
|
|
|
|
* the CPRDUPD) register.
|
|
|
|
*/
|
|
|
|
|
2013-11-07 09:17:46 -06:00
|
|
|
cprd = (fsrc + (info->frequency >> 1)) / info->frequency;
|
2013-11-06 12:24:51 -06:00
|
|
|
pwm_chan_putreg(chan, SAM_PWM_CPRD_OFFSET, cprd);
|
2013-11-06 10:55:12 -06:00
|
|
|
|
|
|
|
/* Set the PWM duty. Since the PWM is disabled, we can write directly
|
|
|
|
* to the CTDY (vs. the CTDYUPD) register.
|
|
|
|
*/
|
|
|
|
|
|
|
|
regval = b16toi(info->duty * cprd + b16HALF);
|
|
|
|
if (regval > cprd)
|
|
|
|
{
|
2013-11-07 09:17:46 -06:00
|
|
|
/* Rounding up could cause the duty value to exceed CPRD (?) */
|
2013-11-06 10:55:12 -06:00
|
|
|
|
|
|
|
regval = cprd;
|
|
|
|
}
|
|
|
|
|
2013-11-06 12:24:51 -06:00
|
|
|
pwm_chan_putreg(chan, SAM_PWM_CDTY_OFFSET, regval);
|
2013-11-07 09:17:46 -06:00
|
|
|
pwmvdbg("Fsrc=%d cprd=%d cdty=%d\n", fsrc, cprd, regval);
|
2013-11-06 10:55:12 -06:00
|
|
|
|
|
|
|
/* Enable the channel */
|
|
|
|
|
2013-11-06 12:24:51 -06:00
|
|
|
pwm_putreg(chan, SAM_PWM_ENA_OFFSET, PWM_CHID(chan->channel));
|
2013-11-06 14:46:19 -06:00
|
|
|
pwm_dumpregs(chan, "After start");
|
2013-11-06 10:55:12 -06:00
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Name: pwm_stop
|
|
|
|
*
|
|
|
|
* Description:
|
|
|
|
* Stop the pulsed output and reset the timer resources
|
|
|
|
*
|
|
|
|
* Input parameters:
|
|
|
|
* dev - A reference to the lower half PWM driver state structure
|
|
|
|
*
|
|
|
|
* Returned Value:
|
|
|
|
* Zero on success; a negated errno value on failure
|
|
|
|
*
|
|
|
|
* Assumptions:
|
|
|
|
* This function is called to stop the pulsed output at anytime. This
|
|
|
|
* method is also called from the timer interrupt handler when a repetition
|
|
|
|
* count expires... automatically stopping the timer.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
static int pwm_stop(FAR struct pwm_lowerhalf_s *dev)
|
|
|
|
{
|
|
|
|
FAR struct sam_pwm_chan_s *chan = (FAR struct sam_pwm_chan_s *)dev;
|
|
|
|
|
|
|
|
pwmvdbg("Channel %d\n", chan->channel);
|
|
|
|
|
|
|
|
/* Disable further PWM interrupts from this channel */
|
|
|
|
|
2013-11-06 12:24:51 -06:00
|
|
|
pwm_putreg(chan, SAM_PWM_IDR1_OFFSET,
|
2013-11-06 10:55:12 -06:00
|
|
|
PWM_INT1_CHID(chan->channel) | PWM_INT1_FCHID(chan->channel));
|
|
|
|
|
|
|
|
/* Disable the channel */
|
|
|
|
|
2013-11-06 12:24:51 -06:00
|
|
|
pwm_putreg(chan, SAM_PWM_DIS_OFFSET, PWM_CHID(chan->channel));
|
2013-11-06 10:55:12 -06:00
|
|
|
pwm_dumpregs(chan, "After stop");
|
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Name: pwm_ioctl
|
|
|
|
*
|
|
|
|
* Description:
|
|
|
|
* Lower-half logic may support platform-specific ioctl commands
|
|
|
|
*
|
|
|
|
* Input parameters:
|
|
|
|
* dev - A reference to the lower half PWM driver state structure
|
|
|
|
* cmd - The ioctl command
|
|
|
|
* arg - The argument accompanying the ioctl command
|
|
|
|
*
|
|
|
|
* Returned Value:
|
|
|
|
* Zero on success; a negated errno value on failure
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
static int pwm_ioctl(FAR struct pwm_lowerhalf_s *dev, int cmd, unsigned long arg)
|
|
|
|
{
|
|
|
|
#ifdef CONFIG_DEBUG_PWM
|
|
|
|
FAR struct sam_pwm_chan_s *chan = (FAR struct sam_pwm_chan_s *)dev;
|
|
|
|
|
|
|
|
/* There are no platform-specific ioctl commands */
|
|
|
|
|
|
|
|
pwmvdbg("Channel %d\n", chan->channel);
|
|
|
|
#endif
|
|
|
|
return -ENOTTY;
|
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Name: pwm_ioctl
|
|
|
|
*
|
|
|
|
* Description:
|
|
|
|
* Lower-half logic may support platform-specific ioctl commands
|
|
|
|
*
|
|
|
|
* Input parameters:
|
|
|
|
* dev - A reference to the lower half PWM driver state structure
|
|
|
|
* cmd - The ioctl command
|
|
|
|
* arg - The argument accompanying the ioctl command
|
|
|
|
*
|
|
|
|
* Returned Value:
|
|
|
|
* Zero on success; a negated errno value on failure
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
static void pwm_resetpins(FAR struct sam_pwm_chan_s *chan)
|
|
|
|
{
|
|
|
|
if (chan->ohpincfg)
|
|
|
|
{
|
|
|
|
(void)sam_configpio(PWM_MKINPUT(chan->ohpincfg));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (chan->olpincfg)
|
|
|
|
{
|
|
|
|
(void)sam_configpio(PWM_MKINPUT(chan->olpincfg));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (chan->fipincfg)
|
|
|
|
{
|
|
|
|
(void)sam_configpio(PWM_MKINPUT(chan->fipincfg));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Public Functions
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Name: sam_pwminitialize
|
|
|
|
*
|
|
|
|
* Description:
|
2013-11-06 12:24:51 -06:00
|
|
|
* Initialize one PWM channel for use with the upper_level PWM driver.
|
2013-11-06 10:55:12 -06:00
|
|
|
*
|
|
|
|
* Input Parameters:
|
|
|
|
* channel - A number identifying the PWM channel use.
|
|
|
|
*
|
|
|
|
* Returned Value:
|
|
|
|
* On success, a pointer to the SAMA5 lower half PWM driver is returned.
|
|
|
|
* NULL is returned on any failure.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
FAR struct pwm_lowerhalf_s *sam_pwminitialize(int channel)
|
|
|
|
{
|
|
|
|
FAR struct sam_pwm_chan_s *chan;
|
|
|
|
uint32_t regval;
|
|
|
|
|
|
|
|
pwmvdbg("Channel %d\n", channel);
|
|
|
|
|
2013-11-06 12:24:51 -06:00
|
|
|
switch (channel)
|
2013-11-06 10:55:12 -06:00
|
|
|
{
|
|
|
|
#ifdef CONFIG_SAMA5_PWM_CHAN0
|
|
|
|
case 0:
|
|
|
|
/* Select the Channel 0 interface */
|
|
|
|
|
|
|
|
chan = &g_pwm_chan0;
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef CONFIG_SAMA5_PWM_CHAN1
|
|
|
|
case 1:
|
|
|
|
/* Select the Channel 1 interface */
|
|
|
|
|
|
|
|
chan = &g_pwm_chan1;
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef CONFIG_SAMA5_PWM_CHAN2
|
|
|
|
case 2:
|
|
|
|
/* Select the Channel 2 interface */
|
|
|
|
|
|
|
|
chan = &g_pwm_chan2;
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef CONFIG_SAMA5_PWM_CHAN3
|
|
|
|
case 3:
|
|
|
|
/* Select the Channel 3 interface */
|
|
|
|
|
|
|
|
chan = &g_pwm_chan3;
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
default:
|
|
|
|
pwmdbg("ERROR: Channel invalid or not configured: %d\n", channel);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Have we already performed the one time initialization of the overall
|
|
|
|
* PWM peripheral?
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (!g_pwm.initialized)
|
|
|
|
{
|
|
|
|
/* Enable the PWM peripheral clock */
|
|
|
|
|
2013-11-06 12:24:51 -06:00
|
|
|
sam_pwm_enableclk();
|
2013-11-06 10:55:12 -06:00
|
|
|
|
|
|
|
/* Set clock A and clock B */
|
|
|
|
|
2013-11-06 12:24:51 -06:00
|
|
|
regval = (CLKA_PRE_BITS | CLKA_DIV_BITS | CLKB_PRE_BITS | CLKB_DIV_BITS);
|
|
|
|
pwm_putreg(chan, SAM_PWM_CLK_OFFSET, regval);
|
2013-11-06 10:55:12 -06:00
|
|
|
|
|
|
|
/* Disable all PWM interrupts at the PWM peripheral */
|
|
|
|
|
2013-11-06 12:24:51 -06:00
|
|
|
pwm_putreg(chan, SAM_PWM_IDR1_OFFSET, PWM_INT1_ALL);
|
|
|
|
pwm_putreg(chan, SAM_PWM_IDR2_OFFSET, PWM_INT2_ALL);
|
2013-11-06 10:55:12 -06:00
|
|
|
|
|
|
|
/* Attach the PWM interrupt handler */
|
|
|
|
|
|
|
|
#ifdef PWM_INTERRUPTS
|
|
|
|
ret = irq_attach(SAM_IRQ_PWM, pwm_interrupt);
|
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
pwmdbg("ERROR: Failed to attach IRQ%d\n", channel);
|
|
|
|
return NULL;
|
2014-04-13 16:22:22 -06:00
|
|
|
|
2013-11-06 10:55:12 -06:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* Clear any pending PWM interrupts */
|
|
|
|
|
2013-11-06 12:24:51 -06:00
|
|
|
(void)pwm_getreg(chan, SAM_PWM_ISR1_OFFSET);
|
|
|
|
(void)pwm_getreg(chan, SAM_PWM_ISR2_OFFSET);
|
2013-11-06 10:55:12 -06:00
|
|
|
|
|
|
|
/* Enable PWM interrupts at the AIC */
|
|
|
|
|
|
|
|
#ifdef PWM_INTERRUPTS
|
|
|
|
up_enable_irq(SAM_IRQ_PWM);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* Now were are initialized */
|
|
|
|
|
|
|
|
g_pwm.initialized = true;
|
2013-11-06 14:46:19 -06:00
|
|
|
pwm_dumpregs(chan, "After Initialization");
|
2013-11-06 10:55:12 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Configure all pins for this channel as inputs */
|
|
|
|
|
|
|
|
pwm_resetpins(chan);
|
|
|
|
|
|
|
|
/* Return the lower-half driver instance for this channel */
|
|
|
|
|
|
|
|
return (FAR struct pwm_lowerhalf_s *)chan;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* CONFIG_SAMA5_PWM */
|