aboutsummaryrefslogtreecommitdiffstats
path: root/src/pru/pru0.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/pru/pru0.c')
-rw-r--r--src/pru/pru0.c229
1 files changed, 229 insertions, 0 deletions
diff --git a/src/pru/pru0.c b/src/pru/pru0.c
new file mode 100644
index 00000000..e4bd1c46
--- /dev/null
+++ b/src/pru/pru0.c
@@ -0,0 +1,229 @@
+// Code to handle IO on PRU0 and pass the messages to PRU1
+//
+// Copyright (C) 2017 Kevin O'Connor <kevin@koconnor.net>
+//
+// This file may be distributed under the terms of the GNU GPLv3 license.
+
+#include <stdint.h> // uint32_t
+#include <string.h> // memset
+#include <pru/io.h> // write_r31
+#include <pru_cfg.h> // CT_CFG
+#include <pru_intc.h> // CT_INTC
+#include <pru_rpmsg.h> // pru_rpmsg_send
+#include <pru_virtio_ids.h> // VIRTIO_ID_RPMSG
+#include <rsc_types.h> // resource_table
+#include "board/io.h" // readl
+#include "compiler.h" // __section
+#include "internal.h" // SHARED_MEM
+
+
+/****************************************************************
+ * IO
+ ****************************************************************/
+
+#define CHAN_NAME "rpmsg-pru"
+#define CHAN_DESC "Channel 30"
+#define CHAN_PORT 30
+
+static uint16_t transport_dst;
+
+static void
+check_can_send(struct pru_rpmsg_transport *transport)
+{
+ for (;;) {
+ uint32_t send_pop_pos = SHARED_MEM->send_pop_pos;
+ uint32_t count = readl(&SHARED_MEM->send_data[send_pop_pos].count);
+ if (!count)
+ // Queue empty
+ break;
+ pru_rpmsg_send(
+ transport, CHAN_PORT, transport_dst
+ , &SHARED_MEM->send_data[send_pop_pos].data, count);
+ barrier();
+ writel(&SHARED_MEM->send_data[send_pop_pos].count, 0);
+ SHARED_MEM->send_pop_pos = (
+ (send_pop_pos + 1) % ARRAY_SIZE(SHARED_MEM->send_data));
+ }
+}
+
+static void
+check_can_read(struct pru_rpmsg_transport *transport)
+{
+ if (readl(&SHARED_MEM->read_count))
+ // main processing pru is busy
+ return;
+ uint16_t dst, len;
+ int16_t ret = pru_rpmsg_receive(
+ transport, &transport_dst, &dst, SHARED_MEM->read_data, &len);
+ if (ret || !len)
+ return;
+ SHARED_MEM->read_pos = 0;
+ barrier();
+ writel(&SHARED_MEM->read_count, len);
+ write_r31(R31_WRITE_IRQ_SELECT | (KICK_PRU1_EVENT - R31_WRITE_IRQ_OFFSET));
+}
+
+static void
+process_io(struct pru_rpmsg_transport *transport)
+{
+ for (;;) {
+ if (!(read_r31() & (1 << (WAKE_PRU0_IRQ + R31_IRQ_OFFSET))))
+ continue;
+ CT_INTC.SECR0 = (1 << KICK_PRU0_FROM_ARM_EVENT) | (1 << KICK_PRU0_EVENT);
+ check_can_send(transport);
+ check_can_read(transport);
+ }
+}
+
+
+/****************************************************************
+ * Resource table
+ ****************************************************************/
+
+/*
+ * 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[] = {
+ {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},
+};
+
+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 */
+ {
+ (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
+ /* no config data */
+ },
+ /* the two vrings */
+ {
+ 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
+ },
+ {
+ 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
+ },
+
+ {
+ TYPE_CUSTOM, TYPE_PRU_INTS,
+ sizeof(struct fw_rsc_custom_ints),
+ { /* PRU_INTS version */
+ {
+ 0x0000,
+ /* Channel-to-host mapping, 255 for unused */
+ {
+ WAKE_PRU0_IRQ, WAKE_PRU1_IRQ, WAKE_ARM_IRQ,
+ 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,
+ },
+ },
+ },
+};
+
+
+/****************************************************************
+ * Startup
+ ****************************************************************/
+
+#define VIRTIO_CONFIG_S_DRIVER_OK 4
+
+int
+main(void)
+{
+ // allow access to external memory
+ CT_CFG.SYSCFG_bit.STANDBY_INIT = 0;
+
+ // clear all irqs
+ CT_INTC.SECR0 = 0xffffffff;
+ CT_INTC.SECR1 = 0xffffffff;
+
+ /* 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 */
+ struct pru_rpmsg_transport transport;
+ pru_rpmsg_init(&transport,
+ &resourceTable.rpmsg_vring0,
+ &resourceTable.rpmsg_vring1,
+ KICK_ARM_EVENT,
+ KICK_PRU0_FROM_ARM_EVENT);
+
+ /* 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)
+ ;
+
+ // Wait for PRU1 to be ready
+ memset(SHARED_MEM, 0, sizeof(*SHARED_MEM));
+ writel(&SHARED_MEM->signal, SIGNAL_PRU0_WAITING);
+ while (readl(&SHARED_MEM->signal) != SIGNAL_PRU1_READY)
+ ;
+ writel(&SHARED_MEM->signal, 0);
+
+ process_io(&transport);
+}