pru: Initial support for the Beaglebone PRU
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
This commit is contained in:
parent
ccaa25eaa5
commit
2255176228
|
@ -8,12 +8,15 @@ choice
|
||||||
bool "Atmega AVR"
|
bool "Atmega AVR"
|
||||||
config MACH_SAM3X8E
|
config MACH_SAM3X8E
|
||||||
bool "SAM3x8e (Arduino Due)"
|
bool "SAM3x8e (Arduino Due)"
|
||||||
|
config MACH_PRU
|
||||||
|
bool "Beaglebone PRU"
|
||||||
config MACH_SIMU
|
config MACH_SIMU
|
||||||
bool "Host simulator"
|
bool "Host simulator"
|
||||||
endchoice
|
endchoice
|
||||||
|
|
||||||
source "src/avr/Kconfig"
|
source "src/avr/Kconfig"
|
||||||
source "src/sam3x8e/Kconfig"
|
source "src/sam3x8e/Kconfig"
|
||||||
|
source "src/pru/Kconfig"
|
||||||
source "src/simulator/Kconfig"
|
source "src/simulator/Kconfig"
|
||||||
|
|
||||||
# The HAVE_GPIO_x options allow boards to disable support for some
|
# The HAVE_GPIO_x options allow boards to disable support for some
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
# Kconfig settings for PRU
|
||||||
|
|
||||||
|
if MACH_PRU
|
||||||
|
|
||||||
|
config BOARD_DIRECTORY
|
||||||
|
string
|
||||||
|
default "pru"
|
||||||
|
|
||||||
|
config CLOCK_FREQ
|
||||||
|
int
|
||||||
|
default 200000000
|
||||||
|
|
||||||
|
endif
|
|
@ -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 $@
|
|
@ -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)
|
||||||
|
;
|
||||||
|
}
|
|
@ -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=®s->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=®s->datain, .bit=bit };
|
||||||
|
fail:
|
||||||
|
shutdown("Not an input pin");
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t
|
||||||
|
gpio_in_read(struct gpio_in g)
|
||||||
|
{
|
||||||
|
return !!(*g.reg & g.bit);
|
||||||
|
}
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||||
|
}
|
|
@ -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 )
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
Loading…
Reference in New Issue