source: rtems/bsps/powerpc/qoriq/mpci/intercom.c @ e560ee85

Last change on this file since e560ee85 was e560ee85, checked in by Joel Sherrill <joel@…>, on 03/01/22 at 21:38:55

bsps/powerpc/: Scripted embedded brains header file clean up

Updates #4625.

  • Property mode set to 100644
File size: 11.5 KB
Line 
1/**
2 * @file
3 *
4 * @ingroup RTEMSBSPsPowerPCQorIQInterCom
5 *
6 * @brief Inter-Processor Communication implementation.
7 */
8
9/*
10 * Copyright (c) 2011 embedded brains GmbH.  All rights reserved.
11 *
12 * The license and distribution terms for this file may be
13 * found in the file LICENSE in this distribution or at
14 * http://www.rtems.org/license/LICENSE.
15 */
16
17#include <assert.h>
18#include <string.h>
19
20#include <rtems.h>
21
22#include <libcpu/powerpc-utility.h>
23
24#include <bspopts.h>
25#include <bsp/irq.h>
26#include <bsp/qoriq.h>
27#include <bsp/intercom.h>
28
29#ifndef QORIQ_IS_HYPERVISOR_GUEST
30
31#define INTERCOM_EVENT_IPI RTEMS_EVENT_13
32#define INTERCOM_EVENT_WAKE_UP RTEMS_EVENT_14
33
34#define PACKET_SIZE_COUNT 4
35
36#define ONE_CORE(core) (1U << (core))
37#define ALL_CORES ((1U << INTERCOM_CORE_COUNT) - 1U)
38#define OTHER_CORES(core) (ALL_CORES & ~ONE_CORE(core))
39
40#define IPI_INDEX 0
41
42typedef struct consumer {
43        struct consumer *next;
44        rtems_id task;
45} consumer;
46
47typedef struct {
48        consumer *head;
49        uint32_t cache_line_alignment [7];
50} consumer_list;
51
52typedef struct {
53        uint32_t lock;
54        intercom_packet *head;
55        size_t size;
56        uint32_t cores_to_notify;
57        uint32_t cache_line_alignment [4];
58        consumer_list waiting_consumers [INTERCOM_CORE_COUNT];
59} free_list;
60
61typedef struct {
62        uint32_t lock;
63        intercom_packet *head;
64        intercom_packet *tail;
65        uint32_t cache_line_alignment [5];
66} core_fifo;
67
68typedef struct {
69        free_list free_lists [PACKET_SIZE_COUNT];
70        core_fifo core_fifos [INTERCOM_CORE_COUNT];
71        intercom_service services [INTERCOM_CORE_COUNT][INTERCOM_SERVICE_COUNT];
72        void *service_args [INTERCOM_CORE_COUNT][INTERCOM_SERVICE_COUNT];
73        uint32_t ready_lock;
74        uint32_t ready;
75        uint32_t cache_line_alignment [6];
76} control;
77
78static control *const intercom = (control *) QORIQ_INTERCOM_AREA_BEGIN;
79
80static const size_t packet_sizes [PACKET_SIZE_COUNT] = {
81        64,
82        512,
83        2048,
84        4096
85};
86
87static void send_event(rtems_id task, rtems_event_set event)
88{
89        rtems_status_code sc = RTEMS_SUCCESSFUL;
90
91        sc = rtems_event_send(task, event);
92        assert(sc == RTEMS_SUCCESSFUL);
93}
94
95static void wait_for_event(rtems_event_set in)
96{
97        rtems_status_code sc = RTEMS_SUCCESSFUL;
98        rtems_event_set out;
99
100        sc = rtems_event_receive(
101                in,
102                RTEMS_EVENT_ALL | RTEMS_WAIT,
103                RTEMS_NO_TIMEOUT,
104                &out
105        );
106        assert(sc == RTEMS_SUCCESSFUL);
107}
108
109static void intercom_handler(void *arg)
110{
111        rtems_id task = (rtems_id) (uintptr_t) arg;
112        send_event(task, INTERCOM_EVENT_IPI);
113}
114
115static void notify_core_by_index(int core)
116{
117        uint32_t self = ppc_processor_id();
118        qoriq.pic.per_cpu [self].ipidr [IPI_INDEX].reg = ONE_CORE(core);
119}
120
121static void notify_cores(uint32_t cores)
122{
123        uint32_t self = ppc_processor_id();
124        qoriq.pic.per_cpu [self].ipidr [IPI_INDEX].reg = cores;
125}
126
127void qoriq_intercom_free_packet(intercom_packet *packet)
128{
129        free_list *list = &intercom->free_lists [packet->size_index];
130
131        uint32_t msr = qoriq_spin_lock(&list->lock);
132        intercom_packet *first = list->head;
133        list->head = packet;
134        packet->glue.next = first;
135        uint32_t cores = list->cores_to_notify;
136        if (cores != 0) {
137                list->cores_to_notify = 0;
138                notify_cores(cores);
139        }
140        qoriq_spin_unlock(&list->lock, msr);
141}
142
143static void default_service(intercom_packet *packet, void *arg)
144{
145        qoriq_intercom_free_packet(packet);
146}
147
148static void process_free_lists(free_list *free_lists, uint32_t self)
149{
150        int i = 0;
151
152        for (i = 0; i < PACKET_SIZE_COUNT; ++i) {
153                free_list *list = &free_lists [i];
154
155                uint32_t msr = qoriq_spin_lock(&list->lock);
156                consumer *waiting_consumer = list->waiting_consumers [self].head;
157                list->waiting_consumers [self].head = NULL;
158                qoriq_spin_unlock(&list->lock, msr);
159
160                while (waiting_consumer != NULL) {
161                        send_event(waiting_consumer->task, INTERCOM_EVENT_WAKE_UP);
162                        waiting_consumer = waiting_consumer->next;
163                }
164        }
165}
166
167static void process_core_fifo(core_fifo *fifo, intercom_service *services, void **service_args)
168{
169        uint32_t msr = qoriq_spin_lock(&fifo->lock);
170        intercom_packet *packet = fifo->head;
171        fifo->head = NULL;
172        qoriq_spin_unlock(&fifo->lock, msr);
173
174        while (packet != NULL) {
175                intercom_packet *current = packet;
176                intercom_type type_index = current->type_index;
177                packet = current->glue.next;
178                (*services [type_index])(current, service_args [type_index]);
179        }
180}
181
182static void intercom_task(rtems_task_argument arg)
183{
184        uint32_t self = ppc_processor_id();
185        free_list *free_lists = &intercom->free_lists [0];
186        intercom_service *services = &intercom->services [self][0];
187        void **service_args = &intercom->service_args [self][0];
188        core_fifo *fifo = &intercom->core_fifos [self];
189
190        while (true) {
191                process_free_lists(free_lists, self);
192                process_core_fifo(fifo, services, service_args);
193                wait_for_event(INTERCOM_EVENT_IPI);
194        }
195}
196
197static intercom_packet *free_list_and_packet_init(
198        free_list *list,
199        size_t count,
200        intercom_packet *current,
201        intercom_size size_index,
202        size_t size
203)
204{
205        intercom_packet *last = current;
206        size_t inc = 1 + size / sizeof(*current);
207        size_t i = 0;
208
209        assert(count > 0);
210        assert(size % sizeof(*current) == 0);
211
212        list->size = size;
213        list->head = current;
214        for (i = 0; i < count; ++i) {
215                intercom_packet *next = current + inc;
216                current->glue.next = next;
217                current->size_index = size_index;
218                last = current;
219                current = next;
220        }
221        last->glue.next = NULL;
222
223        return current;
224}
225
226static void basic_init(void)
227{
228        char *begin = (char *) QORIQ_INTERCOM_AREA_BEGIN;
229        size_t size = QORIQ_INTERCOM_AREA_SIZE;
230        int i = 0;
231
232        memset(begin, 0, size);
233
234        assert(size % packet_sizes [PACKET_SIZE_COUNT - 1] == 0);
235
236        /* Calculate data area sizes */
237        size_t data_sizes [PACKET_SIZE_COUNT];
238        data_sizes [PACKET_SIZE_COUNT - 1] = size / 2;
239        for (i = PACKET_SIZE_COUNT - 2; i > 0; --i) {
240                data_sizes [i] = data_sizes [i + 1] / 2;
241        }
242        data_sizes [i] = data_sizes [i + 1];
243
244        /* Calculate packet counts */
245        size_t packet_counts [PACKET_SIZE_COUNT];
246        size_t count = 0;
247        for (i = 1; i < PACKET_SIZE_COUNT; ++i) {
248                packet_counts [i] = data_sizes [i] / packet_sizes [i];
249                count += packet_counts [i];
250        }
251        packet_counts [0] = (data_sizes [0] - sizeof(control) - count * sizeof(intercom_packet))
252                / (sizeof(intercom_packet) + packet_sizes [0]);
253
254        /* Initialize free lists and packets */
255        intercom_packet *packet = (intercom_packet *) (begin + sizeof(control));
256        for (i = 0; i < PACKET_SIZE_COUNT; ++i) {
257                packet = free_list_and_packet_init(
258                        &intercom->free_lists [i],
259                        packet_counts [i],
260                        packet,
261                        i,
262                        packet_sizes [i]
263                );
264        }
265
266        rtems_cache_flush_multiple_data_lines(begin, size);
267        ppc_synchronize_data();
268}
269
270static void services_init(uint32_t self)
271{
272        int i = 0;
273
274        for (i = 0; i < INTERCOM_SERVICE_COUNT; ++i) {
275                if (intercom->services [self][i] == NULL) {
276                        intercom->services [self][i] = default_service;
277                }
278        }
279}
280
281void qoriq_intercom_init(void)
282{
283        rtems_status_code sc = RTEMS_SUCCESSFUL;
284        rtems_id task = RTEMS_ID_NONE;
285        uint32_t self = ppc_processor_id();
286
287        sc = rtems_task_create(
288                rtems_build_name('I', 'C', 'O', 'M'),
289                10,
290                0,
291                RTEMS_DEFAULT_MODES,
292                RTEMS_DEFAULT_ATTRIBUTES,
293                &task
294        );
295        assert(sc == RTEMS_SUCCESSFUL);
296
297        sc = qoriq_pic_set_priority(
298                QORIQ_IRQ_IPI_0,
299                QORIQ_PIC_PRIORITY_LOWEST,
300                NULL
301        );
302        assert(sc == RTEMS_SUCCESSFUL);
303
304        sc = rtems_interrupt_handler_install(
305                QORIQ_IRQ_IPI_0,
306                "INTERCOM",
307                RTEMS_INTERRUPT_UNIQUE,
308                intercom_handler,
309                (void *) (uintptr_t) task
310        );
311        assert(sc == RTEMS_SUCCESSFUL);
312
313        if (self == 0) {
314                basic_init();
315        }
316
317        services_init(self);
318
319        sc = rtems_task_start(task, intercom_task, 0);
320        assert(sc == RTEMS_SUCCESSFUL);
321}
322
323void qoriq_intercom_start(void)
324{
325        uint32_t self = ppc_processor_id();
326        uint32_t ready = 0;
327
328        while (ready != ALL_CORES) {
329                uint32_t msr = qoriq_spin_lock(&intercom->ready_lock);
330                ready = intercom->ready;
331                intercom->ready = ready | ONE_CORE(self);
332                qoriq_spin_unlock(&intercom->ready_lock, msr);
333        }
334}
335
336static intercom_packet *allocate(intercom_type type, free_list *list)
337{
338        uint32_t self = ppc_processor_id();
339        intercom_packet *packet = NULL;
340        consumer poor = {
341                .task = rtems_task_self()
342        };
343
344        while (packet == NULL) {
345                uint32_t msr = qoriq_spin_lock(&list->lock);
346                packet = list->head;
347                if (packet != NULL) {
348                        list->head = packet->glue.next;
349                } else {
350                        consumer *first = list->waiting_consumers [self].head;
351                        list->waiting_consumers [self].head = &poor;
352                        poor.next = first;
353                        if (first == NULL) {
354                                list->cores_to_notify |= ONE_CORE(self);
355                        }
356                }
357                qoriq_spin_unlock(&list->lock, msr);
358
359                if (packet == NULL) {
360                        wait_for_event(INTERCOM_EVENT_WAKE_UP);
361                }
362        }
363
364        packet->glue.next = NULL;
365        packet->type_index = type;
366        packet->flags = 0;
367        packet->size = list->size;
368
369        return packet;
370}
371
372intercom_packet *qoriq_intercom_allocate_packet(intercom_type type, intercom_size size)
373{
374        assert((unsigned) type < INTERCOM_SERVICE_COUNT);
375        assert((unsigned) size < PACKET_SIZE_COUNT);
376
377        return allocate(type, &intercom->free_lists [size]);
378}
379
380void qoriq_intercom_send_packets(int destination_core, intercom_packet *first, intercom_packet *last)
381{
382        assert(destination_core >= 0);
383        assert(destination_core < INTERCOM_CORE_COUNT);
384
385        core_fifo *fifo = &intercom->core_fifos [destination_core];
386
387        uint32_t msr = qoriq_spin_lock(&fifo->lock);
388        last->glue.next = NULL;
389        if (fifo->head != NULL) {
390                fifo->tail->glue.next = first;
391        } else {
392                fifo->head = first;
393                notify_core_by_index(destination_core);
394        }
395        fifo->tail = last;
396        qoriq_spin_unlock(&fifo->lock, msr);
397}
398
399void qoriq_intercom_broadcast_packets(intercom_packet *first, intercom_packet *last)
400{
401        int i = 0;
402
403        for (i = 1; i < INTERCOM_CORE_COUNT; ++i) {
404                intercom_packet *clone_first = NULL;
405                intercom_packet *clone_last = NULL;
406
407                intercom_packet *current = first;
408                while (current != NULL) {
409                        intercom_packet *clone = qoriq_intercom_clone_packet(current);
410                        if (clone_first == NULL) {
411                                clone_first = clone;
412                        }
413                        if (clone_last != NULL) {
414                                clone_last->glue.next = clone;
415                        }
416                        clone_last = clone;
417                        current = current->glue.next;
418                }
419
420                qoriq_intercom_send_packets(i, clone_first, clone_last);
421        }
422
423        qoriq_intercom_send_packets(0, first, last);
424}
425
426void qoriq_intercom_send(int destination_core, intercom_type type, intercom_size size, const void *buf, size_t n)
427{
428        assert((unsigned) size < PACKET_SIZE_COUNT);
429
430        size_t remaining = n;
431        size_t packet_size = packet_sizes [size];
432        const char *src = buf;
433        intercom_packet *first = NULL;
434        intercom_packet *last = NULL;
435
436        do {
437                intercom_packet *packet = qoriq_intercom_allocate_packet(
438                        type,
439                        size
440                );
441                if (first == NULL) {
442                        first = packet;
443                }
444                if (last != NULL) {
445                        last->glue.next = packet;
446                }
447                last = packet;
448                size_t current_size = remaining < packet_size ? remaining : packet_size;
449                remaining -= current_size;
450                packet->size = current_size;
451                const char *current = src;
452                src += current_size;
453                memcpy(packet->data, current, current_size);
454        } while (remaining > 0);
455
456        qoriq_intercom_send_packets(destination_core, first, last);
457}
458
459void qoriq_intercom_service_install(intercom_type type, intercom_service service, void *arg)
460{
461        assert((unsigned) type < INTERCOM_SERVICE_COUNT);
462
463        uint32_t self = ppc_processor_id();
464        intercom->service_args [self][type] = arg;
465        ppc_enforce_in_order_execution_of_io();
466        intercom->services [self][type] = service;
467}
468
469void qoriq_intercom_service_remove(intercom_type type)
470{
471        assert((unsigned) type < INTERCOM_SERVICE_COUNT);
472
473        uint32_t self = ppc_processor_id();
474        intercom->services [self][type] = default_service;
475        ppc_enforce_in_order_execution_of_io();
476        intercom->service_args [self][type] = NULL;
477}
478
479intercom_packet *qoriq_intercom_clone_packet(const intercom_packet *packet)
480{
481        intercom_packet *clone = qoriq_intercom_allocate_packet(
482                packet->type_index,
483                packet->size_index
484        );
485
486        clone->size = packet->size;
487        memcpy(clone->data, packet->data, clone->size);
488
489        return clone;
490}
491
492#endif /* !QORIQ_IS_HYPERVISOR_GUEST */
Note: See TracBrowser for help on using the repository browser.