2017-05-16 20:41:44 +03:00
|
|
|
// Code to handle IO on PRU0 and pass the messages to PRU1
|
2017-05-07 04:47:04 +03:00
|
|
|
//
|
|
|
|
// Copyright (C) 2017 Kevin O'Connor <kevin@koconnor.net>
|
|
|
|
//
|
|
|
|
// This file may be distributed under the terms of the GNU GPLv3 license.
|
|
|
|
|
2017-06-30 01:14:39 +03:00
|
|
|
#include <setjmp.h> // setjmp
|
2017-05-07 04:47:04 +03:00
|
|
|
#include <stdint.h> // uint32_t
|
2017-05-16 20:41:44 +03:00
|
|
|
#include <string.h> // memset
|
|
|
|
#include <pru/io.h> // write_r31
|
|
|
|
#include <pru_cfg.h> // CT_CFG
|
2017-05-07 04:47:04 +03:00
|
|
|
#include <pru_intc.h> // CT_INTC
|
|
|
|
#include <pru_rpmsg.h> // pru_rpmsg_send
|
|
|
|
#include <pru_virtio_ids.h> // VIRTIO_ID_RPMSG
|
2017-05-16 20:41:44 +03:00
|
|
|
#include <rsc_types.h> // resource_table
|
|
|
|
#include "board/io.h" // readl
|
2017-06-30 01:14:39 +03:00
|
|
|
#include "command.h" // command_add_frame
|
2017-05-16 20:41:44 +03:00
|
|
|
#include "compiler.h" // __section
|
|
|
|
#include "internal.h" // SHARED_MEM
|
2017-06-30 01:14:39 +03:00
|
|
|
#include "sched.h" // sched_shutdown
|
|
|
|
|
|
|
|
struct pru_rpmsg_transport transport;
|
|
|
|
static uint16_t transport_dst;
|
2017-05-07 04:47:04 +03:00
|
|
|
|
|
|
|
|
|
|
|
/****************************************************************
|
2017-05-16 20:41:44 +03:00
|
|
|
* IO
|
2017-05-07 04:47:04 +03:00
|
|
|
****************************************************************/
|
|
|
|
|
2017-05-16 20:41:44 +03:00
|
|
|
#define CHAN_NAME "rpmsg-pru"
|
|
|
|
#define CHAN_DESC "Channel 30"
|
|
|
|
#define CHAN_PORT 30
|
2017-05-07 04:47:04 +03:00
|
|
|
|
2017-09-08 00:08:58 +03:00
|
|
|
#define RPMSG_HDR_SIZE 16
|
2018-05-24 19:49:23 +03:00
|
|
|
static uint8_t transmit_buf[RPMSG_BUF_SIZE - RPMSG_HDR_SIZE];
|
2017-09-08 00:08:58 +03:00
|
|
|
static int transmit_pos;
|
|
|
|
|
|
|
|
// Transmit all pending message blocks
|
|
|
|
static void
|
|
|
|
flush_messages(void)
|
|
|
|
{
|
|
|
|
if (!transmit_pos)
|
|
|
|
return;
|
|
|
|
pru_rpmsg_send(&transport, CHAN_PORT, transport_dst
|
|
|
|
, transmit_buf, transmit_pos);
|
|
|
|
transmit_pos = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Generate a message block and queue it for transmission
|
|
|
|
static void
|
2018-05-24 19:49:23 +03:00
|
|
|
build_message(uint8_t *msg, int msglen)
|
2017-09-08 00:08:58 +03:00
|
|
|
{
|
|
|
|
if (transmit_pos + msglen > sizeof(transmit_buf))
|
|
|
|
flush_messages();
|
|
|
|
memcpy(&transmit_buf[transmit_pos], msg, msglen);
|
|
|
|
command_add_frame(&transmit_buf[transmit_pos], msglen);
|
|
|
|
transmit_pos += msglen;
|
|
|
|
}
|
|
|
|
|
2017-06-30 01:14:39 +03:00
|
|
|
// Check if there is data to be sent from PRU1 to the host
|
2017-05-16 20:41:44 +03:00
|
|
|
static void
|
2017-06-30 01:14:39 +03:00
|
|
|
check_can_send(void)
|
2017-05-07 04:47:04 +03:00
|
|
|
{
|
2017-05-16 20:41:44 +03:00
|
|
|
for (;;) {
|
|
|
|
uint32_t send_pop_pos = SHARED_MEM->send_pop_pos;
|
2017-07-01 00:04:30 +03:00
|
|
|
struct shared_response_buffer *s = &SHARED_MEM->send_data[send_pop_pos];
|
|
|
|
uint32_t count = readl(&s->count);
|
2017-05-16 20:41:44 +03:00
|
|
|
if (!count)
|
|
|
|
// Queue empty
|
|
|
|
break;
|
2017-09-08 00:08:58 +03:00
|
|
|
build_message(s->data, count);
|
2017-07-01 00:04:30 +03:00
|
|
|
writel(&s->count, 0);
|
2017-05-16 20:41:44 +03:00
|
|
|
SHARED_MEM->send_pop_pos = (
|
|
|
|
(send_pop_pos + 1) % ARRAY_SIZE(SHARED_MEM->send_data));
|
2017-05-07 04:47:04 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-30 01:14:39 +03:00
|
|
|
// Wait for PRU1 to finish processing a command
|
2017-05-16 20:41:44 +03:00
|
|
|
static void
|
2017-06-30 01:14:39 +03:00
|
|
|
wait_pru1_command(void)
|
2017-05-07 04:47:04 +03:00
|
|
|
{
|
2017-06-30 01:14:39 +03:00
|
|
|
while (readl(&SHARED_MEM->next_command))
|
|
|
|
check_can_send();
|
|
|
|
check_can_send();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Signal PRU1 that a new command is ready
|
|
|
|
static void
|
|
|
|
send_pru1_command(const struct command_parser *cp)
|
|
|
|
{
|
|
|
|
barrier();
|
|
|
|
SHARED_MEM->next_command = cp;
|
|
|
|
barrier();
|
|
|
|
write_r31(R31_WRITE_IRQ_SELECT | (KICK_PRU1_EVENT - R31_WRITE_IRQ_OFFSET));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Instruct PRU1 to shutdown
|
|
|
|
static void
|
|
|
|
send_pru1_shutdown(void)
|
|
|
|
{
|
|
|
|
wait_pru1_command();
|
|
|
|
send_pru1_command(SHARED_MEM->shutdown_handler);
|
|
|
|
wait_pru1_command();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Dispatch all the commands in a message block
|
|
|
|
static void
|
2018-05-24 19:49:23 +03:00
|
|
|
do_dispatch(uint8_t *buf, uint32_t msglen)
|
2017-06-30 01:14:39 +03:00
|
|
|
{
|
2018-05-24 19:49:23 +03:00
|
|
|
uint8_t *p = &buf[MESSAGE_HEADER_SIZE];
|
|
|
|
uint8_t *msgend = &buf[msglen-MESSAGE_TRAILER_SIZE];
|
2017-06-30 01:14:39 +03:00
|
|
|
while (p < msgend) {
|
|
|
|
// Parse command
|
2018-05-24 19:49:23 +03:00
|
|
|
uint_fast8_t cmdid = *p++;
|
2017-06-30 01:14:39 +03:00
|
|
|
const struct command_parser *cp = &SHARED_MEM->command_index[cmdid];
|
|
|
|
if (!cmdid || cmdid >= SHARED_MEM->command_index_size
|
|
|
|
|| cp->num_args > ARRAY_SIZE(SHARED_MEM->next_command_args)) {
|
|
|
|
send_pru1_shutdown();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
p = command_parsef(p, msgend, cp, SHARED_MEM->next_command_args);
|
|
|
|
|
|
|
|
send_pru1_command(ALT_PRU_PTR(cp));
|
|
|
|
wait_pru1_command();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// See if there are commands from the host ready to be processed
|
2017-09-04 03:45:47 +03:00
|
|
|
static int
|
2017-06-30 01:14:39 +03:00
|
|
|
check_can_read(void)
|
|
|
|
{
|
|
|
|
// Read data
|
2017-05-16 20:41:44 +03:00
|
|
|
uint16_t dst, len;
|
2018-05-24 19:49:23 +03:00
|
|
|
uint8_t *p = SHARED_MEM->read_data;
|
2017-06-30 01:14:39 +03:00
|
|
|
int16_t ret = pru_rpmsg_receive(&transport, &transport_dst, &dst, p, &len);
|
|
|
|
if (ret)
|
2017-09-04 03:45:47 +03:00
|
|
|
return ret == PRU_RPMSG_NO_BUF_AVAILABLE;
|
2017-06-30 01:14:39 +03:00
|
|
|
|
2017-06-30 21:02:32 +03:00
|
|
|
// Check for force shutdown request
|
|
|
|
if (len == 15 && p[14] == '\n' && memcmp(p, "FORCE_SHUTDOWN\n", 15) == 0) {
|
|
|
|
send_pru1_shutdown();
|
2017-09-04 03:45:47 +03:00
|
|
|
return 0;
|
2017-06-30 21:02:32 +03:00
|
|
|
}
|
|
|
|
|
2017-06-30 01:14:39 +03:00
|
|
|
// Parse data into message blocks
|
|
|
|
for (;;) {
|
2018-05-24 19:49:23 +03:00
|
|
|
uint_fast8_t pop_count, msglen = len > MESSAGE_MAX ? MESSAGE_MAX : len;
|
|
|
|
int_fast8_t ret = command_find_block(p, msglen, &pop_count);
|
2017-06-30 01:14:39 +03:00
|
|
|
if (!ret)
|
|
|
|
break;
|
2018-05-24 20:30:24 +03:00
|
|
|
if (ret > 0) {
|
2017-06-30 01:14:39 +03:00
|
|
|
do_dispatch(p, pop_count);
|
2018-05-24 20:30:24 +03:00
|
|
|
command_send_ack();
|
|
|
|
}
|
2017-06-30 01:14:39 +03:00
|
|
|
p += pop_count;
|
|
|
|
len -= pop_count;
|
|
|
|
}
|
2017-09-04 03:45:47 +03:00
|
|
|
return 0;
|
2017-05-07 04:47:04 +03:00
|
|
|
}
|
|
|
|
|
2017-06-30 01:14:39 +03:00
|
|
|
// Main processing loop
|
2017-05-16 20:41:44 +03:00
|
|
|
static void
|
2017-06-30 01:14:39 +03:00
|
|
|
process_io(void)
|
2017-05-07 04:47:04 +03:00
|
|
|
{
|
2017-05-16 20:41:44 +03:00
|
|
|
for (;;) {
|
2019-02-27 22:16:58 +03:00
|
|
|
CT_INTC.SECR0 = ((1 << KICK_PRU0_FROM_ARM_EVENT)
|
|
|
|
| (1 << KICK_PRU0_EVENT));
|
2017-06-30 01:14:39 +03:00
|
|
|
check_can_send();
|
2017-09-04 03:45:47 +03:00
|
|
|
int can_sleep = check_can_read();
|
2017-09-08 00:08:58 +03:00
|
|
|
if (can_sleep) {
|
|
|
|
flush_messages();
|
2017-09-11 20:07:54 +03:00
|
|
|
while (!(read_r31() & (1 << (WAKE_PRU0_IRQ + R31_IRQ_OFFSET)))) {
|
|
|
|
//asm("slp 1");
|
|
|
|
}
|
2017-09-08 00:08:58 +03:00
|
|
|
}
|
2017-05-16 20:41:44 +03:00
|
|
|
}
|
2017-05-07 04:47:04 +03:00
|
|
|
}
|
|
|
|
|
2017-06-30 01:14:39 +03:00
|
|
|
// Startup initialization
|
|
|
|
static void
|
|
|
|
setup_io(void)
|
|
|
|
{
|
|
|
|
// Fixup pointers in command_parsers
|
|
|
|
SHARED_MEM->command_index = ALT_PRU_PTR(SHARED_MEM->command_index);
|
|
|
|
struct command_parser *p = (void*)SHARED_MEM->command_index;
|
|
|
|
int i;
|
|
|
|
for (i=0; i<SHARED_MEM->command_index_size; i++, p++)
|
|
|
|
if (p->param_types)
|
|
|
|
p->param_types = ALT_PRU_PTR(p->param_types);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/****************************************************************
|
|
|
|
* Compatibility wrappers
|
|
|
|
****************************************************************/
|
|
|
|
|
|
|
|
// shutdown() compatibility code
|
|
|
|
uint8_t ctr_lookup_static_string(const char *str)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static jmp_buf shutdown_jmp;
|
|
|
|
|
|
|
|
// Handle shutdown()
|
|
|
|
void
|
|
|
|
sched_shutdown(uint_fast8_t reason)
|
|
|
|
{
|
|
|
|
longjmp(shutdown_jmp, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Generate messages - only used for ack/nak messages
|
|
|
|
void
|
|
|
|
console_sendf(const struct command_encoder *ce, va_list args)
|
|
|
|
{
|
2018-05-24 19:49:23 +03:00
|
|
|
uint8_t buf[MESSAGE_MIN];
|
2017-09-08 00:08:58 +03:00
|
|
|
build_message(buf, sizeof(buf));
|
2017-06-30 01:14:39 +03:00
|
|
|
}
|
|
|
|
|
2017-05-07 04:47:04 +03:00
|
|
|
|
|
|
|
/****************************************************************
|
2017-05-16 20:41:44 +03:00
|
|
|
* Resource table
|
2017-05-07 04:47:04 +03:00
|
|
|
****************************************************************/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Sizes of the virtqueues (expressed in number of buffers supported,
|
|
|
|
* and must be power of 2)
|
|
|
|
*/
|
|
|
|
#define PRU_RPMSG_VQ0_SIZE 16
|
|
|
|
#define PRU_RPMSG_VQ1_SIZE 16
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The feature bitmap for virtio rpmsg
|
|
|
|
*/
|
|
|
|
#define VIRTIO_RPMSG_F_NS 0 //name service notifications
|
|
|
|
|
|
|
|
/* This firmware supports name service notifications as one of its features */
|
|
|
|
#define RPMSG_PRU_C0_FEATURES (1 << VIRTIO_RPMSG_F_NS)
|
|
|
|
|
|
|
|
/* Definition for unused interrupts */
|
|
|
|
#define HOST_UNUSED 255
|
|
|
|
|
|
|
|
/* Mapping sysevts to a channel. Each pair contains a sysevt, channel. */
|
|
|
|
static struct ch_map pru_intc_map[] = {
|
2017-05-16 20:41:44 +03:00
|
|
|
{IEP_EVENT, WAKE_PRU1_IRQ},
|
|
|
|
{KICK_ARM_EVENT, WAKE_ARM_IRQ},
|
|
|
|
{KICK_PRU0_FROM_ARM_EVENT, WAKE_PRU0_IRQ},
|
|
|
|
{KICK_PRU0_EVENT, WAKE_PRU0_IRQ},
|
|
|
|
{KICK_PRU1_EVENT, WAKE_PRU1_IRQ},
|
2017-05-07 04:47:04 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
struct my_resource_table {
|
|
|
|
struct resource_table base;
|
|
|
|
|
|
|
|
uint32_t offset[2]; /* Should match 'num' in actual definition */
|
|
|
|
|
|
|
|
/* rpmsg vdev entry */
|
|
|
|
struct fw_rsc_vdev rpmsg_vdev;
|
|
|
|
struct fw_rsc_vdev_vring rpmsg_vring0;
|
|
|
|
struct fw_rsc_vdev_vring rpmsg_vring1;
|
|
|
|
|
|
|
|
/* intc definition */
|
|
|
|
struct fw_rsc_custom pru_ints;
|
|
|
|
} resourceTable __section(".resource_table") = {
|
|
|
|
{
|
|
|
|
1, /* Resource table version: only version 1 is
|
|
|
|
* supported by the current driver */
|
|
|
|
2, /* number of entries in the table */
|
|
|
|
{ 0, 0 }, /* reserved, must be zero */
|
|
|
|
},
|
|
|
|
/* offsets to entries */
|
|
|
|
{
|
|
|
|
offsetof(struct my_resource_table, rpmsg_vdev),
|
|
|
|
offsetof(struct my_resource_table, pru_ints),
|
|
|
|
},
|
|
|
|
|
|
|
|
/* rpmsg vdev entry */
|
|
|
|
{
|
2019-02-27 22:16:58 +03:00
|
|
|
(uint32_t)TYPE_VDEV, //type
|
|
|
|
(uint32_t)VIRTIO_ID_RPMSG, //id
|
|
|
|
(uint32_t)0, //notifyid
|
|
|
|
(uint32_t)RPMSG_PRU_C0_FEATURES,//dfeatures
|
|
|
|
(uint32_t)0, //gfeatures
|
|
|
|
(uint32_t)0, //config_len
|
|
|
|
(uint8_t)0, //status
|
|
|
|
(uint8_t)2, //num_of_vrings, only two is supported
|
|
|
|
{(uint8_t)0, (uint8_t)0 }, //reserved
|
2017-05-07 04:47:04 +03:00
|
|
|
/* no config data */
|
|
|
|
},
|
|
|
|
/* the two vrings */
|
|
|
|
{
|
2019-02-27 22:16:58 +03:00
|
|
|
0, //da, will be populated by host, can't pass it in
|
|
|
|
16, //align (bytes),
|
|
|
|
PRU_RPMSG_VQ0_SIZE, //num of descriptors
|
|
|
|
0, //notifyid, will be populated, can't pass right now
|
|
|
|
0 //reserved
|
2017-05-07 04:47:04 +03:00
|
|
|
},
|
|
|
|
{
|
2019-02-27 22:16:58 +03:00
|
|
|
0, //da, will be populated by host, can't pass it in
|
|
|
|
16, //align (bytes),
|
|
|
|
PRU_RPMSG_VQ1_SIZE, //num of descriptors
|
|
|
|
0, //notifyid, will be populated, can't pass right now
|
|
|
|
0 //reserved
|
2017-05-07 04:47:04 +03:00
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
TYPE_CUSTOM, TYPE_PRU_INTS,
|
|
|
|
sizeof(struct fw_rsc_custom_ints),
|
|
|
|
{ /* PRU_INTS version */
|
|
|
|
{
|
|
|
|
0x0000,
|
|
|
|
/* Channel-to-host mapping, 255 for unused */
|
|
|
|
{
|
2017-05-16 20:41:44 +03:00
|
|
|
WAKE_PRU0_IRQ, WAKE_PRU1_IRQ, WAKE_ARM_IRQ,
|
2017-05-07 04:47:04 +03:00
|
|
|
HOST_UNUSED, HOST_UNUSED, HOST_UNUSED,
|
|
|
|
HOST_UNUSED, HOST_UNUSED, HOST_UNUSED, HOST_UNUSED
|
|
|
|
},
|
|
|
|
/* Number of evts being mapped to channels */
|
|
|
|
(sizeof(pru_intc_map) / sizeof(struct ch_map)),
|
|
|
|
/* Pointer to the structure containing mapped events */
|
|
|
|
pru_intc_map,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/****************************************************************
|
2017-05-16 20:41:44 +03:00
|
|
|
* Startup
|
2017-05-07 04:47:04 +03:00
|
|
|
****************************************************************/
|
|
|
|
|
|
|
|
#define VIRTIO_CONFIG_S_DRIVER_OK 4
|
|
|
|
|
2017-05-16 20:41:44 +03:00
|
|
|
int
|
|
|
|
main(void)
|
2017-05-07 04:47:04 +03:00
|
|
|
{
|
2017-05-16 20:41:44 +03:00
|
|
|
// allow access to external memory
|
|
|
|
CT_CFG.SYSCFG_bit.STANDBY_INIT = 0;
|
|
|
|
|
|
|
|
// clear all irqs
|
|
|
|
CT_INTC.SECR0 = 0xffffffff;
|
|
|
|
CT_INTC.SECR1 = 0xffffffff;
|
2017-05-07 04:47:04 +03:00
|
|
|
|
|
|
|
/* Make sure the Linux drivers are ready for RPMsg communication */
|
|
|
|
volatile uint8_t *status = &resourceTable.rpmsg_vdev.status;
|
|
|
|
while (!(*status & VIRTIO_CONFIG_S_DRIVER_OK))
|
|
|
|
;
|
|
|
|
|
|
|
|
/* Initialize the RPMsg transport structure */
|
|
|
|
pru_rpmsg_init(&transport,
|
|
|
|
&resourceTable.rpmsg_vring0,
|
|
|
|
&resourceTable.rpmsg_vring1,
|
2017-05-16 20:41:44 +03:00
|
|
|
KICK_ARM_EVENT,
|
|
|
|
KICK_PRU0_FROM_ARM_EVENT);
|
2017-05-07 04:47:04 +03:00
|
|
|
|
|
|
|
/* Create the RPMsg channel between the PRU and ARM user space
|
|
|
|
* using the transport structure. */
|
|
|
|
while (pru_rpmsg_channel(RPMSG_NS_CREATE, &transport, CHAN_NAME
|
|
|
|
, CHAN_DESC, CHAN_PORT) != PRU_RPMSG_SUCCESS)
|
|
|
|
;
|
2017-05-16 20:41:44 +03:00
|
|
|
|
2017-07-13 05:16:16 +03:00
|
|
|
// Allow PRU0 and PRU1 to wake from sleep
|
|
|
|
PRU0_CTRL.WAKEUP_EN = 1 << (WAKE_PRU0_IRQ + R31_IRQ_OFFSET);
|
|
|
|
PRU1_CTRL.WAKEUP_EN = 1 << (WAKE_PRU1_IRQ + R31_IRQ_OFFSET);
|
|
|
|
|
2017-05-11 21:53:00 +03:00
|
|
|
// Wait for PRU1 to be ready
|
2017-07-13 03:02:23 +03:00
|
|
|
memset(SHARED_MEM, 0, sizeof(*SHARED_MEM));
|
2017-05-16 20:41:44 +03:00
|
|
|
writel(&SHARED_MEM->signal, SIGNAL_PRU0_WAITING);
|
|
|
|
while (readl(&SHARED_MEM->signal) != SIGNAL_PRU1_READY)
|
|
|
|
;
|
|
|
|
writel(&SHARED_MEM->signal, 0);
|
|
|
|
|
2017-06-30 01:14:39 +03:00
|
|
|
// Setup incoming message parser
|
|
|
|
setup_io();
|
|
|
|
|
|
|
|
// Support shutdown
|
|
|
|
int ret = setjmp(shutdown_jmp);
|
|
|
|
if (ret)
|
|
|
|
send_pru1_shutdown();
|
|
|
|
|
|
|
|
// Main loop
|
|
|
|
process_io();
|
2017-05-07 04:47:04 +03:00
|
|
|
}
|