diff options
author | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2018-04-20 13:38:33 +0200 |
---|---|---|
committer | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2018-04-20 14:32:43 +0200 |
commit | 1efa1c8389604dcf303b9acfa26c0ae60db9d9b4 (patch) | |
tree | 0fe569d6ad62623bf820f1dcd3620227995fae75 /bsps/powerpc/qoriq | |
parent | bsps/sparc: Move network drivers to bsps (diff) | |
download | rtems-1efa1c8389604dcf303b9acfa26c0ae60db9d9b4.tar.bz2 |
bsps: Move MPCI support to bsps
This patch is a part of the BSP source reorganization.
Update #3285.
Diffstat (limited to 'bsps/powerpc/qoriq')
-rw-r--r-- | bsps/powerpc/qoriq/mpci/intercom-mpci.c | 124 | ||||
-rw-r--r-- | bsps/powerpc/qoriq/mpci/intercom.c | 498 | ||||
-rw-r--r-- | bsps/powerpc/qoriq/mpci/lock.S | 52 |
3 files changed, 674 insertions, 0 deletions
diff --git a/bsps/powerpc/qoriq/mpci/intercom-mpci.c b/bsps/powerpc/qoriq/mpci/intercom-mpci.c new file mode 100644 index 0000000000..2cc45dd079 --- /dev/null +++ b/bsps/powerpc/qoriq/mpci/intercom-mpci.c @@ -0,0 +1,124 @@ +/** + * @file + * + * @ingroup QorIQInterCom + * + * @brief Inter-Processor Communication implementation. + */ + +/* + * Copyright (c) 2011 embedded brains GmbH. All rights reserved. + * + * embedded brains GmbH + * Obere Lagerstr. 30 + * 82178 Puchheim + * Germany + * <rtems@embedded-brains.de> + * + * 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 <assert.h> + +#include <libcpu/powerpc-utility.h> + +#include <bsp/intercom.h> + +#ifdef RTEMS_MULTIPROCESSING + +typedef struct { + intercom_packet *head; + intercom_packet *tail; +} mpic_fifo; + +static mpic_fifo fifo; + +static void mpci_service(intercom_packet *packet, void *arg) +{ + rtems_interrupt_level level; + + rtems_interrupt_disable(level); + packet->glue.next = NULL; + if (fifo.head != NULL) { + fifo.tail->glue.next = packet; + } else { + fifo.head = packet; + } + fifo.tail = packet; + rtems_interrupt_enable(level); + + rtems_multiprocessing_announce(); +} + +static void mpci_init(void) +{ + qoriq_intercom_service_install(INTERCOM_TYPE_MPCI, mpci_service, NULL); +} + +static intercom_packet *packet_of_prefix(rtems_packet_prefix *prefix) +{ + return (intercom_packet *) ((char *) prefix - sizeof(intercom_packet)); +} + +static rtems_packet_prefix *prefix_of_packet(intercom_packet *packet) +{ + return (rtems_packet_prefix *) packet->data; +} + +static void mpci_get_packet(rtems_packet_prefix **prefix_ptr) +{ + intercom_packet *packet = qoriq_intercom_allocate_packet( + INTERCOM_TYPE_MPCI, + INTERCOM_SIZE_512 + ); + *prefix_ptr = prefix_of_packet(packet); +} + +static void mpci_return_packet(rtems_packet_prefix *prefix) +{ + intercom_packet *packet = packet_of_prefix(prefix); + + qoriq_intercom_free_packet(packet); +} + +static void mpci_send_packet(uint32_t destination_node, rtems_packet_prefix *prefix) +{ + intercom_packet *packet = packet_of_prefix(prefix); + if (destination_node != MPCI_ALL_NODES) { + qoriq_intercom_send_packet((int) destination_node - 1, packet); + } else { + uint32_t self = ppc_processor_id(); + int other = self == 0 ? 1 : 0; + + qoriq_intercom_send_packet(other, packet); + } +} + +static void mpci_receive_packet(rtems_packet_prefix **prefix_ptr) +{ + rtems_interrupt_level level; + + rtems_interrupt_disable(level); + intercom_packet *packet = fifo.head; + if (packet != NULL) { + fifo.head = packet->glue.next; + *prefix_ptr = prefix_of_packet(packet); + } else { + *prefix_ptr = NULL; + } + rtems_interrupt_enable(level); +} + +rtems_mpci_table qoriq_intercom_mpci = { + .default_timeout = UINT32_MAX, + .maximum_packet_size = 512, + .initialization = mpci_init, + .get_packet = mpci_get_packet, + .return_packet = mpci_return_packet, + .send_packet = mpci_send_packet, + .receive_packet = mpci_receive_packet +}; + +#endif /* RTEMS_MULTIPROCESSING */ diff --git a/bsps/powerpc/qoriq/mpci/intercom.c b/bsps/powerpc/qoriq/mpci/intercom.c new file mode 100644 index 0000000000..e7e0d5650f --- /dev/null +++ b/bsps/powerpc/qoriq/mpci/intercom.c @@ -0,0 +1,498 @@ +/** + * @file + * + * @ingroup QorIQInterCom + * + * @brief Inter-Processor Communication implementation. + */ + +/* + * Copyright (c) 2011 embedded brains GmbH. All rights reserved. + * + * embedded brains GmbH + * Obere Lagerstr. 30 + * 82178 Puchheim + * Germany + * <rtems@embedded-brains.de> + * + * 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 <assert.h> +#include <string.h> + +#include <rtems.h> + +#include <libcpu/powerpc-utility.h> + +#include <bspopts.h> +#include <bsp/irq.h> +#include <bsp/qoriq.h> +#include <bsp/intercom.h> + +#ifndef QORIQ_IS_HYPERVISOR_GUEST + +#define INTERCOM_EVENT_IPI RTEMS_EVENT_13 +#define INTERCOM_EVENT_WAKE_UP RTEMS_EVENT_14 + +#define PACKET_SIZE_COUNT 4 + +#define ONE_CORE(core) (1U << (core)) +#define ALL_CORES ((1U << INTERCOM_CORE_COUNT) - 1U) +#define OTHER_CORES(core) (ALL_CORES & ~ONE_CORE(core)) + +#define IPI_INDEX 0 + +typedef struct consumer { + struct consumer *next; + rtems_id task; +} consumer; + +typedef struct { + consumer *head; + uint32_t cache_line_alignment [7]; +} consumer_list; + +typedef struct { + uint32_t lock; + intercom_packet *head; + size_t size; + uint32_t cores_to_notify; + uint32_t cache_line_alignment [4]; + consumer_list waiting_consumers [INTERCOM_CORE_COUNT]; +} free_list; + +typedef struct { + uint32_t lock; + intercom_packet *head; + intercom_packet *tail; + uint32_t cache_line_alignment [5]; +} core_fifo; + +typedef struct { + free_list free_lists [PACKET_SIZE_COUNT]; + core_fifo core_fifos [INTERCOM_CORE_COUNT]; + intercom_service services [INTERCOM_CORE_COUNT][INTERCOM_SERVICE_COUNT]; + void *service_args [INTERCOM_CORE_COUNT][INTERCOM_SERVICE_COUNT]; + uint32_t ready_lock; + uint32_t ready; + uint32_t cache_line_alignment [6]; +} control; + +static control *const intercom = (control *) QORIQ_INTERCOM_AREA_BEGIN; + +static const size_t packet_sizes [PACKET_SIZE_COUNT] = { + 64, + 512, + 2048, + 4096 +}; + +static void send_event(rtems_id task, rtems_event_set event) +{ + rtems_status_code sc = RTEMS_SUCCESSFUL; + + sc = rtems_event_send(task, event); + assert(sc == RTEMS_SUCCESSFUL); +} + +static void wait_for_event(rtems_event_set in) +{ + rtems_status_code sc = RTEMS_SUCCESSFUL; + rtems_event_set out; + + sc = rtems_event_receive( + in, + RTEMS_EVENT_ALL | RTEMS_WAIT, + RTEMS_NO_TIMEOUT, + &out + ); + assert(sc == RTEMS_SUCCESSFUL); +} + +static void intercom_handler(void *arg) +{ + rtems_id task = (rtems_id) (uintptr_t) arg; + send_event(task, INTERCOM_EVENT_IPI); +} + +static void notify_core_by_index(int core) +{ + uint32_t self = ppc_processor_id(); + qoriq.pic.per_cpu [self].ipidr [IPI_INDEX].reg = ONE_CORE(core); +} + +static void notify_cores(uint32_t cores) +{ + uint32_t self = ppc_processor_id(); + qoriq.pic.per_cpu [self].ipidr [IPI_INDEX].reg = cores; +} + +void qoriq_intercom_free_packet(intercom_packet *packet) +{ + free_list *list = &intercom->free_lists [packet->size_index]; + + uint32_t msr = qoriq_spin_lock(&list->lock); + intercom_packet *first = list->head; + list->head = packet; + packet->glue.next = first; + uint32_t cores = list->cores_to_notify; + if (cores != 0) { + list->cores_to_notify = 0; + notify_cores(cores); + } + qoriq_spin_unlock(&list->lock, msr); +} + +static void default_service(intercom_packet *packet, void *arg) +{ + qoriq_intercom_free_packet(packet); +} + +static void process_free_lists(free_list *free_lists, uint32_t self) +{ + int i = 0; + + for (i = 0; i < PACKET_SIZE_COUNT; ++i) { + free_list *list = &free_lists [i]; + + uint32_t msr = qoriq_spin_lock(&list->lock); + consumer *waiting_consumer = list->waiting_consumers [self].head; + list->waiting_consumers [self].head = NULL; + qoriq_spin_unlock(&list->lock, msr); + + while (waiting_consumer != NULL) { + send_event(waiting_consumer->task, INTERCOM_EVENT_WAKE_UP); + waiting_consumer = waiting_consumer->next; + } + } +} + +static void process_core_fifo(core_fifo *fifo, intercom_service *services, void **service_args) +{ + uint32_t msr = qoriq_spin_lock(&fifo->lock); + intercom_packet *packet = fifo->head; + fifo->head = NULL; + qoriq_spin_unlock(&fifo->lock, msr); + + while (packet != NULL) { + intercom_packet *current = packet; + intercom_type type_index = current->type_index; + packet = current->glue.next; + (*services [type_index])(current, service_args [type_index]); + } +} + +static void intercom_task(rtems_task_argument arg) +{ + uint32_t self = ppc_processor_id(); + free_list *free_lists = &intercom->free_lists [0]; + intercom_service *services = &intercom->services [self][0]; + void **service_args = &intercom->service_args [self][0]; + core_fifo *fifo = &intercom->core_fifos [self]; + + while (true) { + process_free_lists(free_lists, self); + process_core_fifo(fifo, services, service_args); + wait_for_event(INTERCOM_EVENT_IPI); + } +} + +static intercom_packet *free_list_and_packet_init( + free_list *list, + size_t count, + intercom_packet *current, + intercom_size size_index, + size_t size +) +{ + intercom_packet *last = current; + size_t inc = 1 + size / sizeof(*current); + size_t i = 0; + + assert(count > 0); + assert(size % sizeof(*current) == 0); + + list->size = size; + list->head = current; + for (i = 0; i < count; ++i) { + intercom_packet *next = current + inc; + current->glue.next = next; + current->size_index = size_index; + last = current; + current = next; + } + last->glue.next = NULL; + + return current; +} + +static void basic_init(void) +{ + char *begin = (char *) QORIQ_INTERCOM_AREA_BEGIN; + size_t size = QORIQ_INTERCOM_AREA_SIZE; + int i = 0; + + memset(begin, 0, size); + + assert(size % packet_sizes [PACKET_SIZE_COUNT - 1] == 0); + + /* Calculate data area sizes */ + size_t data_sizes [PACKET_SIZE_COUNT]; + data_sizes [PACKET_SIZE_COUNT - 1] = size / 2; + for (i = PACKET_SIZE_COUNT - 2; i > 0; --i) { + data_sizes [i] = data_sizes [i + 1] / 2; + } + data_sizes [i] = data_sizes [i + 1]; + + /* Calculate packet counts */ + size_t packet_counts [PACKET_SIZE_COUNT]; + size_t count = 0; + for (i = 1; i < PACKET_SIZE_COUNT; ++i) { + packet_counts [i] = data_sizes [i] / packet_sizes [i]; + count += packet_counts [i]; + } + packet_counts [0] = (data_sizes [0] - sizeof(control) - count * sizeof(intercom_packet)) + / (sizeof(intercom_packet) + packet_sizes [0]); + + /* Initialize free lists and packets */ + intercom_packet *packet = (intercom_packet *) (begin + sizeof(control)); + for (i = 0; i < PACKET_SIZE_COUNT; ++i) { + packet = free_list_and_packet_init( + &intercom->free_lists [i], + packet_counts [i], + packet, + i, + packet_sizes [i] + ); + } + + rtems_cache_flush_multiple_data_lines(begin, size); + ppc_synchronize_data(); +} + +static void services_init(uint32_t self) +{ + int i = 0; + + for (i = 0; i < INTERCOM_SERVICE_COUNT; ++i) { + if (intercom->services [self][i] == NULL) { + intercom->services [self][i] = default_service; + } + } +} + +void qoriq_intercom_init(void) +{ + rtems_status_code sc = RTEMS_SUCCESSFUL; + rtems_id task = RTEMS_ID_NONE; + uint32_t self = ppc_processor_id(); + + sc = rtems_task_create( + rtems_build_name('I', 'C', 'O', 'M'), + 10, + 0, + RTEMS_DEFAULT_MODES, + RTEMS_DEFAULT_ATTRIBUTES, + &task + ); + assert(sc == RTEMS_SUCCESSFUL); + + sc = qoriq_pic_set_priority( + QORIQ_IRQ_IPI_0, + QORIQ_PIC_PRIORITY_LOWEST, + NULL + ); + assert(sc == RTEMS_SUCCESSFUL); + + sc = rtems_interrupt_handler_install( + QORIQ_IRQ_IPI_0, + "INTERCOM", + RTEMS_INTERRUPT_UNIQUE, + intercom_handler, + (void *) (uintptr_t) task + ); + assert(sc == RTEMS_SUCCESSFUL); + + if (self == 0) { + basic_init(); + } + + services_init(self); + + sc = rtems_task_start(task, intercom_task, 0); + assert(sc == RTEMS_SUCCESSFUL); +} + +void qoriq_intercom_start(void) +{ + uint32_t self = ppc_processor_id(); + uint32_t ready = 0; + + while (ready != ALL_CORES) { + uint32_t msr = qoriq_spin_lock(&intercom->ready_lock); + ready = intercom->ready; + intercom->ready = ready | ONE_CORE(self); + qoriq_spin_unlock(&intercom->ready_lock, msr); + } +} + +static intercom_packet *allocate(intercom_type type, free_list *list) +{ + uint32_t self = ppc_processor_id(); + intercom_packet *packet = NULL; + consumer poor = { + .task = rtems_task_self() + }; + + while (packet == NULL) { + uint32_t msr = qoriq_spin_lock(&list->lock); + packet = list->head; + if (packet != NULL) { + list->head = packet->glue.next; + } else { + consumer *first = list->waiting_consumers [self].head; + list->waiting_consumers [self].head = &poor; + poor.next = first; + if (first == NULL) { + list->cores_to_notify |= ONE_CORE(self); + } + } + qoriq_spin_unlock(&list->lock, msr); + + if (packet == NULL) { + wait_for_event(INTERCOM_EVENT_WAKE_UP); + } + } + + packet->glue.next = NULL; + packet->type_index = type; + packet->flags = 0; + packet->size = list->size; + + return packet; +} + +intercom_packet *qoriq_intercom_allocate_packet(intercom_type type, intercom_size size) +{ + assert((unsigned) type < INTERCOM_SERVICE_COUNT); + assert((unsigned) size < PACKET_SIZE_COUNT); + + return allocate(type, &intercom->free_lists [size]); +} + +void qoriq_intercom_send_packets(int destination_core, intercom_packet *first, intercom_packet *last) +{ + assert(destination_core >= 0); + assert(destination_core < INTERCOM_CORE_COUNT); + + core_fifo *fifo = &intercom->core_fifos [destination_core]; + + uint32_t msr = qoriq_spin_lock(&fifo->lock); + last->glue.next = NULL; + if (fifo->head != NULL) { + fifo->tail->glue.next = first; + } else { + fifo->head = first; + notify_core_by_index(destination_core); + } + fifo->tail = last; + qoriq_spin_unlock(&fifo->lock, msr); +} + +void qoriq_intercom_broadcast_packets(intercom_packet *first, intercom_packet *last) +{ + int i = 0; + + for (i = 1; i < INTERCOM_CORE_COUNT; ++i) { + intercom_packet *clone_first = NULL; + intercom_packet *clone_last = NULL; + + intercom_packet *current = first; + while (current != NULL) { + intercom_packet *clone = qoriq_intercom_clone_packet(current); + if (clone_first == NULL) { + clone_first = clone; + } + if (clone_last != NULL) { + clone_last->glue.next = clone; + } + clone_last = clone; + current = current->glue.next; + } + + qoriq_intercom_send_packets(i, clone_first, clone_last); + } + + qoriq_intercom_send_packets(0, first, last); +} + +void qoriq_intercom_send(int destination_core, intercom_type type, intercom_size size, const void *buf, size_t n) +{ + assert((unsigned) size < PACKET_SIZE_COUNT); + + size_t remaining = n; + size_t packet_size = packet_sizes [size]; + const char *src = buf; + intercom_packet *first = NULL; + intercom_packet *last = NULL; + + do { + intercom_packet *packet = qoriq_intercom_allocate_packet( + type, + size + ); + if (first == NULL) { + first = packet; + } + if (last != NULL) { + last->glue.next = packet; + } + last = packet; + size_t current_size = remaining < packet_size ? remaining : packet_size; + remaining -= current_size; + packet->size = current_size; + const char *current = src; + src += current_size; + memcpy(packet->data, current, current_size); + } while (remaining > 0); + + qoriq_intercom_send_packets(destination_core, first, last); +} + +void qoriq_intercom_service_install(intercom_type type, intercom_service service, void *arg) +{ + assert((unsigned) type < INTERCOM_SERVICE_COUNT); + + uint32_t self = ppc_processor_id(); + intercom->service_args [self][type] = arg; + ppc_enforce_in_order_execution_of_io(); + intercom->services [self][type] = service; +} + +void qoriq_intercom_service_remove(intercom_type type) +{ + assert((unsigned) type < INTERCOM_SERVICE_COUNT); + + uint32_t self = ppc_processor_id(); + intercom->services [self][type] = default_service; + ppc_enforce_in_order_execution_of_io(); + intercom->service_args [self][type] = NULL; +} + +intercom_packet *qoriq_intercom_clone_packet(const intercom_packet *packet) +{ + intercom_packet *clone = qoriq_intercom_allocate_packet( + packet->type_index, + packet->size_index + ); + + clone->size = packet->size; + memcpy(clone->data, packet->data, clone->size); + + return clone; +} + +#endif /* !QORIQ_IS_HYPERVISOR_GUEST */ diff --git a/bsps/powerpc/qoriq/mpci/lock.S b/bsps/powerpc/qoriq/mpci/lock.S new file mode 100644 index 0000000000..c6e72a7533 --- /dev/null +++ b/bsps/powerpc/qoriq/mpci/lock.S @@ -0,0 +1,52 @@ +/** + * @file + * + * @ingroup QorIQInterCom + * + * @brief qoriq_spin_lock() and qoriq_spin_unlock() implementation. + */ + +/* + * Copyright (c) 2011 embedded brains GmbH. All rights reserved. + * + * embedded brains GmbH + * Obere Lagerstr. 30 + * 82178 Puchheim + * Germany + * <rtems@embedded-brains.de> + * + * 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 <libcpu/powerpc-utility.h> + + .global qoriq_spin_lock + .global qoriq_spin_unlock + +qoriq_spin_lock: + li r0, 1 + mfmsr r4 + GET_INTERRUPT_MASK r5 + andc r5, r4, r5 + b 2f +1: + mtmsr r4 +2: + lwarx r6, r0, r3 + cmpwi r6, 0 + bne 2b + mtmsr r5 + stwcx. r0, r0, r3 + bne 1b + isync + mr r3, r4 + blr + +qoriq_spin_unlock: + msync + li r0, 0 + stw r0, 0(r3) + mtmsr r4 + blr |