1
0
mirror of https://github.com/aolofsson/oh.git synced 2025-01-17 20:02:53 +08:00

Updated mailbox test software and added trial kernel files

Signed-off-by: Peter Saunderson <peteasa@gmail.com>
This commit is contained in:
Peter Saunderson 2015-12-20 19:59:35 +00:00
parent 51fb4ad4a4
commit a442dd4171
3 changed files with 1146 additions and 101 deletions

View File

@ -0,0 +1,660 @@
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pid.h>
#include <linux/fdtable.h>
#include <linux/rcupdate.h>
#include <linux/eventfd.h>
#include <linux/workqueue.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/cdev.h>
#include <linux/platform_device.h>
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include <linux/gfp.h>
#include <asm/uaccess.h>
#ifdef CONFIG_OF
/* For open firmware. */
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/of_platform.h>
#endif
#include <linux/epiphany.h>
MODULE_AUTHOR("Peter Saunderson");
MODULE_DESCRIPTION("Adapteva Epiphany Driver");
MODULE_LICENSE("GPL");
#define UseReservedMem 1
#define DRIVER_NAME "epiphany"
/* The physical address of the start and end of the Epiphany device memory */
#define EPIPHANY_MEM_START 0x80800000UL
#define EPIPHANY_MEM_END 0xC0000000UL
/* The physical address of the DRAM shared between the host and Epiphany */
#define HOST_MEM_START 0x3E000000UL
#define HOST_MEM_END 0x40000000UL
/* Physical address range that can be mapped to FPGA logic */
/* TODO: Define in devicetree */
#define PL_MEM_START 0x40000000UL
#define PL_MEM_END 0x80000000UL
#define ERX_REG_START 0x810F0300
#define ERX_REG_END 0x810F03FF
#define ERX_CFG_REG 0x0 // 0xF0300
#define MAILBOX_LO_REG 0x20 // 0xF0320
#define MAILBOX_HI_REG 0x24 // 0xF0324
#define MAILBOX_STATE 0x28 // 0xF0328
#define MAILBOX_ENABLE (0x1 << 28) // bit 28 in ERX_CFG_REG
static int major = 0;
static dev_t dev_no = 0;
static struct cdev epiphany_cdev;
static struct class *class_epiphany = 0;
static struct device *dev_epiphany = 0;
static epiphany_alloc_t global_shm;
/* Work stucture */
static struct workqueue_struct *irq_workqueue;
typedef struct
{
struct work_struct work;
struct task_struct * userspace_task;
} irq_work_t;
typedef struct {
irq_work_t irq_work; // @ start of structure so that work is at start
unsigned int irq;
void __iomem *reg_base;
} epiphany_mailbox_t;
static epiphany_mailbox_t mailbox;
static struct eventfd_ctx * efd_ctx = NULL;
static int mailbox_notifier = -1;
static int mailbox_lo = 0;
static int mailbox_hi = 0;
// mailbox_notifier is the eventfd sent by ioctl to the driver.
// Use epoll_wait on the user side to detect the arrival of
// an interrupt. Note epoll_wait can be cancelled by
// waiting on a second eventfd descriptor.
DEVICE_INT_ATTR(mailbox_notifier, S_IRUGO, mailbox_notifier);
// mailbox content is read during interrupt servicing
// the user side code can read the sysfs files for the messages
DEVICE_INT_ATTR(mailbox_lo, S_IRUGO, mailbox_lo);
DEVICE_INT_ATTR(mailbox_hi, S_IRUGO, mailbox_hi);
static struct attribute *attrs[] = {
&dev_attr_mailbox_notifier.attr.attr,
&dev_attr_mailbox_lo.attr.attr,
&dev_attr_mailbox_hi.attr.attr, NULL,
};
static struct attribute_group attr_group = {
.attrs = attrs,
};
static const struct attribute_group *attr_groups[] = {
&attr_group, NULL,
};
/* Function prototypes */
static int epiphany_of_probe(struct platform_device *op);
static int __init epiphany_init(void);
static int epiphany_probe(struct platform_device *pdev);
static int epiphany_remove(struct platform_device *pdev);
static void __exit epiphany_exit(void);
static int epiphany_open(struct inode *, struct file *);
static int epiphany_release(struct inode *, struct file *);
static int epiphany_map_host_memory(struct vm_area_struct *vma);
static int epiphany_map_device_memory(struct vm_area_struct *vma);
static int epiphany_mmap(struct file *, struct vm_area_struct *);
static long epiphany_ioctl(struct file *, unsigned int, unsigned long);
static void reg_write(epiphany_mailbox_t *mailbox, u32 reg, u32 val);
static u32 reg_read(epiphany_mailbox_t *mailbox, u32 reg);
static void enable_mailbox_irq(void);
static void disable_mailbox_irq(void);
static u32 read_mailbox_lo(void);
static u32 read_mailbox_hi(void);
static void irq_work_func(struct work_struct *work);
static irqreturn_t mailbox_irq_handler(int irq, void *data);
static struct file_operations epiphany_fops = {
.owner = THIS_MODULE,
.open = epiphany_open,
.release = epiphany_release,
.mmap = epiphany_mmap,
.unlocked_ioctl = epiphany_ioctl
};
#ifdef CONFIG_OF
/* Match table for device tree binding */
static const struct of_device_id epiphany_of_match[] = {
{ .compatible = "xlnx,parallella-base-1.0"},
{},
};
MODULE_DEVICE_TABLE(of, epiphany_of_match);
#else
#define epiphany_of_match NULL
#endif
#ifdef CONFIG_OF
static int epiphany_of_probe(struct platform_device *op)
{
unsigned int elinkId;
int retval;
retval = of_property_read_u32(op->dev.of_node, "xlnx,read-tag-addr", &elinkId);
printk(KERN_INFO
"epiphany_of_probe(): elinkId: 0x%x\n", elinkId);
return retval;
}
#else
static int epiphany_of_probe(struct platform_device *op)
{
return -EINVAL;
}
#endif /* CONFIG_OF */
static struct platform_driver epiphany_platform_driver = {
.probe = epiphany_probe,
.remove = epiphany_remove,
.driver = {
.name = DRIVER_NAME,
.of_match_table = epiphany_of_match,
},
};
static int __init epiphany_init(void)
{
int retval;
void *ptr_err = 0;
// Allocate character device numbers
// unregistered on error and in .exit
retval = alloc_chrdev_region(&dev_no, 0, 1, DRIVER_NAME);
if (retval < 0) {
printk(KERN_ERR "Failed to register the epiphany driver\n");
return retval;
}
major = MAJOR(dev_no);
dev_no = MKDEV(major, 0);
// Create device class for epiphany
// destroyed on error and in .exit
class_epiphany = class_create(THIS_MODULE, DRIVER_NAME);
if (IS_ERR(ptr_err = class_epiphany)) {
retval = PTR_ERR(ptr_err);
goto err2;
}
// Assign the attribute groups to create the sysfs files
class_epiphany->dev_groups = attr_groups;
// Register the driver with platform
// unregistered in .exit
retval = platform_driver_register(&epiphany_platform_driver);
if (retval) {
goto err;
}
return retval;
err:
class_destroy(class_epiphany);
err2:
unregister_chrdev_region(dev_no, 1);
return retval;
}
static int epiphany_probe(struct platform_device *pdev)
{
int retval = 0;
void *ptr_err = 0;
const struct of_device_id *match;
struct resource *io;
match = of_match_device(epiphany_of_match, &pdev->dev);
if (!match)
{
return -ENODEV;
}
retval = epiphany_of_probe(pdev);
if (retval)
{
return -ENODEV;
}
#if UseReservedMem
/*
** Use the system reserved memory until we have a way
** to tell epiphany what the dynamically allocated address is
*/
global_shm.size = GLOBAL_SHM_SIZE;
global_shm.flags = 0;
global_shm.bus_addr = 0x8e000000 + 0x01000000; /* From platform.hdf + shared_dram offset */
global_shm.phy_addr = HOST_MEM_START + 0x01000000; /* From platform.hdf + shared_dram offset */
global_shm.kvirt_addr = (unsigned long)ioremap_nocache(global_shm.phy_addr, 0x01000000); /* FIXME: not portable */
global_shm.uvirt_addr = 0; /* Set by user when mmapped */
global_shm.mmap_handle = global_shm.phy_addr;
#else
// Allocate shared memory
// Zero the shared memory
memset(&global_shm, 0, sizeof(global_shm));
global_shm.size = GLOBAL_SHM_SIZE;
global_shm.flags = GFP_KERNEL;
global_shm.kvirt_addr = __get_free_pages(GFP_KERNEL,
get_order(GLOBAL_SHM_SIZE));
if (!global_shm.kvirt_addr) {
printk(KERN_ERR
"epiphany_probe() - Unable to allocate contiguous "
"memory for global shared region\n");
goto err;
}
global_shm.phy_addr = __pa(global_shm.kvirt_addr);
global_shm.bus_addr = global_shm.phys_addr;
#endif
// Zero the Global Shared Memory region
memset((void *)global_shm.kvirt_addr, 0, GLOBAL_SHM_SIZE);
printk(KERN_INFO
"epiphany_probe() - shared memory: bus 0x%08lx, phy 0x%08lx, kvirt 0x%08lx\n",
global_shm.bus_addr, global_shm.phy_addr, global_shm.kvirt_addr);
// Map the rx registers for mailbox access
io = platform_get_resource(pdev, IORESOURCE_MEM, 0);
printk(KERN_INFO
"epiphany_probe() - registers: start 0x%08lx, end 0x%08lx, name %s, flags %x\n",
(unsigned long)io->start, (unsigned long)io->end, io->name, io->flags);
// for now keep it simple and dont reference the device tree stuff
// not sure if ioremap here will prevent other user space mmap!
io->start = ERX_REG_START;
io->end = ERX_REG_END;
mailbox.reg_base = devm_ioremap_resource(&pdev->dev, io);
if (IS_ERR(mailbox.reg_base))
{
dev_err(&pdev->dev, "failed to map mailbox registers\n");
return PTR_ERR(mailbox.reg_base);
}
// Get the mailbox irq from device tree
mailbox.irq = platform_get_irq(pdev, 0);
if (0 > mailbox.irq) {
dev_err(&pdev->dev, "irq resource not found\n");
return mailbox.irq;
}
printk(KERN_INFO
"epiphany_probe(): mailbox irq: 0x%x\n", mailbox.irq);
// Register the mailbox irq handler
// removed on error and in .remove
retval = devm_request_irq(&pdev->dev, mailbox.irq, mailbox_irq_handler,
0, pdev->name, &mailbox);
if (0 > retval) {
dev_err(&pdev->dev, "request_irq failed\n");
printk(KERN_ERR
"epiphany_probe() - Unable to request IRQ %d\n", mailbox.irq);
goto err;
}
// Initialize the workqueue
// Killed on error and in .remove
irq_workqueue = create_workqueue("irq_work_queue");
if (irq_workqueue)
{
INIT_WORK(((struct work_struct *)&mailbox), irq_work_func);
}
// Initialize the cdev structure
// deleted on error and in .remove
cdev_init(&epiphany_cdev, &epiphany_fops);
epiphany_cdev.owner = THIS_MODULE;
// Add the character driver to the system
// deleted on error and in .remove
retval = cdev_add(&epiphany_cdev, dev_no, 1);
if (0 > retval) {
printk(KERN_ERR
"epiphany_probe() - Unable to add character device\n");
goto err1;
}
// Create the character device
// deleted in .remove
dev_epiphany = device_create(class_epiphany, NULL, dev_no, NULL,
DRIVER_NAME);
if (IS_ERR(ptr_err = dev_epiphany)) {
retval = PTR_ERR(ptr_err);
goto err2;
}
return 0;
err2:
cdev_del(&epiphany_cdev);
err1:
destroy_workqueue(irq_workqueue);
devm_free_irq(&pdev->dev, mailbox.irq, &mailbox);
err:
return retval;
}
static int epiphany_remove(struct platform_device *pdev)
{
// Disable the interrupt first
disable_mailbox_irq();
// Remove interrupt handler
if (mailbox.irq > 0)
{
devm_free_irq(&pdev->dev, mailbox.irq, &mailbox);
}
// flush the queue
flush_workqueue(irq_workqueue);
// destroy the queue
destroy_workqueue(irq_workqueue);
#if (UseReservedMem == 0)
free_pages(global_shm.kvirt_addr, get_order(global_shm.size));
#endif
device_destroy(class_epiphany, MKDEV(major, 0));
cdev_del(&epiphany_cdev);
return 0;
}
static void __exit epiphany_exit(void)
{
// Unregister driver from plaform
platform_driver_unregister(&epiphany_platform_driver);
// Destroy the epiphany class
class_destroy(class_epiphany);
// Unregister the character device numbers
unregister_chrdev_region(dev_no, 1);
}
static int epiphany_open(struct inode *inode, struct file *file)
{
return 0;
}
static int epiphany_release(struct inode *inode, struct file *file)
{
return 0;
}
static const struct vm_operations_struct mmap_mem_ops = {
#ifdef CONFIG_HAVE_IOREMAP_PROT
.access = generic_access_phys
#endif
};
/**
* Map memory that can be shared between the Epiphany
* device and user-space
*/
static int epiphany_map_host_memory(struct vm_area_struct *vma)
{
int err;
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
err = remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
vma->vm_end - vma->vm_start, vma->vm_page_prot);
if (err) {
printk(KERN_ERR "Failed mapping host memory to vma 0x%08lx, "
"size 0x%08lx, page offset 0x%08lx\n",
vma->vm_start, vma->vm_end - vma->vm_start,
vma->vm_pgoff);
}
return err;
}
static int epiphany_map_device_memory(struct vm_area_struct *vma)
{
int err, retval = 0;
unsigned long pfn = vma->vm_pgoff;
unsigned long size = vma->vm_end - vma->vm_start;
vma->vm_flags |= (VM_IO | VM_DONTEXPAND | VM_DONTDUMP);
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
#ifdef EHalUsesOffsetsRatherThanAbsoluteAddress
pfn = (EPIPHANY_MEM_START + off) >> PAGE_SHIFT;
#endif
err = io_remap_pfn_range(vma, vma->vm_start, pfn, size,
vma->vm_page_prot);
if (err) {
printk(KERN_ERR "Failed mapping device memory to vma 0x%08lx, "
"size 0x%08lx, page offset 0x%08lx\n",
vma->vm_start, vma->vm_end - vma->vm_start,
vma->vm_pgoff);
retval = -EAGAIN;
}
return retval;
}
static int epiphany_mmap(struct file *file, struct vm_area_struct *vma)
{
int retval = 0;
unsigned long off = vma->vm_pgoff << PAGE_SHIFT;
unsigned long size = vma->vm_end - vma->vm_start;
vma->vm_ops = &mmap_mem_ops;
if ((EPIPHANY_MEM_START <= off ) && ((off + size) <= EPIPHANY_MEM_END)) {
retval = epiphany_map_device_memory(vma);
} else if ((PL_MEM_START <= off ) && ((off + size) <= PL_MEM_END)) {
retval = epiphany_map_device_memory(vma);
} else if ((HOST_MEM_START <= off) && ((off + size) <= HOST_MEM_END)) {
retval = epiphany_map_host_memory(vma);
} else {
printk(KERN_DEBUG "epiphany_mmap - invalid request to map "
"0x%08lx, length 0x%08lx bytes\n", off, size);
retval = -EINVAL;
}
return retval;
}
static long epiphany_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
int retval = 0;
int err = 0;
epiphany_alloc_t *ealloc = NULL;
mailbox_notifier_t notifier;
if (_IOC_TYPE(cmd) != EPIPHANY_IOC_MAGIC) {
return -ENOTTY;
}
if (_IOC_NR(cmd) > EPIPHANY_IOC_MAXNR) {
return -ENOTTY;
}
if (_IOC_DIR(cmd) & _IOC_READ) {
err =
!access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd));
} else if (_IOC_DIR(cmd) & _IOC_WRITE) {
err =
!access_ok(VERIFY_WRITE, (void __user *)arg,
_IOC_SIZE(cmd));
}
if (err) {
return -EFAULT;
}
switch (cmd) {
case EPIPHANY_IOC_GETSHM:
ealloc = (epiphany_alloc_t *) (arg);
if (copy_to_user(ealloc, &global_shm, sizeof(*ealloc))) {
printk(KERN_ERR "EPIPHANY_IOC_GETSHM - failed\n");
retval = -EACCES;
}
break;
case EPIPHANY_IOC_MB_ENABLE:
enable_mailbox_irq();
break;
case EPIPHANY_IOC_MB_DISABLE:
disable_mailbox_irq();
break;
case EPIPHANY_IOC_MB_NOTIFIER:
// TODO lock access
if (copy_from_user(&notifier, (void __user *)arg, sizeof(mailbox_notifier_t)))
{
printk(KERN_ERR "EPIPHANY_IOC_MB_NOTIFIER - failed\n");
retval = -EACCES;
}
if (notifier.old_notifier != mailbox_notifier)
{
printk(KERN_ERR "EPIPHANY_IOC_MB_NOTIFIER - %d != %d\n", notifier.old_notifier, mailbox_notifier);
retval = -EACCES;
}
else
{
// flush the queue
flush_workqueue(irq_workqueue);
mailbox_notifier = notifier.new_notifier;
}
if (0 < mailbox_notifier)
{
// Save the userspace task for later use
mailbox.irq_work.userspace_task = pid_task(find_vpid(current->pid), PIDTYPE_PID);
}
// TODO unlock
break;
default: /* Redundant, cmd was checked against MAXNR */
return -ENOTTY;
}
return retval;
}
static inline void reg_write(epiphany_mailbox_t *mailbox, u32 reg, u32 val)
{
iowrite32(val, mailbox->reg_base + reg);
}
static inline u32 reg_read(epiphany_mailbox_t *mailbox, u32 reg)
{
return ioread32(mailbox->reg_base + reg);
}
static inline void enable_mailbox_irq(void)
{
u32 cfg;
// How to lock ERX_CFG_REG access - could be used from user side at same time!
cfg = reg_read(&mailbox, ERX_CFG_REG);
reg_write(&mailbox, ERX_CFG_REG, cfg | MAILBOX_ENABLE);
}
static inline void disable_mailbox_irq(void)
{
u32 cfg;
// How to lock ERX_CFG_REG access - could be used from user side at same time!
cfg = reg_read(&mailbox, ERX_CFG_REG);
reg_write(&mailbox, ERX_CFG_REG, cfg & ~MAILBOX_ENABLE);
}
static inline u32 read_mailbox_lo(void)
{
return reg_read(&mailbox, (u32)MAILBOX_LO_REG);
}
static inline u32 read_mailbox_hi(void)
{
return reg_read(&mailbox, (u32)MAILBOX_HI_REG);
}
static void irq_work_func(struct work_struct *work)
{
irq_work_t * irq_work = (irq_work_t *)work;
struct file * efd_file = NULL;
mailbox_lo = (int)read_mailbox_lo();
mailbox_hi = (int)read_mailbox_hi();
// current file is always used at the time of the interrupt
if (0 < mailbox_notifier)
{
rcu_read_lock();
efd_file = fcheck_files(irq_work->userspace_task->files, mailbox_notifier);
rcu_read_unlock();
// printk(KERN_INFO "EPIPHANY_IOC_MB_NOTIFIER: %p\n", efd_file);
efd_ctx = eventfd_ctx_fileget(efd_file);
if (!efd_ctx)
{
printk(KERN_ERR "EPIPHANY_IOC_MB_NOTIFIER: failed to get eventfd file\n");
// TODO consider setting mailbox_notifier back to default
// this might complicate the user side
// mailbox_notifier = -1;
return;
}
// send the event
eventfd_signal(efd_ctx, 1);
}
}
/**
* mailbox_irq_handler - Mailbox Interrupt handler
* @irq: IRQ number
* @data: Pointer to
*
* Return: IRQ_HANDLED/IRQ_NONE
*/
static irqreturn_t mailbox_irq_handler(int irq, void *data)
{
epiphany_mailbox_t *mailbox = (epiphany_mailbox_t *)data;
// disable the interrupt
disable_mailbox_irq();
if (0 < mailbox_notifier && irq_workqueue)
{
queue_work(irq_workqueue, &mailbox->irq_work.work);
}
return IRQ_HANDLED;
}
module_init(epiphany_init);
module_exit(epiphany_exit);

View File

@ -0,0 +1,60 @@
#ifndef EPIPHANY_H
#define EPIPHANY_H
#include <linux/ioctl.h>
/** Length of the Global shared memory region */
#define GLOBAL_SHM_SIZE (4<<20)
#define SHM_LOCK_NAME "/eshmlock"
#define SHM_MAGIC 0xabcdef00
typedef struct _EPIPHANY_ALLOC
{
unsigned long size;
unsigned long flags;
unsigned long bus_addr; /* out */
unsigned long phy_addr; /* out */
unsigned long kvirt_addr; /* out */
unsigned long uvirt_addr; /* out */
unsigned long mmap_handle; /* Handle to use for mmap */
} epiphany_alloc_t;
typedef struct _MAILBOX_NOTIFIER
{
int old_notifier;
int new_notifier;
} mailbox_notifier_t;
#define EPIPHANY_IOC_MAGIC 'k'
/**
* If you add an IOC command, please update the
* EPIPHANY_IOC_MAXNR macro
*/
#define EPIPHANY_IOC_GETSHM_CMD 24
#define EPIPHANY_IOC_MB_DISABLE_CMD 25
#define EPIPHANY_IOC_MB_ENABLE_CMD 26
#define EPIPHANY_IOC_MB_NOTIFIER_CMD 27
#define EPIPHANY_IOC_MAXNR 27
#define EPIPHANY_IOC_GETSHM _IOWR(EPIPHANY_IOC_MAGIC, EPIPHANY_IOC_GETSHM_CMD, epiphany_alloc_t *)
#define EPIPHANY_IOC_MB_ENABLE _IO(EPIPHANY_IOC_MAGIC, EPIPHANY_IOC_MB_ENABLE_CMD)
#define EPIPHANY_IOC_MB_DISABLE _IO(EPIPHANY_IOC_MAGIC, EPIPHANY_IOC_MB_DISABLE_CMD)
#define EPIPHANY_IOC_MB_NOTIFIER _IOW(EPIPHANY_IOC_MAGIC, EPIPHANY_IOC_MB_NOTIFIER_CMD, mailbox_notifier_t *)
/**
* mailbox notifier file
*/
#define MAILBOX_NOTIFIER "/sys/class/epiphany/epiphany/mailbox_notifier"
/**
* mailbox high byte valid after interrupt
*/
#define MAILBOX_HI "/sys/class/epiphany/epiphany/mailbox_hi"
/**
* mailbox low byte valid after interrupt
*/
#define MAILBOX_LO "/sys/class/epiphany/epiphany/mailbox_lo"
#endif

View File

@ -2,6 +2,8 @@
#include <stdlib.h>
#include <stdio.h>
#include <sys/epoll.h>
#include <sys/eventfd.h>
#include <string.h>
#include <e-hal.h>
#include <e-loader.h>
@ -11,96 +13,79 @@
#include <unistd.h>
#include <ctype.h>
#include <err.h>
#include <errno.h> // for errno
#include <stdint.h>
#include <assert.h>
#include <uapi/linux/epiphany.h> // for ioctl numbers
#define TAPS 64
// Epiphany system registers
typedef enum {
E_SYS_RESET = 0xF0200,
E_SYS_CLKCFG = 0xF0204,
E_SYS_CHIPID = 0xF0208,
E_SYS_VERSION = 0xF020c,
E_SYS_TXCFG = 0xF0210,
E_SYS_RXCFG = 0xF0300,
E_SYS_RXDMACFG = 0xF0500,
} e_sys_reg_id_t;
#define E_SYS_TXSTATUS (0xF0214)
#define E_SYS_RXSTATUS (0xF0304)
#define E_SYS_RXIDELAY0 (0xF0310)
#define E_SYS_RXIDELAY1 (0xF0314)
#define EPIPHANY_DEV "/dev/epiphany"
typedef union {
unsigned int reg;
struct {
unsigned int reset:1;
// unsigned int chip_reset:1;
// unsigned int reset:1;
};
} e_sys_reset_t;
#ifdef EPIPHANY_IOC_MB_ENABLE
typedef struct _MAILBOX_CONTROL
{
int running; // 1 if ready; otherwise not ready
int devfd; // handle for epiphany device driver
int epollfd; // handle for blocking wait on notification
int kernelEventfd; // handle for kernel notification
struct epoll_event *events;
mailbox_notifier_t mailbox_notifier;
} mailbox_control_t;
static mailbox_control_t mc;
#endif
typedef union {
unsigned int reg;
struct {
unsigned int cclk_enable:1;
unsigned int lclk_enable:1;
unsigned int cclk_bypass:1;
unsigned int lclk_bypass:1;
unsigned int cclk_divider:4;
unsigned int lclk_divider:4;
};
} e_sys_clkcfg_t;
int InitialEpiphany();
void CloseEpiphany();
int InitialTestMessageStore();
void CloseTestMessageStore();
int InitialMailboxNotifier();
int OpenEpiphanyDevice();
int OpenKernelEventMonitor();
void CloseMailboxNotifier();
void CancelMailboxNotifier();
int WaitForMailboxNotifier();
void PrintStuffOfInterest();
typedef union {
unsigned int reg;
struct {
unsigned int col:6;
unsigned int row:6;
};
} e_sys_chipid_t;
static int UseInterrupts = 0;
static e_sys_rxcfg_t rxcfg = { .reg = 0 };
typedef union {
unsigned int reg;
struct {
unsigned int platform:8;
unsigned int revision:8;
};
} e_sys_version_t;
typedef union {
unsigned int reg;
struct {
unsigned int enable:1;
unsigned int mmu_enable:1;
unsigned int remap_cfg:2;
unsigned int ctrlmode:4;
unsigned int ctrlmode_select:1;
unsigned int transmit_mode:3;
};
} e_sys_txcfg_t;
typedef union {
unsigned int reg;
struct {
unsigned int testmode:1;
unsigned int mmu_enable:1;
unsigned int remap_cfg:2;
unsigned int remap_mask:12;
unsigned int remap_base:12;
unsigned int timeout:2;
};
} e_sys_rxcfg_t;
typedef union {
unsigned int reg;
struct {
unsigned int enable:1;
unsigned int master_mode:1;
unsigned int __reserved1:3;
unsigned int width:2;
unsigned int __reserved2:3;
unsigned int message_mode:1;
unsigned int src_shift:1;
unsigned int dst_shift:1;
};
} e_sys_rx_dmacfg_t;
static int idelay[TAPS]={0x00000000,0x00000000,//0
0x11111111,0x00000001,//1
0x22222222,0x00000002,//2
0x33333333,0x00000003,//3
0x44444444,0x00000004,//4
0x55555555,0x00000005,//5
0x66666666,0x00000006,//6
0x77777777,0x00000007,//7
0x88888888,0x00000008,//8
0x99999999,0x00000009,//9
0xaaaaaaaa,0x0000000a,//10
0xbbbbbbbb,0x0000000b,//11
0xcccccccc,0x0000000c,//12
0xdddddddd,0x0000000d,//13
0xeeeeeeee,0x0000000e,//14
0xffffffff,0x0000000f,//15
0x00000000,0x00000010,//16
0x11111111,0x00000011,//17
0x22222222,0x00000012,//18
0x33333333,0x00000013,//29
0x44444444,0x00000014,//20
0x55555555,0x00000015,//21
0x66666666,0x00000016,//22
0x77777777,0x00000017,//23
0x88888888,0x00000018,//24
0x99999999,0x00000019,//25
0xaaaaaaaa,0x0000001a,//26
0xbbbbbbbb,0x0000001b,//27
0xcccccccc,0x0000001c,//28
0xdddddddd,0x0000001d,//29
0xeeeeeeee,0x0000001e,//30
0xffffffff,0x0000001f};//31
int main(int argc, char *argv[]){
e_loader_diag_t e_verbose;
@ -116,6 +101,16 @@ int main(int argc, char *argv[]){
unsigned result[N];
unsigned data = 0xDEADBEEF;
unsigned tmp,fail;
unsigned row, col, loopcount;
int delayNo = 7;
srand(time(NULL));
if (UseInterrupts)
{
// Initialize the Mailbox notifier
InitialMailboxNotifier();
}
//Gets ELF file name from command line
strcpy(elfFile, "./bin/e-task.elf");
@ -125,26 +120,97 @@ int main(int argc, char *argv[]){
e_init(NULL);
my_reset_system();
e_get_platform_info(&platform);
e_open(&dev, 0, 0, 1, 1); //open core 0,0
//Start program
e_load_group(elfFile, &dev, 0, 0, 1, 1, E_TRUE);
for (loopcount=0; loopcount<50; loopcount++)
{
// Draw a random core
row = rand() % platform.rows;
col = rand() % platform.cols;
//This wait will be replaced by interrupt
//how to enable interrupt in kernel?
usleep(100000);
if (!UseInterrupts)
{
// Without interrupts
// For some reason fixed row and col is better
row = 0;
col = 0;
//Reading mailbox
int pre_stat = ee_read_esys(0xF0328);
int mbox_lo = ee_read_esys(0xF0320);
int mbox_hi = ee_read_esys(0xF0324);
int post_stat = ee_read_esys(0xF0328);
// post_stat = ee_read_esys(0xF0304);
// With interrupts fixed row and col fails earlier!
}
printf ("PRE_STAT=%08x POST_STAT=%08x LO=%08x HI=%08x\n", pre_stat, post_stat, mbox_lo, mbox_hi);
printf("INFO: loopcount: %d, row: %d, col: %d\n", loopcount, row, col);
e_open(&dev, row, col, 1, 1); //open core 0,0
e_reset_group(&dev);
if (sizeof(int) != ee_write_esys(E_SYS_RXIDELAY0, idelay[((delayNo+1)*2)-2]))
{
printf("INFO: setting idelay0 failed\n");
}
if (sizeof(int) != ee_write_esys(E_SYS_RXIDELAY1, idelay[((delayNo+1)*2)-1]))
{
printf("INFO: setting idelay1 failed\n");
}
if (UseInterrupts)
{
// Configure epoll to listen for interrupt event
int rtn = ArmMailboxNotifier();
if (rtn)
{
int rtns = errno;
printf ("main(): EPOLL_CTL_ADD kernelEventfd failed! %s errno: %d\n", strerror(rtns), rtns);
break;
}
// Enable the mailbox interrupt
rxcfg.reg = rxcfg.reg | (0x1 << 28);
if (sizeof(int) != ee_write_esys(E_SYS_RXCFG, rxcfg.reg))
{
printf("main(): Failed set rxcfg register\n");
}
}
//Start program
printf("main(%d,%d): Load core\n", row, col);
if (e_load_group(elfFile, &dev, 0, 0, 1, 1, E_TRUE))
{
break;
}
printf("main(%d,%d): Core Loaded\n", row, col);
if (UseInterrupts)
{
WaitForMailboxNotifier();
}
else
{
usleep(100000);
}
//Reading mailbox
int pre_stat = ee_read_esys(0xF0328);
int mbox_lo = ee_read_esys(0xF0320);
int mbox_hi = ee_read_esys(0xF0324);
int post_stat = ee_read_esys(0xF0328);
// post_stat = ee_read_esys(0xF0304);
printf ("PRE_STAT=%08x POST_STAT=%08x LO=%08x HI=%08x\n", pre_stat, post_stat, mbox_lo, mbox_hi);
PrintStuffOfInterest();
e_close(&dev);
}
if (UseInterrupts)
{
CloseMailboxNotifier();
}
//Close down Epiphany device
e_close(&dev);
e_finalize();
//self check
@ -163,7 +229,6 @@ int my_reset_system(void)
uint32_t divider;
uint32_t chipid;
e_sys_txcfg_t txcfg = { .reg = 0 };
e_sys_rxcfg_t rxcfg = { .reg = 0 };
e_sys_rx_dmacfg_t rx_dmacfg = { .reg = 0 };
e_sys_clkcfg_t clkcfg = { .reg = 0 };
e_sys_reset_t resetcfg = { .reg = 0 };
@ -240,13 +305,273 @@ int my_reset_system(void)
rc = E_OK;
cleanup_platform:
if (E_OK != rc) printf("e_reset_system(): fails\n");
e_close(&dev);
usleep(1000);
return E_OK;
return rc;
err:
warnx("e_reset_system(): Failed\n");
usleep(1000);
return E_ERR;
}
void PrintStuffOfInterest()
{
// Print stuff of interest
int etx_status = ee_read_esys(E_SYS_TXSTATUS);
int etx_config = ee_read_esys(E_SYS_TXCFG);
int erx_status = ee_read_esys(E_SYS_RXSTATUS);
int erx_config = ee_read_esys(E_SYS_RXCFG);
int erx_idelay0 = ee_read_esys(E_SYS_RXIDELAY0);
int erx_idelay1 = ee_read_esys(E_SYS_RXIDELAY1);
printf("INFO: etx_status: 0x%x, etx_config: 0x%x, erx_status: 0x%x, erx_config: 0x%x\n", etx_status, etx_config, erx_status, erx_config);
printf("INFO: erx_idelay0: 0x%x, erx_idelay1: 0x%x\n", erx_idelay0, erx_idelay1);
}
int InitialMailboxNotifier()
{
#ifdef EPIPHANY_IOC_MB_ENABLE
int returns;
mc.running = 0;
// Open an epoll object
mc.epollfd = epoll_create(1);
if ( -1 == mc.epollfd ) {
printf("InitialMailboxNotifier(): epoll open failure.");
return E_ERR;
}
returns = OpenEpiphanyDevice();
if (returns)
{
printf("InitialMailboxNotifier(): epiphany open failure.");
close(mc.epollfd);
return returns;
}
returns = OpenKernelEventMonitor();
if (returns)
{
printf("InitialMailboxNotifier(): mailbox sysfs monitor open failure.");
// Tidy up
close(mc.devfd);
close(mc.epollfd);
return returns;
}
// Now allocate the event list
mc.events = calloc(2, sizeof(struct epoll_event));
if (NULL == mc.events)
{
printf("InitialMailboxNotifier(): malloc of event memory failure.");
// Tidy up
struct epoll_event event;
epoll_ctl (mc.epollfd, EPOLL_CTL_DEL, mc.kernelEventfd, &event);
close(mc.kernelEventfd);
close(mc.devfd);
close(mc.epollfd);
return returns;
}
mc.running = 1;
return returns;
#endif
return 0;
}
int OpenEpiphanyDevice()
{
#ifdef EPIPHANY_IOC_MB_ENABLE
// Now open the epiphany device for mailbox interrupt control
mc.devfd = open(EPIPHANY_DEV, O_RDWR | O_SYNC);
// printf ("OpenEpiphanyDevice(): mc.devfd %d\n", mc.devfd);
if ( -1 == mc.devfd )
{
int rtn = errno;
printf ("InitialMaiboxNotifier(): epiphany device open failed! %s errno %d\n", strerror(rtn), rtn);
return E_ERR;
}
#endif
return 0;
}
int OpenKernelEventMonitor()
{
#ifdef EPIPHANY_IOC_MB_ENABLE
int returns;
char notifier[8];
int notifierfd;
int oldNotifier;
// Open the kernel Event Notifier
mc.kernelEventfd = eventfd(0, EFD_NONBLOCK);
if ( -1 == mc.kernelEventfd )
{
int rtn = errno;
printf ("InitialMailboxNotifier(): Kernel Event Notifier open failure! %s errno: %d\n", strerror(rtn), rtn);
return E_ERR;
}
// Add the kernelEventfd handle to epoll
returns = ModifyNotifier(mc.kernelEventfd, EPOLL_CTL_ADD);
if (returns)
{
int rtn = errno;
printf ("InitialMailboxNotifier(): EPOLL_CTL_ADD kernelEventfd failed! %s errno: %d kernelEventfd: %d\n", strerror(rtn), rtn, mc.kernelEventfd);
// Tidy up
close(mc.kernelEventfd);
return rtn;
}
// Starting from scratch with no other application running
// read the current kernel mailbox_notifier fd handle
// If the current kernel mailbox_notifier fd handle is -1 there is no
// other application using the mailbox.
notifierfd = open(MAILBOX_NOTIFIER, O_RDONLY);
oldNotifier = -1;
if (0 < notifierfd)
{
int rtn = read(notifierfd, notifier, 8);
// printf ("InitialMailboxNotifier(): returns: %d, Old notifier fd: %s\n", rtn, notifier);
if (rtn)
{
sscanf(notifier, "%d", &oldNotifier);
}
close(notifierfd);
}
// Starting from scratch ignore other applications and override them
// by passing the old kernel mailbox_notifier fd handle to the driver
// and replace this with the new fd
mc.mailbox_notifier.old_notifier = oldNotifier;
mc.mailbox_notifier.new_notifier = mc.kernelEventfd;
if ( -1 == ioctl(mc.devfd, EPIPHANY_IOC_MB_NOTIFIER, &mc.mailbox_notifier) )
{
int rtn = errno;
printf("InitialMailboxNotifier(): Failed to send notifier to driver. %s errno: %d kernelEventfd: %d\n", strerror(rtn), rtn, mc.kernelEventfd);
// Tidy up
struct epoll_event event;
epoll_ctl (mc.epollfd, EPOLL_CTL_DEL, mc.kernelEventfd, &event);
close(mc.kernelEventfd);
mc.kernelEventfd = -1;
return rtn;
}
return returns;
#endif
return 0;
}
int ArmMailboxNotifier()
{
#ifdef EPIPHANY_IOC_MB_ENABLE
if (mc.running)
{
return ModifyNotifier(mc.kernelEventfd, EPOLL_CTL_MOD);
}
return E_ERR;
#endif
return 0;
}
int ModifyNotifier(int fd, int operation)
{
return UpdateEpoll(fd, operation, EPOLLIN | EPOLLET);
}
int UpdateEpoll(int fd, int operation, uint32_t waitOnEvent)
{
#ifdef EPIPHANY_IOC_MB_ENABLE
int returns;
struct epoll_event event;
returns = E_ERR;
// Add the kernelEventfd handle to epoll
event.data.fd = fd;
event.events = waitOnEvent;
returns = epoll_ctl (mc.epollfd, operation, fd, &event);
if (returns)
{
returns = errno;
printf ("InitialMailboxNotifier(): epoll_ctl failed! %s errno: %d operation: %d, event: %d, fd: %d\n", strerror(returns), returns, operation, waitOnEvent, fd);
}
return returns;
#endif
return 0;
}
int WaitForMailboxNotifier()
{
#ifdef EPIPHANY_IOC_MB_ENABLE
int numberOfEvents;
size_t bytesRead;
int64_t eventfdCount;
numberOfEvents = epoll_wait(mc.epollfd, mc.events, 2, -1);
if ((1 < numberOfEvents) || (mc.events[0].data.fd != mc.kernelEventfd))
{
printf("INFO: WaitForMailboxNotifier(): Cancelled!\n");
}
if (0 > numberOfEvents)
{
int epollerrno = errno;
printf("WaitForMailboxNotifier(): epoll_wait failed! %s errno %d\n", strerror(epollerrno), epollerrno);
}
bytesRead = read(mc.kernelEventfd, &eventfdCount, sizeof(int64_t));
if (0 > bytesRead)
{
// failure to reset the eventfd counter to zero
// can cause lockups!
int eventfderrno = errno;
printf("ERROR: WaitForMailboxNotifier(): lockup likely: eventfd counter reset failed! %s errno %d\n", strerror(eventfderrno), eventfderrno);
}
// printf("WaitForMailboxNotifier(): bytesRead: %d, eventfdCount: %d\n", bytesRead, eventfdCount);
return numberOfEvents;
#else
// do the best we can and wait
usleep(100000);
return 0;
#endif
}
void CloseMailboxNotifier()
{
#ifdef EPIPHANY_IOC_MB_ENABLE
//printf ("INFO: MailboxNotifier Closing\n");
if (mc.running)
{
if (0 < mc.kernelEventfd)
{
mc.mailbox_notifier.old_notifier = mc.kernelEventfd;
mc.mailbox_notifier.new_notifier = -1;
ioctl(mc.devfd, EPIPHANY_IOC_MB_NOTIFIER, &mc.mailbox_notifier);
}
struct epoll_event event;
epoll_ctl (mc.epollfd, EPOLL_CTL_DEL, mc.kernelEventfd, &event);
free((void *)mc.events);
close(mc.kernelEventfd);
mc.kernelEventfd = -1;
close(mc.devfd);
close(mc.epollfd);
}
#endif
}