18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Intel IXP4xx Queue Manager driver for Linux 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2007 Krzysztof Halasa <khc@pm.waw.pl> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/ioport.h> 98c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 108c2ecf20Sopenharmony_ci#include <linux/kernel.h> 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/of.h> 138c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 148c2ecf20Sopenharmony_ci#include <linux/soc/ixp4xx/qmgr.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_cistatic struct qmgr_regs __iomem *qmgr_regs; 178c2ecf20Sopenharmony_cistatic int qmgr_irq_1; 188c2ecf20Sopenharmony_cistatic int qmgr_irq_2; 198c2ecf20Sopenharmony_cistatic spinlock_t qmgr_lock; 208c2ecf20Sopenharmony_cistatic u32 used_sram_bitmap[4]; /* 128 16-dword pages */ 218c2ecf20Sopenharmony_cistatic void (*irq_handlers[QUEUES])(void *pdev); 228c2ecf20Sopenharmony_cistatic void *irq_pdevs[QUEUES]; 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#if DEBUG_QMGR 258c2ecf20Sopenharmony_cichar qmgr_queue_descs[QUEUES][32]; 268c2ecf20Sopenharmony_ci#endif 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_civoid qmgr_put_entry(unsigned int queue, u32 val) 298c2ecf20Sopenharmony_ci{ 308c2ecf20Sopenharmony_ci#if DEBUG_QMGR 318c2ecf20Sopenharmony_ci BUG_ON(!qmgr_queue_descs[queue]); /* not yet requested */ 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci printk(KERN_DEBUG "Queue %s(%i) put %X\n", 348c2ecf20Sopenharmony_ci qmgr_queue_descs[queue], queue, val); 358c2ecf20Sopenharmony_ci#endif 368c2ecf20Sopenharmony_ci __raw_writel(val, &qmgr_regs->acc[queue][0]); 378c2ecf20Sopenharmony_ci} 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ciu32 qmgr_get_entry(unsigned int queue) 408c2ecf20Sopenharmony_ci{ 418c2ecf20Sopenharmony_ci u32 val; 428c2ecf20Sopenharmony_ci val = __raw_readl(&qmgr_regs->acc[queue][0]); 438c2ecf20Sopenharmony_ci#if DEBUG_QMGR 448c2ecf20Sopenharmony_ci BUG_ON(!qmgr_queue_descs[queue]); /* not yet requested */ 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci printk(KERN_DEBUG "Queue %s(%i) get %X\n", 478c2ecf20Sopenharmony_ci qmgr_queue_descs[queue], queue, val); 488c2ecf20Sopenharmony_ci#endif 498c2ecf20Sopenharmony_ci return val; 508c2ecf20Sopenharmony_ci} 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic int __qmgr_get_stat1(unsigned int queue) 538c2ecf20Sopenharmony_ci{ 548c2ecf20Sopenharmony_ci return (__raw_readl(&qmgr_regs->stat1[queue >> 3]) 558c2ecf20Sopenharmony_ci >> ((queue & 7) << 2)) & 0xF; 568c2ecf20Sopenharmony_ci} 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_cistatic int __qmgr_get_stat2(unsigned int queue) 598c2ecf20Sopenharmony_ci{ 608c2ecf20Sopenharmony_ci BUG_ON(queue >= HALF_QUEUES); 618c2ecf20Sopenharmony_ci return (__raw_readl(&qmgr_regs->stat2[queue >> 4]) 628c2ecf20Sopenharmony_ci >> ((queue & 0xF) << 1)) & 0x3; 638c2ecf20Sopenharmony_ci} 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci/** 668c2ecf20Sopenharmony_ci * qmgr_stat_empty() - checks if a hardware queue is empty 678c2ecf20Sopenharmony_ci * @queue: queue number 688c2ecf20Sopenharmony_ci * 698c2ecf20Sopenharmony_ci * Returns non-zero value if the queue is empty. 708c2ecf20Sopenharmony_ci */ 718c2ecf20Sopenharmony_ciint qmgr_stat_empty(unsigned int queue) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci BUG_ON(queue >= HALF_QUEUES); 748c2ecf20Sopenharmony_ci return __qmgr_get_stat1(queue) & QUEUE_STAT1_EMPTY; 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci/** 788c2ecf20Sopenharmony_ci * qmgr_stat_below_low_watermark() - checks if a queue is below low watermark 798c2ecf20Sopenharmony_ci * @queue: queue number 808c2ecf20Sopenharmony_ci * 818c2ecf20Sopenharmony_ci * Returns non-zero value if the queue is below low watermark. 828c2ecf20Sopenharmony_ci */ 838c2ecf20Sopenharmony_ciint qmgr_stat_below_low_watermark(unsigned int queue) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci if (queue >= HALF_QUEUES) 868c2ecf20Sopenharmony_ci return (__raw_readl(&qmgr_regs->statne_h) >> 878c2ecf20Sopenharmony_ci (queue - HALF_QUEUES)) & 0x01; 888c2ecf20Sopenharmony_ci return __qmgr_get_stat1(queue) & QUEUE_STAT1_NEARLY_EMPTY; 898c2ecf20Sopenharmony_ci} 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci/** 928c2ecf20Sopenharmony_ci * qmgr_stat_full() - checks if a hardware queue is full 938c2ecf20Sopenharmony_ci * @queue: queue number 948c2ecf20Sopenharmony_ci * 958c2ecf20Sopenharmony_ci * Returns non-zero value if the queue is full. 968c2ecf20Sopenharmony_ci */ 978c2ecf20Sopenharmony_ciint qmgr_stat_full(unsigned int queue) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci if (queue >= HALF_QUEUES) 1008c2ecf20Sopenharmony_ci return (__raw_readl(&qmgr_regs->statf_h) >> 1018c2ecf20Sopenharmony_ci (queue - HALF_QUEUES)) & 0x01; 1028c2ecf20Sopenharmony_ci return __qmgr_get_stat1(queue) & QUEUE_STAT1_FULL; 1038c2ecf20Sopenharmony_ci} 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci/** 1068c2ecf20Sopenharmony_ci * qmgr_stat_overflow() - checks if a hardware queue experienced overflow 1078c2ecf20Sopenharmony_ci * @queue: queue number 1088c2ecf20Sopenharmony_ci * 1098c2ecf20Sopenharmony_ci * Returns non-zero value if the queue experienced overflow. 1108c2ecf20Sopenharmony_ci */ 1118c2ecf20Sopenharmony_ciint qmgr_stat_overflow(unsigned int queue) 1128c2ecf20Sopenharmony_ci{ 1138c2ecf20Sopenharmony_ci return __qmgr_get_stat2(queue) & QUEUE_STAT2_OVERFLOW; 1148c2ecf20Sopenharmony_ci} 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_civoid qmgr_set_irq(unsigned int queue, int src, 1178c2ecf20Sopenharmony_ci void (*handler)(void *pdev), void *pdev) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci unsigned long flags; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci spin_lock_irqsave(&qmgr_lock, flags); 1228c2ecf20Sopenharmony_ci if (queue < HALF_QUEUES) { 1238c2ecf20Sopenharmony_ci u32 __iomem *reg; 1248c2ecf20Sopenharmony_ci int bit; 1258c2ecf20Sopenharmony_ci BUG_ON(src > QUEUE_IRQ_SRC_NOT_FULL); 1268c2ecf20Sopenharmony_ci reg = &qmgr_regs->irqsrc[queue >> 3]; /* 8 queues per u32 */ 1278c2ecf20Sopenharmony_ci bit = (queue % 8) * 4; /* 3 bits + 1 reserved bit per queue */ 1288c2ecf20Sopenharmony_ci __raw_writel((__raw_readl(reg) & ~(7 << bit)) | (src << bit), 1298c2ecf20Sopenharmony_ci reg); 1308c2ecf20Sopenharmony_ci } else 1318c2ecf20Sopenharmony_ci /* IRQ source for queues 32-63 is fixed */ 1328c2ecf20Sopenharmony_ci BUG_ON(src != QUEUE_IRQ_SRC_NOT_NEARLY_EMPTY); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci irq_handlers[queue] = handler; 1358c2ecf20Sopenharmony_ci irq_pdevs[queue] = pdev; 1368c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&qmgr_lock, flags); 1378c2ecf20Sopenharmony_ci} 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_cistatic irqreturn_t qmgr_irq1_a0(int irq, void *pdev) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci int i, ret = 0; 1438c2ecf20Sopenharmony_ci u32 en_bitmap, src, stat; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci /* ACK - it may clear any bits so don't rely on it */ 1468c2ecf20Sopenharmony_ci __raw_writel(0xFFFFFFFF, &qmgr_regs->irqstat[0]); 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci en_bitmap = __raw_readl(&qmgr_regs->irqen[0]); 1498c2ecf20Sopenharmony_ci while (en_bitmap) { 1508c2ecf20Sopenharmony_ci i = __fls(en_bitmap); /* number of the last "low" queue */ 1518c2ecf20Sopenharmony_ci en_bitmap &= ~BIT(i); 1528c2ecf20Sopenharmony_ci src = __raw_readl(&qmgr_regs->irqsrc[i >> 3]); 1538c2ecf20Sopenharmony_ci stat = __raw_readl(&qmgr_regs->stat1[i >> 3]); 1548c2ecf20Sopenharmony_ci if (src & 4) /* the IRQ condition is inverted */ 1558c2ecf20Sopenharmony_ci stat = ~stat; 1568c2ecf20Sopenharmony_ci if (stat & BIT(src & 3)) { 1578c2ecf20Sopenharmony_ci irq_handlers[i](irq_pdevs[i]); 1588c2ecf20Sopenharmony_ci ret = IRQ_HANDLED; 1598c2ecf20Sopenharmony_ci } 1608c2ecf20Sopenharmony_ci } 1618c2ecf20Sopenharmony_ci return ret; 1628c2ecf20Sopenharmony_ci} 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_cistatic irqreturn_t qmgr_irq2_a0(int irq, void *pdev) 1668c2ecf20Sopenharmony_ci{ 1678c2ecf20Sopenharmony_ci int i, ret = 0; 1688c2ecf20Sopenharmony_ci u32 req_bitmap; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci /* ACK - it may clear any bits so don't rely on it */ 1718c2ecf20Sopenharmony_ci __raw_writel(0xFFFFFFFF, &qmgr_regs->irqstat[1]); 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci req_bitmap = __raw_readl(&qmgr_regs->irqen[1]) & 1748c2ecf20Sopenharmony_ci __raw_readl(&qmgr_regs->statne_h); 1758c2ecf20Sopenharmony_ci while (req_bitmap) { 1768c2ecf20Sopenharmony_ci i = __fls(req_bitmap); /* number of the last "high" queue */ 1778c2ecf20Sopenharmony_ci req_bitmap &= ~BIT(i); 1788c2ecf20Sopenharmony_ci irq_handlers[HALF_QUEUES + i](irq_pdevs[HALF_QUEUES + i]); 1798c2ecf20Sopenharmony_ci ret = IRQ_HANDLED; 1808c2ecf20Sopenharmony_ci } 1818c2ecf20Sopenharmony_ci return ret; 1828c2ecf20Sopenharmony_ci} 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_cistatic irqreturn_t qmgr_irq(int irq, void *pdev) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci int i, half = (irq == qmgr_irq_1 ? 0 : 1); 1888c2ecf20Sopenharmony_ci u32 req_bitmap = __raw_readl(&qmgr_regs->irqstat[half]); 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci if (!req_bitmap) 1918c2ecf20Sopenharmony_ci return 0; 1928c2ecf20Sopenharmony_ci __raw_writel(req_bitmap, &qmgr_regs->irqstat[half]); /* ACK */ 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci while (req_bitmap) { 1958c2ecf20Sopenharmony_ci i = __fls(req_bitmap); /* number of the last queue */ 1968c2ecf20Sopenharmony_ci req_bitmap &= ~BIT(i); 1978c2ecf20Sopenharmony_ci i += half * HALF_QUEUES; 1988c2ecf20Sopenharmony_ci irq_handlers[i](irq_pdevs[i]); 1998c2ecf20Sopenharmony_ci } 2008c2ecf20Sopenharmony_ci return IRQ_HANDLED; 2018c2ecf20Sopenharmony_ci} 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_civoid qmgr_enable_irq(unsigned int queue) 2058c2ecf20Sopenharmony_ci{ 2068c2ecf20Sopenharmony_ci unsigned long flags; 2078c2ecf20Sopenharmony_ci int half = queue / 32; 2088c2ecf20Sopenharmony_ci u32 mask = 1 << (queue & (HALF_QUEUES - 1)); 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci spin_lock_irqsave(&qmgr_lock, flags); 2118c2ecf20Sopenharmony_ci __raw_writel(__raw_readl(&qmgr_regs->irqen[half]) | mask, 2128c2ecf20Sopenharmony_ci &qmgr_regs->irqen[half]); 2138c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&qmgr_lock, flags); 2148c2ecf20Sopenharmony_ci} 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_civoid qmgr_disable_irq(unsigned int queue) 2178c2ecf20Sopenharmony_ci{ 2188c2ecf20Sopenharmony_ci unsigned long flags; 2198c2ecf20Sopenharmony_ci int half = queue / 32; 2208c2ecf20Sopenharmony_ci u32 mask = 1 << (queue & (HALF_QUEUES - 1)); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci spin_lock_irqsave(&qmgr_lock, flags); 2238c2ecf20Sopenharmony_ci __raw_writel(__raw_readl(&qmgr_regs->irqen[half]) & ~mask, 2248c2ecf20Sopenharmony_ci &qmgr_regs->irqen[half]); 2258c2ecf20Sopenharmony_ci __raw_writel(mask, &qmgr_regs->irqstat[half]); /* clear */ 2268c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&qmgr_lock, flags); 2278c2ecf20Sopenharmony_ci} 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_cistatic inline void shift_mask(u32 *mask) 2308c2ecf20Sopenharmony_ci{ 2318c2ecf20Sopenharmony_ci mask[3] = mask[3] << 1 | mask[2] >> 31; 2328c2ecf20Sopenharmony_ci mask[2] = mask[2] << 1 | mask[1] >> 31; 2338c2ecf20Sopenharmony_ci mask[1] = mask[1] << 1 | mask[0] >> 31; 2348c2ecf20Sopenharmony_ci mask[0] <<= 1; 2358c2ecf20Sopenharmony_ci} 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci#if DEBUG_QMGR 2388c2ecf20Sopenharmony_ciint qmgr_request_queue(unsigned int queue, unsigned int len /* dwords */, 2398c2ecf20Sopenharmony_ci unsigned int nearly_empty_watermark, 2408c2ecf20Sopenharmony_ci unsigned int nearly_full_watermark, 2418c2ecf20Sopenharmony_ci const char *desc_format, const char* name) 2428c2ecf20Sopenharmony_ci#else 2438c2ecf20Sopenharmony_ciint __qmgr_request_queue(unsigned int queue, unsigned int len /* dwords */, 2448c2ecf20Sopenharmony_ci unsigned int nearly_empty_watermark, 2458c2ecf20Sopenharmony_ci unsigned int nearly_full_watermark) 2468c2ecf20Sopenharmony_ci#endif 2478c2ecf20Sopenharmony_ci{ 2488c2ecf20Sopenharmony_ci u32 cfg, addr = 0, mask[4]; /* in 16-dwords */ 2498c2ecf20Sopenharmony_ci int err; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci BUG_ON(queue >= QUEUES); 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci if ((nearly_empty_watermark | nearly_full_watermark) & ~7) 2548c2ecf20Sopenharmony_ci return -EINVAL; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci switch (len) { 2578c2ecf20Sopenharmony_ci case 16: 2588c2ecf20Sopenharmony_ci cfg = 0 << 24; 2598c2ecf20Sopenharmony_ci mask[0] = 0x1; 2608c2ecf20Sopenharmony_ci break; 2618c2ecf20Sopenharmony_ci case 32: 2628c2ecf20Sopenharmony_ci cfg = 1 << 24; 2638c2ecf20Sopenharmony_ci mask[0] = 0x3; 2648c2ecf20Sopenharmony_ci break; 2658c2ecf20Sopenharmony_ci case 64: 2668c2ecf20Sopenharmony_ci cfg = 2 << 24; 2678c2ecf20Sopenharmony_ci mask[0] = 0xF; 2688c2ecf20Sopenharmony_ci break; 2698c2ecf20Sopenharmony_ci case 128: 2708c2ecf20Sopenharmony_ci cfg = 3 << 24; 2718c2ecf20Sopenharmony_ci mask[0] = 0xFF; 2728c2ecf20Sopenharmony_ci break; 2738c2ecf20Sopenharmony_ci default: 2748c2ecf20Sopenharmony_ci return -EINVAL; 2758c2ecf20Sopenharmony_ci } 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci cfg |= nearly_empty_watermark << 26; 2788c2ecf20Sopenharmony_ci cfg |= nearly_full_watermark << 29; 2798c2ecf20Sopenharmony_ci len /= 16; /* in 16-dwords: 1, 2, 4 or 8 */ 2808c2ecf20Sopenharmony_ci mask[1] = mask[2] = mask[3] = 0; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci if (!try_module_get(THIS_MODULE)) 2838c2ecf20Sopenharmony_ci return -ENODEV; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci spin_lock_irq(&qmgr_lock); 2868c2ecf20Sopenharmony_ci if (__raw_readl(&qmgr_regs->sram[queue])) { 2878c2ecf20Sopenharmony_ci err = -EBUSY; 2888c2ecf20Sopenharmony_ci goto err; 2898c2ecf20Sopenharmony_ci } 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci while (1) { 2928c2ecf20Sopenharmony_ci if (!(used_sram_bitmap[0] & mask[0]) && 2938c2ecf20Sopenharmony_ci !(used_sram_bitmap[1] & mask[1]) && 2948c2ecf20Sopenharmony_ci !(used_sram_bitmap[2] & mask[2]) && 2958c2ecf20Sopenharmony_ci !(used_sram_bitmap[3] & mask[3])) 2968c2ecf20Sopenharmony_ci break; /* found free space */ 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci addr++; 2998c2ecf20Sopenharmony_ci shift_mask(mask); 3008c2ecf20Sopenharmony_ci if (addr + len > ARRAY_SIZE(qmgr_regs->sram)) { 3018c2ecf20Sopenharmony_ci printk(KERN_ERR "qmgr: no free SRAM space for" 3028c2ecf20Sopenharmony_ci " queue %i\n", queue); 3038c2ecf20Sopenharmony_ci err = -ENOMEM; 3048c2ecf20Sopenharmony_ci goto err; 3058c2ecf20Sopenharmony_ci } 3068c2ecf20Sopenharmony_ci } 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci used_sram_bitmap[0] |= mask[0]; 3098c2ecf20Sopenharmony_ci used_sram_bitmap[1] |= mask[1]; 3108c2ecf20Sopenharmony_ci used_sram_bitmap[2] |= mask[2]; 3118c2ecf20Sopenharmony_ci used_sram_bitmap[3] |= mask[3]; 3128c2ecf20Sopenharmony_ci __raw_writel(cfg | (addr << 14), &qmgr_regs->sram[queue]); 3138c2ecf20Sopenharmony_ci#if DEBUG_QMGR 3148c2ecf20Sopenharmony_ci snprintf(qmgr_queue_descs[queue], sizeof(qmgr_queue_descs[0]), 3158c2ecf20Sopenharmony_ci desc_format, name); 3168c2ecf20Sopenharmony_ci printk(KERN_DEBUG "qmgr: requested queue %s(%i) addr = 0x%02X\n", 3178c2ecf20Sopenharmony_ci qmgr_queue_descs[queue], queue, addr); 3188c2ecf20Sopenharmony_ci#endif 3198c2ecf20Sopenharmony_ci spin_unlock_irq(&qmgr_lock); 3208c2ecf20Sopenharmony_ci return 0; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_cierr: 3238c2ecf20Sopenharmony_ci spin_unlock_irq(&qmgr_lock); 3248c2ecf20Sopenharmony_ci module_put(THIS_MODULE); 3258c2ecf20Sopenharmony_ci return err; 3268c2ecf20Sopenharmony_ci} 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_civoid qmgr_release_queue(unsigned int queue) 3298c2ecf20Sopenharmony_ci{ 3308c2ecf20Sopenharmony_ci u32 cfg, addr, mask[4]; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci BUG_ON(queue >= QUEUES); /* not in valid range */ 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci spin_lock_irq(&qmgr_lock); 3358c2ecf20Sopenharmony_ci cfg = __raw_readl(&qmgr_regs->sram[queue]); 3368c2ecf20Sopenharmony_ci addr = (cfg >> 14) & 0xFF; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci BUG_ON(!addr); /* not requested */ 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci switch ((cfg >> 24) & 3) { 3418c2ecf20Sopenharmony_ci case 0: mask[0] = 0x1; break; 3428c2ecf20Sopenharmony_ci case 1: mask[0] = 0x3; break; 3438c2ecf20Sopenharmony_ci case 2: mask[0] = 0xF; break; 3448c2ecf20Sopenharmony_ci case 3: mask[0] = 0xFF; break; 3458c2ecf20Sopenharmony_ci } 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci mask[1] = mask[2] = mask[3] = 0; 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci while (addr--) 3508c2ecf20Sopenharmony_ci shift_mask(mask); 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci#if DEBUG_QMGR 3538c2ecf20Sopenharmony_ci printk(KERN_DEBUG "qmgr: releasing queue %s(%i)\n", 3548c2ecf20Sopenharmony_ci qmgr_queue_descs[queue], queue); 3558c2ecf20Sopenharmony_ci qmgr_queue_descs[queue][0] = '\x0'; 3568c2ecf20Sopenharmony_ci#endif 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci while ((addr = qmgr_get_entry(queue))) 3598c2ecf20Sopenharmony_ci printk(KERN_ERR "qmgr: released queue %i not empty: 0x%08X\n", 3608c2ecf20Sopenharmony_ci queue, addr); 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci __raw_writel(0, &qmgr_regs->sram[queue]); 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci used_sram_bitmap[0] &= ~mask[0]; 3658c2ecf20Sopenharmony_ci used_sram_bitmap[1] &= ~mask[1]; 3668c2ecf20Sopenharmony_ci used_sram_bitmap[2] &= ~mask[2]; 3678c2ecf20Sopenharmony_ci used_sram_bitmap[3] &= ~mask[3]; 3688c2ecf20Sopenharmony_ci irq_handlers[queue] = NULL; /* catch IRQ bugs */ 3698c2ecf20Sopenharmony_ci spin_unlock_irq(&qmgr_lock); 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci module_put(THIS_MODULE); 3728c2ecf20Sopenharmony_ci} 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_cistatic int ixp4xx_qmgr_probe(struct platform_device *pdev) 3758c2ecf20Sopenharmony_ci{ 3768c2ecf20Sopenharmony_ci int i, err; 3778c2ecf20Sopenharmony_ci irq_handler_t handler1, handler2; 3788c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 3798c2ecf20Sopenharmony_ci struct resource *res; 3808c2ecf20Sopenharmony_ci int irq1, irq2; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 3838c2ecf20Sopenharmony_ci if (!res) 3848c2ecf20Sopenharmony_ci return -ENODEV; 3858c2ecf20Sopenharmony_ci qmgr_regs = devm_ioremap_resource(dev, res); 3868c2ecf20Sopenharmony_ci if (IS_ERR(qmgr_regs)) 3878c2ecf20Sopenharmony_ci return PTR_ERR(qmgr_regs); 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci irq1 = platform_get_irq(pdev, 0); 3908c2ecf20Sopenharmony_ci if (irq1 <= 0) 3918c2ecf20Sopenharmony_ci return irq1 ? irq1 : -EINVAL; 3928c2ecf20Sopenharmony_ci qmgr_irq_1 = irq1; 3938c2ecf20Sopenharmony_ci irq2 = platform_get_irq(pdev, 1); 3948c2ecf20Sopenharmony_ci if (irq2 <= 0) 3958c2ecf20Sopenharmony_ci return irq2 ? irq2 : -EINVAL; 3968c2ecf20Sopenharmony_ci qmgr_irq_2 = irq2; 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci /* reset qmgr registers */ 3998c2ecf20Sopenharmony_ci for (i = 0; i < 4; i++) { 4008c2ecf20Sopenharmony_ci __raw_writel(0x33333333, &qmgr_regs->stat1[i]); 4018c2ecf20Sopenharmony_ci __raw_writel(0, &qmgr_regs->irqsrc[i]); 4028c2ecf20Sopenharmony_ci } 4038c2ecf20Sopenharmony_ci for (i = 0; i < 2; i++) { 4048c2ecf20Sopenharmony_ci __raw_writel(0, &qmgr_regs->stat2[i]); 4058c2ecf20Sopenharmony_ci __raw_writel(0xFFFFFFFF, &qmgr_regs->irqstat[i]); /* clear */ 4068c2ecf20Sopenharmony_ci __raw_writel(0, &qmgr_regs->irqen[i]); 4078c2ecf20Sopenharmony_ci } 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci __raw_writel(0xFFFFFFFF, &qmgr_regs->statne_h); 4108c2ecf20Sopenharmony_ci __raw_writel(0, &qmgr_regs->statf_h); 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci for (i = 0; i < QUEUES; i++) 4138c2ecf20Sopenharmony_ci __raw_writel(0, &qmgr_regs->sram[i]); 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci if (cpu_is_ixp42x_rev_a0()) { 4168c2ecf20Sopenharmony_ci handler1 = qmgr_irq1_a0; 4178c2ecf20Sopenharmony_ci handler2 = qmgr_irq2_a0; 4188c2ecf20Sopenharmony_ci } else 4198c2ecf20Sopenharmony_ci handler1 = handler2 = qmgr_irq; 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci err = devm_request_irq(dev, irq1, handler1, 0, "IXP4xx Queue Manager", 4228c2ecf20Sopenharmony_ci NULL); 4238c2ecf20Sopenharmony_ci if (err) { 4248c2ecf20Sopenharmony_ci dev_err(dev, "failed to request IRQ%i (%i)\n", 4258c2ecf20Sopenharmony_ci irq1, err); 4268c2ecf20Sopenharmony_ci return err; 4278c2ecf20Sopenharmony_ci } 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci err = devm_request_irq(dev, irq2, handler2, 0, "IXP4xx Queue Manager", 4308c2ecf20Sopenharmony_ci NULL); 4318c2ecf20Sopenharmony_ci if (err) { 4328c2ecf20Sopenharmony_ci dev_err(dev, "failed to request IRQ%i (%i)\n", 4338c2ecf20Sopenharmony_ci irq2, err); 4348c2ecf20Sopenharmony_ci return err; 4358c2ecf20Sopenharmony_ci } 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci used_sram_bitmap[0] = 0xF; /* 4 first pages reserved for config */ 4388c2ecf20Sopenharmony_ci spin_lock_init(&qmgr_lock); 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci dev_info(dev, "IXP4xx Queue Manager initialized.\n"); 4418c2ecf20Sopenharmony_ci return 0; 4428c2ecf20Sopenharmony_ci} 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_cistatic int ixp4xx_qmgr_remove(struct platform_device *pdev) 4458c2ecf20Sopenharmony_ci{ 4468c2ecf20Sopenharmony_ci synchronize_irq(qmgr_irq_1); 4478c2ecf20Sopenharmony_ci synchronize_irq(qmgr_irq_2); 4488c2ecf20Sopenharmony_ci return 0; 4498c2ecf20Sopenharmony_ci} 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_cistatic const struct of_device_id ixp4xx_qmgr_of_match[] = { 4528c2ecf20Sopenharmony_ci { 4538c2ecf20Sopenharmony_ci .compatible = "intel,ixp4xx-ahb-queue-manager", 4548c2ecf20Sopenharmony_ci }, 4558c2ecf20Sopenharmony_ci {}, 4568c2ecf20Sopenharmony_ci}; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_cistatic struct platform_driver ixp4xx_qmgr_driver = { 4598c2ecf20Sopenharmony_ci .driver = { 4608c2ecf20Sopenharmony_ci .name = "ixp4xx-qmgr", 4618c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(ixp4xx_qmgr_of_match), 4628c2ecf20Sopenharmony_ci }, 4638c2ecf20Sopenharmony_ci .probe = ixp4xx_qmgr_probe, 4648c2ecf20Sopenharmony_ci .remove = ixp4xx_qmgr_remove, 4658c2ecf20Sopenharmony_ci}; 4668c2ecf20Sopenharmony_cimodule_platform_driver(ixp4xx_qmgr_driver); 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 4698c2ecf20Sopenharmony_ciMODULE_AUTHOR("Krzysztof Halasa"); 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ciEXPORT_SYMBOL(qmgr_put_entry); 4728c2ecf20Sopenharmony_ciEXPORT_SYMBOL(qmgr_get_entry); 4738c2ecf20Sopenharmony_ciEXPORT_SYMBOL(qmgr_stat_empty); 4748c2ecf20Sopenharmony_ciEXPORT_SYMBOL(qmgr_stat_below_low_watermark); 4758c2ecf20Sopenharmony_ciEXPORT_SYMBOL(qmgr_stat_full); 4768c2ecf20Sopenharmony_ciEXPORT_SYMBOL(qmgr_stat_overflow); 4778c2ecf20Sopenharmony_ciEXPORT_SYMBOL(qmgr_set_irq); 4788c2ecf20Sopenharmony_ciEXPORT_SYMBOL(qmgr_enable_irq); 4798c2ecf20Sopenharmony_ciEXPORT_SYMBOL(qmgr_disable_irq); 4808c2ecf20Sopenharmony_ci#if DEBUG_QMGR 4818c2ecf20Sopenharmony_ciEXPORT_SYMBOL(qmgr_queue_descs); 4828c2ecf20Sopenharmony_ciEXPORT_SYMBOL(qmgr_request_queue); 4838c2ecf20Sopenharmony_ci#else 4848c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__qmgr_request_queue); 4858c2ecf20Sopenharmony_ci#endif 4868c2ecf20Sopenharmony_ciEXPORT_SYMBOL(qmgr_release_queue); 487