xref: /linux/drivers/sh/intc/userimask.c (revision 3fd6c59042dbba50391e30862beac979491145fe)
12be6bb0cSPaul Mundt /*
22be6bb0cSPaul Mundt  * Support for hardware-assisted userspace interrupt masking.
32be6bb0cSPaul Mundt  *
42be6bb0cSPaul Mundt  * Copyright (C) 2010  Paul Mundt
52be6bb0cSPaul Mundt  *
62be6bb0cSPaul Mundt  * This file is subject to the terms and conditions of the GNU General Public
72be6bb0cSPaul Mundt  * License.  See the file "COPYING" in the main directory of this archive
82be6bb0cSPaul Mundt  * for more details.
92be6bb0cSPaul Mundt  */
102be6bb0cSPaul Mundt #define pr_fmt(fmt) "intc: " fmt
112be6bb0cSPaul Mundt 
122be6bb0cSPaul Mundt #include <linux/errno.h>
13f4e73bfcSKay Sievers #include <linux/device.h>
142be6bb0cSPaul Mundt #include <linux/init.h>
152be6bb0cSPaul Mundt #include <linux/io.h>
16a102a088SNobuhiro Iwamatsu #include <linux/stat.h>
1787dfb311SMasahiro Yamada #include <linux/sizes.h>
182be6bb0cSPaul Mundt #include "internals.h"
192be6bb0cSPaul Mundt 
202be6bb0cSPaul Mundt static void __iomem *uimask;
212be6bb0cSPaul Mundt 
222be6bb0cSPaul Mundt static ssize_t
show_intc_userimask(struct device * dev,struct device_attribute * attr,char * buf)23f4e73bfcSKay Sievers show_intc_userimask(struct device *dev,
24f4e73bfcSKay Sievers 		    struct device_attribute *attr, char *buf)
252be6bb0cSPaul Mundt {
262be6bb0cSPaul Mundt 	return sprintf(buf, "%d\n", (__raw_readl(uimask) >> 4) & 0xf);
272be6bb0cSPaul Mundt }
282be6bb0cSPaul Mundt 
292be6bb0cSPaul Mundt static ssize_t
store_intc_userimask(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)30f4e73bfcSKay Sievers store_intc_userimask(struct device *dev,
31f4e73bfcSKay Sievers 		     struct device_attribute *attr,
322be6bb0cSPaul Mundt 		     const char *buf, size_t count)
332be6bb0cSPaul Mundt {
342be6bb0cSPaul Mundt 	unsigned long level;
35*c3e878caSHongbo Li 	int ret;
362be6bb0cSPaul Mundt 
37*c3e878caSHongbo Li 	ret = kstrtoul(buf, 10, &level);
38*c3e878caSHongbo Li 	if (ret != 0)
39*c3e878caSHongbo Li 		return ret;
402be6bb0cSPaul Mundt 
412be6bb0cSPaul Mundt 	/*
422be6bb0cSPaul Mundt 	 * Minimal acceptable IRQ levels are in the 2 - 16 range, but
432be6bb0cSPaul Mundt 	 * these are chomped so as to not interfere with normal IRQs.
442be6bb0cSPaul Mundt 	 *
452be6bb0cSPaul Mundt 	 * Level 1 is a special case on some CPUs in that it's not
462be6bb0cSPaul Mundt 	 * directly settable, but given that USERIMASK cuts off below a
472be6bb0cSPaul Mundt 	 * certain level, we don't care about this limitation here.
482be6bb0cSPaul Mundt 	 * Level 0 on the other hand equates to user masking disabled.
492be6bb0cSPaul Mundt 	 *
502be6bb0cSPaul Mundt 	 * We use the default priority level as a cut off so that only
512be6bb0cSPaul Mundt 	 * special case opt-in IRQs can be mangled.
522be6bb0cSPaul Mundt 	 */
532be6bb0cSPaul Mundt 	if (level >= intc_get_dfl_prio_level())
542be6bb0cSPaul Mundt 		return -EINVAL;
552be6bb0cSPaul Mundt 
562be6bb0cSPaul Mundt 	__raw_writel(0xa5 << 24 | level << 4, uimask);
572be6bb0cSPaul Mundt 
582be6bb0cSPaul Mundt 	return count;
592be6bb0cSPaul Mundt }
602be6bb0cSPaul Mundt 
61f4e73bfcSKay Sievers static DEVICE_ATTR(userimask, S_IRUSR | S_IWUSR,
622be6bb0cSPaul Mundt 		   show_intc_userimask, store_intc_userimask);
632be6bb0cSPaul Mundt 
642be6bb0cSPaul Mundt 
userimask_sysdev_init(void)652be6bb0cSPaul Mundt static int __init userimask_sysdev_init(void)
662be6bb0cSPaul Mundt {
6793c42c0bSGreg Kroah-Hartman 	struct device *dev_root;
6893c42c0bSGreg Kroah-Hartman 	int ret = 0;
6993c42c0bSGreg Kroah-Hartman 
702be6bb0cSPaul Mundt 	if (unlikely(!uimask))
712be6bb0cSPaul Mundt 		return -ENXIO;
722be6bb0cSPaul Mundt 
7393c42c0bSGreg Kroah-Hartman 	dev_root = bus_get_dev_root(&intc_subsys);
7493c42c0bSGreg Kroah-Hartman 	if (dev_root) {
7593c42c0bSGreg Kroah-Hartman 		ret = device_create_file(dev_root, &dev_attr_userimask);
7693c42c0bSGreg Kroah-Hartman 		put_device(dev_root);
7793c42c0bSGreg Kroah-Hartman 	}
7893c42c0bSGreg Kroah-Hartman 	return ret;
792be6bb0cSPaul Mundt }
802be6bb0cSPaul Mundt late_initcall(userimask_sysdev_init);
812be6bb0cSPaul Mundt 
register_intc_userimask(unsigned long addr)822be6bb0cSPaul Mundt int register_intc_userimask(unsigned long addr)
832be6bb0cSPaul Mundt {
842be6bb0cSPaul Mundt 	if (unlikely(uimask))
852be6bb0cSPaul Mundt 		return -EBUSY;
862be6bb0cSPaul Mundt 
874bdc0d67SChristoph Hellwig 	uimask = ioremap(addr, SZ_4K);
882be6bb0cSPaul Mundt 	if (unlikely(!uimask))
892be6bb0cSPaul Mundt 		return -ENOMEM;
902be6bb0cSPaul Mundt 
912be6bb0cSPaul Mundt 	pr_info("userimask support registered for levels 0 -> %d\n",
922be6bb0cSPaul Mundt 		intc_get_dfl_prio_level() - 1);
932be6bb0cSPaul Mundt 
942be6bb0cSPaul Mundt 	return 0;
952be6bb0cSPaul Mundt }
96