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