Path: blob/master/arch/arm/mach-ixp4xx/ixp4xx_qmgr.c
10817 views
/*1* Intel IXP4xx Queue Manager driver for Linux2*3* Copyright (C) 2007 Krzysztof Halasa <[email protected]>4*5* This program is free software; you can redistribute it and/or modify it6* under the terms of version 2 of the GNU General Public License7* as published by the Free Software Foundation.8*/910#include <linux/ioport.h>11#include <linux/interrupt.h>12#include <linux/kernel.h>13#include <linux/module.h>14#include <mach/qmgr.h>1516struct qmgr_regs __iomem *qmgr_regs;17static struct resource *mem_res;18static spinlock_t qmgr_lock;19static u32 used_sram_bitmap[4]; /* 128 16-dword pages */20static void (*irq_handlers[QUEUES])(void *pdev);21static void *irq_pdevs[QUEUES];2223#if DEBUG_QMGR24char qmgr_queue_descs[QUEUES][32];25#endif2627void qmgr_set_irq(unsigned int queue, int src,28void (*handler)(void *pdev), void *pdev)29{30unsigned long flags;3132spin_lock_irqsave(&qmgr_lock, flags);33if (queue < HALF_QUEUES) {34u32 __iomem *reg;35int bit;36BUG_ON(src > QUEUE_IRQ_SRC_NOT_FULL);37reg = &qmgr_regs->irqsrc[queue >> 3]; /* 8 queues per u32 */38bit = (queue % 8) * 4; /* 3 bits + 1 reserved bit per queue */39__raw_writel((__raw_readl(reg) & ~(7 << bit)) | (src << bit),40reg);41} else42/* IRQ source for queues 32-63 is fixed */43BUG_ON(src != QUEUE_IRQ_SRC_NOT_NEARLY_EMPTY);4445irq_handlers[queue] = handler;46irq_pdevs[queue] = pdev;47spin_unlock_irqrestore(&qmgr_lock, flags);48}495051static irqreturn_t qmgr_irq1_a0(int irq, void *pdev)52{53int i, ret = 0;54u32 en_bitmap, src, stat;5556/* ACK - it may clear any bits so don't rely on it */57__raw_writel(0xFFFFFFFF, &qmgr_regs->irqstat[0]);5859en_bitmap = qmgr_regs->irqen[0];60while (en_bitmap) {61i = __fls(en_bitmap); /* number of the last "low" queue */62en_bitmap &= ~BIT(i);63src = qmgr_regs->irqsrc[i >> 3];64stat = qmgr_regs->stat1[i >> 3];65if (src & 4) /* the IRQ condition is inverted */66stat = ~stat;67if (stat & BIT(src & 3)) {68irq_handlers[i](irq_pdevs[i]);69ret = IRQ_HANDLED;70}71}72return ret;73}747576static irqreturn_t qmgr_irq2_a0(int irq, void *pdev)77{78int i, ret = 0;79u32 req_bitmap;8081/* ACK - it may clear any bits so don't rely on it */82__raw_writel(0xFFFFFFFF, &qmgr_regs->irqstat[1]);8384req_bitmap = qmgr_regs->irqen[1] & qmgr_regs->statne_h;85while (req_bitmap) {86i = __fls(req_bitmap); /* number of the last "high" queue */87req_bitmap &= ~BIT(i);88irq_handlers[HALF_QUEUES + i](irq_pdevs[HALF_QUEUES + i]);89ret = IRQ_HANDLED;90}91return ret;92}939495static irqreturn_t qmgr_irq(int irq, void *pdev)96{97int i, half = (irq == IRQ_IXP4XX_QM1 ? 0 : 1);98u32 req_bitmap = __raw_readl(&qmgr_regs->irqstat[half]);99100if (!req_bitmap)101return 0;102__raw_writel(req_bitmap, &qmgr_regs->irqstat[half]); /* ACK */103104while (req_bitmap) {105i = __fls(req_bitmap); /* number of the last queue */106req_bitmap &= ~BIT(i);107i += half * HALF_QUEUES;108irq_handlers[i](irq_pdevs[i]);109}110return IRQ_HANDLED;111}112113114void qmgr_enable_irq(unsigned int queue)115{116unsigned long flags;117int half = queue / 32;118u32 mask = 1 << (queue & (HALF_QUEUES - 1));119120spin_lock_irqsave(&qmgr_lock, flags);121__raw_writel(__raw_readl(&qmgr_regs->irqen[half]) | mask,122&qmgr_regs->irqen[half]);123spin_unlock_irqrestore(&qmgr_lock, flags);124}125126void qmgr_disable_irq(unsigned int queue)127{128unsigned long flags;129int half = queue / 32;130u32 mask = 1 << (queue & (HALF_QUEUES - 1));131132spin_lock_irqsave(&qmgr_lock, flags);133__raw_writel(__raw_readl(&qmgr_regs->irqen[half]) & ~mask,134&qmgr_regs->irqen[half]);135__raw_writel(mask, &qmgr_regs->irqstat[half]); /* clear */136spin_unlock_irqrestore(&qmgr_lock, flags);137}138139static inline void shift_mask(u32 *mask)140{141mask[3] = mask[3] << 1 | mask[2] >> 31;142mask[2] = mask[2] << 1 | mask[1] >> 31;143mask[1] = mask[1] << 1 | mask[0] >> 31;144mask[0] <<= 1;145}146147#if DEBUG_QMGR148int qmgr_request_queue(unsigned int queue, unsigned int len /* dwords */,149unsigned int nearly_empty_watermark,150unsigned int nearly_full_watermark,151const char *desc_format, const char* name)152#else153int __qmgr_request_queue(unsigned int queue, unsigned int len /* dwords */,154unsigned int nearly_empty_watermark,155unsigned int nearly_full_watermark)156#endif157{158u32 cfg, addr = 0, mask[4]; /* in 16-dwords */159int err;160161BUG_ON(queue >= QUEUES);162163if ((nearly_empty_watermark | nearly_full_watermark) & ~7)164return -EINVAL;165166switch (len) {167case 16:168cfg = 0 << 24;169mask[0] = 0x1;170break;171case 32:172cfg = 1 << 24;173mask[0] = 0x3;174break;175case 64:176cfg = 2 << 24;177mask[0] = 0xF;178break;179case 128:180cfg = 3 << 24;181mask[0] = 0xFF;182break;183default:184return -EINVAL;185}186187cfg |= nearly_empty_watermark << 26;188cfg |= nearly_full_watermark << 29;189len /= 16; /* in 16-dwords: 1, 2, 4 or 8 */190mask[1] = mask[2] = mask[3] = 0;191192if (!try_module_get(THIS_MODULE))193return -ENODEV;194195spin_lock_irq(&qmgr_lock);196if (__raw_readl(&qmgr_regs->sram[queue])) {197err = -EBUSY;198goto err;199}200201while (1) {202if (!(used_sram_bitmap[0] & mask[0]) &&203!(used_sram_bitmap[1] & mask[1]) &&204!(used_sram_bitmap[2] & mask[2]) &&205!(used_sram_bitmap[3] & mask[3]))206break; /* found free space */207208addr++;209shift_mask(mask);210if (addr + len > ARRAY_SIZE(qmgr_regs->sram)) {211printk(KERN_ERR "qmgr: no free SRAM space for"212" queue %i\n", queue);213err = -ENOMEM;214goto err;215}216}217218used_sram_bitmap[0] |= mask[0];219used_sram_bitmap[1] |= mask[1];220used_sram_bitmap[2] |= mask[2];221used_sram_bitmap[3] |= mask[3];222__raw_writel(cfg | (addr << 14), &qmgr_regs->sram[queue]);223#if DEBUG_QMGR224snprintf(qmgr_queue_descs[queue], sizeof(qmgr_queue_descs[0]),225desc_format, name);226printk(KERN_DEBUG "qmgr: requested queue %s(%i) addr = 0x%02X\n",227qmgr_queue_descs[queue], queue, addr);228#endif229spin_unlock_irq(&qmgr_lock);230return 0;231232err:233spin_unlock_irq(&qmgr_lock);234module_put(THIS_MODULE);235return err;236}237238void qmgr_release_queue(unsigned int queue)239{240u32 cfg, addr, mask[4];241242BUG_ON(queue >= QUEUES); /* not in valid range */243244spin_lock_irq(&qmgr_lock);245cfg = __raw_readl(&qmgr_regs->sram[queue]);246addr = (cfg >> 14) & 0xFF;247248BUG_ON(!addr); /* not requested */249250switch ((cfg >> 24) & 3) {251case 0: mask[0] = 0x1; break;252case 1: mask[0] = 0x3; break;253case 2: mask[0] = 0xF; break;254case 3: mask[0] = 0xFF; break;255}256257mask[1] = mask[2] = mask[3] = 0;258259while (addr--)260shift_mask(mask);261262#if DEBUG_QMGR263printk(KERN_DEBUG "qmgr: releasing queue %s(%i)\n",264qmgr_queue_descs[queue], queue);265qmgr_queue_descs[queue][0] = '\x0';266#endif267268while ((addr = qmgr_get_entry(queue)))269printk(KERN_ERR "qmgr: released queue %i not empty: 0x%08X\n",270queue, addr);271272__raw_writel(0, &qmgr_regs->sram[queue]);273274used_sram_bitmap[0] &= ~mask[0];275used_sram_bitmap[1] &= ~mask[1];276used_sram_bitmap[2] &= ~mask[2];277used_sram_bitmap[3] &= ~mask[3];278irq_handlers[queue] = NULL; /* catch IRQ bugs */279spin_unlock_irq(&qmgr_lock);280281module_put(THIS_MODULE);282}283284static int qmgr_init(void)285{286int i, err;287irq_handler_t handler1, handler2;288289mem_res = request_mem_region(IXP4XX_QMGR_BASE_PHYS,290IXP4XX_QMGR_REGION_SIZE,291"IXP4xx Queue Manager");292if (mem_res == NULL)293return -EBUSY;294295qmgr_regs = ioremap(IXP4XX_QMGR_BASE_PHYS, IXP4XX_QMGR_REGION_SIZE);296if (qmgr_regs == NULL) {297err = -ENOMEM;298goto error_map;299}300301/* reset qmgr registers */302for (i = 0; i < 4; i++) {303__raw_writel(0x33333333, &qmgr_regs->stat1[i]);304__raw_writel(0, &qmgr_regs->irqsrc[i]);305}306for (i = 0; i < 2; i++) {307__raw_writel(0, &qmgr_regs->stat2[i]);308__raw_writel(0xFFFFFFFF, &qmgr_regs->irqstat[i]); /* clear */309__raw_writel(0, &qmgr_regs->irqen[i]);310}311312__raw_writel(0xFFFFFFFF, &qmgr_regs->statne_h);313__raw_writel(0, &qmgr_regs->statf_h);314315for (i = 0; i < QUEUES; i++)316__raw_writel(0, &qmgr_regs->sram[i]);317318if (cpu_is_ixp42x_rev_a0()) {319handler1 = qmgr_irq1_a0;320handler2 = qmgr_irq2_a0;321} else322handler1 = handler2 = qmgr_irq;323324err = request_irq(IRQ_IXP4XX_QM1, handler1, 0, "IXP4xx Queue Manager",325NULL);326if (err) {327printk(KERN_ERR "qmgr: failed to request IRQ%i (%i)\n",328IRQ_IXP4XX_QM1, err);329goto error_irq;330}331332err = request_irq(IRQ_IXP4XX_QM2, handler2, 0, "IXP4xx Queue Manager",333NULL);334if (err) {335printk(KERN_ERR "qmgr: failed to request IRQ%i (%i)\n",336IRQ_IXP4XX_QM2, err);337goto error_irq2;338}339340used_sram_bitmap[0] = 0xF; /* 4 first pages reserved for config */341spin_lock_init(&qmgr_lock);342343printk(KERN_INFO "IXP4xx Queue Manager initialized.\n");344return 0;345346error_irq2:347free_irq(IRQ_IXP4XX_QM1, NULL);348error_irq:349iounmap(qmgr_regs);350error_map:351release_mem_region(IXP4XX_QMGR_BASE_PHYS, IXP4XX_QMGR_REGION_SIZE);352return err;353}354355static void qmgr_remove(void)356{357free_irq(IRQ_IXP4XX_QM1, NULL);358free_irq(IRQ_IXP4XX_QM2, NULL);359synchronize_irq(IRQ_IXP4XX_QM1);360synchronize_irq(IRQ_IXP4XX_QM2);361iounmap(qmgr_regs);362release_mem_region(IXP4XX_QMGR_BASE_PHYS, IXP4XX_QMGR_REGION_SIZE);363}364365module_init(qmgr_init);366module_exit(qmgr_remove);367368MODULE_LICENSE("GPL v2");369MODULE_AUTHOR("Krzysztof Halasa");370371EXPORT_SYMBOL(qmgr_regs);372EXPORT_SYMBOL(qmgr_set_irq);373EXPORT_SYMBOL(qmgr_enable_irq);374EXPORT_SYMBOL(qmgr_disable_irq);375#if DEBUG_QMGR376EXPORT_SYMBOL(qmgr_queue_descs);377EXPORT_SYMBOL(qmgr_request_queue);378#else379EXPORT_SYMBOL(__qmgr_request_queue);380#endif381EXPORT_SYMBOL(qmgr_release_queue);382383384