Aditional documentation can be found in the RTEMS wiki http://www.rtems.org/wiki/index.php/Raspberry_Pi_BSP_Peripherals
and on my GSOC development blog http://asuolgsoc2014.wordpress.com/2014/08/18/testing-the-project/ Device drivers and test cases used to test this work were moved to a github repository https://github.com/asuol/RTEMS_rpi_testing --- c/src/lib/libbsp/arm/raspberrypi/Makefile.am | 11 +- c/src/lib/libbsp/arm/raspberrypi/configure.ac | 12 + c/src/lib/libbsp/arm/raspberrypi/gpio/gpio.c | 745 +++++++++++++++++++++ c/src/lib/libbsp/arm/raspberrypi/i2c/i2c.c | 427 ++++++++++++ c/src/lib/libbsp/arm/raspberrypi/i2c/i2c_init.c | 78 +++ c/src/lib/libbsp/arm/raspberrypi/i2c/spi.c | 564 ++++++++++++++++ c/src/lib/libbsp/arm/raspberrypi/i2c/spi_init.c | 83 +++ c/src/lib/libbsp/arm/raspberrypi/include/gpio.h | 198 ++++++ c/src/lib/libbsp/arm/raspberrypi/include/i2c.h | 166 +++++ c/src/lib/libbsp/arm/raspberrypi/include/irq.h | 6 +- .../libbsp/arm/raspberrypi/include/raspberrypi.h | 79 ++- c/src/lib/libbsp/arm/raspberrypi/irq/irq.c | 103 ++- c/src/lib/libbsp/arm/raspberrypi/preinstall.am | 8 + .../lib/libbsp/arm/raspberrypi/startup/bspstart.c | 17 +- 14 files changed, 2475 insertions(+), 22 deletions(-) create mode 100644 c/src/lib/libbsp/arm/raspberrypi/gpio/gpio.c create mode 100644 c/src/lib/libbsp/arm/raspberrypi/i2c/i2c.c create mode 100644 c/src/lib/libbsp/arm/raspberrypi/i2c/i2c_init.c create mode 100644 c/src/lib/libbsp/arm/raspberrypi/i2c/spi.c create mode 100644 c/src/lib/libbsp/arm/raspberrypi/i2c/spi_init.c create mode 100644 c/src/lib/libbsp/arm/raspberrypi/include/gpio.h create mode 100644 c/src/lib/libbsp/arm/raspberrypi/include/i2c.h diff --git a/c/src/lib/libbsp/arm/raspberrypi/Makefile.am b/c/src/lib/libbsp/arm/raspberrypi/Makefile.am index 839c8de..81dc196 100644 --- a/c/src/lib/libbsp/arm/raspberrypi/Makefile.am +++ b/c/src/lib/libbsp/arm/raspberrypi/Makefile.am @@ -44,6 +44,8 @@ include_bsp_HEADERS += include/irq.h include_bsp_HEADERS += include/mmu.h include_bsp_HEADERS += include/usart.h include_bsp_HEADERS += include/raspberrypi.h +include_bsp_HEADERS += include/gpio.h +include_bsp_HEADERS += include/i2c.h include_libcpu_HEADERS = ../../../libcpu/arm/shared/include/cache_.h \ ../../../libcpu/arm/shared/include/arm-cp15.h @@ -79,7 +81,6 @@ libbsp_a_SOURCES += ../../shared/bspclean.c libbsp_a_SOURCES += ../../shared/bspgetworkarea.c libbsp_a_SOURCES += ../../shared/bsplibc.c libbsp_a_SOURCES += ../../shared/bsppost.c -libbsp_a_SOURCES += ../../shared/bsppredriverhook.c libbsp_a_SOURCES += ../../shared/bsppretaskinghook.c libbsp_a_SOURCES += ../../shared/cpucounterread.c libbsp_a_SOURCES += ../../shared/cpucounterdiff.c @@ -119,11 +120,19 @@ libbsp_a_SOURCES += clock/clockdrv.c ../../../shared/clockdrv_shell.h # Timer libbsp_a_SOURCES += misc/timer.c +# GPIO + +libbsp_a_SOURCES += gpio/gpio.c + # RTC # SSP # I2C +libbsp_a_SOURCES += i2c/i2c.c +libbsp_a_SOURCES += i2c/i2c_init.c +libbsp_a_SOURCES += i2c/spi.c +libbsp_a_SOURCES += i2c/spi_init.c # Cache libbsp_a_SOURCES += ../../../libcpu/shared/src/cache_manager.c diff --git a/c/src/lib/libbsp/arm/raspberrypi/configure.ac b/c/src/lib/libbsp/arm/raspberrypi/configure.ac index 9bd6883..ef83c9d 100644 --- a/c/src/lib/libbsp/arm/raspberrypi/configure.ac +++ b/c/src/lib/libbsp/arm/raspberrypi/configure.ac @@ -24,6 +24,18 @@ AM_CONDITIONAL(HAS_NETWORKING,test "$HAS_NETWORKING" = "yes") RTEMS_BSPOPTS_SET([BSP_START_RESET_VECTOR],[*],[]) RTEMS_BSPOPTS_HELP([BSP_START_RESET_VECTOR],[reset vector address for BSP start]) +RTEMS_BSPOPTS_SET([I2C_IO_MODE],[*],[1]) +RTEMS_BSPOPTS_HELP([I2C_IO_MODE],[Define to 1 to use interrupt-driven I/O with the Raspberry Pi I2C bus. If defined to other value the access will be polled-driven.]) + +RTEMS_BSPOPTS_SET([SPI_IO_MODE],[*],[1]) +RTEMS_BSPOPTS_HELP([SPI_IO_MODE],[Define to 1 to use interrupt-driven I/O with the Raspberry Pi SPI bus. If defined to other value the access will be polled-driven.]) + +RTEMS_BSPOPTS_SET([BSP_ENABLE_SPI],[*],[0]) +RTEMS_BSPOPTS_HELP([BSP_ENABLE_SPI],[Define to 1 to use the SPI bus. This will register the bus in /dev/spi and also any device driver mentioned in i2c/spi_init.c .]) + +RTEMS_BSPOPTS_SET([BSP_ENABLE_I2C],[*],[1]) +RTEMS_BSPOPTS_HELP([BSP_ENABLE_I2C],[Define to 1 to use the I2C bus. This will register the bus in /dev/i2c and also any device driver mentioned in i2c/i2c_init.c .]) + RTEMS_BSP_CLEANUP_OPTIONS(0, 0) RTEMS_BSP_LINKCMDS diff --git a/c/src/lib/libbsp/arm/raspberrypi/gpio/gpio.c b/c/src/lib/libbsp/arm/raspberrypi/gpio/gpio.c new file mode 100644 index 0000000..d245339 --- /dev/null +++ b/c/src/lib/libbsp/arm/raspberrypi/gpio/gpio.c @@ -0,0 +1,745 @@ +/** + * @file gpio.c + * + * @ingroup raspberrypi_gpio + * + * @brief Support for the Raspberry PI GPIO. + * + */ + +/* + * Copyright (c) 2014 Andre Marques <andre.lousa.marques at gmail.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 <bsp/raspberrypi.h> +#include <bsp/irq.h> +#include <bsp/gpio.h> + +#include <stdlib.h> + +/* Calculates a bitmask to assign an alternate function to a given pin. */ +#define SELECT_PIN_FUNCTION(fn, pn) (fn << ((pn % 10) * 3)) + +static bool is_initialized = false; + +rpi_gpio_pin *gpio_pin; + +/** + * @brief Waits a number of CPU cycles. + * + * @param[in] cycles The number of CPU cycles to wait. + * + */ +static void arm_delay (int cycles) +{ + int i; + + for (i = 0; i < cycles; i++) + asm volatile ("nop"); +} + +/** + * @brief Initializes the GPIO API. + * Allocates space to the gpio_pin array and sets every pin as NOT_USED. + * If the API has already been initialized silently exits. + */ +void gpio_initialize(void) +{ + int i; + + if ( is_initialized ) + return; + + is_initialized = true; + + gpio_pin = (rpi_gpio_pin *) malloc(GPIO_PIN_COUNT * sizeof(rpi_gpio_pin)); + + for ( i = 0; i < GPIO_PIN_COUNT; i++ ) { + gpio_pin[i].pin_type = NOT_USED; + gpio_pin[i].enabled_interrupt = NONE; + + gpio_pin[i].h_args.debouncing_tick_count = 0; + } +} + +/** + * @brief Gives an output GPIO pin the logical value of 1. + * + * @param[in] pin The Raspberry Pi GPIO pin label number (not is position + * on the header). + * + * @retval 0 Pin was set successfully. + * @retval -1 The received pin is not configured as an digital output. + */ +int gpio_set(int pin) +{ + if (gpio_pin[pin-1].pin_type != DIGITAL_OUTPUT) + return -1; + + BCM2835_REG(BCM2835_GPIO_GPSET0) = (1 << pin); + + return 0; +} + +/** + * @brief Gives an output GPIO pin the logical value of 0. + * + * @param[in] pin The Raspberry Pi GPIO pin label number (not is position + * on the header). + * + * @retval 0 Pin was cleared successfully. + * @retval -1 The received pin is not configured as an digital output. + */ +int gpio_clear(int pin) +{ + if (gpio_pin[pin-1].pin_type != DIGITAL_OUTPUT) + return -1; + + BCM2835_REG(BCM2835_GPIO_GPCLR0) = (1 << pin); + + return 0; +} + +/** + * @brief Gets the value (level) of a GPIO input pin. + * + * @param[in] pin The Raspberry Pi GPIO pin label number (not is position + * on the header). + * + * @retval The function returns 0 or 1 depending on the pin current + * logical value. + */ +int gpio_get_val(int pin) +{ + return BCM2835_REG(BCM2835_GPIO_GPLEV0) &= (1 << (pin)); +} + +/** + * @brief Configures a GPIO pin to perform a certain function. + * + * @param[in] pin The Raspberry Pi GPIO pin label number (not is position + * on the header). + * @param[in] type The new function of the pin. + * + * @retval 0 Pin was configured successfully. + * @retval -1 The received pin is already being used, or unknown function. + */ +int gpio_select_pin(int pin, rpi_pin type) +{ + /* Calculate the pin function select register address. */ + volatile unsigned int *pin_addr = (unsigned int *)BCM2835_GPIO_REGS_BASE + (pin / 10); + + /* If the pin is already being used returns with an error. */ + if ( gpio_pin[pin-1].pin_type != NOT_USED ) + return -1; + + /* Sets pin function select bits as zero (DIGITAL_INPUT).*/ + *(pin_addr) &= ~SELECT_PIN_FUNCTION(7, pin); + + switch ( type ) { + case DIGITAL_INPUT: + + /* Digital input is set by default before this switch. */ + + break; + + case DIGITAL_OUTPUT: + + *(pin_addr) |= SELECT_PIN_FUNCTION(1, pin); + + break; + + case ALT_FUNC_0: + + *(pin_addr) |= SELECT_PIN_FUNCTION(4, pin); + + break; + + case ALT_FUNC_1: + + *(pin_addr) |= SELECT_PIN_FUNCTION(5, pin); + + break; + + case ALT_FUNC_2: + + *(pin_addr) |= SELECT_PIN_FUNCTION(6, pin); + + break; + + case ALT_FUNC_3: + + *(pin_addr) |= SELECT_PIN_FUNCTION(7, pin); + + break; + + case ALT_FUNC_4: + + *(pin_addr) |= SELECT_PIN_FUNCTION(3, pin); + + break; + + case ALT_FUNC_5: + + *(pin_addr) |= SELECT_PIN_FUNCTION(2, pin); + + break; + + default: + return -1; + } + + /* If the alternate function was successfuly assigned to the pin, + * record that information on the gpio_pin structure. */ + gpio_pin[pin-1].pin_type = type; + + return 0; +} + +/** + * @brief Configures the pull resistor setting of an array of GPIO pins. + * + * @param[in] pins Array of Raspberry Pi GPIO pin label numbers (not their position + * on the header). + * @param[in] pin_count Number of pins on the @var pins array. + * @param[in] mode The pull resistor mode. + * + * @retval 0 Pull resistor successfully configured. + * @retval -1 Unknown pull resistor mode. + */ +static int +set_input_mode(int *pins, int pin_count, int pin_mask, rpi_gpio_input_mode mode) +{ + int i; + + /* Set control signal. */ + switch ( mode ) { + case PULL_UP: + BCM2835_REG(BCM2835_GPIO_GPPUD) = (1 << 1); + break; + + case PULL_DOWN: + BCM2835_REG(BCM2835_GPIO_GPPUD) = (1 << 0); + break; + + case NO_PULL_RESISTOR: + BCM2835_REG(BCM2835_GPIO_GPPUD) = 0; + break; + + default: + return -1; + } + + /* Wait 150 cyles, as per BCM2835 documentation. */ + arm_delay(150); + + /* Setup clock for the control signal. */ + BCM2835_REG(BCM2835_GPIO_GPPUDCLK0) = pin_mask; + + arm_delay(150); + + /* Remove the control signal. */ + BCM2835_REG(BCM2835_GPIO_GPPUD) = 0; + + /* Remove the clock. */ + BCM2835_REG(BCM2835_GPIO_GPPUDCLK0) = 0; + + /* If the operation was successful, record that information + * on the gpio_pin structure so it can be recalled later. */ + for ( i = 0; i < pin_count; i++ ) + gpio_pin[pins[i]-1].input_mode = mode; + + return 0; +} + +/** + * @brief Configures a single GPIO pin pull resistor. + * + * @param[in] pin Raspberry Pi GPIO pin label number (not its position + * on the header). + * @param[in] mode The pull resistor mode. + * + * @retval 0 Pull resistor successfully configured. + * @retval -1 @see set_input_mode(). + */ +int gpio_input_mode(int pin, rpi_gpio_input_mode mode) +{ + int pin_mask = (1 << pin); + int pins[1]; + + /* If the desired actuation mode is already set, silently exits. */ + if ( gpio_pin[pin-1].input_mode == mode ) + return 0; + + pins[0] = pin; + + return set_input_mode(pins, 1, pin_mask, mode); +} + +/** + * @brief Sets the same pull-up/down resistors actuation mode to multiple GPIO input pins. + * There is a maximum number of 32 pins per call, which is enough for + * Raspberry Pi models A and B (17 GPIOs on P1 GPIO header) + * and also model B+ (28 GPIOs on J8 GPIO header). + * + * @param[in] pins Array of Raspberry Pi GPIO pin label numbers (not their position + * on the header). + * @param[in] pin_count Number of pins on the @var pins array. + * @param[in] mode The pull resistor mode. + * + * @retval 0 Pull resistor successfully configured. + * @retval -1 Unknown pull resistor mode. + */ +int gpio_setup_input_mode(int *pins, int pin_count, rpi_gpio_input_mode mode) +{ + uint32_t pin_mask = 0; + int diff_mode_counter = 0; + int i; + + if ( pin_count > 32 ) + return -1; + + /* Cycle through the given pins to check if this operation will have an effect + * on the resistor actuation mode of any one of the pins. + * Every pin that currently uses a different pull resistor mode sets a bit + * in its corresponding place on a bitmask. If the mode for a pin will not change + * then the diff_mode_counter variable is increased. */ + for ( i = 0; i < pin_count; i++ ) { + if ( gpio_pin[pins[i] - 1].input_mode != mode ) + pin_mask |= (1 << pins[i]); + + else + diff_mode_counter++; + } + + /* If no pin will have its resistor mode changed silently exits, avoiding an + * unnecessary access to the Rasberry Pi memory registers. */ + if ( diff_mode_counter == 0 ) + return 0; + + return set_input_mode(pins, pin_count, pin_mask, mode); +} + +/** + * @brief Disables a GPIO pin on the APiI, making it available to be used + * by anyone on the system. + * + * @param[in] dev_pin Raspberry Pi GPIO pin label number (not its position + * on the header). + * + * @retval 0 Pin successfully disabled on the API. + * @retval -1 Could not disable an ative interrupt on this pin. + */ +int gpio_disable_pin(int dev_pin) +{ + rtems_status_code sc; + rpi_gpio_pin *pin; + + pin = &gpio_pin[dev_pin-1]; + + pin->pin_type = NOT_USED; + + /* If the pin has an enabled interrupt then remove the handler. */ + if ( pin->enabled_interrupt != NONE ) { + sc = gpio_disable_interrupt(dev_pin); + + if ( sc != RTEMS_SUCCESSFUL ) + return -1; + } + + return sc; +} + +/** + * @brief Setups a JTAG interface using the P1 GPIO pin header + * for the models A/B and J8 header on the B+. + * The following pins should be unused before calling this function: + * GPIO 4, 22, 24, 25 and 27. + * + * @param[in] dev_pin Raspberry Pi GPIO pin label number (not its position + * on the header). + * + * @retval 0 JTAG interface successfully configured. + * @retval -1 At least one of the required pins is currently occupied. + */ +int gpio_select_jtag(void) +{ + /* Setup gpio 4 alt5 ARM_TDI. */ + if ( gpio_select_pin(4, ALT_FUNC_5) < 0 ) + return -1; + + /* Setup gpio 22 alt4 ARM_TRST. */ + if ( gpio_select_pin(22, ALT_FUNC_4) < 0 ) + return -1; + + /* Setup gpio 24 alt4 ARM_TDO. */ + if ( gpio_select_pin(24, ALT_FUNC_4) < 0 ) + return -1; + + /* Setup gpio 25 alt4 ARM_TCK. */ + if ( gpio_select_pin(25, ALT_FUNC_4) < 0 ) + return -1; + + /* Setup gpio 27 alt4 ARM_TMS. */ + if ( gpio_select_pin(27, ALT_FUNC_4) < 0 ) + return -1; + + return 0; +} + +/** + * @brief Setups a SPI interface using the P1 GPIO pin header + * for the models A/B and J8 header on the B+. + * The following pins should be unused before calling this function: + * GPIO 7, 8, 9, 10 and 11. + * + * @param[in] dev_pin Raspberry Pi GPIO pin label number (not its position + * on the header). + * + * @retval 0 SPI interface successfully configured. + * @retval -1 At least one of the required pins is currently occupied. + */ +int gpio_select_spi_p1(void) +{ + /* SPI master 0 MISO data line. */ + if ( gpio_select_pin(9, ALT_FUNC_0) < 0 ) + return -1; + + /* SPI master 0 MOSI data line. */ + if ( gpio_select_pin(10, ALT_FUNC_0) < 0 ) + return -1; + + /* SPI master 0 SCLK clock line. */ + if ( gpio_select_pin(11, ALT_FUNC_0) < 0 ) + return -1; + + /* SPI master 0 CE_0 chip enable line. */ + if ( gpio_select_pin(8, ALT_FUNC_0) < 0 ) + return -1; + + /* SPI master 0 CE_1 chip enable line. */ + if ( gpio_select_pin(7, ALT_FUNC_0) < 0 ) + return -1; + + return 0; +} + +/** + * @brief Setups a I2C interface using the P1 GPIO pin header + * for the models A/B and J8 header on the B+. + * The following pins should be unused before calling this function: + * GPIO 2 and 3. + * + * @param[in] dev_pin Raspberry Pi GPIO pin label number (not its position + * on the header). + * + * @retval 0 JTAG interface successfully configured. + * @retval -1 At least one of the required pins is currently occupied. + */ +int gpio_select_i2c_p1_rev2(void) +{ + int pins[] = {2,3}; + + /* I2C BSC1 SDA data line. */ + if ( gpio_select_pin(2, ALT_FUNC_0) < 0 ) + return -1; + + /* I2C BSC1 SCL clock line. */ + if ( gpio_select_pin(3, ALT_FUNC_0) < 0 ) + return -1; + + /* Enable pins 2 and 3 pull-up resistors. */ + if ( gpio_setup_input_mode(pins, 2, PULL_UP) < 0 ) + return -1; + + return 0; +} + +/** + * @brief De-bounces a switch by requiring a certain time to pass between interrupts. + * Any interrupt fired too close to the last will be ignored as it is probably + * the result of a involuntary switch/button bounce after being released. + * + * @param[in] dev_pin Raspberry Pi GPIO pin label number (not its position + * on the header). + * + * @retval 0 Interrupt is likely provoked by a user press on the switch. + * @retval -1 Interrupt was generated too close to the last one. Probable switch bounce. + */ +static int debounce_switch(int dev_pin) +{ + rtems_interval time; + rpi_gpio_pin *pin; + + pin = &gpio_pin[dev_pin-1]; + + time = rtems_clock_get_ticks_since_boot(); + + if ( (time - pin->h_args.last_isr_tick) < pin->h_args.debouncing_tick_count ) + return -1; + + pin->h_args.last_isr_tick = time; + + return 0; +} + +/** + * @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) (); +} + +/** + * @brief Defines for a GPIO input pin the number of clock ticks that must pass before + * an generated interrupt is garanteed to be generated by the user and not by + * a bouncing switch/button. + * + * @param[in] dev_pin Raspberry Pi GPIO pin label number (not its position + * on the header). + * + * @retval 0 De-bounce function successfully attached to the pin. + * @retval -1 The current pin is not configured as a digital input, hence + * it can not be connected to a switch. + */ +int gpio_debounce_switch(int dev_pin, int ticks) +{ + if ( gpio_pin[dev_pin-1].pin_type != DIGITAL_INPUT ) + return -1; + + gpio_pin[dev_pin-1].h_args.debouncing_tick_count = ticks; + + return 0; +} + +/** + * @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. + * + * @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)) +{ + 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.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)); + + 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; +} + +/** + * @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; +} diff --git a/c/src/lib/libbsp/arm/raspberrypi/i2c/i2c.c b/c/src/lib/libbsp/arm/raspberrypi/i2c/i2c.c new file mode 100644 index 0000000..838add4 --- /dev/null +++ b/c/src/lib/libbsp/arm/raspberrypi/i2c/i2c.c @@ -0,0 +1,427 @@ +/** + * @file i2c.c + * + * @ingroup raspberrypi_i2c + * + * @brief Support for the I2C bus on the Raspberry Pi GPIO P1 header (model A/B) + * and GPIO J8 header on model B+. + */ + +/* + * Copyright (c) 2014 Andre Marques <andre.lousa.marques at gmail.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. + */ + +/* + * TODO: + * - Clock stretching (currently using default values) + * - 10-bit addressing + * - Falling/Rising edge delays (currently using default values) + */ + +#include <bsp.h> +#include <bsp/raspberrypi.h> +#include <bsp/gpio.h> +#include <bsp/irq.h> +#include <bsp/i2c.h> + +/** + * @brief Calculates a clock divider to be used with the BSC core clock rate + * to set a I2C clock rate the closest (<=) to a desired frequency. + * + * @param[in] clock_hz The desired clock frequency for the I2C bus operation. + * @param[out] clock_divider Pointer to a variable where the calculated + * clock divider will be stored. + * + * @retval RTEMS_SUCCESSFUL Successfully calculated a valid clock divider. + * @retval RTEMS_INVALID_NUMBER The resulting clock divider is invalid, due to + * an invalid BSC_CORE_CLOCK_HZ + * or clock_hz value. + */ +static rtems_status_code bcm2835_i2c_calculate_clock_divider(uint32_t clock_hz, uint16_t *clock_divider) +{ + uint16_t divider; + uint32_t clock_rate; + + /* Calculates an initial clock divider. */ + divider = BSC_CORE_CLK_HZ / clock_hz; + + if ( divider < 0 || divider > 0xFFFF ) + return RTEMS_INVALID_NUMBER; + + clock_rate = BSC_CORE_CLK_HZ / divider; + + /* If the resulting clock rate is greater than desired, try the next greater divider. */ + while ( clock_rate > clock_hz ) + { + divider++; + + clock_rate = BSC_CORE_CLK_HZ / divider; + } + + *clock_divider = divider; + + return RTEMS_SUCCESSFUL; +} + +/** + * @brief Set the I2C bus clock divider. + * + * @param[in] bushdl Pointer to the libi2c API bus driver data structure. + * @param[in] tfr_mode Pointer to a libi2c API transfer mode data structure. + * + * @retval RTEMS_SUCCESSFUL Successfully setup the bus transfer mode as desired. + * @retval RTEMS_INVALID_NUMBER @see bcm2835_i2c_calculate_clock_divider(). + */ +static rtems_status_code bcm2835_i2c_set_tfr_mode(rtems_libi2c_bus_t *bushdl, const rtems_libi2c_tfr_mode_t *tfr_mode) +{ + rtems_status_code sc; + uint16_t clock_divider; + + /* Calculate the most appropriate clock divider. */ + sc = bcm2835_i2c_calculate_clock_divider(tfr_mode->baudrate, &clock_divider); + + if ( sc != RTEMS_SUCCESSFUL ) + return sc; + + /* Set clock divider. */ + BCM2835_REG(BCM2835_I2C_DIV) = clock_divider; + + return sc; +} + +/** + * @brief Reads/writes to/from the I2C bus. + * + * @param[in] bushdl Pointer to the libi2c API bus driver data structure. + * @param[in] rd_buf Read buffer. If not NULL the function will read from + * the bus and store the read on this buffer. + * @param[in] wr_buf Write buffer. If not NULL the function will write the + * contents of this buffer to the bus. + * @param[in] buffer_size Size of the non-NULL buffer. + * + * @retval -1 Could not send/receive data to/from the bus. + * @retval >=0 The number of bytes read/written. + */ +static int bcm2835_i2c_read_write(rtems_libi2c_bus_t * bushdl, unsigned char *rd_buf, const unsigned char *wr_buf, int buffer_size) +{ + bcm2835_i2c_softc_t *softc_ptr = &(((bcm2835_i2c_desc_t *)(bushdl))->softc); + + uint32_t bytes_sent = buffer_size; + + /* Since there is a maximum of 0xFFFF packets per transfer + * (size of the DLEN register), count how many transfers will be + * needed and adjust each transfer size accordingly. */ + int transfer_count = buffer_size / 0xFFFF; + uint16_t dlen_buffer_size; + + /* If the buffer size is a multiple of the max size per transfer, + * round up the transfer count. */ + if ( buffer_size % 0xFFFF != 0 ) + transfer_count++; + + do { + if (transfer_count > 1) + dlen_buffer_size = 0xFFFF; + else + dlen_buffer_size = (buffer_size & 0xFFFF); + + /* Set the DLEN register, which specifies how many data packets will be transferred. */ + BCM2835_REG(BCM2835_I2C_DLEN) = dlen_buffer_size; + + /* Clear the acknowledgment and clock stretching error status. */ + BCM2835_REG(BCM2835_I2C_S) |= (3 << 8); + + /* While there is data to transfer. */ + while ( dlen_buffer_size >= 1 ) { + + /* If writing. */ + if ( rd_buf == NULL ) { + + /* If transfer is not active, send start bit. */ + if( (BCM2835_REG(BCM2835_I2C_S) & (1 << 0)) == 0) + BCM2835_REG(BCM2835_I2C_C) |= (1 << 7); + + /* If using the I2C bus in interrupt-driven mode. */ + if ( I2C_IO_MODE == 1 ) { + + /* Generate interrupts on the TXW bit condition. */ + BCM2835_REG(BCM2835_I2C_C) |= (1 << 9); + + if ( rtems_semaphore_obtain(softc_ptr->irq_sema_id, RTEMS_WAIT, 50) != RTEMS_SUCCESSFUL ) + return -1; + } + + /* If using the bus in polling mode. */ + else + /* Poll TXW bit until there is space available to write. */ + while ( (BCM2835_REG(BCM2835_I2C_S) & (1 << 2)) == 0 ) + ; + + /* Write data to the TX FIFO. */ + BCM2835_REG(BCM2835_I2C_FIFO) = (*(uint8_t *)wr_buf); + + wr_buf++; + + /* Check for acknowledgment or clock stretching errors. */ + if ( (BCM2835_REG(BCM2835_I2C_S) & (1 << 8)) || (BCM2835_REG(BCM2835_I2C_S) & (1 << 9)) ) + return -1; + } + + /* If reading. */ + else { + /* Send start bit. Before any read a libi2c_send_addr call should be made signaling a read operation. */ + BCM2835_REG(BCM2835_I2C_C) |= (1 << 7); + + /* Check for an acknowledgment error. */ + if ( (BCM2835_REG(BCM2835_I2C_S) & (1 << 8)) != 0) + return -1; + + /* Poll RXD bit until there is data on the RX FIFO to read. */ + while ( (BCM2835_REG(BCM2835_I2C_S) & (1 << 5)) == 0 ) + ; + + /* Read data from the RX FIFO. */ + (*(uint8_t *)rd_buf) = BCM2835_REG(BCM2835_I2C_FIFO) & 0xFF; + + rd_buf++; + + /* Check for acknowledgment or clock stretching errors. */ + if ( (BCM2835_REG(BCM2835_I2C_S) & (1 << 8)) || (BCM2835_REG(BCM2835_I2C_S) & (1 << 9)) ) + return -1; + } + + dlen_buffer_size--; + transfer_count--; + buffer_size--; + } + } while ( transfer_count > 0 ); + + /* If using the I2C bus in interrupt-driven mode. */ + if ( I2C_IO_MODE == 1 ) { + /* Generate interrupts on the DONE bit condition. */ + BCM2835_REG(BCM2835_I2C_C) |= (1 << 8); + + if ( rtems_semaphore_obtain(softc_ptr->irq_sema_id, RTEMS_WAIT, 50) != RTEMS_SUCCESSFUL ) + return -1; + } + + /* If using the bus in polling mode. */ + else + /* Poll DONE bit until data has been sent. */ + while ( (BCM2835_REG(BCM2835_I2C_S) & (1 << 1)) == 0 ) + ; + + bytes_sent -= buffer_size; + + return bytes_sent; +} + +/** + * @brief Handler function that is called on any I2C interrupt. + * + * There are 3 situations that can generate an interrupt: + * + * 1. Transfer (read/write) complete; + * 2. The TX FIFO has space for more data (during a write transfer); + * 3. The RX FIFO is full. + * + * Because the I2C FIFO has a 16 byte size, the 3. situation is not + * as useful to many applications as knowing that at least 1 byte can + * be read from the RX FIFO. For that reason this information is + * got through polling the RXD bit even in interrupt-driven mode. + * + * This leaves only 2 interrupts to be caught. At any given time + * when no I2C bus transfer is taking place no I2C interrupts are + * generated, and they do they are only enabled one at a time: + * + * - When trying to write, the 2. interrupt is enabled to signal that + * data can be written on the TX FIFO, avoiding data loss in case + * it is full. When caught the handler disables that interrupt from + * being generated and releases the irq semaphore, which will allow + * the transfer process to continue (by writing to the TX FIFO); + * + * - When the transfer is done on Raspberry side, the 1. interrupt is + * enabled for the device to signal it has finished the transfer as + * well. When caught the handler disables that interrupt from being + * generated and releases the irq semaphore, marking the end of the + * transfer. + * + * @param[in] arg Void pointer to the bus data structure. + */ +static void i2c_handler(void* arg) +{ + bcm2835_i2c_softc_t *softc_ptr = (bcm2835_i2c_softc_t *) arg; + + /* If the current enabled interrupt is on the TXW condition, disable it. */ + if ( (BCM2835_REG(BCM2835_I2C_C) & (1 << 9)) ) + BCM2835_REG(BCM2835_I2C_C) &= ~(1 << 9); + + /* If the current enabled interrupt is on the DONE condition, disable it. */ + else if ( (BCM2835_REG(BCM2835_I2C_C) & (1 << 8)) ) + BCM2835_REG(BCM2835_I2C_C) &= ~(1 << 8); + + /* Release the irq semaphore. */ + rtems_semaphore_release(softc_ptr->irq_sema_id); +} + +/** + * @brief Low level function to initialize the I2C bus. + * This function is used by the libi2c API. + * + * @param[in] bushdl Pointer to the libi2c API bus driver data structure. + * + * @retval RTEMS_SUCCESSFUL SPI bus successfully initialized. + * @retval Any other status code @see rtems_semaphore_create() and + * @see rtems_interrupt_handler_install(). + */ +rtems_status_code bcm2835_i2c_init(rtems_libi2c_bus_t * bushdl) +{ + bcm2835_i2c_softc_t *softc_ptr = &(((bcm2835_i2c_desc_t *)(bushdl))->softc); + rtems_status_code sc = RTEMS_SUCCESSFUL; + + if ( softc_ptr->initialized == 1 ) + return sc; + + softc_ptr->initialized = 1; + + /* Enable the I2C BSC interface. */ + BCM2835_REG(BCM2835_I2C_C) |= (1 << 15); + + /* If the access to the bus is configured to be interrupt-driven. */ + if ( I2C_IO_MODE == 1 ) { + sc = rtems_semaphore_create(rtems_build_name('i','2','c','s'), 0, RTEMS_FIFO | RTEMS_SIMPLE_BINARY_SEMAPHORE, 0, &softc_ptr->irq_sema_id); + + sc = rtems_interrupt_handler_install(BCM2835_IRQ_ID_I2C, NULL, RTEMS_INTERRUPT_UNIQUE, (rtems_interrupt_handler) i2c_handler, softc_ptr); + } + + return sc; +} + +/** + * @brief Low level function that would send a start condition over the I2C bus. + * Because of the way the BSC controller implements the I2C protocol, the + * start sequence is sent whenever appropriate in bcm2835_i2c_read_write. + * Instead this function clears the bus FIFOS before each new data + * transfer. + * This function is used by the libi2c API. + * + * @param[in] bushdl Pointer to the libi2c API bus driver data structure. + * + * @retval RTEMS_SUCCESSFUL + */ +rtems_status_code bcm2835_i2c_send_start(rtems_libi2c_bus_t * bushdl) +{ + /* Clear FIFOs. */ + BCM2835_REG(BCM2835_I2C_C) |= (3 << 4); + + return RTEMS_SUCCESSFUL; +} + +/** + * @brief Low level function that would send a stop condition over the I2C bus, + * however the BSC controller send this condition automatically when the + * DLEN (data length - the number of bytes to be transferred) register + * value reaches 0. + * For that reason, it is here just to satisfy, the libi2c API, + * which requires this function. + * + * @param[in] bushdl Pointer to the libi2c API bus driver data structure. + * + * @retval RTEMS_SUCCESSFUL + */ +rtems_status_code bcm2835_i2c_stop(rtems_libi2c_bus_t * bushdl) +{ + return RTEMS_SUCCESSFUL; +} + +/** + * @brief Low level function which addresses a I2C device. + * This function is used by the libi2c API. + * + * @param[in] bushdl Pointer to the libi2c API bus driver data structure. + * @param[in] addr Address of a connected I2C device + * @param[in] rw Defines the nature of the transfer which will take place with + * the addressed device - 0 to write and 1 to read. + * + * @retval RTEMS_SUCCESSFUL The device has been successfully addressed. + */ +rtems_status_code bcm2835_i2c_send_addr(rtems_libi2c_bus_t * bushdl, uint32_t addr, int rw) +{ + /* Address slave device. */ + BCM2835_REG(BCM2835_I2C_A) = addr; + + /* Set read/write bit. + * If writing. */ + if ( rw == 0 ) + BCM2835_REG(BCM2835_I2C_C) &= ~(1 << 0); + + /* If reading. */ + else + BCM2835_REG(BCM2835_I2C_C) |= (1 << 0); + + return RTEMS_SUCCESSFUL; +} + +/** + * @brief Low level function that reads a number of bytes from the I2C bus + * on to a buffer. + * This function is used by the libi2c API. + * + * @param[in] bushdl Pointer to the libi2c API bus driver data structure. + * @param[in] bytes Buffer where the data read from the bus will be stored. + * @param[in] nbytes Number of bytes to be read from the bus to the bytes buffer. + * + * @retval @see bcm2835_i2c_read_write(). + */ +int bcm2835_i2c_read_bytes(rtems_libi2c_bus_t * bushdl, unsigned char *bytes, int nbytes) +{ + return bcm2835_i2c_read_write(bushdl, bytes, NULL, nbytes); +} + +/** + * @brief Low level function that writes a number of bytes from a buffer + * to the I2C bus. + * This function is used by the libi2c API. + * + * @param[in] bushdl Pointer to the libi2c API bus driver data structure. + * @param[in] bytes Buffer with data to send through the bus. + * @param[in] nbytes Number of bytes to be written from the bytes buffer + to the bus. + * + * @retval @see bcm2835_i2c_read_write(). + */ +int bcm2835_i2c_write_bytes(rtems_libi2c_bus_t * bushdl, unsigned char *bytes, int nbytes) +{ + return bcm2835_i2c_read_write(bushdl, NULL, bytes, nbytes); +} + +/** + * @brief Low level function that is used to perform ioctl + * operations on the bus. Currently only setups + * the bus transfer mode, namely the bus clock divider. + * This function is used by the libi2c API. + * + * @param[in] bushdl Pointer to the libi2c API bus driver data structure. + * @param[in] cmd IOCTL request command. + * @param[in] arg Arguments needed to fulfill the requested IOCTL command. + * + * @retval -1 Unknown request command. + * @retval >=0 @see bcm2835_i2c_set_tfr_mode(). + */ +int bcm2835_i2c_ioctl(rtems_libi2c_bus_t * bushdl, int cmd, void *arg) +{ + switch ( cmd ) { + case RTEMS_LIBI2C_IOCTL_SET_TFRMODE: + + return bcm2835_i2c_set_tfr_mode(bushdl, (const rtems_libi2c_tfr_mode_t *)arg); + + default: + return -1; + } + + return 0; +} diff --git a/c/src/lib/libbsp/arm/raspberrypi/i2c/i2c_init.c b/c/src/lib/libbsp/arm/raspberrypi/i2c/i2c_init.c new file mode 100644 index 0000000..f231537 --- /dev/null +++ b/c/src/lib/libbsp/arm/raspberrypi/i2c/i2c_init.c @@ -0,0 +1,78 @@ +/** + * @file i2c_init.c + * + * @ingroup raspberrypi_i2c + * + * @brief Raspberry Pi I2C bus initialization. + */ + +/* + * Copyright (c) 2014 Andre Marques <andre.lousa.marques at gmail.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 <bsp/raspberrypi.h> +#include <bsp/gpio.h> +#include <bsp/i2c.h> + +static rtems_libi2c_bus_ops_t bcm2835_i2c_ops = { + init: bcm2835_i2c_init, + send_start: bcm2835_i2c_send_start, + send_stop: bcm2835_i2c_stop, + send_addr: bcm2835_i2c_send_addr, + read_bytes: bcm2835_i2c_read_bytes, + write_bytes: bcm2835_i2c_write_bytes, + ioctl: bcm2835_i2c_ioctl +}; + +static bcm2835_i2c_desc_t bcm2835_i2c_bus_desc = { + { + ops: &bcm2835_i2c_ops, + size: sizeof(bcm2835_i2c_bus_desc) + }, + { + initialized: 0 + } +}; + +/* Register drivers here for all the devices + * which require access to the I2C bus. + * + * The libi2c function "rtems_libi2c_register_drv" must be used to + * register each device driver, using the received i2c bus number. + * + * This function returns 0 on success. */ +int BSP_i2c_register_drivers(int i2c_bus_number) +{ + int rv = 0; + + return rv; +} + +int BSP_i2c_init(void) +{ + int rv; + + /* Initialize the libi2c API. */ + rtems_libi2c_initialize (); + + /* Enable the I2C interface on the Raspberry Pi P1 GPIO header. */ + gpio_initialize (); + + if ( gpio_select_i2c_p1_rev2() < 0 ) + return RTEMS_RESOURCE_IN_USE; + + /* Register the I2C bus. */ + rv = rtems_libi2c_register_bus("/dev/i2c", &(bcm2835_i2c_bus_desc.bus_desc)); + + if ( rv < 0 ) + return -rv; + + /* Register SPI device drivers. */ + rv = BSP_i2c_register_drivers(rv); + + return 0; +} diff --git a/c/src/lib/libbsp/arm/raspberrypi/i2c/spi.c b/c/src/lib/libbsp/arm/raspberrypi/i2c/spi.c new file mode 100644 index 0000000..c260f19 --- /dev/null +++ b/c/src/lib/libbsp/arm/raspberrypi/i2c/spi.c @@ -0,0 +1,564 @@ +/** + * @file spi.c + * + * @ingroup raspberrypi_i2c + * + * @brief Support for the SPI bus on the Raspberry Pi GPIO P1 header (model A/B) + * and GPIO J8 header on model B+. + */ + +/* + * Copyright (c) 2014 Andre Marques <andre.lousa.marques at gmail.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. + */ + +/* + * STATUS: + * - Bi-directional mode untested + * - Write-only devices not supported + */ + +#include <bsp.h> +#include <bsp/raspberrypi.h> +#include <bsp/gpio.h> +#include <bsp/irq.h> +#include <bsp/i2c.h> + +/** + * @brief Calculates a clock divider to be used with the GPU core clock rate + * to set a SPI clock rate the closest (<=) to a desired frequency. + * + * @param[in] clock_hz The desired clock frequency for the SPI bus operation. + * @param[out] clock_divider Pointer to a variable where the calculated + * clock divider will be stored. + * + * @retval RTEMS_SUCCESSFUL Successfully calculated a valid clock divider. + * @retval RTEMS_INVALID_NUMBER The resulting clock divider is invalid, due to + * an invalid GPU_CORE_CLOCK_RATE + * or clock_hz value. + */ +static rtems_status_code bcm2835_spi_calculate_clock_divider(uint32_t clock_hz, uint16_t *clock_divider) +{ + uint16_t divider; + uint32_t clock_rate; + + /* Calculates an initial clock divider. */ + divider = GPU_CORE_CLOCK_RATE / clock_hz; + + if ( divider < 0 || divider > 65536 ) + return RTEMS_INVALID_NUMBER; + + /* Because the divider must be a power of two (as per the BCM2835 datasheet), + * calculate the next greater power of two. */ + divider--; + + divider |= (divider >> 1); + divider |= (divider >> 2); + divider |= (divider >> 4); + divider |= (divider >> 8); + + divider++; + + clock_rate = GPU_CORE_CLOCK_RATE / divider; + + /* If the resulting clock rate is greater than the desired frequency, + * try the next greater power of two divider. */ + while ( clock_rate > clock_hz ) { + divider = (divider << 1); + + clock_rate = GPU_CORE_CLOCK_RATE / divider; + } + + *clock_divider = divider; + + return RTEMS_SUCCESSFUL; +} + +/** + * @brief Set the SPI bus transfer mode. + * + * @param[in] bushdl Pointer to the libi2c API bus driver data structure. + * @param[in] tfr_mode Pointer to a libi2c API transfer mode data structure. + * + * @retval RTEMS_SUCCESSFUL Successfully setup the bus transfer mode as desired. + * @retval RTEMS_INVALID_NUMBER This can have two meanings: + * 1. The specified number of bytes per char is not + * 8, 16, 24 or 32; + * 2. @see bcm2835_spi_calculate_clock_divider() + */ +static rtems_status_code bcm2835_spi_set_tfr_mode(rtems_libi2c_bus_t *bushdl, const rtems_libi2c_tfr_mode_t *tfr_mode) +{ + bcm2835_spi_softc_t *softc_ptr = &(((bcm2835_spi_desc_t *)(bushdl))->softc); + rtems_status_code sc = RTEMS_SUCCESSFUL; + uint16_t clock_divider; + + /* Set the dummy character. */ + softc_ptr->dummy_char = tfr_mode->idle_char; + + /* Calculate the most appropriate clock divider. */ + sc = bcm2835_spi_calculate_clock_divider(tfr_mode->baudrate, &clock_divider); + + if ( sc != RTEMS_SUCCESSFUL ) + return sc; + + /* Set the bus clock divider. */ + BCM2835_REG(BCM2835_SPI_CLK) = clock_divider; + + /* Calculate how many bytes each character has. + * Only multiples of 8 bits are accepted for the transaction. */ + switch ( tfr_mode->bits_per_char ) + { + case 8: + case 16: + case 24: + case 32: + + softc_ptr->bytes_per_char = tfr_mode->bits_per_char / 8; + + break; + + default: + return RTEMS_INVALID_NUMBER; + } + + /* Check the data mode (most or least significant bit first) and calculate + * the correcting bit shift value to apply on the data before sending. */ + if ( tfr_mode->lsb_first ) + softc_ptr->bit_shift = 32 - tfr_mode->bits_per_char; + + /* If MSB first. */ + else + softc_ptr->bit_shift = 0; + + /* Set SPI clock polarity. + * If clock_inv is TRUE, the clock is active high.*/ + if ( tfr_mode->clock_inv ) + /* Rest state of clock is low. */ + BCM2835_REG(BCM2835_SPI_CS) &= ~(1 << 3); + + else + /* Rest state of clock is high. */ + BCM2835_REG(BCM2835_SPI_CS) |= (1 << 3); + + /* Set SPI clock phase. + * If clock_phs is true, clock starts toggling + * at the start of the data transfer. */ + if ( tfr_mode->clock_phs ) + /* First SCLK transition at beginning of data bit. */ + BCM2835_REG(BCM2835_SPI_CS) |= (1 << 2); + + else + /* First SCLK transition at middle of data bit. */ + BCM2835_REG(BCM2835_SPI_CS) &= ~(1 << 2); + + return sc; +} + +/** + * @brief Reads/writes to/from the SPI bus. + * + * @param[in] bushdl Pointer to the libi2c API bus driver data structure. + * @param[in] rd_buf Read buffer. If not NULL the function will read from + * the bus and store the read on this buffer. + * @param[in] wr_buf Write buffer. If not NULL the function will write the + * contents of this buffer to the bus. + * @param[in] buffer_size Size of the non-NULL buffer. + * + * @retval -1 Could not send/receive data to/from the bus. + * @retval >=0 The number of bytes read/written. + */ +static int bcm2835_spi_read_write(rtems_libi2c_bus_t * bushdl, unsigned char *rd_buf, const unsigned char *wr_buf, int buffer_size) +{ + bcm2835_spi_softc_t *softc_ptr = &(((bcm2835_spi_desc_t *)(bushdl))->softc); + + uint8_t bytes_per_char = softc_ptr->bytes_per_char; + uint8_t bit_shift = softc_ptr->bit_shift; + uint32_t dummy_char = softc_ptr->dummy_char; + + uint32_t bytes_sent = buffer_size; + uint32_t fifo_data; + + /* Clear SPI bus FIFOs. */ + BCM2835_REG(BCM2835_SPI_CS) |= (3 << 4); + + /* Set SPI transfer active. */ + BCM2835_REG(BCM2835_SPI_CS) |= (1 << 7); + + /* If using the SPI bus in interrupt-driven mode. */ + if ( SPI_IO_MODE == 1 ) { + softc_ptr->irq_write = 1; + + BCM2835_REG(BCM2835_SPI_CS) |= (1 << 9); + + if ( rtems_semaphore_obtain(softc_ptr->irq_sema_id, RTEMS_WAIT, 50) != RTEMS_SUCCESSFUL ) + return -1; + } + + /* If using the bus in polling mode. */ + else + /* Poll TXD bit until there is space to write at least one byte on the TX FIFO. */ + while ( (BCM2835_REG(BCM2835_SPI_CS) & (1 << 18)) == 0 ) + ; + + /* While there is data to be transferred. */ + while ( buffer_size >= bytes_per_char ) { + /* If reading from the bus, send a dummy character to the device. */ + if ( rd_buf != NULL ) + BCM2835_REG(BCM2835_SPI_FIFO) = dummy_char; + + /* If writing to the bus, move the buffer data to the TX FIFO. */ + else { + switch ( bytes_per_char ) { + case 1: + + BCM2835_REG(BCM2835_SPI_FIFO) = (((*wr_buf) & 0xFF) << bit_shift); + break; + + case 2: + + BCM2835_REG(BCM2835_SPI_FIFO) = (((*wr_buf) & 0xFFFF) << bit_shift); + break; + + case 3: + + BCM2835_REG(BCM2835_SPI_FIFO) = (((*wr_buf) & 0xFFFFFF) << bit_shift); + + break; + + case 4: + + BCM2835_REG(BCM2835_SPI_FIFO) = ((*wr_buf) << bit_shift); + break; + + default: + return -1; + } + + wr_buf += bytes_per_char; + + buffer_size -= bytes_per_char; + } + + /* If using bi-directional SPI. */ + if ( softc_ptr->bidirectional == 1 ) + /* Change bus direction to read from the slave device. */ + BCM2835_REG(BCM2835_SPI_CS) |= (1 << 12); + + /* If using the SPI bus in interrupt-driven mode. */ + if ( SPI_IO_MODE == 1 ) { + softc_ptr->irq_write = 0; + + BCM2835_REG(BCM2835_SPI_CS) |= (1 << 9); + + if ( rtems_semaphore_obtain(softc_ptr->irq_sema_id, RTEMS_WAIT, 50) != RTEMS_SUCCESSFUL ) + return -1; + } + + /* If using the bus in polling mode. */ + else { + /* Poll the Done bit until the data transfer is complete. */ + while ( (BCM2835_REG(BCM2835_SPI_CS) & (1 << 16)) == 0 ) + ; + + /* Poll the RXD bit until there is at least one byte on the RX FIFO to be read. */ + while ( (BCM2835_REG(BCM2835_SPI_CS) & (1 << 17)) == 0 ) + ; + } + + /* If writing to the bus, read the dummy char sent by the slave device. */ + if ( rd_buf == NULL ) + fifo_data = BCM2835_REG(BCM2835_SPI_FIFO) & 0xFF; + + /* If reading from the bus, retrieve data from the RX FIFO and store it on the buffer. */ + if ( rd_buf != NULL ) { + switch ( bytes_per_char ) { + case 1: + + fifo_data = BCM2835_REG(BCM2835_SPI_FIFO) & 0xFF; + (*rd_buf) = (fifo_data >> bit_shift); + break; + + case 2: + + fifo_data = BCM2835_REG(BCM2835_SPI_FIFO) & 0xFFFF; + (*rd_buf) = (fifo_data >> bit_shift); + break; + + case 3: + + fifo_data = BCM2835_REG(BCM2835_SPI_FIFO) & 0xFFFFFF; + (*rd_buf) = (fifo_data >> bit_shift); + break; + + case 4: + + fifo_data = BCM2835_REG(BCM2835_SPI_FIFO); + (*rd_buf) = (fifo_data >> bit_shift); + break; + + default: + return -1; + } + + rd_buf += bytes_per_char; + + buffer_size -= bytes_per_char; + } + + /* If using bi-directional SPI. */ + if ( softc_ptr->bidirectional == 1 ) + /* Restore bus direction to write to the slave. */ + BCM2835_REG(BCM2835_SPI_CS) &= ~(1 << 12); + } + + /* If using the SPI bus in interrupt-driven mode. */ + if ( SPI_IO_MODE == 1 ) { + softc_ptr->irq_write = 1; + + BCM2835_REG(BCM2835_SPI_CS) |= (1 << 9); + + if ( rtems_semaphore_obtain(softc_ptr->irq_sema_id, RTEMS_WAIT, 50) != RTEMS_SUCCESSFUL ) + return -1; + } + + /* If using the bus in polling mode. */ + else + /* Poll the Done bit until the data transfer is complete. */ + while ( (BCM2835_REG(BCM2835_SPI_CS) & (1 << 16)) == 0 ) + ; + + bytes_sent -= buffer_size; + + return bytes_sent; +} + +/** + * @brief Handler function that is called on any SPI interrupt. + * + * There are 2 situations that can generate an interrupt: + * + * 1. Transfer (read/write) complete; + * 2. RX FIFO full. + * + * Because the 2. situation is not useful to many applications, + * the only interrupt that is generated and handled is the + * transfer complete interrupt. + * + * The objective of the handler is then, depending on the transfer + * context (reading or writing on the bus), to check if there is enough + * space available on the TX FIFO to send data over the bus (if writing) + * or if the slave device has sent enough data to be fetched from the + * RX FIFO (if reading). + * + * When any of these two conditions occur, disables further interrupts + * to be generated and releases a irq semaphore which will allow the + * following transfer to proceed. + * + * @param[in] arg Void pointer to the bus data structure. + */ +static void spi_handler(void* arg) +{ + bcm2835_spi_softc_t *softc_ptr = (bcm2835_spi_softc_t *) arg; + + /* If waiting to write to the bus, expect the TXD bit to be set, or + * if waiting to read from the bus, expect the RXD bit to be set + * before releasing the irq semaphore. */ + if (( softc_ptr->irq_write == 1 && (BCM2835_REG(BCM2835_SPI_CS) & (1 << 18)) != 0 ) + || + ( softc_ptr->irq_write == 0 && (BCM2835_REG(BCM2835_SPI_CS) & (1 << 17)) != 0 )) + { + /* Disable the SPI interrupt generation when a transfer is complete. */ + BCM2835_REG(BCM2835_SPI_CS) &= ~(1 << 9); + + /* Release the irq semaphore. */ + rtems_semaphore_release(softc_ptr->irq_sema_id); + } +} + +/** + * @brief Low level function to initialize the SPI bus. + * This function is used by the libi2c API. + * + * @param[in] bushdl Pointer to the libi2c API bus driver data structure. + * + * @retval RTEMS_SUCCESSFUL SPI bus successfully initialized. + * @retval Any other status code @see rtems_semaphore_create() and + * @see rtems_interrupt_handler_install(). + */ +rtems_status_code bcm2835_spi_init(rtems_libi2c_bus_t * bushdl) +{ + bcm2835_spi_softc_t *softc_ptr = &(((bcm2835_spi_desc_t *)(bushdl))->softc); + rtems_status_code sc = RTEMS_SUCCESSFUL; + + if ( softc_ptr->initialized == 1 ) + return sc; + + softc_ptr->initialized = 1; + + /* FIXME: This should be set on the device driver itself and configured + * during the bus transfer mode setup or another ioctl request. */ + softc_ptr->bidirectional = 0; + + /* If using the SPI bus in interrupt-driven mode. */ + if ( SPI_IO_MODE == 1 ) { + sc = rtems_semaphore_create(rtems_build_name('s','p','i','s'), 0, RTEMS_FIFO | RTEMS_SIMPLE_BINARY_SEMAPHORE, 0, &softc_ptr->irq_sema_id); + + if ( sc != RTEMS_SUCCESSFUL ) + return sc; + + sc = rtems_interrupt_handler_install(BCM2835_IRQ_ID_SPI, NULL, RTEMS_INTERRUPT_UNIQUE, (rtems_interrupt_handler) spi_handler, softc_ptr); + } + + return sc; +} + +/** + * @brief Low level function that would send a start condition over an I2C bus. + * As it is not required to access a SPI bus it is here just to satisfy + * the libi2c API, which requires this function. + * + * @param[in] bushdl Pointer to the libi2c API bus driver data structure. + * + * @retval RTEMS_SUCCESSFUL + */ +rtems_status_code bcm2835_spi_send_start(rtems_libi2c_bus_t * bushdl) +{ + return RTEMS_SUCCESSFUL; +} + +/** + * @brief Low level function that terminates a SPI transfer. + * It stops the SPI transfer and unselects the current SPI slave device. + * This function is used by the libi2c API. + * + * @param[in] bushdl Pointer to the libi2c API bus driver data structure. + * + * @retval RTEMS_SUCCESSFUL The slave device has been successfully unselected. + * @retval RTEMS_INVALID_ADDRESS The stored slave address is neither 0 or 1. + */ +rtems_status_code bcm2835_spi_stop(rtems_libi2c_bus_t * bushdl) +{ + bcm2835_spi_softc_t *softc_ptr = &(((bcm2835_spi_desc_t *)(bushdl))->softc); + + uint32_t addr = softc_ptr->current_slave_addr; + uint32_t chip_select_bit = 21 + addr; + + /* Set SPI transfer as not active. */ + BCM2835_REG(BCM2835_SPI_CS) &= ~(1 << 7); + + /* Unselect the active SPI slave. */ + switch ( addr ) { + case 0: + case 1: + + BCM2835_REG(BCM2835_SPI_CS) |= (1 << chip_select_bit); + break; + + default: + return RTEMS_INVALID_ADDRESS; + } + + return RTEMS_SUCCESSFUL; +} + +/** + * @brief Low level function which addresses a SPI slave device. + * This function is used by the libi2c API. + * + * @param[in] bushdl Pointer to the libi2c API bus driver data structure. + * @param[in] addr SPI slave select line address (0 for CE0 or 1 for CE1). + * @param[in] rw This values is unnecessary to address a SPI device and its + * presence here is only to fulfill a libi2c requirement. + * + * @retval RTEMS_SUCCESSFUL The slave device has been successfully addressed. + * @retval RTEMS_INVALID_ADDRESS The received address is neither 0 or 1. + */ +rtems_status_code bcm2835_spi_send_addr(rtems_libi2c_bus_t * bushdl, uint32_t addr, int rw) +{ + bcm2835_spi_softc_t *softc_ptr = &(((bcm2835_spi_desc_t *)(bushdl))->softc); + + /* Calculates the bit corresponding to the received address + * on the SPI control register. */ + uint32_t chip_select_bit = 21 + addr; + + /* Save which slave will be currently addressed, + * so it can be unselected later. */ + softc_ptr->current_slave_addr = addr; + + /* Select one of the two available SPI slave address lines. */ + switch ( addr ) { + case 0: + case 1: + + BCM2835_REG(BCM2835_SPI_CS) &= ~(1 << chip_select_bit); + break; + + default: + return RTEMS_INVALID_ADDRESS; + } + + return RTEMS_SUCCESSFUL; +} + +/** + * @brief Low level function that reads a number of bytes from the SPI bus + * on to a buffer. + * This function is used by the libi2c API. + * + * @param[in] bushdl Pointer to the libi2c API bus driver data structure. + * @param[in] bytes Buffer where the data read from the bus will be stored. + * @param[in] nbytes Number of bytes to be read from the bus to the bytes buffer. + * + * @retval @see bcm2835_spi_read_write(). + */ +int bcm2835_spi_read_bytes(rtems_libi2c_bus_t * bushdl, unsigned char *bytes, int nbytes) +{ + return bcm2835_spi_read_write(bushdl, bytes, NULL, nbytes); +} + +/** + * @brief Low level function that writes a number of bytes from a buffer + * to the SPI bus. + * This function is used by the libi2c API. + * + * @param[in] bushdl Pointer to the libi2c API bus driver data structure. + * @param[in] bytes Buffer with data to send over the SPI bus. + * @param[in] nbytes Number of bytes to be written from the bytes buffer + to the bus. + * + * @retval @see bcm2835_spi_read_write(). + */ +int bcm2835_spi_write_bytes(rtems_libi2c_bus_t * bushdl, unsigned char *bytes, int nbytes) +{ + return bcm2835_spi_read_write(bushdl, NULL, bytes, nbytes); +} + +/** + * @brief Low level function that is used to perform ioctl + * operations on the bus. Currently only setups + * the bus transfer mode. + * This function is used by the libi2c API. + * + * @param[in] bushdl Pointer to the libi2c API bus driver data structure. + * @param[in] cmd IOCTL request command. + * @param[in] arg Arguments needed to fulfill the requested IOCTL command. + * + * @retval -1 Unknown request command. + * @retval >=0 @see bcm2835_spi_set_tfr_mode(). + */ +int bcm2835_spi_ioctl(rtems_libi2c_bus_t * bushdl, int cmd, void *arg) +{ + switch ( cmd ) { + case RTEMS_LIBI2C_IOCTL_SET_TFRMODE: + + return bcm2835_spi_set_tfr_mode(bushdl, (const rtems_libi2c_tfr_mode_t *)arg); + + default: + return -1; + } + + return 0; +} diff --git a/c/src/lib/libbsp/arm/raspberrypi/i2c/spi_init.c b/c/src/lib/libbsp/arm/raspberrypi/i2c/spi_init.c new file mode 100644 index 0000000..cde59f1 --- /dev/null +++ b/c/src/lib/libbsp/arm/raspberrypi/i2c/spi_init.c @@ -0,0 +1,83 @@ +/** + * @file spi_init.c + * + * @ingroup raspberrypi_i2c + * + * @brief Raspberry Pi SPI bus initialization. + */ + +/* + * Copyright (c) 2014 Andre Marques <andre.lousa.marques at gmail.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 <bsp/raspberrypi.h> +#include <bsp/gpio.h> +#include <bsp/i2c.h> + +static rtems_libi2c_bus_ops_t bcm2835_spi_ops = { + init: bcm2835_spi_init, + send_start: bcm2835_spi_send_start, + send_stop: bcm2835_spi_stop, + send_addr: bcm2835_spi_send_addr, + read_bytes: bcm2835_spi_read_bytes, + write_bytes: bcm2835_spi_write_bytes, + ioctl: bcm2835_spi_ioctl +}; + +static bcm2835_spi_desc_t bcm2835_spi_bus_desc = { + { + ops: &bcm2835_spi_ops, + size: sizeof(bcm2835_spi_bus_desc) + }, + { + initialized: 0 + } +}; + +/* Register drivers here for all the devices + * which require access to the SPI bus. + * + * The libi2c function "rtems_libi2c_register_drv" must be used to + * register each device driver, using the received spi bus number. + * + * This function returns 0 on success. */ +int BSP_spi_register_drivers(int spi_bus_number) +{ + int rv = 0; + + return rv; +} + +int BSP_spi_init(void) +{ + int rv; + + /* Initialize the libi2c API. */ + rtems_libi2c_initialize (); + + /* Enable the SPI interface on the Raspberry Pi P1 GPIO header. */ + gpio_initialize (); + + if ( gpio_select_spi_p1() < 0 ) + return RTEMS_RESOURCE_IN_USE; + + /* Clear SPI control register and clear SPI FIFOs. */ + BCM2835_REG(BCM2835_SPI_CS) = 0x0000030; + + /* Register the SPI bus. */ + rv = rtems_libi2c_register_bus("/dev/spi", &(bcm2835_spi_bus_desc.bus_desc)); + + if ( rv < 0 ) + return rv; + + /* Register SPI device drivers. */ + rv = BSP_spi_register_drivers(rv); + + return rv; +} + + diff --git a/c/src/lib/libbsp/arm/raspberrypi/include/gpio.h b/c/src/lib/libbsp/arm/raspberrypi/include/gpio.h new file mode 100644 index 0000000..3b4e178 --- /dev/null +++ b/c/src/lib/libbsp/arm/raspberrypi/include/gpio.h @@ -0,0 +1,198 @@ +/** + * @file gpio.h + * + * @ingroup raspberrypi_gpio + * + * @brief Raspberry Pi specific GPIO definitions. + */ + +/* + * Copyright (c) 2014 Andre Marques <andre.lousa.marques at gmail.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. + */ + +#ifndef LIBBSP_ARM_RASPBERRYPI_GPIO_H +#define LIBBSP_ARM_RASPBERRYPI_GPIO_H + +#include <rtems.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * @brief Number of total GPIOS on the Raspberry Pi, + * including the internal ones. + */ +#define GPIO_PIN_COUNT 54 + +/** + * @brief The set of possible configurations for a GPIO pull-up resistor. + * + * Enumerated type to define the possible pull-up resistor configuratons + * for an input pin. + */ +typedef enum +{ + PULL_UP, + PULL_DOWN, + NO_PULL_RESISTOR +} rpi_gpio_input_mode; + +/** + * @brief The set of possible functions a pin can have. + * + * Enumerated type to define a pin function. + */ +typedef enum +{ + DIGITAL_INPUT, + DIGITAL_OUTPUT, + ALT_FUNC_0, + ALT_FUNC_1, + ALT_FUNC_2, + ALT_FUNC_3, + ALT_FUNC_4, + ALT_FUNC_5, + NOT_USED +} rpi_pin; + +/** + * @brief The set of possible interrupts an input pin can generate. + * + * Enumerated type to define an input pin interrupt. + */ +typedef enum +{ + FALLING_EDGE, + RISING_EDGE, + BOTH_EDGES, + LOW_LEVEL, + HIGH_LEVEL, + BOTH_LEVELS, + NONE +} gpio_interrupt; + +/** + * @brief Object containing relevant information to a interrupt handler. + * + * Encapsulates relevant data for a GPIO interrupt handler. + */ +typedef struct +{ + int pin_number; + + void (*handler) (void); + + int debouncing_tick_count; + + rtems_interval last_isr_tick; + +} handler_arguments; + +/** + * @brief Object containing information on a GPIO pin. + * + * Encapsulates relevant data about a GPIO pin. + */ +typedef struct +{ + /* The pin type */ + rpi_pin pin_type; + + /* Interrupt handler arguments*/ + handler_arguments h_args; + + gpio_interrupt enabled_interrupt; + + /* GPIO input pin mode. */ + rpi_gpio_input_mode input_mode; + +} rpi_gpio_pin; + +/** @} */ + +/** + * @name gpio Usage + * + * @{ + */ + +/** + * @brief Initializes the GPIO API. + */ +extern void gpio_initialize(void); + +/** + * @brief Turns on the given pin. + */ +extern int gpio_set(int pin); + +/** + * @brief Turns off the given pin. + */ +extern int gpio_clear(int pin); + +/** + * @brief Returns the current value of a GPIO pin. + */ +extern int gpio_get_val(int pin); + +/** + * @brief Selects a GPIO pin for a specific function. + */ +extern int gpio_select_pin(int pin, rpi_pin type); + +/** + * @brief Setups a JTAG pin configuration. + */ +extern int gpio_select_jtag(void); + +/** + * @brief Setups the SPI interface on the RPI P1 GPIO header. + */ +extern int gpio_select_spi_p1(void); + +/** + * @brief Setups the I2C interface on the main (P1) GPIO pin header (rev2). + */ +extern int gpio_select_i2c_p1_rev2(void); + +/** + * @brief Configures a input GPIO pin pull-up resistor. + */ +extern int gpio_input_mode(int pin, rpi_gpio_input_mode mode); + +/** + * @brief Configures several input GPIO pins to the same pull-up resistor setup. + */ +extern int gpio_setup_input_mode(int *pin, int pin_count, rpi_gpio_input_mode mode); + +/** + * @brief Discards any configuration made on this pin. + */ +extern int gpio_disable_pin(int dev_pin); + +/** + * @brief Debouces a switch by requiring a number of clock ticks to pass between interruts. + */ +extern int gpio_debounce_switch(int pin, int ticks); + +/** + * @brief Enables interrupts on the given GPIO pin. + */ +extern int gpio_enable_interrupt(int dev_pin, gpio_interrupt interrupt, void (*handler) (void)); + +/** + * @brief Disables any interrupt enabled on the given GPIO pin. + */ +extern int gpio_disable_interrupt(int dev_pin); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* LIBBSP_ARM_RASPBERRYPI_GPIO_H */ diff --git a/c/src/lib/libbsp/arm/raspberrypi/include/i2c.h b/c/src/lib/libbsp/arm/raspberrypi/include/i2c.h new file mode 100644 index 0000000..bdc53ef --- /dev/null +++ b/c/src/lib/libbsp/arm/raspberrypi/include/i2c.h @@ -0,0 +1,166 @@ +/** + * @file i2c.h + * + * @ingroup raspberrypi_i2c + * + * @brief Raspberry Pi specific I2C and SPI definitions. + */ + +/* + * Copyright (c) 2014 Andre Marques <andre.lousa.marques at gmail.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. + */ + +#ifndef LIBBSP_ARM_RASPBERRYPI_I2C_H +#define LIBBSP_ARM_RASPBERRYPI_I2C_H + +#include <rtems/libi2c.h> + +/** + * @name SPI constants. + * + * @{ + */ + +/** + * @brief GPU processor core clock rate in Hz. + * + * Unless configured otherwise on a "config.txt" file present on the SD card + * the GPU defaults to 250 MHz. Currently only 250 MHz is supported. + */ + +/* TODO: It would be nice if this value could be probed at startup, probably + * using the Mailbox interface since the usual way of setting this on + * the hardware is through a "config.txt" text file on the SD card. + * Having this setup on the configure.ac script would require changing + * the same setting on two different places. */ +#define GPU_CORE_CLOCK_RATE 250000000 + +/** @} */ + +/** + * @name SPI data structures. + * + * @{ + */ + +/** + * @brief Object containing the SPI bus configuration settings. + * + * Encapsulates the current SPI bus configuration. + */ +typedef struct { + int initialized; + uint8_t bytes_per_char; + + /* Shift to be applied on data transfers with + * least significative bit first (LSB) devices. */ + uint8_t bit_shift; + uint32_t dummy_char; + + /* If set to 0 uses 3-wire SPI, with 2 separate data lines (MOSI and MISO), + * if set to 1 uses 2-wire SPI, where the MOSI data line doubles as the + * slave out (SO) and slave in (SI) data lines. */ + int bidirectional; + uint32_t current_slave_addr; + rtems_id irq_sema_id; + int irq_write; +} bcm2835_spi_softc_t; + +typedef struct { + rtems_libi2c_bus_t bus_desc; + bcm2835_spi_softc_t softc; +} bcm2835_spi_desc_t; + +/** @} */ + +/** + * @name SPI directives. + * + * @{ + */ + +rtems_status_code bcm2835_spi_init(rtems_libi2c_bus_t * bushdl); + +rtems_status_code bcm2835_spi_send_start(rtems_libi2c_bus_t * bushdl); + +rtems_status_code bcm2835_spi_stop(rtems_libi2c_bus_t * bushdl); + +rtems_status_code bcm2835_spi_send_addr(rtems_libi2c_bus_t * bushdl, uint32_t addr, int rw); + +int bcm2835_spi_read_bytes(rtems_libi2c_bus_t * bushdl, unsigned char *bytes, int nbytes); + +int bcm2835_spi_write_bytes(rtems_libi2c_bus_t * bushdl, unsigned char *bytes, int nbytes); + +int bcm2835_spi_ioctl(rtems_libi2c_bus_t * bushdl, int cmd, void *arg); + +int BSP_spi_register_drivers(int spi_bus_number); + +int BSP_spi_init(void); + +/** @} */ + +/** + * @name I2C constants. + * + * @{ + */ + + +/** + * @brief BSC controller core clock rate in Hz. + * + * This is set to 150 MHz as per the BCM2835 datasheet. + */ +#define BSC_CORE_CLK_HZ 150000000 + +/** @} */ + +/** + * @name I2C data structures. + * + * @{ + */ + +typedef struct { + int initialized; + rtems_id irq_sema_id; +} bcm2835_i2c_softc_t; + +typedef struct { + rtems_libi2c_bus_t bus_desc; + bcm2835_i2c_softc_t softc; +} bcm2835_i2c_desc_t; + +/** @} */ + +/** + * @name I2C directives. + * + * @{ + */ + +rtems_status_code bcm2835_i2c_init(rtems_libi2c_bus_t * bushdl); + +rtems_status_code bcm2835_i2c_send_start(rtems_libi2c_bus_t * bushdl); + +rtems_status_code bcm2835_i2c_stop(rtems_libi2c_bus_t * bushdl); + +rtems_status_code bcm2835_i2c_send_addr(rtems_libi2c_bus_t * bushdl, uint32_t addr, int rw); + +int bcm2835_i2c_read_bytes(rtems_libi2c_bus_t * bushdl, unsigned char *bytes, int nbytes); + +int bcm2835_i2c_write_bytes(rtems_libi2c_bus_t * bushdl, unsigned char *bytes, int nbytes); + +int bcm2835_i2c_ioctl(rtems_libi2c_bus_t * bushdl, int cmd, void *arg); + +int BSP_i2c_register_drivers(int i2c_bus_number); + +int BSP_i2c_init(void); + +/** @} */ + +#endif /* LIBBSP_ARM_RASPBERRYPI_I2C_H */ diff --git a/c/src/lib/libbsp/arm/raspberrypi/include/irq.h b/c/src/lib/libbsp/arm/raspberrypi/include/irq.h index 8436c2d..fb5f90e 100644 --- a/c/src/lib/libbsp/arm/raspberrypi/include/irq.h +++ b/c/src/lib/libbsp/arm/raspberrypi/include/irq.h @@ -1,5 +1,5 @@ /** - * @file + * @file irq.h * * @ingroup raspberrypi_interrupt * @@ -7,7 +7,8 @@ */ /** - * Copyright (c) 2013 Alan Cudmore + * Copyright (c) 2014 Andre Marques <andre.lousa.marques at gmail.com> + * Copyright (c) 2013 Alan Cudmore * * The license and distribution terms for this file may be * found in the file LICENSE in this distribution or at @@ -62,6 +63,7 @@ #define BSP_IRQ_COUNT (BCM2835_INTC_TOTAL_IRQ) +rtems_status_code bsp_interrupt_vector_enable(rtems_vector_number vector); void raspberrypi_set_exception_handler(Arm_symbolic_exception_name exception, void (*handler)(void)); diff --git a/c/src/lib/libbsp/arm/raspberrypi/include/raspberrypi.h b/c/src/lib/libbsp/arm/raspberrypi/include/raspberrypi.h index 4cc7eec..2e902c6 100644 --- a/c/src/lib/libbsp/arm/raspberrypi/include/raspberrypi.h +++ b/c/src/lib/libbsp/arm/raspberrypi/include/raspberrypi.h @@ -1,6 +1,5 @@ - /** - * @file + * @file raspberrypi.h * * @ingroup raspberrypi_reg * @@ -8,7 +7,8 @@ */ /* - * Copyright (c) 2013 Alan Cudmore. + * Copyright (c) 2014 Andre Marques <andre.lousa.marques at gmail.com> + * Copyright (c) 2013 Alan Cudmore. * * The license and distribution terms for this file may be * found in the file LICENSE in this distribution or at @@ -79,8 +79,16 @@ #define BCM2835_GPIO_GPFSEL1 (BCM2835_GPIO_REGS_BASE+0x04) #define BCM2835_GPIO_GPSET0 (BCM2835_GPIO_REGS_BASE+0x1C) #define BCM2835_GPIO_GPCLR0 (BCM2835_GPIO_REGS_BASE+0x28) +#define BCM2835_GPIO_GPLEV0 (BCM2835_GPIO_REGS_BASE+0x34) +#define BCM2835_GPIO_GPEDS0 (BCM2835_GPIO_REGS_BASE+0x40) +#define BCM2835_GPIO_GPREN0 (BCM2835_GPIO_REGS_BASE+0x4C) +#define BCM2835_GPIO_GPFEN0 (BCM2835_GPIO_REGS_BASE+0x58) +#define BCM2835_GPIO_GPHEN0 (BCM2835_GPIO_REGS_BASE+0x64) +#define BCM2835_GPIO_GPLEN0 (BCM2835_GPIO_REGS_BASE+0x70) +#define BCM2835_GPIO_GPAREN0 (BCM2835_GPIO_REGS_BASE+0x7C) +#define BCM2835_GPIO_GPAFEN0 (BCM2835_GPIO_REGS_BASE+0x88) #define BCM2835_GPIO_GPPUD (BCM2835_GPIO_REGS_BASE+0x94) -#define BCM2835_GPIO_GPPUDCLK0 (BCM2835_GPIO_REGS_BASE+0x98) +#define BCM2835_GPIO_GPPUDCLK0 (BCM2835_GPIO_REGS_BASE+0x98) /** @} */ @@ -107,14 +115,12 @@ /** @} */ - /** * @name UART 0 (PL011) Registers * * @{ */ - #define BCM2835_UART0_BASE (0x20201000) #define BCM2835_UART0_DR (BCM2835_UART0_BASE+0x00) @@ -145,9 +151,68 @@ #define BCM2835_UART0_ICR_RX 0x10 #define BCM2835_UART0_ICR_TX 0x20 +/** @} */ + +/** + * @name I2C (BSC) Registers + * + * @{ + */ + +#define BCM2835_I2C_BASE (0x20804000) + +#define BCM2835_I2C_C (BCM2835_I2C_BASE+0x00) +#define BCM2835_I2C_S (BCM2835_I2C_BASE+0x04) +#define BCM2835_I2C_DLEN (BCM2835_I2C_BASE+0x08) +#define BCM2835_I2C_A (BCM2835_I2C_BASE+0x0C) +#define BCM2835_I2C_FIFO (BCM2835_I2C_BASE+0x10) +#define BCM2835_I2C_DIV (BCM2835_I2C_BASE+0x14) +#define BCM2835_I2C_DEL (BCM2835_I2C_BASE+0x18) +#define BCM2835_I2C_CLKT (BCM2835_I2C_BASE+0x1C) /** @} */ +/** + * @name SPI Registers + * + * @{ + */ + +#define BCM2835_SPI_BASE (0x20204000) + +#define BCM2835_SPI_CS (BCM2835_SPI_BASE+0x00) +#define BCM2835_SPI_FIFO (BCM2835_SPI_BASE+0x04) +#define BCM2835_SPI_CLK (BCM2835_SPI_BASE+0x08) +#define BCM2835_SPI_DLEN (BCM2835_SPI_BASE+0x0C) +#define BCM2835_SPI_LTOH (BCM2835_SPI_BASE+0x10) +#define BCM2835_SPI_DC (BCM2835_SPI_BASE+0x14) + +/** @} */ + +/** + * @name I2C/SPI slave BSC Registers + * + * @{ + */ + +#define BCM2835_I2C_SPI_BASE (0x20214000) + +#define BCM2835_I2C_SPI_DR (BCM2835_I2C_SPI_BASE+0x00) +#define BCM2835_I2C_SPI_RSR (BCM2835_I2C_SPI_BASE+0x04) +#define BCM2835_I2C_SPI_SLV (BCM2835_I2C_SPI_BASE+0x08) +#define BCM2835_I2C_SPI_CR (BCM2835_I2C_SPI_BASE+0x0C) +#define BCM2835_I2C_SPI_FR (BCM2835_I2C_SPI_BASE+0x10) +#define BCM2835_I2C_SPI_IFLS (BCM2835_I2C_SPI_BASE+0x14) +#define BCM2835_I2C_SPI_IMSC (BCM2835_I2C_SPI_BASE+0x18) +#define BCM2835_I2C_SPI_RIS (BCM2835_I2C_SPI_BASE+0x1C) +#define BCM2835_I2C_SPI_MIS (BCM2835_I2C_SPI_BASE+0x20) +#define BCM2835_I2C_SPI_ICR (BCM2835_I2C_SPI_BASE+0x24) +#define BCM2835_I2C_SPI_DMACR (BCM2835_I2C_SPI_BASE+0x28) +#define BCM2835_I2C_SPI_TDR (BCM2835_I2C_SPI_BASE+0x2C) +#define BCM2835_I2C_SPI_GPUSTAT (BCM2835_I2C_SPI_BASE+0x30) +#define BCM2835_I2C_SPI_HCTRL (BCM2835_I2C_SPI_BASE+0x34) + +/** @} */ /** * @name IRQ Registers @@ -170,7 +235,6 @@ /** @} */ - /** * @name GPU Timer Registers * @@ -194,7 +258,6 @@ /** @} */ - /** @} */ #endif /* LIBBSP_ARM_RASPBERRYPI_RASPBERRYPI_H */ diff --git a/c/src/lib/libbsp/arm/raspberrypi/irq/irq.c b/c/src/lib/libbsp/arm/raspberrypi/irq/irq.c index 4132ef9..c2c7d89 100644 --- a/c/src/lib/libbsp/arm/raspberrypi/irq/irq.c +++ b/c/src/lib/libbsp/arm/raspberrypi/irq/irq.c @@ -1,5 +1,5 @@ /** - * @file + * @file irq.c * * @ingroup raspberrypi_interrupt * @@ -7,6 +7,8 @@ */ /* + * Copyright (c) 2014 Andre Marques <andre.lousa.marques at gmail.com> + * * Copyright (c) 2009 * embedded brains GmbH * Obere Lagerstr. 30 @@ -52,22 +54,47 @@ void bsp_interrupt_dispatch(void) rtems_vector_number vector = 255; /* ARM timer */ - if (BCM2835_REG(BCM2835_IRQ_BASIC) && 0x1) + if ( BCM2835_REG(BCM2835_IRQ_BASIC) & 0x1 ) { vector = BCM2835_IRQ_ID_TIMER_0; - } /* UART 0 */ - else if ( BCM2835_REG(BCM2835_IRQ_BASIC) && BCM2835_BIT(19)) + else if ( BCM2835_REG(BCM2835_IRQ_BASIC) & BCM2835_BIT(19) ) { vector = BCM2835_IRQ_ID_UART; } + /* GPIO 0*/ + else if ( BCM2835_REG(BCM2835_IRQ_PENDING2) & BCM2835_BIT(17) ) + { + vector = BCM2835_IRQ_ID_GPIO_0; + } + else if ( BCM2835_REG(BCM2835_IRQ_PENDING2) & BCM2835_BIT(18) ) + { + vector = BCM2835_IRQ_ID_GPIO_1; + } + else if ( BCM2835_REG(BCM2835_IRQ_PENDING2) & BCM2835_BIT(19) ) + { + vector = BCM2835_IRQ_ID_GPIO_2; + } + else if ( BCM2835_REG(BCM2835_IRQ_PENDING2) & BCM2835_BIT(20) ) + { + vector = BCM2835_IRQ_ID_GPIO_3; + } + /* I2C */ + else if ( BCM2835_REG(BCM2835_IRQ_PENDING2) & BCM2835_BIT(21) ) + { + vector = BCM2835_IRQ_ID_I2C; + } + /* SPI */ + else if ( BCM2835_REG(BCM2835_IRQ_PENDING2) & BCM2835_BIT(22) ) + { + vector = BCM2835_IRQ_ID_SPI; + } if ( vector < 255 ) { bsp_interrupt_handler_dispatch(vector); } - } rtems_status_code bsp_interrupt_vector_enable(rtems_vector_number vector) @@ -76,7 +103,7 @@ rtems_status_code bsp_interrupt_vector_enable(rtems_vector_number vector) rtems_interrupt_disable(level); - /* ARM Timer */ + /* ARM Timer */ if ( vector == BCM2835_IRQ_ID_TIMER_0 ) { BCM2835_REG(BCM2835_IRQ_ENABLE_BASIC) = 0x1; @@ -85,8 +112,38 @@ rtems_status_code bsp_interrupt_vector_enable(rtems_vector_number vector) else if ( vector == BCM2835_IRQ_ID_UART ) { BCM2835_REG(BCM2835_IRQ_ENABLE2) = BCM2835_BIT(25); - } + /* GPIO 0 */ + else if ( vector == BCM2835_IRQ_ID_GPIO_0 ) + { + BCM2835_REG(BCM2835_IRQ_ENABLE2) = BCM2835_BIT(17); + } + /* GPIO 1 */ + else if ( vector == BCM2835_IRQ_ID_GPIO_1 ) + { + BCM2835_REG(BCM2835_IRQ_ENABLE2) = BCM2835_BIT(18); + } + /* GPIO 2 */ + else if ( vector == BCM2835_IRQ_ID_GPIO_2 ) + { + BCM2835_REG(BCM2835_IRQ_ENABLE2) = BCM2835_BIT(19); + } + /* GPIO 3 */ + else if ( vector == BCM2835_IRQ_ID_GPIO_3 ) + { + BCM2835_REG(BCM2835_IRQ_ENABLE2) = BCM2835_BIT(20); + } + /* I2C */ + else if ( vector == BCM2835_IRQ_ID_I2C ) + { + BCM2835_REG(BCM2835_IRQ_ENABLE2) = BCM2835_BIT(21); + } + /* SPI */ + else if ( vector == BCM2835_IRQ_ID_SPI ) + { + BCM2835_REG(BCM2835_IRQ_ENABLE2) = BCM2835_BIT(22); + } + rtems_interrupt_enable(level); return RTEMS_SUCCESSFUL; @@ -106,12 +163,42 @@ rtems_status_code bsp_interrupt_vector_disable(rtems_vector_number vector) { BCM2835_REG(BCM2835_IRQ_DISABLE2) = BCM2835_BIT(25); } + /* GPIO 0 */ + else if ( vector == BCM2835_IRQ_ID_GPIO_0 ) + { + BCM2835_REG(BCM2835_IRQ_DISABLE2) = BCM2835_BIT(17); + } + /* GPIO 1 */ + else if ( vector == BCM2835_IRQ_ID_GPIO_1 ) + { + BCM2835_REG(BCM2835_IRQ_DISABLE2) = BCM2835_BIT(18); + } + /* GPIO 2 */ + else if ( vector == BCM2835_IRQ_ID_GPIO_2 ) + { + BCM2835_REG(BCM2835_IRQ_DISABLE2) = BCM2835_BIT(19); + } + /* GPIO 3 */ + else if ( vector == BCM2835_IRQ_ID_GPIO_3 ) + { + BCM2835_REG(BCM2835_IRQ_DISABLE2) = BCM2835_BIT(20); + } + /* I2C */ + else if ( vector == BCM2835_IRQ_ID_I2C ) + { + BCM2835_REG(BCM2835_IRQ_DISABLE2) = BCM2835_BIT(21); + } + /* SPI */ + else if ( vector == BCM2835_IRQ_ID_SPI ) + { + BCM2835_REG(BCM2835_IRQ_DISABLE2) = BCM2835_BIT(22); + } + rtems_interrupt_enable(level); return RTEMS_SUCCESSFUL; } - void bsp_interrupt_handler_default(rtems_vector_number vector) { printk("spurious interrupt: %u\n", vector); diff --git a/c/src/lib/libbsp/arm/raspberrypi/preinstall.am b/c/src/lib/libbsp/arm/raspberrypi/preinstall.am index 70259e2..f9a87e0 100644 --- a/c/src/lib/libbsp/arm/raspberrypi/preinstall.am +++ b/c/src/lib/libbsp/arm/raspberrypi/preinstall.am @@ -130,6 +130,14 @@ $(PROJECT_INCLUDE)/bsp/raspberrypi.h: include/raspberrypi.h $(PROJECT_INCLUDE)/b $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/bsp/raspberrypi.h PREINSTALL_FILES += $(PROJECT_INCLUDE)/bsp/raspberrypi.h +$(PROJECT_INCLUDE)/bsp/gpio.h: include/gpio.h $(PROJECT_INCLUDE)/bsp/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/bsp/gpio.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/bsp/gpio.h + +$(PROJECT_INCLUDE)/bsp/i2c.h: include/i2c.h $(PROJECT_INCLUDE)/bsp/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/bsp/i2c.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/bsp/i2c.h + $(PROJECT_INCLUDE)/libcpu/cache_.h: ../../../libcpu/arm/shared/include/cache_.h $(PROJECT_INCLUDE)/libcpu/$(dirstamp) $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/libcpu/cache_.h PREINSTALL_FILES += $(PROJECT_INCLUDE)/libcpu/cache_.h diff --git a/c/src/lib/libbsp/arm/raspberrypi/startup/bspstart.c b/c/src/lib/libbsp/arm/raspberrypi/startup/bspstart.c index c5786bf..5ca6612 100644 --- a/c/src/lib/libbsp/arm/raspberrypi/startup/bspstart.c +++ b/c/src/lib/libbsp/arm/raspberrypi/startup/bspstart.c @@ -1,5 +1,5 @@ /** - * @file + * @file bspstart.c * * @ingroup arm_start * @@ -7,7 +7,8 @@ */ /* - * Copyright (c) 2013 by Alan Cudmore + * Copyright (c) 2014 Andre Marques <andre.lousa.marques at gmail.com> + * Copyright (c) 2013 by Alan Cudmore * * The license and distribution terms for this file may be * found in the file LICENSE in this distribution or at @@ -22,8 +23,18 @@ #include <bsp/linker-symbols.h> #include <bsp/stackalloc.h> #include <bsp/raspberrypi.h> +#include <bsp/i2c.h> + +void bsp_predriver_hook(void) +{ + if ( BSP_ENABLE_SPI == 1 ) + BSP_spi_init(); + + if ( BSP_ENABLE_I2C == 1 ) + BSP_i2c_init(); +} void bsp_start(void) { - bsp_interrupt_initialize(); + bsp_interrupt_initialize(); } -- 1.8.5.5 _______________________________________________ devel mailing list devel@rtems.org http://lists.rtems.org/mailman/listinfo/devel