summaryrefslogblamecommitdiffstats
path: root/bsps/arm/tms570/irq/irq.c
blob: 9a80e0e3d28e2ed7a872354d5f38f88ea9bca3c3 (plain) (tree)
1
2
3
4
5
6
7
8
9

                                           
   
        
  
                              
  

                                                                    


   
                                                          






                                       







                                                                          
  










                                                                              



                            
                       


                              


                                             

                                                              
 












                                                                       
                             
                              

 



































                                                                             
   





                                            

 


                                          

 


































                                                                             











                                                     

                      
 







                                                 

 





                                                           








                                                                 




                                               













                                                              


                          




                                           

                  

                                                              



                                                  

 











                                                                 




                                                  

                 

                                                              



                                                    

 
                                              


                            
                                                              
                                                                  
                          










                                                                  
                                               


                            



                             
                                                              
                                                                  
                          










                                                        
                                            










                                                        
                                                                     





























                                                                          






















                                                                        
                                                              
 
/* SPDX-License-Identifier: BSD-2-Clause */

/**
 * @file
 *
 * @ingroup RTEMSBSPsARMTMS570
 *
 * @brief This source file contains the interrupt controller support
 *   implementation.
 */

/*
 * Copyright (C) 2014 Premysl Houdek <kom541000@gmail.com>
 *
 * Google Summer of Code 2014 at
 * Czech Technical University in Prague
 * Zikova 1903/4
 * 166 36 Praha 6
 * Czech Republic
 *
 * 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.
 *
 * 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.
 */

#include <bsp.h>
#include <bsp/irq-generic.h>
#include <bsp/tms570.h>
#include <bsp/irq.h>
#include <rtems/score/armv4.h>

#define VIM_CHANCTRL_COUNT 24
#define VIM_CHANMAP_MASK UINT32_C(0x7f)
#define VIM_CHANMAP_SHIFT(i) (24 - (8 * (i)))
#define VIM_REQ_REG(vector) ((vector) >> 5)
#define VIM_REQ_BIT(vector) (UINT32_C(1) << ((vector) & 0x1f))

static void vim_set_channel_request(uint32_t channel, uint32_t request)
{
  uint32_t chanctrl;
  int shift;

  chanctrl = TMS570_VIM.CHANCTRL[channel / 4];
  shift = VIM_CHANMAP_SHIFT(channel % 4);
  chanctrl &= ~(VIM_CHANMAP_MASK << shift);
  chanctrl |= request << shift;
  TMS570_VIM.CHANCTRL[channel / 4] = chanctrl;
}

rtems_status_code tms570_irq_set_priority(
  rtems_vector_number vector,
  uint32_t            priority
)
{
  rtems_interrupt_level level;
  uint32_t current_channel;
  uint32_t chanctrl;
  size_t i;
  size_t j;

  if (!bsp_interrupt_is_valid_vector(vector)) {
   return RTEMS_INVALID_ID;
  }

  if (priority < 2) {
    return RTEMS_INVALID_PRIORITY;
  }

  if (priority >= BSP_INTERRUPT_VECTOR_COUNT) {
    return RTEMS_INVALID_PRIORITY;
  }

  rtems_interrupt_disable(level);
  current_channel = TMS570_VIM.CHANCTRL[priority / 4];
  current_channel >>= VIM_CHANMAP_SHIFT(priority % 4);
  current_channel &= VIM_CHANMAP_MASK;

  for (i = 0; i < VIM_CHANCTRL_COUNT; ++i) {
    chanctrl = TMS570_VIM.CHANCTRL[i];

    for (j = 0; j < 4; ++j) {
      uint32_t channel_vector;

      channel_vector = (chanctrl >> VIM_CHANMAP_SHIFT(j)) & VIM_CHANMAP_MASK;

      if (channel_vector == vector) {
        vim_set_channel_request(i * 4 + j, current_channel);
        goto set_my_request;
      }
    }
  }

set_my_request:

  vim_set_channel_request(priority, vector);
  rtems_interrupt_enable(level);
  return RTEMS_SUCCESSFUL;
}

rtems_status_code tms570_irq_get_priority(
  rtems_vector_number  vector,
  unsigned            *priority
)
{
  rtems_interrupt_level level;
  size_t i;
  size_t j;

  if (priority == NULL) {
    return RTEMS_INVALID_ADDRESS;
  }

  if (!bsp_interrupt_is_valid_vector(vector)) {
   return RTEMS_INVALID_ID;
  }

  rtems_interrupt_disable(level);

  for (i = 0; i < VIM_CHANCTRL_COUNT; ++i) {
    uint32_t chanctrl;

    chanctrl = TMS570_VIM.CHANCTRL[i];

    for (j = 0; j < 4; ++j) {
      uint32_t channel_vector;

      channel_vector = (chanctrl >> VIM_CHANMAP_SHIFT(j)) & VIM_CHANMAP_MASK;

      if (channel_vector == vector) {
        rtems_interrupt_enable(level);
        *priority = i * 4 + j;
        return RTEMS_SUCCESSFUL;
      }
    }
  }

  rtems_interrupt_enable(level);
  *priority = UINT32_MAX;
  return RTEMS_NOT_DEFINED;
}

/**
 * @brief Interrupt dispatch
 *
 * Called by OS to determine which interrupt occured.
 * Function passes control to interrupt handler.
 *
 * @return Void
 */
void bsp_interrupt_dispatch(void)
{
  while (true) {
    uint32_t irqindex;

    irqindex = TMS570_VIM.IRQINDEX;

    if (irqindex == 0) {
      return;
    }

    bsp_interrupt_handler_dispatch(irqindex - 1);
  }
}

static bool can_disable(rtems_vector_number vector)
{
  /* INT_REQ0 and INT_REQ1 are always enabled as FIQ/NMI */
  return vector >= 2;
}

/**
 * @brief enables interrupt vector in the HW
 *
 * Enables HW interrupt for specified vector
 *
 * @param[in] vector vector of the isr which needs to be enabled.
 * @retval RTEMS_INVALID_ID vector is invalid.
 * @retval RTEMS_SUCCESSFUL interrupt source enabled.
 */
rtems_status_code bsp_interrupt_get_attributes(
  rtems_vector_number         vector,
  rtems_interrupt_attributes *attributes
)
{
  bool can_disable_vector;

  bsp_interrupt_assert(bsp_interrupt_is_valid_vector(vector));
  bsp_interrupt_assert(attributes != NULL);

  can_disable_vector = can_disable(vector);
  attributes->is_maskable = can_disable_vector;
  attributes->can_enable = true;
  attributes->maybe_enable = true;
  attributes->can_disable = can_disable_vector;
  attributes->maybe_disable = can_disable_vector;
  attributes->can_get_affinity = true;
  attributes->can_set_affinity = true;

  return RTEMS_SUCCESSFUL;
}

rtems_status_code bsp_interrupt_is_pending(
  rtems_vector_number vector,
  bool               *pending
)
{
  uint32_t intreq;

  bsp_interrupt_assert(bsp_interrupt_is_valid_vector(vector));
  bsp_interrupt_assert(pending != NULL);

  intreq = TMS570_VIM.INTREQ[VIM_REQ_REG(vector)];
  *pending = (intreq & VIM_REQ_BIT(vector)) != 0;
  return RTEMS_SUCCESSFUL;
}

rtems_status_code bsp_interrupt_raise(rtems_vector_number vector)
{
  bsp_interrupt_assert(bsp_interrupt_is_valid_vector(vector));
  return RTEMS_UNSATISFIED;
}

rtems_status_code bsp_interrupt_clear(rtems_vector_number vector)
{
  bsp_interrupt_assert(bsp_interrupt_is_valid_vector(vector));
  return RTEMS_UNSATISFIED;
}

rtems_status_code bsp_interrupt_vector_is_enabled(
  rtems_vector_number vector,
  bool               *enabled
)
{
  uint32_t reqen;

  bsp_interrupt_assert(bsp_interrupt_is_valid_vector(vector));
  bsp_interrupt_assert(enabled != NULL);

  reqen = TMS570_VIM.REQENASET[VIM_REQ_REG(vector)];
  *enabled = (reqen & VIM_REQ_BIT(vector)) != 0;
  return RTEMS_SUCCESSFUL;
}

rtems_status_code bsp_interrupt_vector_enable(
  rtems_vector_number vector
)
{
  bsp_interrupt_assert(bsp_interrupt_is_valid_vector(vector));
  TMS570_VIM.REQENASET[VIM_REQ_REG(vector)] = VIM_REQ_BIT(vector);
  return RTEMS_SUCCESSFUL;
}

/**
 * @brief disables interrupt vector in the HW
 *
 * Disables HW interrupt for specified vector
 *
 * @param[in] vector vector of the isr which needs to be disabled.
 * @retval RTEMS_INVALID_ID vector is invalid.
 * @retval RTEMS_SUCCESSFUL interrupt source disabled.
 */
rtems_status_code bsp_interrupt_vector_disable(
  rtems_vector_number vector
)
{
  if (!can_disable(vector)) {
    return RTEMS_UNSATISFIED;
  }

  bsp_interrupt_assert(bsp_interrupt_is_valid_vector(vector));
  TMS570_VIM.REQENACLR[VIM_REQ_REG(vector)] = VIM_REQ_BIT(vector);
  return RTEMS_SUCCESSFUL;
}

/**
 * @brief Init function of interrupt module
 *
 * Resets vectored interrupt interface to default state.
 * Disables all interrupts.
 * Set all sources as IRQ (not FIR).
 *
 * @retval RTEMS_SUCCESSFUL All is set
 */
void bsp_interrupt_facility_initialize(void)
{
  void (**vim_vec)(void) = (void (**)(void)) 0xFFF82000;
  unsigned int value = 0x00010203;
  unsigned int i = 0;
  uint32_t sctlr;

  /* Disable interrupts */
  for ( i = 0; i < 3; i++ ) {
    TMS570_VIM.REQENACLR[i] = 0xffffffff;
  }
  /* Map default events on interrupt vectors */
  for ( i = 0; i < VIM_CHANCTRL_COUNT; i += 1, value += 0x04040404) {
    TMS570_VIM.CHANCTRL[i] = value;
  }
  /* Set all vectors as IRQ (not FIR) */
  TMS570_VIM.FIRQPR[0] = 3;
  TMS570_VIM.FIRQPR[1] = 0;
  TMS570_VIM.FIRQPR[2] = 0;

  /*
    _CPU_ISR_install_vector(
        ARM_EXCEPTION_IRQ,
        _ARMV4_Exception_interrupt,
        NULL
    );

    Call to setup of interrupt entry in CPU level exception vectors table
    is not used (necessary/possible) because the table is provided
    by c/src/lib/libbsp/arm/shared/start/start.S and POM overlay
    solution remaps that to address zero.
  */

  for ( i = 0; i <= 94; ++i ) {
    vim_vec[i] = _ARMV4_Exception_interrupt;
  }
  /* Clear bit VE in SCTLR register to not use VIM IRQ exception bypass*/
  asm volatile ("mrc p15, 0, %0, c1, c0, 0\n": "=r" (sctlr));
  /*
   * Disable bypass of CPU level exception table for interrupt entry which
   * can be provided by VIM hardware
   */
  sctlr &= ~(1 << 24);
  #if 0
    /*
     * Option to enable exception table bypass for interrupts
     *
     * Because RTEMS requires all interrupts to be serviced through
     * common _ARMV4_Exception_interrupt handler to allow task switching
     * on exit from interrupt working correctly, vim_vec cannot point
     * directly to individual vector handlers and need to point
     * to single entry path. But if TMS570_VIM.IRQINDEX is then used
     * to target execution to corresponding service then for some
     * peripherals (i.e. EMAC) interrupt is already acknowledged
     * by VIM and IRQINDEX is read as zero which leads to spurious
     * interrupt and peripheral not serviced/blocked.
     *
     * To analyze this behavior we used trampolines which setup
     * bsp_interrupt_vector_inject and pass execution to
     * _ARMV4_Exception_interrupt. It works but is more ugly than
     * use of POM remap for these cases where application does not
     * start at address 0x00000000. If RTEMS image is placed at
     * memory space beginning then no of these constructs is necessary.
     */
    sctlr |= 1 << 24;
  #endif
  asm volatile ("mcr p15, 0, %0, c1, c0, 0\n": : "r" (sctlr));
}