summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoy Pledge <Roy.Pledge@freescale.com>2013-04-30 13:58:56 (GMT)
committerFleming Andrew-AFLEMING <AFLEMING@freescale.com>2013-05-15 21:41:42 (GMT)
commit46d48bf547b0524377069fc8b1b96e9ac67619be (patch)
treeee4422f906417e7fb10e3d1c01fa3fc66f8b5d77
parent8e35431b2e5f14e63e58b9296789e5fdc26c377e (diff)
downloadlinux-fsl-qoriq-46d48bf547b0524377069fc8b1b96e9ac67619be.tar.xz
Fix USDPAA IRQ handling to work correctly in 32 bit mode
Also properly respect the NONBLOCK flag in the read() API Make sure IRQ is properly inhibited in the IRQ handler Create a dependancy between USDPAA and USDPAA_IRQ file pointers Change-Id: Idc0c33c8448a402d5d127e7e4e22e629dbfa5912 Signed-off-by: Roy Pledge <Roy.Pledge@freescale.com> Reviewed-on: http://git.am.freescale.net:8181/2310 Reviewed-by: Fleming Andrew-AFLEMING <AFLEMING@freescale.com> Tested-by: Fleming Andrew-AFLEMING <AFLEMING@freescale.com>
-rw-r--r--drivers/staging/fsl_qbman/bman_low.h1
-rw-r--r--drivers/staging/fsl_qbman/bman_private.h5
-rw-r--r--drivers/staging/fsl_qbman/dpa_alloc.c4
-rw-r--r--drivers/staging/fsl_qbman/fsl_usdpaa.c55
-rw-r--r--drivers/staging/fsl_qbman/fsl_usdpaa_irq.c155
-rw-r--r--drivers/staging/fsl_qbman/qman_low.h1
-rw-r--r--drivers/staging/fsl_qbman/qman_private.h6
-rw-r--r--include/linux/fsl_usdpaa.h21
8 files changed, 216 insertions, 32 deletions
diff --git a/drivers/staging/fsl_qbman/bman_low.h b/drivers/staging/fsl_qbman/bman_low.h
index 23dcba0..6f267d9 100644
--- a/drivers/staging/fsl_qbman/bman_low.h
+++ b/drivers/staging/fsl_qbman/bman_low.h
@@ -42,6 +42,7 @@
#define BM_REG_CFG 0x0100
#define BM_REG_SCN(n) (0x0200 + ((n) << 2))
#define BM_REG_ISR 0x0e00
+#define BM_REG_IIR 0x0e0c
/* Cache-enabled register offsets */
#define BM_CL_CR 0x0000
diff --git a/drivers/staging/fsl_qbman/bman_private.h b/drivers/staging/fsl_qbman/bman_private.h
index e57ca68..53aa21b 100644
--- a/drivers/staging/fsl_qbman/bman_private.h
+++ b/drivers/staging/fsl_qbman/bman_private.h
@@ -83,6 +83,11 @@ struct bm_portal_config *bm_get_unused_portal(void);
void bm_put_unused_portal(struct bm_portal_config *pcfg);
void bm_set_liodns(struct bm_portal_config *pcfg);
+/* Lookup a BMan portal associated with an FD */
+struct bm_portal_config *usdpaa_get_bm_portal_config(struct file *filp,
+ void *cinh);
+
+
/* Pool logic in the portal driver, during initialisation, needs to know if
* there's access to CCSR or not (if not, it'll cripple the pool allocator). */
#ifdef CONFIG_FSL_BMAN_CONFIG
diff --git a/drivers/staging/fsl_qbman/dpa_alloc.c b/drivers/staging/fsl_qbman/dpa_alloc.c
index 3fb158e..8656ec3 100644
--- a/drivers/staging/fsl_qbman/dpa_alloc.c
+++ b/drivers/staging/fsl_qbman/dpa_alloc.c
@@ -190,7 +190,9 @@ static int qpool_cleanup(u32 qp)
void qman_release_pool_range(u32 qp, u32 count)
{
u32 total_invalid = release_id_range(&qpalloc, qp,
- count, qpool_cleanup);
+ count, NULL);
+ /* Temporarly disable QMan Pool recovery due to a frequent
+ hang in qpool_cleanup() */
if (total_invalid) {
/* Pool channels are almost always used individually */
if (count == 1)
diff --git a/drivers/staging/fsl_qbman/fsl_usdpaa.c b/drivers/staging/fsl_qbman/fsl_usdpaa.c
index 1a7081c..bce680f 100644
--- a/drivers/staging/fsl_qbman/fsl_usdpaa.c
+++ b/drivers/staging/fsl_qbman/fsl_usdpaa.c
@@ -1054,7 +1054,6 @@ static long ioctl_portal_map(struct file *fp, struct ctx *ctx,
mapping->phys = &mapping->qportal->addr_phys[0];
mapping->user.channel = mapping->qportal->public_cfg.channel;
mapping->user.pools = mapping->qportal->public_cfg.pools;
- mapping->user.irq = mapping->qportal->public_cfg.irq;
} else if (mapping->user.type == usdpaa_portal_bman) {
mapping->bportal = bm_get_unused_portal();
if (!mapping->bportal) {
@@ -1062,7 +1061,6 @@ static long ioctl_portal_map(struct file *fp, struct ctx *ctx,
goto err_get_portal;
}
mapping->phys = &mapping->bportal->addr_phys[0];
- mapping->user.irq = mapping->bportal->public_cfg.irq;
} else {
ret = -EINVAL;
goto err_copy_from_user;
@@ -1261,7 +1259,6 @@ static long usdpaa_ioctl_compat(struct file *fp, unsigned int cmd,
input.addr.cena = ptr_to_compat(converted.addr.cena);
input.channel = converted.channel;
input.pools = converted.pools;
- input.irq = converted.irq;
if (copy_to_user(a, &input, sizeof(input)))
return -EFAULT;
return ret;
@@ -1284,6 +1281,58 @@ static long usdpaa_ioctl_compat(struct file *fp, unsigned int cmd,
return -EINVAL;
}
+struct qm_portal_config *usdpaa_get_qm_portal_config(struct file *filp,
+ void *hint)
+{
+ /* Walk the list of portals for filp and return the config
+ for the portal that matches the hint */
+
+ struct ctx *context;
+ struct portal_mapping *portal;
+
+ /* First sanitize the filp */
+ if (filp->f_op->open != usdpaa_open)
+ return NULL;
+ context = filp->private_data;
+ spin_lock(&context->lock);
+ list_for_each_entry(portal, &context->portals, list) {
+ if (portal->user.type == usdpaa_portal_qman &&
+ portal->user.addr.cinh == hint) {
+ spin_unlock(&context->lock);
+ return portal->qportal;
+ }
+ }
+ spin_unlock(&context->lock);
+ return NULL;
+}
+
+struct bm_portal_config *usdpaa_get_bm_portal_config(struct file *filp,
+ void *hint)
+{
+ /* Walk the list of portals for filp and return the config
+ for the portal that matches the hint */
+
+ struct ctx *context;
+ struct portal_mapping *portal;
+
+ /* First sanitize the filp */
+ if (filp->f_op->open != usdpaa_open)
+ return NULL;
+
+ context = filp->private_data;
+
+ spin_lock(&context->lock);
+ list_for_each_entry(portal, &context->portals, list) {
+ if (portal->user.type == usdpaa_portal_bman &&
+ portal->user.addr.cinh == hint) {
+ spin_unlock(&context->lock);
+ return portal->bportal;
+ }
+ }
+ spin_unlock(&context->lock);
+ return NULL;
+}
+
static const struct file_operations usdpaa_fops = {
.open = usdpaa_open,
.release = usdpaa_release,
diff --git a/drivers/staging/fsl_qbman/fsl_usdpaa_irq.c b/drivers/staging/fsl_qbman/fsl_usdpaa_irq.c
index faf3e5d..a2c609f 100644
--- a/drivers/staging/fsl_qbman/fsl_usdpaa_irq.c
+++ b/drivers/staging/fsl_qbman/fsl_usdpaa_irq.c
@@ -44,14 +44,21 @@
#include <linux/uaccess.h>
#include <linux/fsl_usdpaa.h>
#include <linux/module.h>
+#include <linux/fdtable.h>
+#include <linux/file.h>
+
+#include "qman_low.h"
+#include "bman_low.h"
struct usdpaa_irq_ctx {
int irq_set; /* Set to true once the irq is set via ioctl */
unsigned int irq_num;
- unsigned int last_irq_count; /* Last value returned from read */
- unsigned int irq_count; /* Number of irqs since last read */
+ u32 last_irq_count; /* Last value returned from read */
+ u32 irq_count; /* Number of irqs since last read */
wait_queue_head_t wait_queue; /* Waiting processes */
spinlock_t lock;
+ void *inhibit_addr; /* inhibit register address */
+ struct file *usdpaa_filp;
};
@@ -74,8 +81,13 @@ static int usdpaa_irq_open(struct inode *inode, struct file *filp)
static int usdpaa_irq_release(struct inode *inode, struct file *filp)
{
struct usdpaa_irq_ctx *ctx = filp->private_data;
- if (ctx->irq_set)
+ if (ctx->irq_set) {
+ /* Inhibit the IRQ */
+ out_be32(ctx->inhibit_addr, 0x1);
free_irq(ctx->irq_num, ctx);
+ ctx->irq_set = 0;
+ fput(ctx->usdpaa_filp);
+ }
kfree(filp->private_data);
return 0;
}
@@ -83,38 +95,96 @@ static int usdpaa_irq_release(struct inode *inode, struct file *filp)
irqreturn_t usdpaa_irq_handler(int irq, void *_ctx)
{
+ unsigned long flags;
struct usdpaa_irq_ctx *ctx = _ctx;
- spin_lock(&ctx->lock);
+ spin_lock_irqsave(&ctx->lock, flags);
++ctx->irq_count;
wake_up_all(&ctx->wait_queue);
- spin_unlock(&ctx->lock);
+ spin_unlock_irqrestore(&ctx->lock, flags);
+ /* Set the inhibit register. This will be reenabled
+ once the USDPAA code handles the IRQ */
+ out_be32(ctx->inhibit_addr, 0x1);
return IRQ_HANDLED;
}
-static long usdpaa_irq_ioctl(struct file *fp, unsigned int cmd,
- unsigned long arg)
+
+
+static int map_irq(struct file *fp, struct usdpaa_ioctl_irq_map *irq_map)
{
- int ret;
struct usdpaa_irq_ctx *ctx = fp->private_data;
+ struct qm_portal_config *qportal = NULL;
+ struct bm_portal_config *bportal = NULL;
+ int irq, ret;
+ void *inhibit_reg;
+ struct file *old_filp = ctx->usdpaa_filp;
- if (cmd != USDPAA_IOCTL_PORTAL_IRQ_MAP)
+ ctx->usdpaa_filp = fget(irq_map->fd);
+ if (!ctx->usdpaa_filp) {
+ pr_err("fget() returned NULL for fd %d\n", irq_map->fd);
return -EINVAL;
+ }
- /* Lookup IRQ number for portal */
+ if (irq_map->type == usdpaa_portal_qman) {
+ qportal = usdpaa_get_qm_portal_config(ctx->usdpaa_filp,
+ irq_map->portal_cinh);
+ if (!qportal) {
+ pr_err("Couldn't associate info to QMan Portal\n");
+ fput(ctx->usdpaa_filp);
+ return -EINVAL;
+ }
+ /* Lookup IRQ number for portal */
+ irq = qportal->public_cfg.irq;
+ inhibit_reg = qportal->addr_virt[1] + QM_REG_IIR;
+ } else {
+ bportal = usdpaa_get_bm_portal_config(ctx->usdpaa_filp,
+ irq_map->portal_cinh);
+ if (!bportal) {
+ pr_err("Couldn't associate info to BMan Portal\n");
+ fput(ctx->usdpaa_filp);
+ return -EINVAL;
+ }
+ /* Lookup IRQ number for portal */
+ irq = bportal->public_cfg.irq;
+ inhibit_reg = bportal->addr_virt[1] + BM_REG_IIR;
+ }
if (ctx->irq_set) {
+ fput(old_filp);
free_irq(ctx->irq_num, ctx);
- ctx->irq_set = 0;
}
- ret = request_irq(arg, usdpaa_irq_handler, 0, "usdpaa_irq", ctx);
+
+ ctx->irq_set = 1;
+ ctx->irq_num = irq;
+ ctx->inhibit_addr = inhibit_reg;
+
+ ret = request_irq(irq, usdpaa_irq_handler, 0,
+ "usdpaa_irq", ctx);
if (ret) {
- pr_err("request_irq for irq %lu failed, ret= %d\n", arg, ret);
+ pr_err("request_irq for irq %d failed, ret= %d\n", irq, ret);
+ ctx->irq_set = 0;
+ fput(ctx->usdpaa_filp);
return ret;
}
- ctx->irq_set = 1;
- ctx->irq_num = arg;
return 0;
};
+static long usdpaa_irq_ioctl(struct file *fp, unsigned int cmd,
+ unsigned long arg)
+{
+ int ret;
+ struct usdpaa_ioctl_irq_map irq_map;
+
+ if (cmd != USDPAA_IOCTL_PORTAL_IRQ_MAP) {
+ pr_err("Unknown command 0x%x\n", cmd);
+ return -EINVAL;
+ }
+
+ ret = copy_from_user(&irq_map, (void __user *)arg,
+ sizeof(irq_map));
+ if (ret)
+ return ret;
+ return map_irq(fp, &irq_map);
+};
+
static ssize_t usdpaa_irq_read(struct file *filp, char __user *buff,
size_t count, loff_t *offp)
{
@@ -126,15 +196,26 @@ static ssize_t usdpaa_irq_read(struct file *filp, char __user *buff,
return -EINVAL;
}
- ret = wait_event_interruptible(ctx->wait_queue,
+ if (count < sizeof(ctx->irq_count)) {
+ pr_err("USDPAA IRQ Read too small\n");
+ return -EINVAL;
+ }
+ if (ctx->irq_count == ctx->last_irq_count) {
+ if (filp->f_flags & O_NONBLOCK)
+ return -EAGAIN;
+
+ ret = wait_event_interruptible(ctx->wait_queue,
ctx->irq_count != ctx->last_irq_count);
- if (ret == -ERESTARTSYS)
- return ret;
+ if (ret == -ERESTARTSYS)
+ return ret;
+ }
- if (copy_to_user(buff, &ctx->irq_count, sizeof(unsigned int)))
- return -EFAULT;
ctx->last_irq_count = ctx->irq_count;
- return sizeof(unsigned int);
+
+ if (copy_to_user(buff, &ctx->last_irq_count,
+ sizeof(ctx->last_irq_count)))
+ return -EFAULT;
+ return sizeof(ctx->irq_count);
}
@@ -142,24 +223,48 @@ static unsigned int usdpaa_irq_poll(struct file *filp, poll_table *wait)
{
struct usdpaa_irq_ctx *ctx = filp->private_data;
unsigned int ret = 0;
+ unsigned long flags;
if (!ctx->irq_set)
return POLLHUP;
poll_wait(filp, &ctx->wait_queue, wait);
- spin_lock_irq(&ctx->lock);
+ spin_lock_irqsave(&ctx->lock, flags);
if (ctx->irq_count != ctx->last_irq_count)
- ret = POLLIN | POLLRDNORM;
- spin_unlock_irq(&ctx->lock);
+ ret |= POLLIN | POLLRDNORM;
+ spin_unlock_irqrestore(&ctx->lock, flags);
return ret;
}
+static long usdpaa_irq_ioctl_compat(struct file *fp, unsigned int cmd,
+ unsigned long arg)
+{
+ void __user *a = (void __user *)arg;
+ switch (cmd) {
+#ifdef CONFIG_COMPAT
+ case USDPAA_IOCTL_PORTAL_IRQ_MAP_COMPAT:
+ {
+ struct compat_ioctl_irq_map input;
+ struct usdpaa_ioctl_irq_map converted;
+ if (copy_from_user(&input, a, sizeof(input)))
+ return -EFAULT;
+ converted.type = input.type;
+ converted.fd = input.fd;
+ converted.portal_cinh = compat_ptr(input.portal_cinh);
+ return map_irq(fp, &converted);
+ }
+#endif
+ default:
+ return usdpaa_irq_ioctl(fp, cmd, arg);
+ }
+};
+
static const struct file_operations usdpaa_irq_fops = {
.open = usdpaa_irq_open,
.release = usdpaa_irq_release,
.unlocked_ioctl = usdpaa_irq_ioctl,
- .compat_ioctl = usdpaa_irq_ioctl,
+ .compat_ioctl = usdpaa_irq_ioctl_compat,
.read = usdpaa_irq_read,
.poll = usdpaa_irq_poll
};
diff --git a/drivers/staging/fsl_qbman/qman_low.h b/drivers/staging/fsl_qbman/qman_low.h
index e210f6a..1205ac7 100644
--- a/drivers/staging/fsl_qbman/qman_low.h
+++ b/drivers/staging/fsl_qbman/qman_low.h
@@ -51,6 +51,7 @@
#define QM_REG_MR_ITR 0x0088
#define QM_REG_CFG 0x0100
#define QM_REG_ISR 0x0e00
+#define QM_REG_IIR 0x0e0c
#define QM_REG_ITPR 0x0e14
/* Cache-enabled register offsets */
diff --git a/drivers/staging/fsl_qbman/qman_private.h b/drivers/staging/fsl_qbman/qman_private.h
index e43b459..6179943 100644
--- a/drivers/staging/fsl_qbman/qman_private.h
+++ b/drivers/staging/fsl_qbman/qman_private.h
@@ -235,6 +235,12 @@ int qman_testwrite_cgr(struct qman_cgr *cgr, u64 i_bcnt,
int qman_setup_fq_lookup_table(size_t num_entries);
#endif
+/* Lookup a QMan portal associated with an FD */
+struct qm_portal_config *usdpaa_get_qm_portal_config(struct file *filp,
+ void *cinh);
+
+
+
/*************************************************/
/* QMan s/w corenet portal, low-level i/face */
/*************************************************/
diff --git a/include/linux/fsl_usdpaa.h b/include/linux/fsl_usdpaa.h
index dc2a063..de017a6 100644
--- a/include/linux/fsl_usdpaa.h
+++ b/include/linux/fsl_usdpaa.h
@@ -177,7 +177,6 @@ struct usdpaa_ioctl_portal_map {
/* Qman-specific return values */
uint16_t channel;
uint32_t pools;
- uint32_t irq;
};
#ifdef CONFIG_COMPAT
@@ -193,7 +192,6 @@ struct compat_usdpaa_ioctl_portal_map {
/* Qman-specific return values */
uint16_t channel;
uint32_t pools;
- uint32_t irq;
};
#define USDPAA_IOCTL_PORTAL_MAP_COMPAT \
_IOWR(USDPAA_IOCTL_MAGIC, 0x07, struct compat_usdpaa_ioctl_portal_map)
@@ -206,8 +204,25 @@ struct compat_usdpaa_ioctl_portal_map {
#define USDPAA_IOCTL_PORTAL_UNMAP \
_IOW(USDPAA_IOCTL_MAGIC, 0x08, struct usdpaa_portal_map)
+struct usdpaa_ioctl_irq_map {
+ enum usdpaa_portal_type type; /* Type of portal to map */
+ int fd; /* File descriptor that contains the portal */
+ void *portal_cinh; /* Cache inhibited area to identify the portal */
+};
+
#define USDPAA_IOCTL_PORTAL_IRQ_MAP \
- _IOW(USDPAA_IOCTL_MAGIC, 0x09, uint32_t)
+ _IOW(USDPAA_IOCTL_MAGIC, 0x09, struct usdpaa_ioctl_irq_map)
+
+#ifdef CONFIG_COMPAT
+
+struct compat_ioctl_irq_map {
+ enum usdpaa_portal_type type; /* Type of portal to map */
+ compat_int_t fd; /* File descriptor that contains the portal */
+ compat_uptr_t portal_cinh; /* Used identify the portal */};
+
+#define USDPAA_IOCTL_PORTAL_IRQ_MAP_COMPAT \
+ _IOW(USDPAA_IOCTL_MAGIC, 0x09, struct compat_ioctl_irq_map)
+#endif
/* ioctl to query the amount of DMA memory used in the system */
struct usdpaa_ioctl_dma_used {