aboutsummaryrefslogtreecommitdiffstats
path: root/src/pru
diff options
context:
space:
mode:
authorKevin O'Connor <kevin@koconnor.net>2017-05-06 21:47:04 -0400
committerKevin O'Connor <kevin@koconnor.net>2017-05-15 15:15:52 -0400
commit2255176228effc67e868ca343f7354da2b7f4081 (patch)
treef9f91e9c08c20d731f88d1ca509f2bbeed157e11 /src/pru
parentccaa25eaa5d10291bff332f88194befe643b729f (diff)
downloadkutter-2255176228effc67e868ca343f7354da2b7f4081.tar.gz
kutter-2255176228effc67e868ca343f7354da2b7f4081.tar.xz
kutter-2255176228effc67e868ca343f7354da2b7f4081.zip
pru: Initial support for the Beaglebone PRU
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
Diffstat (limited to 'src/pru')
-rw-r--r--src/pru/Kconfig13
-rw-r--r--src/pru/Makefile27
-rw-r--r--src/pru/console.c230
-rw-r--r--src/pru/gpio.c142
-rw-r--r--src/pru/gpio.h22
-rw-r--r--src/pru/internal.h18
-rw-r--r--src/pru/main.c150
-rw-r--r--src/pru/pru.lds9
-rw-r--r--src/pru/pru1.c29
9 files changed, 640 insertions, 0 deletions
diff --git a/src/pru/Kconfig b/src/pru/Kconfig
new file mode 100644
index 00000000..483c0447
--- /dev/null
+++ b/src/pru/Kconfig
@@ -0,0 +1,13 @@
+# Kconfig settings for PRU
+
+if MACH_PRU
+
+config BOARD_DIRECTORY
+ string
+ default "pru"
+
+config CLOCK_FREQ
+ int
+ default 200000000
+
+endif
diff --git a/src/pru/Makefile b/src/pru/Makefile
new file mode 100644
index 00000000..9cdb40c9
--- /dev/null
+++ b/src/pru/Makefile
@@ -0,0 +1,27 @@
+# Additional PRU build rules
+
+# Setup the toolchain
+CROSS_PREFIX=pru-
+
+dirs-y += src/pru src/generic
+dirs-y += lib/pru_rpmsg
+
+CFLAGS += -Os -mmcu=am335x.pru0
+CFLAGS += -Ilib/pru_rpmsg/include -Ilib/pru_rpmsg/include/am335x
+
+CFLAGS_klipper.o := $(filter-out -mmcu=%, $(CFLAGS_klipper.o))
+CFLAGS_klipper.elf := $(CFLAGS) -minrt -T src/pru/pru.lds
+CFLAGS_pru1.elf := $(filter-out -mmcu=%, $(CFLAGS)) -mmcu=am335x.pru1
+
+# Add source files
+src-y := $(filter-out debugcmds.c, $(src-y))
+src-y += pru/main.c pru/console.c pru/gpio.c
+src-y += generic/crc16_ccitt.c generic/timer_irq.c
+src-y += ../lib/pru_rpmsg/pru_rpmsg.c ../lib/pru_rpmsg/pru_virtqueue.c
+
+# Build the additional PRU1 binary
+target-y += $(OUT)pru1.elf
+
+$(OUT)pru1.elf: $(OUT)src/pru/pru1.o
+ @echo " Linking $@"
+ $(Q)$(CC) $(CFLAGS_pru1.elf) $^ -o $@
diff --git a/src/pru/console.c b/src/pru/console.c
new file mode 100644
index 00000000..c22e6c82
--- /dev/null
+++ b/src/pru/console.c
@@ -0,0 +1,230 @@
+// PRU input/output via RPMsg
+//
+// 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> // memcpy
+#include <pru/io.h> // read_r31
+#include <pru_intc.h> // CT_INTC
+#include <pru_rpmsg.h> // pru_rpmsg_send
+#include <pru_virtio_ids.h> // VIRTIO_ID_RPMSG
+#include "board/misc.h" // console_get_input
+#include "internal.h" // WAKE_ARM_EVENT
+#include "sched.h" // DECL_INIT
+
+#define CHAN_NAME "rpmsg-pru"
+#define CHAN_DESC "Channel 30"
+#define CHAN_PORT 30
+
+
+/****************************************************************
+ * Console interface
+ ****************************************************************/
+
+static void
+clear_pending(void)
+{
+ CT_INTC.SECR0 = 1 << GOT_ARM_EVENT;
+}
+
+static struct pru_rpmsg_transport transport;
+static uint16_t transport_dst;
+static char input_data[64];
+
+// XXX
+struct pru_rpmsg_hdr {
+ uint32_t src;
+ uint32_t dst;
+ uint32_t reserved;
+ uint16_t len;
+ uint16_t flags;
+ uint8_t data[0];
+};
+
+// Return a buffer (and length) containing any incoming messages
+char *
+console_get_input(uint8_t *plen)
+{
+ if (!(read_r31() & (1 << (GOT_ARM_IRQ + R31_IRQ_OFFSET))))
+ goto nodata;
+ struct pru_rpmsg_hdr *msg;
+ uint32_t msg_len;
+ int16_t head = pru_virtqueue_get_avail_buf(
+ &transport.virtqueue1, (void**)&msg, &msg_len);
+ if (head < 0) {
+ clear_pending();
+ goto nodata;
+ }
+ transport_dst = msg->src;
+ int len = msg->len < sizeof(input_data) ? msg->len : sizeof(input_data);
+ memcpy(input_data, msg->data, len);
+ pru_virtqueue_add_used_buf(&transport.virtqueue1, head, msg_len);
+ pru_virtqueue_kick(&transport.virtqueue1);
+ *plen = len;
+ return input_data;
+nodata:
+ *plen = 0;
+ return input_data;
+}
+
+// Remove from the receive buffer the given number of bytes
+void
+console_pop_input(uint8_t len)
+{
+}
+
+static char output_data[64];
+
+// Return an output buffer that the caller may fill with transmit messages
+char *
+console_get_output(uint8_t len)
+{
+ if (len > sizeof(output_data))
+ return NULL;
+ return output_data;
+}
+
+// Accept the given number of bytes added to the transmit buffer
+void
+console_push_output(uint8_t len)
+{
+ pru_rpmsg_send(&transport, CHAN_PORT, transport_dst, output_data, len);
+}
+
+
+/****************************************************************
+ * 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, IEP_IRQ},
+ {WAKE_ARM_EVENT, WAKE_ARM_IRQ},
+ {GOT_ARM_EVENT, GOT_ARM_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 */
+ {
+ IEP_IRQ, GOT_ARM_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,
+ },
+ },
+ },
+};
+
+
+/****************************************************************
+ * RPMsg init
+ ****************************************************************/
+
+#define VIRTIO_CONFIG_S_DRIVER_OK 4
+
+void
+console_init(void)
+{
+ clear_pending();
+
+ /* 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,
+ WAKE_ARM_EVENT,
+ GOT_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)
+ ;
+}
diff --git a/src/pru/gpio.c b/src/pru/gpio.c
new file mode 100644
index 00000000..7fdbb0d6
--- /dev/null
+++ b/src/pru/gpio.c
@@ -0,0 +1,142 @@
+// GPIO functions on PRU
+//
+// Copyright (C) 2017 Kevin O'Connor <kevin@koconnor.net>
+//
+// This file may be distributed under the terms of the GNU GPLv3 license.
+
+#include "command.h" // shutdown
+#include "compiler.h" // ARRAY_SIZE
+#include "gpio.h" // gpio_out_setup
+#include "sched.h" // sched_shutdown
+
+
+/****************************************************************
+ * Pin mappings
+ ****************************************************************/
+
+#define GPIO(PORT, NUM) ((PORT) * 32 + (NUM))
+#define GPIO2PORT(PIN) ((PIN) / 32)
+#define GPIO2BIT(PIN) (1<<((PIN) % 32))
+
+struct gpio_regs {
+ uint32_t pad_0[77];
+ volatile uint32_t oe;
+ volatile uint32_t datain;
+ volatile uint32_t dataout;
+ uint32_t pad_140[20];
+ volatile uint32_t cleardataout;
+ volatile uint32_t setdataout;
+};
+
+static struct gpio_regs *digital_regs[] = {
+ (void*)0x44e07000, (void*)0x4804c000, (void*)0x481ac000, (void*)0x481ae000
+};
+
+#define MUXPORT(offset) (((offset)-0x800) / 4)
+
+static uint8_t gpio_mux_offset[32 * ARRAY_SIZE(digital_regs)] = {
+ // gpio 0
+ 0xff , 0xff , MUXPORT(0x950), MUXPORT(0x954),
+ MUXPORT(0x958), MUXPORT(0x95c), 0xff , MUXPORT(0x964),
+ MUXPORT(0x8d0), MUXPORT(0x8d4), MUXPORT(0x8d8), MUXPORT(0x8dc),
+ MUXPORT(0x978), MUXPORT(0x97c), MUXPORT(0x980), MUXPORT(0x984),
+ 0xff , 0xff , 0xff , 0xff ,
+ MUXPORT(0x9b4), 0xff , MUXPORT(0x820), MUXPORT(0x824),
+ 0xff , 0xff , MUXPORT(0x828), MUXPORT(0x82c),
+ 0xff , 0xff , MUXPORT(0x870), MUXPORT(0x874),
+
+ // gpio 1
+ MUXPORT(0x800), MUXPORT(0x804), MUXPORT(0x808), MUXPORT(0x80c),
+ MUXPORT(0x810), MUXPORT(0x814), MUXPORT(0x818), MUXPORT(0x81c),
+ 0xff , 0xff , 0xff , 0xff ,
+ MUXPORT(0x830), MUXPORT(0x834), MUXPORT(0x838), MUXPORT(0x83c),
+ MUXPORT(0x840), MUXPORT(0x844), MUXPORT(0x848), MUXPORT(0x84c),
+ 0xff , 0xff , 0xff , 0xff ,
+ 0xff , 0xff , 0xff , 0xff ,
+ MUXPORT(0x878), MUXPORT(0x87c), MUXPORT(0x880), MUXPORT(0x884),
+
+ // gpio 2
+ 0xff , MUXPORT(0x88c), MUXPORT(0x890), MUXPORT(0x894),
+ MUXPORT(0x898), MUXPORT(0x89c), MUXPORT(0x8a0), MUXPORT(0x8a4),
+ MUXPORT(0x8a8), MUXPORT(0x8ac), MUXPORT(0x8b0), MUXPORT(0x8b4),
+ MUXPORT(0x8b8), MUXPORT(0x8bc), MUXPORT(0x8c0), MUXPORT(0x8c4),
+ MUXPORT(0x8c8), MUXPORT(0x8cc), 0xff , 0xff ,
+ 0xff , 0xff , MUXPORT(0x8e0), MUXPORT(0x8e4),
+ MUXPORT(0x8e8), MUXPORT(0x8ec), 0xff , 0xff ,
+ 0xff , 0xff , 0xff , 0xff ,
+
+ // gpio 3
+ 0xff , 0xff , 0xff , 0xff ,
+ 0xff , 0xff , 0xff , 0xff ,
+ 0xff , 0xff , 0xff , 0xff ,
+ 0xff , 0xff , MUXPORT(0x990), MUXPORT(0x994),
+ MUXPORT(0x998), MUXPORT(0x99c), MUXPORT(0x9a0), MUXPORT(0x9a4),
+ MUXPORT(0x9a8), MUXPORT(0x9ac), 0xff , 0xff ,
+ 0xff , 0xff , 0xff , 0xff ,
+ 0xff , 0xff , 0xff , 0xff ,
+};
+
+#define MUXREG(mux_offset) ((volatile uint32_t *)0x44e10800 + mux_offset)
+
+
+/****************************************************************
+ * General Purpose Input Output (GPIO) pins
+ ****************************************************************/
+
+struct gpio_out
+gpio_out_setup(uint8_t pin, uint8_t val)
+{
+ if (GPIO2PORT(pin) >= ARRAY_SIZE(digital_regs))
+ goto fail;
+ uint8_t mux_offset = gpio_mux_offset[pin];
+ if (mux_offset == 0xff)
+ goto fail;
+ struct gpio_regs *regs = digital_regs[GPIO2PORT(pin)];
+ uint32_t bit = GPIO2BIT(pin);
+ struct gpio_out rv = (struct gpio_out){ .reg=&regs->cleardataout, .bit=bit };
+ gpio_out_write(rv, val);
+ regs->oe &= ~bit;
+ *MUXREG(mux_offset) = 0x0f;
+ return rv;
+fail:
+ shutdown("Not an output pin");
+}
+
+void
+gpio_out_toggle(struct gpio_out g)
+{
+ gpio_out_write(g, !(*g.reg & g.bit));
+}
+
+void
+gpio_out_write(struct gpio_out g, uint8_t val)
+{
+ volatile uint32_t *reg = g.reg;
+ if (val)
+ reg++;
+ *reg = g.bit;
+}
+
+
+struct gpio_in
+gpio_in_setup(uint8_t pin, int8_t pull_up)
+{
+ if (GPIO2PORT(pin) >= ARRAY_SIZE(digital_regs))
+ goto fail;
+ uint8_t mux_offset = gpio_mux_offset[pin];
+ if (mux_offset == 0xff)
+ goto fail;
+ struct gpio_regs *regs = digital_regs[GPIO2PORT(pin)];
+ uint32_t bit = GPIO2BIT(pin);
+ regs->oe |= bit;
+ *MUXREG(mux_offset) = pull_up > 0 ? 0x37 : (pull_up < 0 ? 0x27 : 0x2f);
+ return (struct gpio_in){ .reg=&regs->datain, .bit=bit };
+fail:
+ shutdown("Not an input pin");
+}
+
+uint8_t
+gpio_in_read(struct gpio_in g)
+{
+ return !!(*g.reg & g.bit);
+}
diff --git a/src/pru/gpio.h b/src/pru/gpio.h
new file mode 100644
index 00000000..65c52724
--- /dev/null
+++ b/src/pru/gpio.h
@@ -0,0 +1,22 @@
+#ifndef __PRU_GPIO_H
+#define __PRU_GPIO_H
+
+#include <stdint.h>
+#include <stdbool.h>
+
+struct gpio_out {
+ volatile uint32_t *reg;
+ uint32_t bit;
+};
+struct gpio_out gpio_out_setup(uint8_t pin, uint8_t val);
+void gpio_out_toggle(struct gpio_out g);
+void gpio_out_write(struct gpio_out g, uint8_t val);
+
+struct gpio_in {
+ volatile uint32_t *reg;
+ uint32_t bit;
+};
+struct gpio_in gpio_in_setup(uint8_t pin, int8_t pull_up);
+uint8_t gpio_in_read(struct gpio_in g);
+
+#endif // gpio.h
diff --git a/src/pru/internal.h b/src/pru/internal.h
new file mode 100644
index 00000000..b279f3d0
--- /dev/null
+++ b/src/pru/internal.h
@@ -0,0 +1,18 @@
+#ifndef __PRU_INTERNAL_H
+#define __PRU_INTERNAL_H
+// Local definitions for PRU code
+
+#define IEP_IRQ 0
+#define GOT_ARM_IRQ 1
+#define WAKE_ARM_IRQ 2
+
+#define IEP_EVENT 7
+#define GOT_ARM_EVENT 17
+#define WAKE_ARM_EVENT 16
+
+#define R31_IRQ_OFFSET 30
+
+// console.c
+void console_init(void);
+
+#endif // internal.h
diff --git a/src/pru/main.c b/src/pru/main.c
new file mode 100644
index 00000000..c9c6422a
--- /dev/null
+++ b/src/pru/main.c
@@ -0,0 +1,150 @@
+// Main starting point for PRU code.
+//
+// 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> // read_r31
+#include <pru_cfg.h> // CT_CFG
+#include <pru_iep.h> // CT_IEP
+#include <pru_intc.h> // CT_INTC
+#include "autoconf.h" // CONFIG_CLOCK_FREQ
+#include "board/misc.h" // timer_from_us
+#include "board/irq.h" // irq_disable
+#include "command.h" // shutdown
+#include "generic/timer_irq.h" // timer_dispatch_many
+#include "internal.h" // IEP_IRQ
+#include "sched.h" // sched_main
+
+DECL_CONSTANT(MCU, "pru");
+
+
+/****************************************************************
+ * Timers
+ ****************************************************************/
+
+void
+irq_disable(void)
+{
+}
+
+void
+irq_enable(void)
+{
+}
+
+irqstatus_t
+irq_save(void)
+{
+ return 0;
+}
+
+void
+irq_restore(irqstatus_t flag)
+{
+}
+
+static void
+timer_set(uint32_t value)
+{
+ CT_IEP.TMR_CMP0 = value;
+ CT_INTC.SECR0 = 1 << IEP_EVENT;
+}
+
+uint32_t
+timer_read_time(void)
+{
+ return CT_IEP.TMR_CNT;
+}
+
+static void
+_irq_poll(void)
+{
+ CT_IEP.TMR_CMP_STS = 0xff;
+ uint32_t next = timer_dispatch_many();
+ timer_set(next);
+}
+void
+irq_poll(void)
+{
+ if (read_r31() & (1 << (IEP_IRQ + R31_IRQ_OFFSET)))
+ _irq_poll();
+}
+
+static void
+timer_shutdown(void)
+{
+ // Reenable timer irq
+ timer_set(timer_read_time() + 50);
+ CT_IEP.TMR_CMP_STS = 0xff;
+ __delay_cycles(4);
+ CT_INTC.SECR0 = 1 << IEP_EVENT;
+}
+DECL_SHUTDOWN(timer_shutdown);
+
+static void
+timer_init(void)
+{
+ timer_set(0);
+ CT_IEP.TMR_CMP_CFG = 0x01 << 1;
+ CT_IEP.TMR_GLB_CFG = 0x11;
+ timer_shutdown();
+}
+
+
+/****************************************************************
+ * Allocator
+ ****************************************************************/
+
+extern char _heap_start;
+static void *heap_ptr = &_heap_start;
+
+#define STACK_SIZE 256
+#define END_MEM ((void*)(8*1024 - STACK_SIZE))
+
+// Allocate an area of memory
+void *
+alloc_chunk(size_t size)
+{
+ if (heap_ptr + size > END_MEM)
+ shutdown("alloc_chunk failed");
+ void *data = heap_ptr;
+ heap_ptr += size;
+ memset(data, 0, size);
+ return data;
+}
+
+// Allocate an array of chunks
+void *
+alloc_chunks(size_t size, size_t count, size_t *avail)
+{
+ size_t can_alloc = 0;
+ void *p = heap_ptr;
+ for (; can_alloc <= count && p + size <= END_MEM; can_alloc++, p += size)
+ ;
+ if (!can_alloc)
+ shutdown("alloc_chunks failed");
+ *avail = can_alloc;
+ return alloc_chunk(size * can_alloc);
+}
+
+
+/****************************************************************
+ * Startup
+ ****************************************************************/
+
+// Main entry point
+int
+main(void)
+{
+ // allow access to external memory
+ CT_CFG.SYSCFG_bit.STANDBY_INIT = 0;
+
+ console_init();
+ timer_init();
+
+ sched_main();
+ return 0;
+}
diff --git a/src/pru/pru.lds b/src/pru/pru.lds
new file mode 100644
index 00000000..db36803c
--- /dev/null
+++ b/src/pru/pru.lds
@@ -0,0 +1,9 @@
+/* Final linker script klipper.elf on the PRU */
+SECTIONS
+{
+ /* binutils on the PRU doesn't support --gc-sections so manually
+ * discard the .compile_time_request section */
+ /DISCARD/ : {
+ *( .compile_time_request )
+ }
+}
diff --git a/src/pru/pru1.c b/src/pru/pru1.c
new file mode 100644
index 00000000..3a850a60
--- /dev/null
+++ b/src/pru/pru1.c
@@ -0,0 +1,29 @@
+// Code to halt the unneeded 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 <pru/io.h> // __halt
+#include <rsc_types.h> // resource_table
+#include "compiler.h" // __section
+
+struct my_resource_table {
+ struct resource_table base;
+
+ uint32_t offset[1]; /* Should match 'num' in actual definition */
+} resourceTable __section(".resource_table") = {
+ {
+ 1, /* Resource table version: only version 1 is
+ * supported by the current driver */
+ 0, /* number of entries in the table */
+ { 0, 0 }, /* reserved, must be zero */
+ },
+};
+
+int
+main(void)
+{
+ __halt();
+}