Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:23:57

0001 /* SPDX-License-Identifier: BSD-2-Clause */
0002 
0003 /**
0004  * @file
0005  *
0006  * @ingroup RTEMSBSPsPowerPCQorIQInterCom
0007  *
0008  * @brief Inter-Processor Communication implementation.
0009  */
0010 
0011 /*
0012  * Copyright (c) 2011 embedded brains GmbH & Co. KG
0013  *
0014  * Redistribution and use in source and binary forms, with or without
0015  * modification, are permitted provided that the following conditions
0016  * are met:
0017  * 1. Redistributions of source code must retain the above copyright
0018  *    notice, this list of conditions and the following disclaimer.
0019  * 2. Redistributions in binary form must reproduce the above copyright
0020  *    notice, this list of conditions and the following disclaimer in the
0021  *    documentation and/or other materials provided with the distribution.
0022  *
0023  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
0024  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0025  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0026  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
0027  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
0028  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
0029  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
0030  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
0031  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
0032  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
0033  * POSSIBILITY OF SUCH DAMAGE.
0034  */
0035 
0036 #include <assert.h>
0037 #include <string.h>
0038 
0039 #include <rtems.h>
0040 
0041 #include <libcpu/powerpc-utility.h>
0042 
0043 #include <bspopts.h>
0044 #include <bsp/irq.h>
0045 #include <bsp/qoriq.h>
0046 #include <bsp/intercom.h>
0047 
0048 #ifndef QORIQ_IS_HYPERVISOR_GUEST
0049 
0050 #define INTERCOM_EVENT_IPI RTEMS_EVENT_13
0051 #define INTERCOM_EVENT_WAKE_UP RTEMS_EVENT_14
0052 
0053 #define PACKET_SIZE_COUNT 4
0054 
0055 #define ONE_CORE(core) (1U << (core))
0056 #define ALL_CORES ((1U << INTERCOM_CORE_COUNT) - 1U)
0057 #define OTHER_CORES(core) (ALL_CORES & ~ONE_CORE(core))
0058 
0059 #define IPI_INDEX 0
0060 
0061 typedef struct consumer {
0062     struct consumer *next;
0063     rtems_id task;
0064 } consumer;
0065 
0066 typedef struct {
0067     consumer *head;
0068     uint32_t cache_line_alignment [7];
0069 } consumer_list;
0070 
0071 typedef struct {
0072     uint32_t lock;
0073     intercom_packet *head;
0074     size_t size;
0075     uint32_t cores_to_notify;
0076     uint32_t cache_line_alignment [4];
0077     consumer_list waiting_consumers [INTERCOM_CORE_COUNT];
0078 } free_list;
0079 
0080 typedef struct {
0081     uint32_t lock;
0082     intercom_packet *head;
0083     intercom_packet *tail;
0084     uint32_t cache_line_alignment [5];
0085 } core_fifo;
0086 
0087 typedef struct {
0088     free_list free_lists [PACKET_SIZE_COUNT];
0089     core_fifo core_fifos [INTERCOM_CORE_COUNT];
0090     intercom_service services [INTERCOM_CORE_COUNT][INTERCOM_SERVICE_COUNT];
0091     void *service_args [INTERCOM_CORE_COUNT][INTERCOM_SERVICE_COUNT];
0092     uint32_t ready_lock;
0093     uint32_t ready;
0094     uint32_t cache_line_alignment [6];
0095 } control;
0096 
0097 static control *const intercom = (control *) QORIQ_INTERCOM_AREA_BEGIN;
0098 
0099 static const size_t packet_sizes [PACKET_SIZE_COUNT] = {
0100     64,
0101     512,
0102     2048,
0103     4096
0104 };
0105 
0106 static void send_event(rtems_id task, rtems_event_set event)
0107 {
0108     rtems_status_code sc = RTEMS_SUCCESSFUL;
0109 
0110     sc = rtems_event_send(task, event);
0111     assert(sc == RTEMS_SUCCESSFUL);
0112 }
0113 
0114 static void wait_for_event(rtems_event_set in)
0115 {
0116     rtems_status_code sc = RTEMS_SUCCESSFUL;
0117     rtems_event_set out;
0118 
0119     sc = rtems_event_receive(
0120         in,
0121         RTEMS_EVENT_ALL | RTEMS_WAIT,
0122         RTEMS_NO_TIMEOUT,
0123         &out
0124     );
0125     assert(sc == RTEMS_SUCCESSFUL);
0126 }
0127 
0128 static void intercom_handler(void *arg)
0129 {
0130     rtems_id task = (rtems_id) (uintptr_t) arg;
0131     send_event(task, INTERCOM_EVENT_IPI);
0132 }
0133 
0134 static void notify_core_by_index(int core)
0135 {
0136     uint32_t self = ppc_processor_id();
0137     qoriq.pic.per_cpu [self].ipidr [IPI_INDEX].reg = ONE_CORE(core);
0138 }
0139 
0140 static void notify_cores(uint32_t cores)
0141 {
0142     uint32_t self = ppc_processor_id();
0143     qoriq.pic.per_cpu [self].ipidr [IPI_INDEX].reg = cores;
0144 }
0145 
0146 void qoriq_intercom_free_packet(intercom_packet *packet)
0147 {
0148     free_list *list = &intercom->free_lists [packet->size_index];
0149 
0150     uint32_t msr = qoriq_spin_lock(&list->lock);
0151     intercom_packet *first = list->head;
0152     list->head = packet;
0153     packet->glue.next = first;
0154     uint32_t cores = list->cores_to_notify;
0155     if (cores != 0) {
0156         list->cores_to_notify = 0;
0157         notify_cores(cores);
0158     }
0159     qoriq_spin_unlock(&list->lock, msr);
0160 }
0161 
0162 static void default_service(intercom_packet *packet, void *arg)
0163 {
0164     qoriq_intercom_free_packet(packet);
0165 }
0166 
0167 static void process_free_lists(free_list *free_lists, uint32_t self)
0168 {
0169     int i = 0;
0170 
0171     for (i = 0; i < PACKET_SIZE_COUNT; ++i) {
0172         free_list *list = &free_lists [i];
0173 
0174         uint32_t msr = qoriq_spin_lock(&list->lock);
0175         consumer *waiting_consumer = list->waiting_consumers [self].head;
0176         list->waiting_consumers [self].head = NULL;
0177         qoriq_spin_unlock(&list->lock, msr);
0178 
0179         while (waiting_consumer != NULL) {
0180             send_event(waiting_consumer->task, INTERCOM_EVENT_WAKE_UP);
0181             waiting_consumer = waiting_consumer->next;
0182         }
0183     }
0184 }
0185 
0186 static void process_core_fifo(core_fifo *fifo, intercom_service *services, void **service_args)
0187 {
0188     uint32_t msr = qoriq_spin_lock(&fifo->lock);
0189     intercom_packet *packet = fifo->head;
0190     fifo->head = NULL;
0191     qoriq_spin_unlock(&fifo->lock, msr);
0192 
0193     while (packet != NULL) {
0194         intercom_packet *current = packet;
0195         intercom_type type_index = current->type_index;
0196         packet = current->glue.next;
0197         (*services [type_index])(current, service_args [type_index]);
0198     }
0199 }
0200 
0201 static void intercom_task(rtems_task_argument arg)
0202 {
0203     uint32_t self = ppc_processor_id();
0204     free_list *free_lists = &intercom->free_lists [0];
0205     intercom_service *services = &intercom->services [self][0];
0206     void **service_args = &intercom->service_args [self][0];
0207     core_fifo *fifo = &intercom->core_fifos [self];
0208 
0209     while (true) {
0210         process_free_lists(free_lists, self);
0211         process_core_fifo(fifo, services, service_args);
0212         wait_for_event(INTERCOM_EVENT_IPI);
0213     }
0214 }
0215 
0216 static intercom_packet *free_list_and_packet_init(
0217     free_list *list,
0218     size_t count,
0219     intercom_packet *current,
0220     intercom_size size_index,
0221     size_t size
0222 )
0223 {
0224     intercom_packet *last = current;
0225     size_t inc = 1 + size / sizeof(*current);
0226     size_t i = 0;
0227 
0228     assert(count > 0);
0229     assert(size % sizeof(*current) == 0);
0230 
0231     list->size = size;
0232     list->head = current;
0233     for (i = 0; i < count; ++i) {
0234         intercom_packet *next = current + inc;
0235         current->glue.next = next;
0236         current->size_index = size_index;
0237         last = current;
0238         current = next;
0239     }
0240     last->glue.next = NULL;
0241 
0242     return current;
0243 }
0244 
0245 static void basic_init(void)
0246 {
0247     char *begin = (char *) QORIQ_INTERCOM_AREA_BEGIN;
0248     size_t size = QORIQ_INTERCOM_AREA_SIZE;
0249     int i = 0;
0250 
0251     memset(begin, 0, size);
0252 
0253     assert(size % packet_sizes [PACKET_SIZE_COUNT - 1] == 0);
0254 
0255     /* Calculate data area sizes */
0256     size_t data_sizes [PACKET_SIZE_COUNT];
0257     data_sizes [PACKET_SIZE_COUNT - 1] = size / 2;
0258     for (i = PACKET_SIZE_COUNT - 2; i > 0; --i) {
0259         data_sizes [i] = data_sizes [i + 1] / 2;
0260     }
0261     data_sizes [i] = data_sizes [i + 1];
0262 
0263     /* Calculate packet counts */
0264     size_t packet_counts [PACKET_SIZE_COUNT];
0265     size_t count = 0;
0266     for (i = 1; i < PACKET_SIZE_COUNT; ++i) {
0267         packet_counts [i] = data_sizes [i] / packet_sizes [i];
0268         count += packet_counts [i];
0269     }
0270     packet_counts [0] = (data_sizes [0] - sizeof(control) - count * sizeof(intercom_packet))
0271         / (sizeof(intercom_packet) + packet_sizes [0]);
0272 
0273     /* Initialize free lists and packets */
0274     intercom_packet *packet = (intercom_packet *) (begin + sizeof(control));
0275     for (i = 0; i < PACKET_SIZE_COUNT; ++i) {
0276         packet = free_list_and_packet_init(
0277             &intercom->free_lists [i],
0278             packet_counts [i],
0279             packet,
0280             i,
0281             packet_sizes [i]
0282         );
0283     }
0284 
0285     rtems_cache_flush_multiple_data_lines(begin, size);
0286     ppc_synchronize_data();
0287 }
0288 
0289 static void services_init(uint32_t self)
0290 {
0291     int i = 0;
0292 
0293     for (i = 0; i < INTERCOM_SERVICE_COUNT; ++i) {
0294         if (intercom->services [self][i] == NULL) {
0295             intercom->services [self][i] = default_service;
0296         }
0297     }
0298 }
0299 
0300 void qoriq_intercom_init(void)
0301 {
0302     rtems_status_code sc = RTEMS_SUCCESSFUL;
0303     rtems_id task = RTEMS_ID_NONE;
0304     uint32_t self = ppc_processor_id();
0305 
0306     sc = rtems_task_create(
0307         rtems_build_name('I', 'C', 'O', 'M'),
0308         10,
0309         0,
0310         RTEMS_DEFAULT_MODES,
0311         RTEMS_DEFAULT_ATTRIBUTES,
0312         &task
0313     );
0314     assert(sc == RTEMS_SUCCESSFUL);
0315 
0316     sc = rtems_interrupt_set_priority(
0317         QORIQ_IRQ_IPI_0,
0318         QORIQ_PIC_PRIORITY_LOWEST
0319     );
0320     assert(sc == RTEMS_SUCCESSFUL);
0321 
0322     sc = rtems_interrupt_handler_install(
0323         QORIQ_IRQ_IPI_0,
0324         "INTERCOM",
0325         RTEMS_INTERRUPT_UNIQUE,
0326         intercom_handler,
0327         (void *) (uintptr_t) task
0328     );
0329     assert(sc == RTEMS_SUCCESSFUL);
0330 
0331     if (self == 0) {
0332         basic_init();
0333     }
0334 
0335     services_init(self);
0336 
0337     sc = rtems_task_start(task, intercom_task, 0);
0338     assert(sc == RTEMS_SUCCESSFUL);
0339 }
0340 
0341 void qoriq_intercom_start(void)
0342 {
0343     uint32_t self = ppc_processor_id();
0344     uint32_t ready = 0;
0345 
0346     while (ready != ALL_CORES) {
0347         uint32_t msr = qoriq_spin_lock(&intercom->ready_lock);
0348         ready = intercom->ready;
0349         intercom->ready = ready | ONE_CORE(self);
0350         qoriq_spin_unlock(&intercom->ready_lock, msr);
0351     }
0352 }
0353 
0354 static intercom_packet *allocate(intercom_type type, free_list *list)
0355 {
0356     uint32_t self = ppc_processor_id();
0357     intercom_packet *packet = NULL;
0358     consumer poor = {
0359         .task = rtems_task_self()
0360     };
0361 
0362     while (packet == NULL) {
0363         uint32_t msr = qoriq_spin_lock(&list->lock);
0364         packet = list->head;
0365         if (packet != NULL) {
0366             list->head = packet->glue.next;
0367         } else {
0368             consumer *first = list->waiting_consumers [self].head;
0369             list->waiting_consumers [self].head = &poor;
0370             poor.next = first;
0371             if (first == NULL) {
0372                 list->cores_to_notify |= ONE_CORE(self);
0373             }
0374         }
0375         qoriq_spin_unlock(&list->lock, msr);
0376 
0377         if (packet == NULL) {
0378             wait_for_event(INTERCOM_EVENT_WAKE_UP);
0379         }
0380     }
0381 
0382     packet->glue.next = NULL;
0383     packet->type_index = type;
0384     packet->flags = 0;
0385     packet->size = list->size;
0386 
0387     return packet;
0388 }
0389 
0390 intercom_packet *qoriq_intercom_allocate_packet(intercom_type type, intercom_size size)
0391 {
0392     assert((unsigned) type < INTERCOM_SERVICE_COUNT);
0393     assert((unsigned) size < PACKET_SIZE_COUNT);
0394 
0395     return allocate(type, &intercom->free_lists [size]);
0396 }
0397 
0398 void qoriq_intercom_send_packets(int destination_core, intercom_packet *first, intercom_packet *last)
0399 {
0400     assert(destination_core >= 0);
0401     assert(destination_core < INTERCOM_CORE_COUNT);
0402 
0403     core_fifo *fifo = &intercom->core_fifos [destination_core];
0404 
0405     uint32_t msr = qoriq_spin_lock(&fifo->lock);
0406     last->glue.next = NULL;
0407     if (fifo->head != NULL) {
0408         fifo->tail->glue.next = first;
0409     } else {
0410         fifo->head = first;
0411         notify_core_by_index(destination_core);
0412     }
0413     fifo->tail = last;
0414     qoriq_spin_unlock(&fifo->lock, msr);
0415 }
0416 
0417 void qoriq_intercom_broadcast_packets(intercom_packet *first, intercom_packet *last)
0418 {
0419     int i = 0;
0420 
0421     for (i = 1; i < INTERCOM_CORE_COUNT; ++i) {
0422         intercom_packet *clone_first = NULL;
0423         intercom_packet *clone_last = NULL;
0424 
0425         intercom_packet *current = first;
0426         while (current != NULL) {
0427             intercom_packet *clone = qoriq_intercom_clone_packet(current);
0428             if (clone_first == NULL) {
0429                 clone_first = clone;
0430             }
0431             if (clone_last != NULL) {
0432                 clone_last->glue.next = clone;
0433             }
0434             clone_last = clone;
0435             current = current->glue.next;
0436         }
0437 
0438         qoriq_intercom_send_packets(i, clone_first, clone_last);
0439     }
0440 
0441     qoriq_intercom_send_packets(0, first, last);
0442 }
0443 
0444 void qoriq_intercom_send(int destination_core, intercom_type type, intercom_size size, const void *buf, size_t n)
0445 {
0446     assert((unsigned) size < PACKET_SIZE_COUNT);
0447 
0448     size_t remaining = n;
0449     size_t packet_size = packet_sizes [size];
0450     const char *src = buf;
0451     intercom_packet *first = NULL;
0452     intercom_packet *last = NULL;
0453 
0454     do {
0455         intercom_packet *packet = qoriq_intercom_allocate_packet(
0456             type,
0457             size
0458         );
0459         if (first == NULL) {
0460             first = packet;
0461         }
0462         if (last != NULL) {
0463             last->glue.next = packet;
0464         }
0465         last = packet;
0466         size_t current_size = remaining < packet_size ? remaining : packet_size;
0467         remaining -= current_size;
0468         packet->size = current_size;
0469         const char *current = src;
0470         src += current_size;
0471         memcpy(packet->data, current, current_size);
0472     } while (remaining > 0);
0473 
0474     qoriq_intercom_send_packets(destination_core, first, last);
0475 }
0476 
0477 void qoriq_intercom_service_install(intercom_type type, intercom_service service, void *arg)
0478 {
0479     assert((unsigned) type < INTERCOM_SERVICE_COUNT);
0480 
0481     uint32_t self = ppc_processor_id();
0482     intercom->service_args [self][type] = arg;
0483     ppc_enforce_in_order_execution_of_io();
0484     intercom->services [self][type] = service;
0485 }
0486 
0487 void qoriq_intercom_service_remove(intercom_type type)
0488 {
0489     assert((unsigned) type < INTERCOM_SERVICE_COUNT);
0490 
0491     uint32_t self = ppc_processor_id();
0492     intercom->services [self][type] = default_service;
0493     ppc_enforce_in_order_execution_of_io();
0494     intercom->service_args [self][type] = NULL;
0495 }
0496 
0497 intercom_packet *qoriq_intercom_clone_packet(const intercom_packet *packet)
0498 {
0499     intercom_packet *clone = qoriq_intercom_allocate_packet(
0500         packet->type_index,
0501         packet->size_index
0502     );
0503 
0504     clone->size = packet->size;
0505     memcpy(clone->data, packet->data, clone->size);
0506 
0507     return clone;
0508 }
0509 
0510 #endif /* !QORIQ_IS_HYPERVISOR_GUEST */