Commit a3f80c8c authored by Jens Korinth's avatar Jens Korinth
Browse files

zynq: implement mmap call

* fixed some bugs, mmapping works now
* also improved portability of read/write ioctls
parent 55cf4382
......@@ -11,32 +11,50 @@ static struct zynq_device _zynq_dev;
static int init_iomapping(void)
{
int retval = 0;
u32 magic_id = 0;
DEVLOG(_zynq_dev.dev_id, TLKM_LF_DEVICE, "I/O mapping 0x%08llx-0x%08llx for GP0",
(u64)ZYNQ_PLATFORM_GP0_BASE,
(u64)(ZYNQ_PLATFORM_GP0_BASE + ZYNQ_PLATFORM_GP0_SIZE - 1));
_zynq_dev.gp_map[0] = ioremap_nocache(ZYNQ_PLATFORM_GP0_BASE, ZYNQ_PLATFORM_GP0_SIZE);
if (IS_ERR(_zynq_dev.gp_map[0])) {
ERR("could not ioremap the AXI register space at 0x%08llx-0x%08llx",
if (!_zynq_dev.gp_map[0] || IS_ERR(_zynq_dev.gp_map[0])) {
DEVERR(_zynq_dev.dev_id,
"could not ioremap the AXI register space at 0x%08llx-0x%08llx",
(u64)ZYNQ_PLATFORM_GP0_BASE,
(u64)(ZYNQ_PLATFORM_GP0_BASE + ZYNQ_PLATFORM_GP0_SIZE - 1));
retval = PTR_ERR(_zynq_dev.gp_map[0]);
goto err_gp0;
}
DEVLOG(_zynq_dev.dev_id, TLKM_LF_DEVICE, "I/O mapping 0x%08llx-0x%08llx for GP1",
(u64)ZYNQ_PLATFORM_GP1_BASE,
(u64)(ZYNQ_PLATFORM_GP1_BASE + ZYNQ_PLATFORM_GP1_SIZE - 1));
_zynq_dev.gp_map[1] = ioremap_nocache(ZYNQ_PLATFORM_GP1_BASE, ZYNQ_PLATFORM_GP1_SIZE);
if (IS_ERR(_zynq_dev.gp_map[1])) {
ERR("could not ioremap the AXI register space at 0x%08llx-0x%08llx",
if (!_zynq_dev.gp_map[1] || IS_ERR(_zynq_dev.gp_map[1])) {
DEVERR(_zynq_dev.dev_id,
"could not ioremap the AXI register space at 0x%08llx-0x%08llx",
(u64)ZYNQ_PLATFORM_GP1_BASE,
(u64)(ZYNQ_PLATFORM_GP1_BASE + ZYNQ_PLATFORM_GP1_SIZE - 1));
retval = PTR_ERR(_zynq_dev.gp_map[1]);
goto err_gp1;
}
DEVLOG(_zynq_dev.dev_id, TLKM_LF_DEVICE, "I/O mapping 0x%08llx-0x%08llx for ST",
(u64)ZYNQ_PLATFORM_STATUS_BASE,
(u64)(ZYNQ_PLATFORM_STATUS_BASE + ZYNQ_PLATFORM_STATUS_SIZE - 1));
_zynq_dev.tapasco_status = ioremap_nocache(ZYNQ_PLATFORM_STATUS_BASE, ZYNQ_PLATFORM_STATUS_SIZE);
if (IS_ERR(_zynq_dev.tapasco_status)) {
ERR("could not ioremap the AXI register space at 0x%08llx-0x%08llx",
if (!_zynq_dev.tapasco_status || IS_ERR(_zynq_dev.tapasco_status)) {
DEVERR(_zynq_dev.dev_id,
"could not ioremap the AXI register space at 0x%08llx-0x%08llx",
(u64)ZYNQ_PLATFORM_STATUS_BASE,
(u64)(ZYNQ_PLATFORM_STATUS_BASE + ZYNQ_PLATFORM_STATUS_SIZE));
retval = PTR_ERR(_zynq_dev.tapasco_status);
goto err_tapasco_status;
}
magic_id = ioread32(_zynq_dev.tapasco_status);
DEVLOG(_zynq_dev.dev_id, TLKM_LF_DEVICE, "magic_id = 0x%08lx", (ulong)magic_id);
DEVLOG(_zynq_dev.dev_id, TLKM_LF_DEVICE,
"I/O mapped all registers successfully: GP0 = 0x%08lx, GP1 = 0x%08lx, ST=0x%08lx",
(ulong)_zynq_dev.gp_map[0], (ulong)_zynq_dev.gp_map[1], (ulong)_zynq_dev.tapasco_status);
return retval;
err_tapasco_status:
......@@ -52,6 +70,10 @@ static void exit_iomapping(void)
iounmap(_zynq_dev.tapasco_status);
iounmap(_zynq_dev.gp_map[1]);
iounmap(_zynq_dev.gp_map[0]);
_zynq_dev.gp_map[0] = NULL;
_zynq_dev.gp_map[1] = NULL;
_zynq_dev.tapasco_status = NULL;
DEVLOG(_zynq_dev.dev_id, TLKM_LF_DEVICE, "released all I/O maps");
}
int zynq_device_init(struct tlkm_device_inst *inst)
......@@ -96,6 +118,7 @@ void zynq_device_exit(struct tlkm_device_inst *inst)
}
#endif /* NDEBUG */
zynq_irq_exit(&_zynq_dev);
exit_iomapping();
inst->private_data = NULL;
LOG(TLKM_LF_DEVICE, "zynq device #%03u exited", _zynq_dev.dev_id);
}
......@@ -7,6 +7,7 @@
#include "zynq_device.h"
#include "zynq_platform.h"
#include "zynq_ioctl.h"
#include "zynq_mmap.h"
static const struct of_device_id zynq_ids[] = {
{ .compatible = ZYNQ_NAME, },
......@@ -19,6 +20,7 @@ static tlkm_device_t zynq_dev = {
.init = zynq_device_init,
.exit = zynq_device_exit,
.ioctl = zynq_ioctl,
.mmap = zynq_mmap,
};
ssize_t zynq_enumerate()
......
......@@ -22,6 +22,9 @@
* @author J. Korinth, TU Darmstadt (jk@esa.cs.tu-darmstadt.de)
**/
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/slab.h>
#include <linux/gfp.h>
#include "tlkm_logging.h"
#include "tlkm_device.h"
......@@ -155,35 +158,41 @@ long zynq_ioctl_copyfrom_free(struct tlkm_device_inst *inst, struct tlkm_bulk_cm
}
static inline
long zynq_ioctl_read(struct tlkm_device_inst *inst, struct tlkm_dev_cmd *cmd)
long zynq_ioctl_read(struct tlkm_device_inst *inst, struct tlkm_copy_cmd *cmd)
{
long ret = -ENXIO;
void __iomem *ptr = NULL;
void *buf = kzalloc(cmd->length, GFP_ATOMIC);
struct zynq_device *dev = (struct zynq_device *)inst->private_data;
DEVLOG(inst->dev_id, TLKM_LF_IOCTL, "received read of %zu bytes from 0x%08llx",
cmd->length, cmd->dev_addr);
if (cmd->dev_addr > ZYNQ_PLATFORM_GP1_BASE) {
if (cmd->dev_addr >= ZYNQ_PLATFORM_GP1_BASE) {
ptr = dev->gp_map[1] + (cmd->dev_addr - ZYNQ_PLATFORM_GP1_BASE);
} else if (cmd->dev_addr < ZYNQ_PLATFORM_GP0_HIGH) {
ptr = dev->gp_map[0] + (cmd->dev_addr - ZYNQ_PLATFORM_GP0_BASE);
} else if (cmd->dev_addr & ZYNQ_PLATFORM_STATUS_BASE) {
} else if (cmd->dev_addr >= ZYNQ_PLATFORM_STATUS_BASE &&
cmd->dev_addr < ZYNQ_PLATFORM_STATUS_HIGH) {
ptr = dev->tapasco_status + (cmd->dev_addr - ZYNQ_PLATFORM_STATUS_BASE);
} else {
DEVERR(inst->dev_id, "invalid address: 0x%08llx", cmd->dev_addr);
return -ENXIO;
}
if ((ret = copy_to_user((void __user *)cmd->user_addr, ptr, cmd->length))) {
DEVERR(inst->dev_id, "could not copy all bytes to user space: %ld", ret);
memcpy_fromio(buf, ptr, cmd->length);
if ((ret = copy_to_user((u32 __user *)cmd->user_addr, buf, cmd->length))) {
DEVERR(inst->dev_id, "could not copy all bytes from 0x%08lx to user space 0x%08lx: %ld",
(ulong)buf, (ulong)cmd->user_addr, ret);
ret = -EAGAIN;
}
kfree(buf);
return ret;
}
static inline
long zynq_ioctl_write(struct tlkm_device_inst *inst, struct tlkm_dev_cmd *cmd)
long zynq_ioctl_write(struct tlkm_device_inst *inst, struct tlkm_copy_cmd *cmd)
{
long ret = -ENXIO;
void __iomem *ptr = NULL;
void *buf = kzalloc(cmd->length, GFP_ATOMIC);
struct zynq_device *dev = (struct zynq_device *)inst->private_data;
DEVLOG(inst->dev_id, TLKM_LF_IOCTL, "received write of %zu bytes to 0x%08llx",
cmd->length, cmd->dev_addr);
......@@ -191,34 +200,39 @@ long zynq_ioctl_write(struct tlkm_device_inst *inst, struct tlkm_dev_cmd *cmd)
ptr = dev->gp_map[1] + (cmd->dev_addr - ZYNQ_PLATFORM_GP1_BASE);
} else if (cmd->dev_addr < ZYNQ_PLATFORM_GP0_HIGH) {
ptr = dev->gp_map[0] + (cmd->dev_addr - ZYNQ_PLATFORM_GP0_BASE);
} else if (cmd->dev_addr & ZYNQ_PLATFORM_STATUS_BASE) {
} else if (cmd->dev_addr >= ZYNQ_PLATFORM_STATUS_BASE &&
cmd->dev_addr < ZYNQ_PLATFORM_STATUS_HIGH) {
ptr = dev->tapasco_status + (cmd->dev_addr - ZYNQ_PLATFORM_STATUS_BASE);
} else {
DEVERR(inst->dev_id, "invalid address: 0x%08llx", cmd->dev_addr);
return -ENXIO;
}
if (ptr && copy_from_user(ptr, (void __user *)cmd->user_addr, cmd->length)) {
if (ptr && copy_from_user(buf, (void __user *)cmd->user_addr, cmd->length)) {
DEVERR(inst->dev_id, "could not copy all bytes from user space");
ret = -EAGAIN;
goto err;
}
memcpy_toio(ptr, buf, cmd->length);
err:
kfree(buf);
return ret;
}
long zynq_ioctl(struct tlkm_device_inst *inst, unsigned int ioctl, unsigned long data)
{
int ret = -ENXIO;
DEVLOG(inst->dev_id, TLKM_LF_IOCTL, "received ioctl: 0x%08x", ioctl);
#define _TLKM_DEV_IOCTL(NAME, name, id, dt) \
if (ioctl == TLKM_DEV_IOCTL_ ## NAME) { \
dt d; \
DEVLOG(inst->dev_id, TLKM_LF_IOCTL, "received ioctl: 0x%08x", ioctl); \
if (copy_from_user(&d, (void __user *)data, sizeof(dt))) { \
DEVERR(inst->dev_id, "could not copy ioctl data from user space"); \
return -EAGAIN; \
return -EFAULT; \
} \
ret = zynq_ioctl_ ## name(inst, &d); \
if (copy_to_user((void __user *)data, &d, sizeof(dt))) { \
DEVERR(inst->dev_id, "could not copy ioctl data to user space"); \
return -EAGAIN; \
return -EFAULT; \
} \
return ret; \
}
......
#include <linux/dma-mapping.h>
#include "tlkm_logging.h"
#include "zynq_mmap.h"
#include "zynq_platform.h"
int zynq_mmap(struct tlkm_device_inst *dp, struct vm_area_struct *vm)
{
ssize_t const sz = vm->vm_end - vm->vm_start;
ulong const off = vm->vm_pgoff << PAGE_SHIFT;
DEVLOG(dp->dev_id, TLKM_LF_CONTROL, "received mmap");
if (ISBETWEEN(off, ZYNQ_PLATFORM_GP0_BASE, ZYNQ_PLATFORM_GP0_HIGH)) {
DEVLOG(dp->dev_id, TLKM_LF_CONTROL, "mapping into GP0: 0x%08lx - 0x%08lx", vm->vm_start, vm->vm_end);
} else if (ISBETWEEN(off, ZYNQ_PLATFORM_GP1_BASE, ZYNQ_PLATFORM_GP1_HIGH)) {
DEVLOG(dp->dev_id, TLKM_LF_CONTROL, "mapping into GP1: 0x%08lx - 0x%08lx", vm->vm_start, vm->vm_end);
} else if (ISBETWEEN(off, ZYNQ_PLATFORM_STATUS_BASE, ZYNQ_PLATFORM_STATUS_HIGH)) {
DEVLOG(dp->dev_id, TLKM_LF_CONTROL, "mapping into ST: 0x%08lx - 0x%08lx", vm->vm_start, vm->vm_end);
} else {
DEVERR(dp->dev_id, "unrecognized offset: 0x%08lx", off);
return -ENXIO;
}
DEVLOG(dp->dev_id, TLKM_LF_CONTROL,
"mapping %zu bytes, from 0x%08lx-0x%08lx", sz, vm->vm_start, vm->vm_end);
vm->vm_page_prot = pgprot_noncached(vm->vm_page_prot);
if (io_remap_pfn_range(vm, vm->vm_start, off >> PAGE_SHIFT, sz, vm->vm_page_prot)) {
DEVWRN(dp->dev_id, "io_remap_pfn_range failed!");
return -EAGAIN;
}
DEVLOG(dp->dev_id, TLKM_LF_CONTROL, "register space mapping successful");
return 0;
}
#ifndef ZYNQ_MMAP_H__
#define ZYNQ_MMAP_H__
#include <linux/fs.h>
#include "tlkm_device.h"
int zynq_mmap(struct tlkm_device_inst *dp, struct vm_area_struct *vm);
#endif /* ZYNQ_MMAP_H__ */
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment