From a442dd417124eb16d297ee69ef0296a57a49fb75 Mon Sep 17 00:00:00 2001 From: Peter Saunderson Date: Sun, 20 Dec 2015 19:59:35 +0000 Subject: [PATCH] Updated mailbox test software and added trial kernel files Signed-off-by: Peter Saunderson --- elink/sw/mailbox/kernel/epiphany.c | 660 +++++++++++++++++++++++++++++ elink/sw/mailbox/kernel/epiphany.h | 60 +++ elink/sw/mailbox/src/e-main.c | 527 ++++++++++++++++++----- 3 files changed, 1146 insertions(+), 101 deletions(-) create mode 100644 elink/sw/mailbox/kernel/epiphany.c create mode 100644 elink/sw/mailbox/kernel/epiphany.h diff --git a/elink/sw/mailbox/kernel/epiphany.c b/elink/sw/mailbox/kernel/epiphany.c new file mode 100644 index 0000000..32dae9b --- /dev/null +++ b/elink/sw/mailbox/kernel/epiphany.c @@ -0,0 +1,660 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_OF +/* For open firmware. */ +#include +#include +#include +#endif + +#include + +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(¬ifier, (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); diff --git a/elink/sw/mailbox/kernel/epiphany.h b/elink/sw/mailbox/kernel/epiphany.h new file mode 100644 index 0000000..2915cb7 --- /dev/null +++ b/elink/sw/mailbox/kernel/epiphany.h @@ -0,0 +1,60 @@ +#ifndef EPIPHANY_H +#define EPIPHANY_H +#include + +/** 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 diff --git a/elink/sw/mailbox/src/e-main.c b/elink/sw/mailbox/src/e-main.c index d7ed4db..13d855b 100644 --- a/elink/sw/mailbox/src/e-main.c +++ b/elink/sw/mailbox/src/e-main.c @@ -2,6 +2,8 @@ #include #include +#include +#include #include #include #include @@ -11,96 +13,79 @@ #include #include #include +#include // for errno #include #include +#include // 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,7 +101,17 @@ 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"); @@ -124,27 +119,98 @@ int main(int argc, char *argv[]){ e_set_host_verbosity(H_D0); e_init(NULL); my_reset_system(); - e_get_platform_info(&platform); - e_open(&dev, 0, 0, 1, 1); //open core 0,0 + e_get_platform_info(&platform); - //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("INFO: loopcount: %d, row: %d, col: %d\n", loopcount, row, col); - printf ("PRE_STAT=%08x POST_STAT=%08x LO=%08x HI=%08x\n", pre_stat, post_stat, mbox_lo, mbox_hi); - + 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 }; @@ -236,17 +301,277 @@ int my_reset_system(void) usleep(1000); if (sizeof(int) != ee_write_esys(E_SYS_TXCFG, txcfg.reg)) goto cleanup_platform; - + 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 +} + +