summaryrefslogblamecommitdiffstats
path: root/c/src/lib/libbsp/arm/beagle/clock.c
blob: 912d90419745eade40a86d9fc600e6ef0d864e98 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13












                                                          
                                        


                  
                              



                              
                                          























                                         
             






















                                                            
      













































                                                      































































                                                                 

 

                                                  
 


                                                          

























































                                                                            








                                                          





                                                                      



                                      














































                                                                     








                                                                            


                                             
/**
 * @file
 *
 * @ingroup arm_beagle
 *
 * @brief Clock driver configuration.
 */

/*
 * Copyright (c) 2014 Ben Gras <beng@shrike-systems.com>.
 *
 * The license and distribution terms for this file may be
 * found in the file LICENSE in this distribution or at
 * http://www.rtems.org/license/LICENSE.
 */

#include <rtems.h>
#include <rtems/timecounter.h>
#include <bsp.h>

#include <libcpu/omap_timer.h>

static struct timecounter beagle_clock_tc;

static omap_timer_registers_t regs_v1 = {
  .TIDR = OMAP3_TIMER_TIDR,
  .TIOCP_CFG = OMAP3_TIMER_TIOCP_CFG,
  .TISTAT = OMAP3_TIMER_TISTAT,
  .TISR = OMAP3_TIMER_TISR,
  .TIER = OMAP3_TIMER_TIER,
  .TWER = OMAP3_TIMER_TWER,
  .TCLR = OMAP3_TIMER_TCLR,
  .TCRR = OMAP3_TIMER_TCRR,
  .TLDR = OMAP3_TIMER_TLDR,
  .TTGR = OMAP3_TIMER_TTGR,
  .TWPS = OMAP3_TIMER_TWPS,
  .TMAR = OMAP3_TIMER_TMAR,
  .TCAR1 = OMAP3_TIMER_TCAR1,
  .TSICR = OMAP3_TIMER_TSICR,
  .TCAR2 = OMAP3_TIMER_TCAR2,
  .TPIR = OMAP3_TIMER_TPIR,
  .TNIR = OMAP3_TIMER_TNIR,
  .TCVR = OMAP3_TIMER_TCVR,
  .TOCR = OMAP3_TIMER_TOCR,
  .TOWR = OMAP3_TIMER_TOWR,
};

#if IS_AM335X
/* AM335X has a different ip block for the non 1ms timers */
static omap_timer_registers_t regs_v2 = {
  .TIDR = AM335X_TIMER_TIDR,
  .TIOCP_CFG = AM335X_TIMER_TIOCP_CFG,
  .TISTAT = AM335X_TIMER_IRQSTATUS_RAW,
  .TISR = AM335X_TIMER_IRQSTATUS,
  .TIER = AM335X_TIMER_IRQENABLE_SET,
  .TWER = AM335X_TIMER_IRQWAKEEN,
  .TCLR = AM335X_TIMER_TCLR,
  .TCRR = AM335X_TIMER_TCRR,
  .TLDR = AM335X_TIMER_TLDR,
  .TTGR = AM335X_TIMER_TTGR,
  .TWPS = AM335X_TIMER_TWPS,
  .TMAR = AM335X_TIMER_TMAR,
  .TCAR1 = AM335X_TIMER_TCAR1,
  .TSICR = AM335X_TIMER_TSICR,
  .TCAR2 = AM335X_TIMER_TCAR2,
  .TPIR = -1,		/* UNDEF */
  .TNIR = -1,		/* UNDEF */
  .TCVR = -1,		/* UNDEF */
  .TOCR = -1,		/* UNDEF */
  .TOWR = -1		/* UNDEF */
};
#endif

/* which timers are in use? target-dependent.
 * initialize at compile time.
 */

#if IS_DM3730

static omap_timer_t dm37xx_timer = {
  .base = OMAP3_GPTIMER1_BASE,
  .irq_nr = OMAP3_GPT1_IRQ,
  .regs = &regs_v1
};

/* free running timer */
static omap_timer_t dm37xx_fr_timer = {
  .base = OMAP3_GPTIMER10_BASE,
  .irq_nr = OMAP3_GPT10_IRQ,
  .regs = &regs_v1
};

static struct omap_timer *fr_timer = &dm37xx_fr_timer;
static struct omap_timer *timer = &dm37xx_timer;

#endif

#if IS_AM335X

/* normal timer */
static omap_timer_t am335x_timer = {
  .base = AM335X_DMTIMER1_1MS_BASE,
  .irq_nr = AM335X_INT_TINT1_1MS,
  .regs = &regs_v1
};

/* free running timer */
static omap_timer_t am335x_fr_timer = {
  .base = AM335X_DMTIMER7_BASE,
  .irq_nr = AM335X_INT_TINT7,
  .regs = &regs_v2
};

static struct omap_timer *fr_timer = &am335x_fr_timer;
static struct omap_timer *timer = &am335x_timer;

#endif

#if IS_AM335X
#define FRCLOCK_HZ (16*1500000)
#endif

#if IS_DM3730
#define FRCLOCK_HZ (8*1625000)
#endif

#ifndef FRCLOCK_HZ
#error expected IS_AM335X or IS_DM3730 to be defined.
#endif

static void
omap3_frclock_init(void)
{
  uint32_t tisr;

#if IS_DM3730
  /* Stop timer */
  mmio_clear(fr_timer->base + fr_timer->regs->TCLR,
      OMAP3_TCLR_ST);

  /* Use functional clock source for GPTIMER10 */
  mmio_set(OMAP3_CM_CLKSEL_CORE, OMAP3_CLKSEL_GPT10);
#endif

#if IS_AM335X
  /* Disable the module and wait for the module to be disabled */
  set32(CM_PER_TIMER7_CLKCTRL, CM_MODULEMODE_MASK,
      CM_MODULEMODE_DISABLED);
  while ((mmio_read(CM_PER_TIMER7_CLKCTRL) & CM_CLKCTRL_IDLEST)
      != CM_CLKCTRL_IDLEST_DISABLE);

  set32(CLKSEL_TIMER7_CLK, CLKSEL_TIMER7_CLK_SEL_MASK,
      CLKSEL_TIMER7_CLK_SEL_SEL2);
  while ((read32(CLKSEL_TIMER7_CLK) & CLKSEL_TIMER7_CLK_SEL_MASK)
      != CLKSEL_TIMER7_CLK_SEL_SEL2);

  /* enable the module and wait for the module to be ready */
  set32(CM_PER_TIMER7_CLKCTRL, CM_MODULEMODE_MASK,
      CM_MODULEMODE_ENABLE);
  while ((mmio_read(CM_PER_TIMER7_CLKCTRL) & CM_CLKCTRL_IDLEST)
      != CM_CLKCTRL_IDLEST_FUNC);

  /* Stop timer */
  mmio_clear(fr_timer->base + fr_timer->regs->TCLR,
      OMAP3_TCLR_ST);
#endif

  /* Start and auto-reload at 0 */
  mmio_write(fr_timer->base + fr_timer->regs->TLDR, 0x0);
  mmio_write(fr_timer->base + fr_timer->regs->TCRR, 0x0);

  /* Set up overflow interrupt */
  tisr = OMAP3_TISR_MAT_IT_FLAG | OMAP3_TISR_OVF_IT_FLAG |
      OMAP3_TISR_TCAR_IT_FLAG;
  /* Clear interrupt status */
  mmio_write(fr_timer->base + fr_timer->regs->TISR, tisr);
  mmio_write(fr_timer->base + fr_timer->regs->TIER,
      OMAP3_TIER_OVF_IT_ENA);

  /* Start timer, without prescaler */
  mmio_set(fr_timer->base + fr_timer->regs->TCLR,
      OMAP3_TCLR_OVF_TRG | OMAP3_TCLR_AR | OMAP3_TCLR_ST);
}

static uint32_t
beagle_clock_get_timecount(struct timecounter *tc)
{
  return mmio_read(fr_timer->base + fr_timer->regs->TCRR);
}

static void
beagle_clock_initialize(void)
{
  uint32_t freq = 1000000UL/rtems_configuration_get_microseconds_per_tick();

  /* we only support 1ms resolution */
  uint32_t tisr;
#if IS_DM3730
  /* Stop timer */
  mmio_clear(timer->base + timer->regs->TCLR, OMAP3_TCLR_ST);

  /* Use 32 KHz clock source for GPTIMER1 */
  mmio_clear(OMAP3_CM_CLKSEL_WKUP, OMAP3_CLKSEL_GPT1);
#endif

#if IS_AM335X
  /* disable the module and wait for the module to be disabled */
  set32(CM_WKUP_TIMER1_CLKCTRL, CM_MODULEMODE_MASK,
      CM_MODULEMODE_DISABLED);
  while ((mmio_read(CM_WKUP_TIMER1_CLKCTRL) & CM_CLKCTRL_IDLEST)
      != CM_CLKCTRL_IDLEST_DISABLE);

  set32(CLKSEL_TIMER1MS_CLK, CLKSEL_TIMER1MS_CLK_SEL_MASK,
      CLKSEL_TIMER1MS_CLK_SEL_SEL2);
  while ((read32(CLKSEL_TIMER1MS_CLK) &
    CLKSEL_TIMER1MS_CLK_SEL_MASK) !=
      CLKSEL_TIMER1MS_CLK_SEL_SEL2);

  /* enable the module and wait for the module to be ready */
  set32(CM_WKUP_TIMER1_CLKCTRL, CM_MODULEMODE_MASK,
      CM_MODULEMODE_ENABLE);
  while ((mmio_read(CM_WKUP_TIMER1_CLKCTRL) & CM_CLKCTRL_IDLEST)
      != CM_CLKCTRL_IDLEST_FUNC);

  /* Stop timer */
  mmio_clear(timer->base + timer->regs->TCLR, OMAP3_TCLR_ST);
#endif

  /* Use 1-ms tick mode for GPTIMER1 TRM 16.2.4.2.1 */
  mmio_write(timer->base + timer->regs->TPIR, 232000);
  mmio_write(timer->base + timer->regs->TNIR, -768000);
  mmio_write(timer->base + timer->regs->TLDR,
      0xffffffff - (32768 / freq) + 1);
  mmio_write(timer->base + timer->regs->TCRR,
      0xffffffff - (32768 / freq) + 1);

  /* Set up overflow interrupt */
  tisr = OMAP3_TISR_MAT_IT_FLAG | OMAP3_TISR_OVF_IT_FLAG |
      OMAP3_TISR_TCAR_IT_FLAG;
  /* Clear interrupt status */
  mmio_write(timer->base + timer->regs->TISR, tisr);
  mmio_write(timer->base + timer->regs->TIER, OMAP3_TIER_OVF_IT_ENA);

  /* Start timer */
  mmio_set(timer->base + timer->regs->TCLR,
      OMAP3_TCLR_OVF_TRG | OMAP3_TCLR_AR | OMAP3_TCLR_ST);
  /* also initilize the free runnning timer */
  omap3_frclock_init();

#if IS_AM335X
  /* Disable AM335X watchdog */
  mmio_write(AM335X_WDT_BASE+AM335X_WDT_WSPR, 0xAAAA);
  while(mmio_read(AM335X_WDT_BASE+AM335X_WDT_WWPS) != 0) ;
  mmio_write(AM335X_WDT_BASE+AM335X_WDT_WSPR, 0x5555);
  while(mmio_read(AM335X_WDT_BASE+AM335X_WDT_WWPS) != 0) ;
#endif

  /* Install timecounter */ \
  beagle_clock_tc.tc_get_timecount = beagle_clock_get_timecount;
  beagle_clock_tc.tc_counter_mask = 0xffffffff;
  beagle_clock_tc.tc_frequency = FRCLOCK_HZ;
  beagle_clock_tc.tc_quality = RTEMS_TIMECOUNTER_QUALITY_CLOCK_DRIVER;
  rtems_timecounter_install(&beagle_clock_tc);
}

static void beagle_clock_at_tick(void)
{
  mmio_write(timer->base + timer->regs->TISR,
    OMAP3_TISR_MAT_IT_FLAG | OMAP3_TISR_OVF_IT_FLAG |
      OMAP3_TISR_TCAR_IT_FLAG);
}

static rtems_interrupt_handler clock_isr = NULL;

static void beagle_clock_handler_install(rtems_interrupt_handler isr)
{
  rtems_status_code sc = RTEMS_SUCCESSFUL;

  sc = rtems_interrupt_handler_install(
    timer->irq_nr,
    "Clock",
    RTEMS_INTERRUPT_UNIQUE,
    isr,
    NULL
  );

  if (sc != RTEMS_SUCCESSFUL) {
    rtems_fatal_error_occurred(0xdeadbeef);
  }
  clock_isr = isr;
}

static void beagle_clock_cleanup(void)
{
  rtems_status_code sc = RTEMS_SUCCESSFUL;

  /* Disable timer */
  mmio_clear(timer->base + timer->regs->TCLR, OMAP3_TCLR_ST);

  /* Remove interrupt handler */
  sc = rtems_interrupt_handler_remove(
    timer->irq_nr,
    clock_isr,
    NULL
  );
  if (sc != RTEMS_SUCCESSFUL) {
    rtems_fatal_error_occurred(0xdeadbeef);
  }
  clock_isr = NULL;

  /* stop frclock */
  mmio_clear(fr_timer->base + fr_timer->regs->TCLR, OMAP3_TCLR_ST);
}

#define Clock_driver_support_at_tick() beagle_clock_at_tick()
#define Clock_driver_support_initialize_hardware() beagle_clock_initialize()
#define Clock_driver_support_install_isr(isr, old_isr) \
  do {                   \
    beagle_clock_handler_install(isr);          \
    old_isr = NULL;              \
  } while (0)

#define Clock_driver_support_shutdown_hardware() beagle_clock_cleanup()

/* Include shared source clock driver code */
#include "../../shared/clockdrv_shell.h"