[PATCH] Add GPIO, I2C and SPI support for the RPi BSP

Joel Sherrill joel.sherrill at oarcorp.com
Fri Oct 31 18:12:58 UTC 2014


On 10/31/2014 11:05 AM, Alan Cudmore wrote:
> I'm going to try to look at the Pi BSP warnings soon. I can try to
> help address some of the issues with this patch as well.
>
Once you figure the warning out, I hope it is easy to fix the other
two BSPs with the same issue. :)
> What is the desired cut off date for 4.11? 
>
Last year. :)

Chris was joking that we measure releases based on the number of
developers who have children during the gap. If I am counting right,
there are 4 children and a grandchild.

Seriously, just as soon as it is ready. Settling the tools, the dynamic
loader, and getting the Beagle merged are also on the list.

--joel
> Alan
>
> On Fri, Oct 31, 2014 at 11:41 AM, Pavel Pisa <ppisa4lists at pikron.com
> <mailto:ppisa4lists at pikron.com>> wrote:
>
>     Hello Andre,
>
>     On Friday 31 of October 2014 12:56:40 Andre Marques wrote:
>
>     > +/**
>     > + * @brief Generic ISR that clears the event register on the
>     Raspberry Pi
>     > and calls + *        an user defined ISR.
>     > + *
>     > + * @param[in] arg Void pointer to a handler_arguments structure.
>     > + */
>     > +static void generic_handler(void* arg)
>     > +{
>     > +  handler_arguments* handler_args;
>     > +  int rv = 0;
>     > +  int pin = 0;
>     > +
>     > +  handler_args = (handler_arguments*) arg;
>     > +
>     > +  pin = handler_args->pin_number;
>     > +
>     > +  /*  If the interrupt was generated by the pin attached to
>     this ISR
>     > clears it. */ +  if ( BCM2835_REG(BCM2835_GPIO_GPEDS0) & (1 <<
>     pin) )
>     > +    BCM2835_REG(BCM2835_GPIO_GPEDS0) &= (1 << pin);
>     > +
>     > +  /* If not lets the next ISR process the interrupt. */
>     > +  else
>     > +    return;
>     > +
>     > +  /* If this pin has the deboucing function attached, call it. */
>     > +  if ( handler_args->debouncing_tick_count > 0 ) {
>     > +    rv = debounce_switch(pin);
>     > +
>     > +    if ( rv < 0 )
>     > +      return;
>     > +  }
>     > +
>     > +  /* Call the user's ISR. */
>     > +  (handler_args->handler) (handler_args->handler_arg);
>     > +}
>
>     There has to be mechanism (array of pointers) for registering args
>     for each pin handler routine.
>
>     > +/**
>     > + * @brief Enables interrupts to be generated on a given GPIO pin.
>     > + *        When fired that interrupt will call the given handler.
>     > + *
>     > + * @param[in] dev_pin Raspberry Pi GPIO pin label number (not
>     its position
>     > + *            on the header).
>     > + * @param[in] interrupt Type of interrupt to enable for the pin.
>     > + * @param[in] handler Pointer to a function that will be called
>     every time
>     > + *                    @var interrupt is generated. This
>     function must have
>     > + *                    no receiving parameters and return void.
>           @param[in] arg or context
>
>     > + *
>     > + * @retval 0 Interrupt successfully enabled for this pin.
>     > + * @retval -1 Could not replace the currently active interrupt
>     on this
>     > pin, or + *            unknown @var interrupt.
>     > + */
>     > +int gpio_enable_interrupt(int dev_pin, gpio_interrupt
>     interrupt, void
>     > (*handler)(void)) +{
>
>
>     int gpio_enable_interrupt(int dev_pin, gpio_interrupt interrupt, void
>        (*handler)(void *arg), void *arg)
>
>     It would worth to be able to register mutiple handlers for single
>     pin for case o emulating shared IRQ line - i.e. ISA bus, then
>     information
>     about IRQ handled/unhandled state has to be returned by handler -
>     but that
>     is not critical. Argument is critical as I have already stated.
>
>     The handler function prototype should be same as for RTEMS ISR
>     registration
>
>     typedef void (*rtems_interrupt_handler)(void *);
>
>     This allows to use drivers for some generic chips to be connected
>     to some external bus same way on different CPU and boards
>     architectures
>     with same ISR handler when directly connected to some external
>     interrupt
>     input and use same driver+handler when interrupt is connected
>     to GPIO ISR multiplexor.
>
>     I have already raised this concern for previous patch version
>     - see mail thread
>
>     http://article.gmane.org/gmane.os.rtems.user/13211
>
>     > +  rtems_status_code sc;
>     > +  rpi_gpio_pin *pin;
>     > +
>     > +  /* Only consider GPIO pins up to 31. */
>     > +  if ( dev_pin > 31 )
>     > +    return -1;
>     > +
>     > +  pin = &gpio_pin[dev_pin-1];
>     > +
>     > +  /* If the pin already has an enabled interrupt removes it first,
>     > +   * as well as its handler. */
>     > +  if ( pin->enabled_interrupt != NONE ) {
>     > +    sc = gpio_disable_interrupt(dev_pin);
>     > +
>     > +    if ( sc != RTEMS_SUCCESSFUL )
>     > +      return -1;
>     > +  }
>     > +
>     > +  pin->h_args.pin_number = dev_pin;
>     > +  pin->h_args.handler = handler;
>
>       +  pin->h_args.handler_arg = arg;
>
>
>     > +
>     > +  pin->h_args.last_isr_tick = rtems_clock_get_ticks_since_boot();
>     > +
>     > +  /* Installs the generic_handler, which will call the user handler
>     > received +   * a parameter. */
>     > +  sc = rtems_interrupt_handler_install(BCM2835_IRQ_ID_GPIO_0,
>     > +                                       NULL,
>     > +                                       RTEMS_INTERRUPT_SHARED,
>     > +                                       (rtems_interrupt_handler)
>     > generic_handler, +                                     
>      &(pin->h_args));
>
>     The use of RTEMS_INTERRUPT_SHARED and registering multiple handlers
>     for single pin group is significant overhead. It would be much faster
>     to register only one handler (when gpio_enable_interrupt is called
>     first time).
>     Then read interrupt status register (if before HW masking, apply mask)
>     and use ffs (find first bit set) in loop to process all pending
>     events.
>     Each time ffs gives the bit, clear the bit in the local copy,
>     retrieve pin description from the array (ffs gives the index)
>     and process user provided handler with corresponding user argument.
>
>     > +  if ( sc != RTEMS_SUCCESSFUL )
>     > +    return -1;
>     > +
>     > +  switch ( interrupt ) {
>     > +    case FALLING_EDGE:
>     > +
>     > +      /* Enables asynchronous falling edge detection. */
>     > +      BCM2835_REG(BCM2835_GPIO_GPAFEN0) |= (1 << dev_pin);
>     > +
>     > +      break;
>     > +
>     > +    case RISING_EDGE:
>     > +
>     > +      /* Enables asynchronous rising edge detection. */
>     > +      BCM2835_REG(BCM2835_GPIO_GPAREN0) |= (1 << dev_pin);
>     > +
>     > +      break;
>     > +
>     > +    case BOTH_EDGES:
>     > +
>     > +      /* Enables asynchronous falling edge detection. */
>     > +      BCM2835_REG(BCM2835_GPIO_GPAFEN0) |= (1 << dev_pin);
>     > +
>     > +      /* Enables asynchronous rising edge detection. */
>     > +      BCM2835_REG(BCM2835_GPIO_GPAREN0) |= (1 << dev_pin);
>     > +
>     > +      break;
>     > +
>     > +    case LOW_LEVEL:
>     > +
>     > +      /* Enables pin low level detection. */
>     > +      BCM2835_REG(BCM2835_GPIO_GPLEN0) |= (1 << dev_pin);
>     > +
>     > +      break;
>     > +
>     > +    case HIGH_LEVEL:
>     > +
>     > +      /* Enables pin high level detection. */
>     > +      BCM2835_REG(BCM2835_GPIO_GPHEN0) |= (1 << dev_pin);
>     > +
>     > +      break;
>     > +
>     > +    case BOTH_LEVELS:
>     > +
>     > +      /* Enables pin low level detection. */
>     > +      BCM2835_REG(BCM2835_GPIO_GPLEN0) |= (1 << dev_pin);
>     > +
>     > +      /* Enables pin high level detection. */
>     > +      BCM2835_REG(BCM2835_GPIO_GPHEN0) |= (1 << dev_pin);
>     > +
>     > +      break;
>     > +
>     > +    case NONE:
>     > +      return 0;
>     > +
>     > +    default:
>     > +      return -1;
>     > +  }
>     > +
>     > +  pin->enabled_interrupt = interrupt;
>     > +
>     > +  return 0;
>     > +}
>
>     The whole function and many others require to define some locking
>     to can be used from paralley running threads.
>
>     There should be functions which do plain enable and disable of IRQ
>     generation and then functions to register and unregister handler
>     for pin.
>     Register can/should probably call enable at the end to be
>     consistent with
>     rtems_interrupt_handler_install(). The enable and disable functions
>     should and in case of RPi can be written (separates set/reset
>     registers and
>     single CPU core so CPU level IRQ disable is cheap) such way that
>     they can be called from ISR context (no mutex locking). On the
>     other hand
>
>     gpio_enable_interrupt/gpio_interrupt_handler_install has quite little
>     other options than to use mutex. Use of CPU level IRQ disable
>     is not good for latencies.
>
>     There are probably some compiler (SMP for case of multiple cores)
>     barriers to ensure that structure is updated before enable
>     of IRQ in hardware.
>
>     > +/**
>     > + * @brief Stops interrupts from being generated from a given
>     GPIO pin
>     > + *        and removes the corresponding handler.
>     > + *
>     > + * @param[in] dev_pin Raspberry Pi GPIO pin label number (not
>     its position
>     > + *            on the header).
>     > + *
>     > + * @retval 0 Interrupt successfully disabled for this pin.
>     > + * @retval -1 Could not remove the current interrupt handler or
>     could not
>     > + *            recognise the current active interrupt on this pin.
>     > + */
>     > +int gpio_disable_interrupt(int dev_pin)
>     > +{
>     > +  rtems_status_code sc;
>     > +  rpi_gpio_pin *pin;
>     > +
>     > +  pin = &gpio_pin[dev_pin-1];
>     > +
>     > +  switch ( pin->enabled_interrupt ) {
>     > +    case FALLING_EDGE:
>     > +
>     > +      /* Disables asynchronous falling edge detection. */
>     > +      BCM2835_REG(BCM2835_GPIO_GPAFEN0) &= ~(1 << dev_pin);
>     > +
>     > +      break;
>     > +
>     > +    case RISING_EDGE:
>     > +
>     > +      /* Disables asynchronous rising edge detection. */
>     > +      BCM2835_REG(BCM2835_GPIO_GPAREN0) &= ~(1 << dev_pin);
>     > +
>     > +      break;
>     > +
>     > +    case BOTH_EDGES:
>     > +
>     > +      /* Disables asynchronous falling edge detection. */
>     > +      BCM2835_REG(BCM2835_GPIO_GPAFEN0) &= ~(1 << dev_pin);
>     > +
>     > +      /* Disables asynchronous rising edge detection. */
>     > +      BCM2835_REG(BCM2835_GPIO_GPAREN0) &= ~(1 << dev_pin);
>     > +
>     > +      break;
>     > +
>     > +    case LOW_LEVEL:
>     > +
>     > +      /* Disables pin low level detection. */
>     > +      BCM2835_REG(BCM2835_GPIO_GPLEN0) &= ~(1 << dev_pin);
>     > +
>     > +      break;
>     > +
>     > +    case HIGH_LEVEL:
>     > +
>     > +      /* Disables pin high level detection. */
>     > +      BCM2835_REG(BCM2835_GPIO_GPHEN0) &= ~(1 << dev_pin);
>     > +
>     > +      break;
>     > +
>     > +    case BOTH_LEVELS:
>     > +
>     > +      /* Disables pin low level detection. */
>     > +      BCM2835_REG(BCM2835_GPIO_GPLEN0) &= ~(1 << dev_pin);
>     > +
>     > +      /* Disables pin high level detection. */
>     > +      BCM2835_REG(BCM2835_GPIO_GPHEN0) &= ~(1 << dev_pin);
>     > +
>     > +      break;
>     > +
>     > +    case NONE:
>     > +      return 0;
>     > +
>     > +    default:
>     > +      return -1;
>     > +  }
>     > +
>     > +  /* Removes the handler. */
>     > +  sc = rtems_interrupt_handler_remove(BCM2835_IRQ_ID_GPIO_0,
>     > +                                      (rtems_interrupt_handler)
>     > generic_handler, +                                     
>     &(pin->h_args));
>     > +
>     > +  if ( sc != RTEMS_SUCCESSFUL )
>     > +    return -1;
>     > +
>     > +  pin->enabled_interrupt = NONE;
>     > +
>     > +  return 0;
>     > +}
>
>
>     You can look how interrupts are used in real-time RPi demo application
>     to control DC motor with incremental encoder signals detections by
>     software.
>     We have succeed to process 25000 interrupts/second with fully
>     preemptive
>     Linux kernel where actual IRQ handlers are run in kernel
>     threads/this means
>     than each ISR results in kernel context switch.
>
>     http://lintarget.sourceforge.net/rpi-motor-control/index.html
>
>     https://github.com/ppisa/rpi-rt-control/blob/master/kernel/modules/rpi_gpio_irc_module.c
>
>     RTEMS should be in much better situation there because its context
>     switch
>     is much lighter - no MMU consideration etc. So same task should
>     be portable for RTEMS easily. Even quadrature decoding can be done in
>     single handler for both pins because there is no latency between
>     HW ISR and its handler running in the kernel thread. So simple
>     state decoding could be used by reading values from GPIO input
>     register.
>
>     Please, try to make API and functions robust and (re)usable.
>     If you have problem to see all consequences then I can help with
>     some advises or if others agree I would prepare some follow up
>     patches to clean the code as my time allows ....
>     I have not so big problem with time to rewrite code but time
>     for testing is much bigger problem.
>
>     Best wishes,
>
>                     Pavel
>
>
>
>
>     _______________________________________________
>     devel mailing list
>     devel at rtems.org <mailto:devel at rtems.org>
>     http://lists.rtems.org/mailman/listinfo/devel
>
>

-- 
Joel Sherrill, Ph.D.             Director of Research & Development
joel.sherrill at OARcorp.com        On-Line Applications Research
Ask me about RTEMS: a free RTOS  Huntsville AL 35805
Support Available                (256) 722-9985

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.rtems.org/pipermail/devel/attachments/20141031/a25123cf/attachment-0001.html>


More information about the devel mailing list