text: /****************************************************************************
   * arch/arm/src/lpc17xx_40xx/lpc17_40_timer.c
   *
   *   Copyright (C) 2014, 2016 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 <debug.h>
  
  #include <nuttx/irq.h>
  #include <nuttx/arch.h>
  #include <nuttx/timers/pwm.h>
  #include <arch/board/board.h>
  
  #include "up_internal.h"
  #include "up_arch.h"
  
  #include "chip.h"
  #include "hardware/lpc17_40_syscon.h"
  #include "lpc17_40_timer.h"
  #include "hardware/lpc176x_pinconfig.h"
  #include "lpc17_40_gpio.h"
  #include "lpc176x_gpio.h"
  
  /* This module then only compiles if there is at least one enabled timer
   * intended for use with the TIMER upper half driver.
   */
  
  #if defined(CONFIG_LPC17_40_TMR0)
  
  /****************************************************************************
   * Pre-processor Definitions
   ****************************************************************************/
  /* PWM/Timer Definitions ****************************************************/
  /* The following definitions are used to identify the various time types */
  
  #define TIMTYPE_BASIC      0  /* Basic timers: TIM6-7 */
  #define TIMTYPE_GENERAL16  1  /* General 16-bit timers: TIM2-5 on F1 */
  #define TIMTYPE_COUNTUP16  2  /* General 16-bit count-up timers: TIM9-14 on F4 */
  #define TIMTYPE_GENERAL32  3  /* General 32-bit timers: TIM2-5 on F4 */
  #define TIMTYPE_ADVANCED   4  /* Advanced timers:  TIM1-8 */
  
  #define TIMTYPE_TIM1       TIMTYPE_ADVANCED
  
  
  /* Debug ********************************************************************/
  
  #ifdef CONFIG_DEBUG_PWM_INFO
  #  define pwm_dumpgpio(p,m) stm32_dumpgpio(p,m)
  #else
  #  define pwm_dumpgpio(p,m)
  #endif
  
  /****************************************************************************
   * Private Types
   ****************************************************************************/
  /* This structure represents the state of one PWM timer */
  
  struct lpc17_40_timer_s
  {
    FAR const struct pwm_ops_s *ops;     /* PWM operations */
    uint8_t                     timid;   /* Timer ID {0,...,7} */
    uint8_t                     channel; /* Timer output channel: {1,..4} */
    uint8_t                     timtype; /* See the TIMTYPE_* definitions */
    uint32_t                    base;    /* The base address of the timer */
    uint32_t                    pincfg;  /* Output pin configuration */
    uint32_t                    pclk;    /* The frequency of the peripheral clock
                                          * that drives the timer module. */
  };
  
  /****************************************************************************
   * Static Function Prototypes
   ****************************************************************************/
  /* Register access */
  
  static uint32_t timer_getreg(struct lpc17_40_timer_s *priv, int offset);
  static void timer_putreg(struct lpc17_40_timer_s *priv, int offset, uint32_t value);
  
  #ifdef CONFIG_DEBUG_PWM_INFO
  static void timer_dumpregs(struct lpc17_40_timer_s *priv, FAR const char *msg);
  #else
  #  define timer_dumpregs(priv,msg)
  #endif
  
  /* Timer management */
  
  static int timer_timer(FAR struct lpc17_40_timer_s *priv,
                       FAR const struct pwm_info_s *info);
  
  /* PWM driver methods */
  
  static int timer_setup(FAR struct pwm_lowerhalf_s *dev);
  static int timer_shutdown(FAR struct pwm_lowerhalf_s *dev);
  
  static int timer_start(FAR struct pwm_lowerhalf_s *dev,
                       FAR const struct pwm_info_s *info);
  
  static int timer_stop(FAR struct pwm_lowerhalf_s *dev);
  static int timer_ioctl(FAR struct pwm_lowerhalf_s *dev,
                       int cmd, unsigned long arg);
  
  /****************************************************************************
   * 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      = timer_setup,
    .shutdown   = timer_shutdown,
    .start      = timer_start,
    .stop       = timer_stop,
    .ioctl      = timer_ioctl,
  };
  
  #ifdef CONFIG_LPC17_40_TMR0
  static struct lpc17_40_timer_s g_pwm1dev =
  {
    .ops        = &g_pwmops,
    .timid      = 1,
    .channel    = CONFIG_LPC17_40_MAT0_PIN,
    .timtype    = TIMTYPE_TIM1,
    .base       = LPC17_40_TMR1_BASE,
    .pincfg     = GPIO_MAT0p1_2,
    .pclk       = (0x1 << 12),
  };
  #endif
  
  /****************************************************************************
   * Private Functions
   ****************************************************************************/
  
  /****************************************************************************
   * Name: timer_getreg
   *
   * Description:
   *   Read the value of an PWM timer register.
   *
   * Input Parameters:
   *   priv - A reference to the PWM block status
   *   offset - The offset to the register to read
   *
   * Returned Value:
   *   The current contents of the specified register
   *
   ****************************************************************************/
  
  static uint32_t timer_getreg(struct lpc17_40_timer_s *priv, int offset)
  {
    return getreg32(priv->base + offset);
  }
  
  /****************************************************************************
   * Name: timer_putreg
   *
   * Description:
   *   Read the value of an PWM timer register.
   *
   * Input Parameters:
   *   priv - A reference to the PWM block status
   *   offset - The offset to the register to read
   *
   * Returned Value:
   *   None
   *
   ****************************************************************************/
  
  static void timer_putreg(struct lpc17_40_timer_s *priv, int offset,
                           uint32_t value)
  {
    putreg32(value, priv->base + offset);
  }
  
  /****************************************************************************
   * Name: timer_dumpregs
   *
   * Description:
   *   Dump all timer registers.
   *
   * Input Parameters:
   *   priv - A reference to the PWM block status
   *
   * Returned Value:
   *   None
   *
   ****************************************************************************/
  
  #ifdef CONFIG_DEBUG_PWM_INFO
  static void timer_dumpregs(struct lpc17_40_timer_s *priv, FAR const char *msg)
  {
    pwminfo("%s:\n", msg);
    pwminfo("  CR1: %04x CR2:  %04x SMCR:  %04x DIER:  %04x\n",
            timer_getreg(priv, LPC17_40_PWM_MR0_OFFSET),
            timer_getreg(priv, LPC17_40_PWM_MR1_OFFSET),
            timer_getreg(priv, LPC17_40_PWM_MR2_OFFSET),
            timer_getreg(priv, LPC17_40_PWM_MR3_OFFSET));
  #if defined(CONFIG_LPC17_40_TMR0)
    if (priv->timtype == TIMTYPE_ADVANCED)
      {
        pwminfo("  RCR: %04x BDTR: %04x DCR:   %04x DMAR:  %04x\n",
                timer_getreg(priv, LPC17_40_PWM_MR0_OFFSET),
                timer_getreg(priv, LPC17_40_PWM_MR1_OFFSET),
                timer_getreg(priv, LPC17_40_PWM_MR2_OFFSET),
                timer_getreg(priv, LPC17_40_PWM_MR3_OFFSET));
      }
    else
  #endif
      {
        pwminfo("  DCR: %04x DMAR: %04x\n",
                timer_getreg(priv, LPC17_40_PWM_MR2_OFFSET),
                timer_getreg(priv, LPC17_40_PWM_MR3_OFFSET));
      }
  }
  #endif
  
  /****************************************************************************
   * Name: timer_timer
   *
   * Description:
   *   (Re-)initialize the timer resources and start the pulsed output
   *
   * Input Parameters:
   *   priv - 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 timer_timer(FAR struct lpc17_40_timer_s *priv,
                         FAR const struct pwm_info_s *info)
  {
    irqstate_t flags;
    uint32_t regval;
  
    flags = enter_critical_section();
  
    putreg32(info->frequency, LPC17_40_TMR0_MR1);  /* Set TIMER0 MR1 = number of counts */
    putreg32(info->frequency, LPC17_40_TMR1_MR0);  /* Set TIMER1 MR0 = number of counts */
  
    putreg32(1, LPC17_40_TMR0_TCR);                /* Start timer0 */
    putreg32(1, LPC17_40_TMR1_TCR);                /* Start timer1 */
  
    leave_critical_section(flags);
    timer_dumpregs(priv, "After starting");
    return OK;
  }
  
  #ifdef XXXXX
  /****************************************************************************
   * Name: timer_interrupt
   *
   * Description:
   *   Handle timer interrupts.
   *
   * Input Parameters:
   *   priv - A reference to the lower half PWM driver state structure
   *
   * Returned Value:
   *   Zero on success; a negated errno value on failure
   *
   ****************************************************************************/
  
  static int timer_interrupt(struct lpc17_40_timer_s *priv)
  {
    uint16_t regval;
  
    /* Verify that this is an update interrupt.  Nothing else is expected. */
  
    regval = timer_getreg(priv, STM32_ATIM_SR_OFFSET);
    DEBUGASSERT((regval & ATIM_SR_UIF) != 0);
  
    /* Clear the UIF interrupt bit */
  
    timer_putreg(priv, STM32_ATIM_SR_OFFSET, regval & ~ATIM_SR_UIF);
  
    /* Calculate the new count by subtracting the number of pulses
     * since the last interrupt.
     */
  
    return OK;
  }
  
  /****************************************************************************
   * Name: timer_tim1/8interrupt
   *
   * Description:
   *   Handle timer 1 and 8 interrupts.
   *
   * Input Parameters:
   *   Standard NuttX interrupt inputs
   *
   * Returned Value:
   *   Zero on success; a negated errno value on failure
   *
   ****************************************************************************/
  
  static int timer_tim1interrupt(int irq, void *context)
  {
    return timer_interrupt(&g_pwm1dev);
  }
  
  #endif /* XXXXX */
  
  /****************************************************************************
   * Name: timer_setup
   *
   * Description:
   *   This method is called when the driver is opened.  The lower half driver
   *   should configure and initialize the device so that it is ready for use.
   *   It should 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
   *
   * Assumptions:
   *   APB1 or 2 clocking for the GPIOs has already been configured by the RCC
   *   logic at power up.
   *
   ****************************************************************************/
  
  static int timer_setup(FAR struct pwm_lowerhalf_s *dev)
  {
    FAR struct lpc17_40_timer_s *priv = (FAR struct lpc17_40_timer_s *)dev;
    irqstate_t flags;
    uint32_t regval;
  
    flags = enter_critical_section();
  
    /* Power on the timer peripherals */
  
    regval  = getreg32(LPC17_40_SYSCON_PCONP);
    regval |= SYSCON_PCONP_PCTIM0;
    regval |= SYSCON_PCONP_PCTIM1;
    regval |= SYSCON_PCONP_PCTIM2;
    regval |= SYSCON_PCONP_PCTIM3;
    putreg32(regval, LPC17_40_SYSCON_PCONP);
  
    /* Select clock for the timer peripheral */
  
    regval  = getreg32(LPC17_40_SYSCON_PCLKSEL0);
    regval &= ~(0x3 << 2);
    regval |= (0x1 << 2);                   /* PCLK_MC peripheral clk=CCLK=12.5 MHz */
    regval &= ~(0x3 << 4);
    regval |= (0x1 << 4);                   /* PCLK_MC peripheral clk=CCLK=12.5 MHz */
    putreg32(regval, LPC17_40_SYSCON_PCLKSEL0);
    regval  = getreg32(LPC17_40_SYSCON_PCLKSEL1);
    regval &= ~(0x3 << 12);
    regval |= (0x1 << 12);                  /* PCLK_MC peripheral clk=CCLK=12.5 MHz */
    regval &= ~(0x3 << 14);
    regval |= (0x1 << 14);                  /* PCLK_MC peripheral clk=CCLK=12.5 MHz */
    putreg32(regval, LPC17_40_SYSCON_PCLKSEL1);
    priv->pclk = (0x1 << 12) | (0x1 << 4);
  
    putreg32(1000, LPC17_40_TMR0_MR1);         /* Set TIMER0 MR1 = number of counts */
  
    putreg32(1, LPC17_40_TMR0_PR);             /* Prescaler count frequency: Fpclk/1 */
    putreg32(~(0x3 << 0), LPC17_40_TMR0_CCR);  /* Prescaler count frequency: Fpclk/1 */
    putreg32(~(0x3 << 0), LPC17_40_TMR0_CTCR); /* Prescaler count frequency: Fpclk/1 */
    putreg32((2 << 3), LPC17_40_TMR0_MCR);     /* Reset on match register MR1 */
  
    /* Output bit toggle on external match event External match on MR1, Toggle
     * external bit
     */
  
    putreg32(((1 << 1) | (3 << 6)), LPC17_40_TMR0_EMR);
    putreg32((1 << 0), LPC17_40_TMR0_TCR);     /* Start timer0 */
  
    /* Configure the output pins GPIO3.26 */
  
    lpc17_40_configgpio(GPIO_MAT0p1_2);
  
    putreg32(500, LPC17_40_TMR1_MR0);          /* Set TIMER1 MR0 = number of counts */
  
    putreg32(1, LPC17_40_TMR1_PR);             /* Prescaler count frequency:Fpclk/1 */
    putreg32(~(0x3 << 0), LPC17_40_TMR1_CCR);  /* Prescaler count frequency:Fpclk/1 */
    putreg32(~(0x3 << 0), LPC17_40_TMR1_CTCR); /* Prescaler count frequency:Fpclk/1 */
    putreg32((2 << 0), LPC17_40_TMR1_MCR);     /* Reset on match register MR0 */
  //  putreg32(((1 << 0 )| (3 << 4)), LPC17_40_TMR1_EMR); /* Output bit toggle on external match event MAT0 */
    putreg32((1 << 0), LPC17_40_TMR1_TCR);     /* Start timer1 */
  
    /* configure the output pins GPIO3.26 */
  //  lpc17_40_configgpio(GPIO_MAT0p1_2);
  
    leave_critical_section(flags);
    pwm_dumpgpio(priv->pincfg, "TIMER setup");
    return OK;
  }
  
  /****************************************************************************
   * Name: timer_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 TIMER driver state structure
   *
   * Returned Value:
   *   Zero on success; a negated errno value on failure
   *
   ****************************************************************************/
  
  static int timer_shutdown(FAR struct pwm_lowerhalf_s *dev)
  {
    FAR struct lpc17_40_timer_s *priv = (FAR struct lpc17_40_timer_s *)dev;
    uint32_t pincfg;
  
    pwminfo("TIM%d pincfg: %08x\n", priv->timid, priv->pincfg);
  
    /* Make sure that the output has been stopped */
  
    return OK;
  }
  
  /****************************************************************************
   * Name: timer_start
   *
   * Description:
   *   (Re-)initialize the timer resources and start the pulsed output
   *
   * Input Parameters:
   *   dev - A reference to the lower half TIMER 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 timer_start(FAR struct pwm_lowerhalf_s *dev,
                         FAR const struct pwm_info_s *info)
  {
    FAR struct lpc17_40_timer_s *priv = (FAR struct lpc17_40_timer_s *)dev;
    return timer_timer(priv, info);
  }
  
  /****************************************************************************
   * Name: timer_stop
   *
   * Description:
   *   Stop the pulsed output and reset the timer resources
   *
   * Input Parameters:
   *   dev - A reference to the lower half TIMER 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 timer_stop(FAR struct pwm_lowerhalf_s *dev)
  {
    FAR struct lpc17_40_timer_s *priv = (FAR struct lpc17_40_timer_s *)dev;
    uint32_t resetbit;
    uint32_t regaddr;
    uint32_t regval;
    irqstate_t flags;
  
    pwminfo("TIM%d\n", priv->timid);
  
    /* Disable interrupts momentary to stop any ongoing timer processing and
     * to prevent any concurrent access to the reset register.
    */
  
    flags = enter_critical_section();
  
    /* Disable further interrupts and stop the timer */
  
    /* Determine which timer to reset */
  
    switch (priv->timid)
      {
  #ifdef CONFIG_LPC17_40_TMR0
        case 1:
          break;
  #endif
      }
  
    /* Reset the timer - stopping the output and putting the timer back
     * into a state where timer_start() can be called.
     */
  
    leave_critical_section(flags);
  
    pwminfo("regaddr: %08x resetbit: %08x\n", regaddr, resetbit);
    timer_dumpregs(priv, "After stop");
    return OK;
  }
  
  /****************************************************************************
   * Name: timer_ioctl
   *
   * Description:
   *   Lower-half logic may support platform-specific ioctl commands
   *
   * Input Parameters:
   *   dev - A reference to the lower half TIMER 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 timer_ioctl(FAR struct pwm_lowerhalf_s *dev, int cmd, unsigned long arg)
  {
  #ifdef CONFIG_DEBUG_PWM_INFO
    FAR struct lpc17_40_timer_s *priv = (FAR struct lpc17_40_timer_s *)dev;
  
    /* There are no platform-specific ioctl commands */
  
    pwminfo("TIM%d\n", priv->timid);
  #endif
    return -ENOTTY;
  }
  
  /****************************************************************************
   * Public Functions
   ****************************************************************************/
  
  /****************************************************************************
   * Name: lpc17_40_timerinitialize
   *
   * Description:
   *   Initialize one timer for use with the upper_level TIMER driver.
   *
   * Input Parameters:
   *   timer - A number identifying the timer use.  The number of valid timer
   *     IDs varies with the STM32 MCU and MCU family but is somewhere in
   *     the range of {1,..,14}.
   *
   * Returned Value:
   *   On success, a pointer to the STM32 lower half TIMER driver is returned.
   *   NULL is returned on any failure.
   *
   ****************************************************************************/
  
  FAR struct pwm_lowerhalf_s *lpc17_40_timerinitialize(int timer)
  {
    FAR struct lpc17_40_timer_s *lower;
  
    pwminfo("TIM%d\n", timer);
  
    switch (timer)
      {
  #ifdef CONFIG_LPC17_40_TMR0
        case 0:
          lower = &g_pwm1dev;
  
          /* Attach but disable the TIM1 update interrupt */
  
          break;
  #endif
  
        default:
          pwmerr("ERROR: No such timer configured\n");
          return NULL;
      }
  
    return (FAR struct pwm_lowerhalf_s *)lower;
  }
  
  #endif /* CONFIG_LPC17_40_TIMn_TIMER, n = 1,...,14 */
  