Notice: We have migrated to GitLab launching 2024-05-01 see here: https://gitlab.rtems.org/

Changes between Initial Version and Version 1 of GSoC/2015/RaspberryPi_peripherals_and_SD_card


Ignore:
Timestamp:
08/21/15 12:02:49 (9 years ago)
Author:
André Marques
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • GSoC/2015/RaspberryPi_peripherals_and_SD_card

    v1 v1  
     1= Raspberry Pi Low Level Peripherals and SD Card  =
     2
     3[[TOC(GSoC/2015/RaspberryPi_peripherals_and_SD_card , depth=2)]]
     4
     5'''Students:''' André Marques.
     6
     7'''Mentors:''' Alan Cudmore, Lou Woods, Jeff Mayes and Josh O'Guin.
     8
     9'''Introduction:''' This project has provided RTEMS with a generic GPIO API and gave the Raspberry Pi BSP GPIO, I2C and SPI support, while giving some good head start on the SD card support.
     10
     11'''Requirements:''' This project requires some familiarity with the RTEMS codebase, low level programming in C and some hardware knowledge.
     12
     13= Project Contributions =
     14
     15This page details the outcome of this GSOC project. A project intro can be found at my blog (a resume of the project proposal) in [1].
     16
     17Additional information can be found on the development blog [2] and github [3].
     18
     19= RTEMS GPIO API =
     20
     21This section intends to document the RTEMS GPIO API developed during this project, which is already in the RTEMS tree and in use by both the raspberry and beagle BSPs. The development blog contains several posts where the evolution of the API throughout the project can be seen.
     22
     23This section is intended to be used in the future as formal documentation for the API in some way, and is organized in three topics:
     24
     25- API operation;
     26- BSP developer manual;
     27- User manual.
     28
     29== API operation ==
     30
     31This section will detail the inner workings of the API, how it processes the received data and how it manages the requests between an user application and a given hardware platform.
     32
     33The API provides functions to user applications, and at the same time provides prototypes for functions that a BSP should implement before the API can be used. Each BSP function have a well defined purpose, so the BSP developer only has to fill in their specific GPIO hardware calls. The API acts as an abstraction layer between user and BSP code, meaning that applications can be used with different platforms with minimal modification (i.e.: pin numbers will probably vary, but they can be accounted for in application configuration tables).
     34
     35=== GPIO bank nomenclature ===
     36
     37In hardware GPIO pins are usually organized in groups called 'bank's which map to hardware registers of the same size where each bit correspond to a pin on that bank. This means that operating on a given pin requires one or more write/read operations on a given register, which can lead to race-conditions between pins within the same register/bank. For that reason the API also organizes the pins in banks, each with an individual lock (more details in the locking subsection) that is held during any given operation.
     38
     39The API has two bank concepts:
     40
     41'''GPIO bank:''' this is the type of bank described earlier. The amount of pins each bank can have is defined by the BSP_GPIO_PINS_PER_BANK constant (up to a maximum of 32 pins), and the amount of banks used is calculated by the API by dividing the total amount of pins for the amount of pins per bank. It also takes into account platforms where the number of pins per bank is not a multiple of the total number of pins, resulting in a shorter last bank.
     42
     43'''GPIO select bank:''' given the description above for a bank we can say that to the API a bank is a register where each bit corresponds to a pin. However, since GPIO pins may be programmed for additional functions other than the basic digital input and output, the function selection registers may require more than one bit per pin to accurately select a function to a given pin. In these cases the platforms will define more registers allocated to the function selection operations than to the other operations, and so the API incorporates that through the concept of a select bank. This bank is managed internally to the API, and the amount of pins each of these banks may have is defined through the BSP_GPIO_PINS_PER_SELECT_BANK constant. Note, however, that this constant is optional and only affects the behavior of the rtems_gpio_multi_select function, which allows for multiple pin function selection through a single GPIO hardware call (calling the rtems_gpio_bsp_multi_select function). If the constant is not given then the pins are selected one by one through rtems_gpio_request_configuration.
     44
     45=== Constants ===
     46
     47Each BSP must provide at least the following constants (through bsp.h or maybe a BSPOPT):
     48
     49'''BSP_GPIO_PIN_COUNT''' - Number of GPIO pins available to the API;
     50'''BSP_GPIO_PINS_PER_BANK''' - Number of GPIO pins per GPIO bank (usually the register size of the platform).
     51
     52The next constant, although desirable, may be omitted:
     53
     54'''BSP_GPIO_PINS_PER_SELECT_BANK''' - If the platform can have have more that the two basic functions for a GPIO pin, it is likely that each function selection register will control fewer pins than the usual GPIO bank. By providing this constant the API can assign multiple GPIO functions on the fewer hardware calls possible when calling rtems_gpio_multi_select. Also note that by providing this constant the API may call the rtems_gpio_bsp_multi_select function, so its implementation will have to be complete. This is more detailed in the following sections.
     55
     56Care should also be taken as each GPIO bank requires a semaphore, so depending on the number of banks used by a platform the application may have to adjust the confdefs.h CONFIGURE_MAXIMUM_SEMAPHORES constant.
     57
     58=== GPIO pin numbering ===
     59
     60When referring to a specific GPIO pin the API expects the GPIO pin number in the processor. This eases the pin identification and avoids storing the bank-pin pair in memory which would lead to greater memory requirements, as every pin in a GPIO bank will have the same bank number (potentially each bank would end with 32 x 32 bits of redundant data to store the same bank number). The API converts the processor numbering to a bank-pin pair (about two division operations), and that is what is sent to the BSP code when needed.
     61
     62BSP developers may still provide constants and pin configurations for applications to refer to, instead of hard coded 'magic' pin numbers. More details in the BSP developer manual section.
     63
     64=== GPIO configurations ===
     65
     66The API supports configuration tables, allowing for better platform management in applications. More details in the User manual section.
     67
     68=== Pin tracking ===
     69
     70One key role of the API is to keep track of the GPIO pins state and to synchronize the access to the GPIO banks. It uses an uni-dimensional array of the following structure:
     71
     72{{{
     73typedef struct
     74{
     75  rtems_gpio_function pin_function;
     76
     77  /* GPIO pull resistor configuration. */
     78  rtems_gpio_pull_mode resistor_mode;
     79
     80  /* If true inverts digital in/out applicational logic. */
     81  bool logic_invert;
     82
     83  /* True if the pin is on a group. */
     84  bool on_group;
     85
     86  /* Interrupt data for a pin. This field is NULL if no interrupt is enabled
     87   * on the pin. */
     88  gpio_pin_interrupt_state *interrupt_state;
     89} gpio_pin;
     90}}}
     91
     92This structure requires 8 bytes of memory per GPIO pin, which may increase if it has enabled interrupts. If it does have enabled interrupts that information will be allocated in the interrupt_state pointer, and its definition is as follows:
     93
     94{{{
     95typedef struct
     96{
     97  /* Currently active interrupt. */
     98  rtems_gpio_interrupt active_interrupt;
     99
     100  /* ISR shared flag. */
     101  rtems_gpio_handler_flag handler_flag;
     102
     103  /* Linked list of interrupt handlers. */
     104  rtems_chain_control handler_chain;
     105
     106  /* Switch-deboucing information. */
     107  uint32_t debouncing_tick_count;
     108  rtems_interval last_isr_tick;
     109} gpio_pin_interrupt_state;
     110}}}
     111
     112Having an array allows for faster retrieval of the pin state, since they can be identified directly with the pin number. This reasoning assumes that no platform will have enough reserved pins (i.e.: pins that are not physically available), such that the memory wasted tracking these ghost pins is worse than fetching the pin state in a more custom data structure such as a tree.
     113
     114=== Pin group ===
     115
     116Apart from single pin operations, the API also allows operations on groups of pins. This can be useful to simplify GPIO operations (i.e.: address multiple pins at once) or to bit-bang data buses and other GPIO-based interfaces.
     117
     118Pin groups are completely managed within the API, and used by applications through an opaque pointer. They request a pin group and provide a group definition (refer to the User manual section) to the following function:
     119
     120{{{
     121extern rtems_status_code rtems_gpio_define_pin_group(
     122  const rtems_gpio_group_definition *group_definition,
     123  rtems_gpio_group *group
     124);
     125}}}
     126
     127This function then parses the configuration to the internal data structure:
     128
     129{{{
     130struct rtems_gpio_group
     131{
     132  rtems_chain_node node;
     133
     134  uint32_t *digital_inputs;
     135  uint32_t digital_input_bank;
     136  uint32_t input_count;
     137
     138  uint32_t *digital_outputs;
     139  uint32_t digital_output_bank;
     140  uint32_t output_count;
     141
     142  uint32_t *bsp_speficifc_pins;
     143  uint32_t bsp_specific_bank;
     144  uint32_t bsp_specific_pin_count;
     145
     146  rtems_id group_lock;
     147};
     148}}}
     149
     150A group is defined by at least one and at most three sets of pins, one set per pin function. All pins within a pin set must belong to the same pin bank (the actual pin numbers stored on the structure are always relative to the registered bank, avoiding further conversion from processor pin number to the bank-pin pair), but different pin sets may belong to different banks. All operations on a pin group are done on the corresponding pin set (i.e.: reading a group will return the values of all pins in the digital_inputs set), and any operation on the group requires the group_lock to be held.
     151
     152This means that any operation on a group requires both the corresponding set bank lock and group lock to be held.
     153
     154The API uses a rtems chain to store all pin groups, so each pin group is a node of that chain. Since the pin definition is an opaque type, when an application requests a pin group it receives a pointer to that pin group, which it passes to the API whenever it requires an group operation. This eliminates the need to iterate through the chain to access pin group data, and eases the process of group addition and removal.
     155
     156The group operations actuate on the pins accordingly to the order they have in the group definition. For instance if the pins 1, 3, 7 and 13 are set as digital outputs in a given group and the value 0x5h (0101b) is written to the group, then pins 1 and 7 will be set with logical low, while 3 and 13 will be set with logical high. To ensure the fewer hardware accesses possible every operation from pin function selection to pin accesses is done through the API’s multi-pin functions. This means that reading the value of a group (which means reading the group’s digital inputs) will translate in a single register call (which is also a reason to have all pins in a set belong to the same bank). The group write function however translates into two register calls, as set and clear registers are usually separate (and that is how the API assumes it, by calling the BSP functions that do multi pin set and multi pin clear).
     157
     158If the group includes pins with BSP-specific functions then the API only performs synchronization operations, and delegates the operation to the BSP through the following call:
     159
     160{{{
     161rtems_status_code rtems_gpio_bsp_specific_group_operation(
     162  uint32_t bank,
     163  uint32_t *pins,
     164  uint32_t pin_count,
     165  void *arg
     166);
     167}}}
     168
     169Any data needed for the operation or any data to be retrieved goes through the arg void pointer.
     170
     171=== Error checking ===
     172
     173The API returns rtems_status_code errors when needed, and the exact errors that each function might give are documented in the corresponding function doxygen documentation. Additional information can be printed in some locations if the DEBUG constant is defined. The API also relies in some assertions in situations where the error should not happen (such as a timeout error while waiting for a semaphore with the NO_WAIT flag).
     174
     175=== Locking ===
     176
     177Locking is implemented on the API through a mutex (binary semaphore) per bank. Since each bank of pins maps to a register in hardware, it must be ensured that multiple threads/cores using the API do not stumble in a race condition while operating on pins within the same bank. At the same time the API tracks the GPIO pins status on an internal data structure, so it must also be ensured that the access to a pin status is atomic (having a mutex per bank ensures that only other banks pin states can be updated). Locks are always acquired before checking the pin state to avoid a race condition with another thread that might be requesting or changing the state.
     178
     179Locks are obtained right before a critical section in every function that has them, and relinquishes them before any other API function call (there is no lock sharing) except if calling a BSP code function, even if that function will have to acquire it again. This eases the complexity of the code, and allows for GPIO context switches, as other pins in that bank may be processed in the meantime.
     180
     181=== Interrupt handling in the API ===
     182
     183As noted previously, a GPIO bank is composed of several pins. Each bank also has an interrupt vector associated which is triggered every time an interrupt is sensed on any pin within that bank. On the other hand applications usually rely on interrupts on specific pins. Each pin may have more than one handler if more than one device is connected to the same pin, in which case each device will have a handler which should begin by probing their corresponding device to know if the culprit is their device, and if so handle it.
     184
     185Multiple handlers per pin the API allows GPIO pins to be used as a shared IRQ line for multiple peripheral devices.
     186
     187To summarize, all pins on a GPIO bank will trigger a common vector, each pin being a separate interrupt source that may in turn have one or more handlers of their own. This does not mean, however, that the pins interrupt handling is completely independent as although each pin has their own handler, we can not simply call all pin handlers on a GPIO bank every time a interrupt is sensed on the vector, as the pins themselves do not have interrupt state data.
     188
     189The API installs one interrupt handler per GPIO bank to which job is to read the bank's interrupt line in the form of a bitmask, iterates the pins with pending interrupts and call the corresponding pin's handlers. Each pin has a list of handlers (defined by the applications) which are called in sequence. If an interrupt is enabled on a pin it always has at least one handler associated, but in the case of a shared IRQ line it may have multiple handlers - one per device connected to the pin.
     190
     191Each handler is expected to return an handled state, but even if the first handler assumes the interrupt the remaining handlers/devices are still called/queried. If no handler assumes the interrupt the API treats it as a spurious interrupt.
     192
     193The API installs a system interrupt handler for a GPIO bank when an application first tries to enable an interrupt on one of its pins, and is removed when the last interrupt is disabled on a pin from that bank. This system interrupt handler may be a regular handler (installed through rtems_interrupt_handler_install), meaning that the application handlers are called from ISR context, or may be a threaded handler (through an interrupt server) meaning that the application handlers are called from a thread/task. Threaded handlers allow for minimal time spent on ISR context, but also means that the interrupt may not be handled right away.
     194
     195Every time an interrupt is sensed on a GPIO bank, all interrupts on that bank are disabled and the hardware interrupt line on that vector is read. Every pin that shows as having a pending interrupt have their handler(s) called, and when only when that is finished can the interrupt line be cleared and interrupts enabled again on that bank.
     196
     197Before calling the pin application handler(s) the API handler begins by applying the software switch debouncing function (if activated), and then all interrupt handlers associated with that pin are called in sequence.
     198
     199== BSP developer manual ==
     200
     201To use the API a BSP should provide their implementation for the BSP specific functions required by the API.
     202
     203This section will detail what the API requires from a BSP.
     204
     205=== Using the API ===
     206
     207To use the API the BSP Makefile should include the API source and header for compilation.
     208
     209The source file is located at rtems-root/c/src/lib/libbsp/shared/gpio.c and the header at rtems-root/c/src/lib/libbsp/shared/include/gpio.h.
     210
     211The API header should be used as a BSP header.
     212
     213=== BSP-defined functions ===
     214
     215Every function that should be implemented by a BSP can be identified in the GPIO API header file by the rtems_gpio_bsp_* that starts the function name. The doxygen documentation on the header is clear of what is expected from every such function.
     216
     217=== Platform GPIO identifiers ===
     218
     219Since the API refers to GPIO pins by their processor pin number and can handle full configuration tables, a BSP can provide human-readable references to pins through constants or pre-defined configurations.
     220
     221For instance a GPIO pin reference may look like this:
     222
     223{{{
     224#define GPIO_40 40
     225}}}
     226
     227or
     228
     229{{{
     230#define ACTIVITY_LED 40
     231}}}
     232
     233to refer to GPIO pin 40 (processor numbering).
     234
     235If a GPIO pin is connected to an on-board peripheral (i.e.: an on-board LED) the BSP might provide a complete GPIO configuration such as the following:
     236
     237{{{
     238const rtems_gpio_pin_conf act_led = {
     239{ /* Activity LED */
     240  .pin_number = 40,
     241  .function = DIGITAL_OUTPUT,
     242  .pull_mode = NO_PULL_UP;
     243  .interrupt = NULL,
     244  .output_enabled = FALSE,
     245  .logic_invert = FALSE,
     246  .bsp_specific = NULL
     247};
     248}}}
     249
     250These configurations can be made available through a BSP header.
     251
     252== User manual ==
     253
     254This section will develop more on how an user application or device driver can use the API. More details on the functions can be seen on the API header doxygen documentation in [4].
     255
     256=== Initializing the API ===
     257
     258API initialization is done by calling the following function:
     259
     260{{{
     261rtems_status_code rtems_gpio_initialize(void);
     262}}}
     263
     264It initializes the pin state structure and creates the bank locks. It uses an atomic flag to ensure that the API initialized only once.
     265
     266=== Pin configurations ===
     267
     268The GPIO API makes it possible to define pin configuration tables to request any given pin configuration. It is also possible to define several configurations for a given pin, and update it as needed during run-time.
     269
     270The data structures to be used are as follows:
     271
     272{{{
     273typedef struct
     274{
     275  /* Processor pin number. */
     276  uint32_t pin_number;
     277  rtems_gpio_function function;
     278
     279  /* Pull resistor setting. */
     280  rtems_gpio_pull_mode pull_mode;
     281
     282  /* If digital out pin, set to TRUE to set the pin to logical high,
     283   * or FALSE for logical low. If not a digital out then this
     284   * is ignored. */
     285  bool output_enabled;
     286
     287  /* If true inverts digital in/out applicational logic. */
     288  bool logic_invert;
     289
     290  /* Pin interrupt configuration. Should be NULL if not used. */
     291  rtems_gpio_interrupt_configuration *interrupt;
     292
     293  /* Structure with BSP specific data, to use during the pin request.
     294   * If function == BSP_SPECIFIC this should have a pointer to
     295   * a rtems_gpio_specific_data structure.
     296   *
     297   * If not this field may be NULL. This is passed to the BSP function
     298   * so any BSP specific data can be passed to it through this pointer. */
     299  void *bsp_specific;
     300} rtems_gpio_pin_conf;
     301}}}
     302
     303If the pin should listen to some interrupt, then that configuration should go in the following structure:
     304
     305{{{
     306typedef struct
     307{
     308  rtems_gpio_interrupt active_interrupt;
     309
     310  rtems_gpio_handler_flag handler_flag;
     311
     312  bool threaded_interrupts;
     313
     314  /* Interrupt handler function. */
     315  rtems_gpio_irq_state (*handler) (void *arg);
     316
     317  /* Interrupt handler function arguments. */
     318  void *arg;
     319
     320  /* Software switch debounce settings. It should contain the amount of clock
     321   * ticks that must pass between interrupts to ensure that the interrupt
     322   * was not caused by a switch bounce.
     323   * If set to 0 this feature is disabled . */
     324  uint32_t debounce_clock_tick_interval;
     325} rtems_gpio_interrupt_configuration;
     326}}}
     327
     328Each pin may only listen to one type of interrupt, although it is possible to listen to either both edges or both levels at the same time. The handler_flag defines if the pin will have a single handler, or potentially more (IRQ shared line among several peripheral devices). At configuration time is only possible to define one handler, so any remaining handler should be installed with rtems_gpio_interrupt_handler_install.
     329
     330The interrupt configuration is separate from the base configuration table as not all pins will have interrupts (i.e.: output pins), so it allows for better modularity. In the event that multiple pins share the same applicational interrupt handler then the same interrupt configuration can be shared among those pins.
     331
     332These configurations can be defined in configuration files included by applications, so if an application is to be used in multiple platforms the application can specify the number of pins and the requirements for those pins for which each affected BSP would provide a configuration file. The application can then be used unmodified just by pulling the right configuration file at compile time.
     333
     334The API deals with configurations through the following set of functions:
     335
     336{{{
     337rtems_status_code rtems_gpio_request_configuration(
     338  const rtems_gpio_pin_conf *conf
     339);
     340
     341rtems_status_code rtems_gpio_update_configuration(
     342  const rtems_gpio_pin_conf *conf
     343);
     344
     345rtems_status_code rtems_gpio_release_configuration(
     346  const rtems_gpio_pin_conf *conf
     347);
     348
     349rtems_status_code rtems_gpio_multi_select(
     350  const rtems_gpio_pin_conf *pins,
     351  uint8_t pin_count
     352);
     353
     354rtems_status_code rtems_gpio_release_multiple_pins(
     355  const rtems_gpio_pin_conf *pins,
     356  uint32_t pin_count
     357);
     358}}}
     359
     360rtems_gpio_request_configuration and rtems_gpio_update_configuration only parse the given configurations and perform the required API calls. Because the API knows the current state of every pin, it can compare the current state with the new one received through rtems_gpio_update_configuration and update it accordingly (if valid, of course).
     361
     362rtems_gpio_multi_select allows several pins to be configured at the same time (single hardware call), or in the worst case, in a single API call (but using one hardware call per pin). This worst case scenario may happen if the platform does not provide the needed information for multiple pin selection, in which case the API will cycle through the configurations, one by one.
     363
     364Apart from multiple pin selection other configurations such as pull resistors or interrupt handling details are done a pin at a time (only the pin function selection is done in parallel). The advantage is that while a pin function selection takes usually two register accesses by a platform (read the selection register and change the required bits while maintaining the other pins data untouched), this function can use those two accesses to select a whole select bank. Without this function each pin selection would (or will if a platform happens to not support it) have access the registers two times per pin.
     365
     366Also note that since each write to a bank implies a write to the corresponding register, multiple pin selection can only help (in terms of register accesses) if the pins being selected belong to the same selection bank, as accessing multiple selection banks will obviously result in distinct calls per selection bank. This is however all managed by the API, and applications should use this function as it not only can ease the code (all pin configurations can be made in a single call) but also at the worst case scenario it translates to a series of rtems_gpio_request_configuration calls, while in the best case it can perform some register access optimizations for BSPs with support for it.
     367
     368When an application no longer requires a pin and wishes to repurpose its function they can be released from the API through either rtems_gpio_release_configuration or rtems_gpio_release_multiple_pins. After being released the pins can be requested again to the desired new function, all during run-time. If the pin function stays the same but only some configuration details change then it can also be accounted for in applications by requiring multiple configurations for a single pin and by simply calling rtems_gpio_update_configuration when needed. If the API detects that the new configuration has some different configuration parameter (other than the function) the API itself updates the current configuration.
     369
     370=== Digital I/O ===
     371
     372These functions calls are to be used to operate pins configured as digital input or output pins. In the case of the multi-pin functions they return/receive a bitmask with the corresponding logical values to be retrieved/applied in the same order as the given array of pins. To applications a logical high is always the value 1 and a logical low is always 0. When requesting a pin an application can also decide to invert the pin logic, on which case if the application sets that pin the API would convert it to a clear operation.
     373
     374{{{
     375rtems_status_code rtems_gpio_multi_set(
     376  uint32_t *pin_numbers,
     377  uint32_t pin_count
     378);
     379
     380rtems_status_code rtems_gpio_multi_clear(
     381  uint32_t *pin_numbers,
     382  uint32_t pin_count
     383);
     384
     385uint32_t rtems_gpio_multi_read(
     386  uint32_t *pin_numbers,
     387  uint32_t pin_count
     388);
     389
     390rtems_status_code rtems_gpio_set(uint32_t pin_number);
     391
     392rtems_status_code rtems_gpio_clear(uint32_t pin_number);
     393
     394int rtems_gpio_get_value(uint32_t pin_number);
     395}}}
     396
     397=== API direct operations ===
     398
     399Although the API can be operated through configuration tables, it can also be used with direct instructions using the following set of functions:
     400
     401{{{
     402rtems_status_code rtems_gpio_request_pin(
     403  uint32_t pin_number,
     404  rtems_gpio_function function,
     405  bool output_enable,
     406  bool logic_invert,
     407  void *bsp_specific
     408);
     409
     410rtems_status_code rtems_gpio_resistor_mode(
     411  uint32_t pin_number,
     412  rtems_gpio_pull_mode mode
     413);
     414
     415rtems_status_code rtems_gpio_release_pin(uint32_t pin_number);
     416
     417rtems_status_code rtems_gpio_debounce_switch(
     418  uint32_t pin_number,
     419  int ticks
     420);
     421
     422rtems_status_code rtems_gpio_interrupt_handler_install(
     423  uint32_t pin_number,
     424  rtems_gpio_irq_state (*handler) (void *arg),
     425  void *arg
     426);
     427
     428rtems_status_code rtems_gpio_enable_interrupt(
     429  uint32_t pin_number,
     430  rtems_gpio_interrupt interrupt,
     431  rtems_gpio_handler_flag flag,
     432  bool threaded_handling,
     433  rtems_gpio_irq_state (*handler) (void *arg),
     434  void *arg
     435);
     436
     437rtems_status_code rtems_gpio_interrupt_handler_remove(
     438  uint32_t pin_number,
     439  rtems_gpio_irq_state (*handler) (void *arg),
     440  void *arg
     441);
     442
     443rtems_status_code rtems_gpio_disable_interrupt(uint32_t pin_number);
     444}}}
     445
     446The API uses these functions internally, and applications for these functions by user applications may include, for instance, shell programs to control GPIO hardware via direct commands.
     447
     448=== API pin release functions ===
     449
     450The functions mentioned previously to release pins from the API allow pin repurposing, by declaring to the API that a given pin or pins are now available for another function. In hardware terms the pin state will remain unchanged, with the only exception being if the pin has interrupts enabled, in which case interrupts are disabled, the interrupt_state pointer in the GPIO internal tracking structure is freed. Pull-up resistor state and the current pin function stay the same in hardware.
     451
     452=== Software debouncing function ===
     453
     454The switch debouncing function (rtems_gpio_debounce_switch) only applies to digital input pins with interrupts enabled. It works by recording the clock tick of the previously handled interrupt, and by requiring a certain number of clock ticks to pass before allowing the next interrupt to be handled.
     455
     456=== Tristate pins ===
     457
     458Tristate refers to the ability of a pin to be both a digital input or output pin, by switching the directions as needed. Since this may be BSP specific, a BSP may have this capability by using the BSP_SPECIFIC function.
     459
     460=== Pin groups ===
     461
     462An application can define groups of pins and perform operations on them as if it was a single pin or entity (as a group may in fact be made of pins with different functions).
     463
     464A group is defined by an application using the following data structure:
     465
     466{{{
     467typedef struct
     468{
     469  const rtems_gpio_pin_conf *digital_inputs;
     470  uint32_t input_count;
     471
     472  const rtems_gpio_pin_conf *digital_outputs;
     473  uint32_t output_count;
     474
     475  const rtems_gpio_pin_conf *bsp_specifics;
     476  uint32_t bsp_specific_pin_count;
     477} rtems_gpio_group_definition;
     478}}}
     479
     480As shown a GPIO group is defined by sets of pins, one set per function type. Each group must have at least one set, but may have up to three sets. Every pin within a set must belong to the same GPIO bank, but different sets can have pins from different banks. This also means that a single set can have at most 32 pins, as that is also the limit of a GPIO bank. All operations within a group are synchronized, so only one operation in the whole group can be executed at any given time, and there are four operations available for groups, one per pin set plus the group release function.
     481
     482To define a pin group an id must first be requested from the API with the following function call:
     483
     484{{{
     485rtems_gpio_group *rtems_gpio_create_pin_group(void);
     486}}}
     487
     488This id should then be used with the API on all group operations.
     489
     490Having the id the application may send in their configuration to the API using the following function:
     491
     492{{{
     493rtems_status_code rtems_gpio_define_pin_group(
     494  const rtems_gpio_group_definition *group_definition,
     495  rtems_gpio_group *group
     496);
     497}}}
     498
     499With the group defined there are a total of four possible operations:
     500
     501{{{
     502rtems_status_code rtems_gpio_write_group(
     503  uint32_t data,
     504  rtems_gpio_group *group
     505);
     506
     507uint32_t rtems_gpio_read_group(rtems_gpio_group *group);
     508
     509rtems_status_code rtems_gpio_group_bsp_specific_operation(
     510  rtems_gpio_group *group,
     511  void *arg
     512);
     513
     514rtems_status_code rtems_gpio_release_pin_group(
     515  rtems_gpio_group *group
     516);
     517}}}
     518
     519The write and read operations receive/return a 32-bit bitmask which is to be applied to the set pins in the same order as defined in the group configuration.
     520
     521The bsp specific function is, as the name implies, bsp specific. If a BSP provides a GPIO function other than digital I/O that can be used by applications in a group then they will/should provide further documentation on what should be passing in the arg void pointer, which may include among other data the actual bsp specific function that is to be applied. The API simply validates the data received (except the void pointer) and transmits that information to the BSP code.
     522
     523== Sample applications ==
     524
     525Sample applications using this API can be found on my raspberry pi testing repository:
     526
     527https://github.com/asuol/RTEMS_rpi_testing/tree/master/GPIO
     528
     529Next is a short summary of what each test case uses/tests. All of these tests use the same circuit requiring two switches and two leds. They poll the switches or wait for an interrupt to lit the LEDs accordingly, with some variations (i.e.: the group test does not require the switches).
     530
     531- LIBGPIO_MULTI_TEST - Uses a configuration table to configure the pins, polling the switches;
     532- LIBGPIO_MULTI_TEST_IRQ - Uses a configuration table to configure the pins with interrupts on both edges for both switches;
     533- LIBGPIO_TEST - Uses direct API calls to poll the two switches;
     534- LIBGPIO_TEST_IRQ - Uses direct API calls to enable interrupts on both edges of the switches;
     535- LIBGPIO_TEST_GROUP - Defines a group with a output pin set, and writes a value to the group. This lits the LEDs accordingly to the value written.
     536
     537The following test case is raspberry pi specific:
     538
     539LIBGPIO_JTAG - Setups the JTAG pin interface on the raspberry pi. More information on JTAG and the PI can be seen in my blog in [5].
     540
     541== Potential Improvements ==
     542
     543This section provides some quick drafts on potential future improvements on the API.
     544
     545=== Parameter validation ===
     546
     547Each function validates the data received as parameters, but most validations can be superfluous if a certain application configuration was tested and is known to work. An application may use a certain GPIO configuration, and the API verifies if the configuration is valid every time it is used. However, if the configuration is to remain untouched, the API will waste time during execution to verify the same configuration over and over again. An approach to this could be to define a constant that would made this checking optional, so they could be used during the development of the application, but as soon as the configurations stabilize these checks could be removed to avoid wasting resources during run-time.
     548
     549=== Interrupt handling SMP support ===
     550
     551Using threaded handlers means that on a SMP setup multiple cores can process multiple interrupts at the same time. On a single core system, however, it is better to have a task per bank, to minimize context switching (as it currently is).
     552
     553A SMP system could benefit of multiple tasks/interrupt servers to allow interrupt handling parallelism, although the current interrupt server implementation only allows a single server in the whole system. In this situation it would also be useful if the vector was re-enabled as soon as possible so any interrupts generated during the handling of a pin's interrupt can be quickly handled in parallel, instead of waiting from the previous interrupts to be processed - remember that in a GPIO vector each pin interrupt is independent - unless it is an interrupt on the same pin).
     554
     555= I2C bus driver =
     556
     557In the last year I have developed an I2C bus driver for the raspberry pi using the libi2c API, however in the meantime a new API ported from Linux (I2C User-Space API) is now in use and so the previous bus driver was ported.
     558
     559The new API allows a bus driver to provide several levels of functionality to device drivers, namely:
     560
     561- I2C_FUNC_I2C - The basic I2C support. All bus drivers using the new API must support this functionality. It also states that the bus driver must recognize/process the message flag I2C_M_RECV_LEN, which is detailed next;
     562- I2C_FUNC_10BIT_ADDR - States that the bus is capable of addressing 10-bit slave devices;
     563- I2C_FUNC_PROTOCOL_MANGLING - States that the bus support variations outside the I2C protocol, such as ignoring data acknowledgments, reversing the bus direction and sending stop conditions at will;
     564- I2C_FUNC_NOSTART - States that the device driver may send multiple messages through the bus without a start condition between messages. In Linux this functionality is included in the mangling functionality;
     565- SMBUS functionality - States that the bus driver also supports System Management Bus (SMBUS) operations.
     566
     567With the new API device drivers gather the information they want to send or receive in i2c_msg arrays which take the slave address, data buffer, buffer size and message flags. The flags indicate to the bus how that particular piece of data should be sent or received through the bus. Since this requires the messages to be parsed and processed accordingly it required some changes on how the bus sent/received the data to/from the bus compared to when the libi2c API was used.
     568
     569The bus driver supports the I2C basic functionality (mandatory) and also 10-bit slave addressing, with some caveats.
     570
     571As noted previously, the I2C basic functionality (I2C_FUNC_I2C) states that the bus must be able to recognize the I2C_M_RECV_LEN message flag. This flag allows the slave device to define the size of a transfer, so in practice when the bus receives a message with this flag it should contain a 32 bytes buffer and a buffer length of 1 byte. The bus reads 1 byte of data from the slave device which indicates how many bytes it will be sending, and assign that value to the buffer size of the message while receiving and storing the message on the buffer (which is limited to 32 bytes). However the Raspberry Pi may not be compatible with this as it will likely require two transfers, with a stop-start condition in-between. This happens because before any transfer the size is defined on the DLEN register, which decrements the value as data is transferred. When the value reaches 0 the Pi sends automatically a stop condition. This may be solved by setting a transfer size of 33 (32 maximum bytes plus the size byte), but on a transfer shorter that 32 bytes the transfer would have to be terminated and it is not sure if the Pi would send a stop condition in that situation.
     572
     573As for the 10-bit slave addressing I was unable to find a device to test it.
     574
     575Both these caveats can be solved with devices that use these functionalities, but that can be hard to find and also time consuming as they would also require device drivers to test them all within the GSOC program. The plan is that if someone happens to have one of such devices and wants/needs to use it with the pi may test those functionalities in the future.
     576
     577The bus driver makes these supported functionalities available to the device drivers (which can retrieve them via an ioctl command to the bus), so they can know if the bus driver supports their I2C bus needs.
     578
     579To use the I2C bus an application must first call one or more of the following functions:
     580
     581{{{
     582void rpi_i2c_init(void);
     583
     584int rpi_i2c_register_bus(
     585  const char *bus_path,
     586  uint32_t bus_clock
     587);
     588
     589int rpi_setup_i2c_bus(void);
     590}}}
     591
     592rpi_i2c_init setups the Raspberry Pi GPIO header to activate the BSC I2C bus, while rpi_i2c_register_bus registers the Raspberry Pi BSC I2C bus with the Linux I2C User-Space API. It takes in the desired path to the bus, as well as the bus clock. Using these functions allow for complete control of these two parameters.
     593
     594However the Pi also provides the rpi_setup_i2c_bus function call which setups the Raspberry Pi BSC I2C bus on the "/dev/i2c" device file, using the default bus clock of 100 KHz.
     595
     596More details on these functions can be found on the raspberry pi I2C header doxygen documentation.
     597
     598A sample device driver and test case using the I2C bus can be found in:
     599
     600https://github.com/asuol/RTEMS_rpi_testing/tree/master/I2C_bus
     601
     602= SPI bus driver =
     603
     604The SPI bus core logic is mostly untouched from last year, featuring some code refactorings and minor changes (e.g.: interrupts no longer wait to acquire a semaphore, but instead wait for a system event). As with I2C the bus is registered in the API and the system with a function call:
     605
     606{{{
     607int rpi_spi_init(bool bidirectional_mode);
     608}}}
     609
     610This function setups the Raspberry Pi SPI bus (located on the GPIO header) on the "/dev/spi" device file, and registers the bus on the libi2c API. The flag it receives defines if the bidirectional mode (2-wire SPI) should be enabled.
     611
     612The bidirectional mode and write only devices (which do not return anything to the pi/master) were not tested with the bus driver.
     613
     614A sample device driver and test case using the SPI bus can be found in:
     615
     616https://github.com/asuol/RTEMS_rpi_testing/tree/master/SPI_bus
     617
     618= Notes on I2C and SPI bus drivers =
     619
     620Because I2C and SPI bus code no longer share the same API, I have separated the two buses into two separate directories in the raspberry BSP. Also the bsppredriverhook function which registered the buses depending on a BSPOPT was replaced back to the shared one, and now both buses provide a function that an application can call to setup the GPIO pins and register the bus in the corresponding API. The test device drivers also provide a device register function, which makes more sense than placing the device registration in the bsp code as it were last year.
     621
     622BSPOPTs are still used to define if either of the buses should be polled or interrupt driven.
     623
     624= SD card support =
     625
     626The raspberry pi uses provides an EMMC (External Mass Media Controller) module by Arasan to operate the SD card host controller. It is compliant to the SD Host controller specification 3.0, and since rtems-libbsd already as a SDHCI and MMC/SD drivers they can be used with the Pi to access the SD card.
     627
     628The sources for the SDHCI driver can be found in:
     629
     630https://git.rtems.org/rtems-libbsd/tree/freebsd/sys/dev/sdhci
     631
     632The driver in the tree defines that any BSP other than powerpc/qoriq results in a kernel panic at given operations. An initial plan is to add the Pi logic and quirks.
     633
     634Since the Pi requires the SDHCI and MMC/SD bus drivers they are registered as nexus devices, and a test case was created (based on testsuites/init01) that links the drivers and allows the SD support to be tested.
     635
     636The current work for SD card support within the rtems-libbsd can be found in:
     637
     638https://github.com/asuol/rtems-libbsd/tree/RPI_SD_CARD
     639
     640When the SDHCI driver is registered with the nexus bus it receives the EMMC base register address, which is defined on the raspberrypi.h header in the raspberry BSP. Since the base address for peripherals change from Pi1 to Pi2, the raspberrypi.h can always provide a constant for it.
     641
     642Some of the changes done to the SDHCI driver was the host reset operation, which the Pi seems to require that both the SD and host controller clocks are disabled during the reset. However for some reason after a few days the reset no longer worked.
     643
     644There is still a need to work out the best way to handle the SD card interrupts. In the past I have simply queried the EMMC interrupt pending registers when the driver expected the SD host or card to be doing something, but in theory the interrupts may be being sent to one of the GPIO interrupt vectors which could then call the SDHCI handler that the driver expects to be called. That is the current status of the SD support, as currently (even with reset problems) the host and card are detected and the host sends CMD0 to the card, but since the handler is not listening to the answer the operation obviously blocks.
     645
     646= References =
     647
     648[1] - https://asuolgsoc2014.wordpress.com/2015/05/03/project-intro/
     649
     650[2] - https://asuolgsoc2014.wordpress.com/
     651
     652[3] - https://github.com/asuol
     653
     654[4] - https://git.rtems.org/rtems/tree/c/src/lib/libbsp/shared/include/gpio.h
     655
     656[5] - https://asuolgsoc2014.wordpress.com/2015/07/14/jtag-code-loading-and-debugging/