Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 43b8774d authored by Paul Mundt's avatar Paul Mundt
Browse files

sh: intc: userimask support.



This adds support for hardware-assisted userspace irq masking for
special priority levels. Due to the SR.IMASK interactivity, only some
platforms implement this in hardware (including but not limited to
SH-4A interrupt controllers, and ARM-based SH-Mobile CPUs). Each CPU
needs to wire this up on its own, for now only SH7786 is wired up as an
example.

Signed-off-by: default avatarPaul Mundt <lethal@linux-sh.org>
parent 12129fea
Loading
Loading
Loading
Loading
+2 −0
Original line number Original line Diff line number Diff line
@@ -732,6 +732,8 @@ config GUSA_RB
	  LLSC, this should be more efficient than the other alternative of
	  LLSC, this should be more efficient than the other alternative of
	  disabling interrupts around the atomic sequence.
	  disabling interrupts around the atomic sequence.


source "drivers/sh/Kconfig"

endmenu
endmenu


menu "Boot options"
menu "Boot options"
+3 −0
Original line number Original line Diff line number Diff line
@@ -21,6 +21,7 @@
#include <linux/mm.h>
#include <linux/mm.h>
#include <linux/dma-mapping.h>
#include <linux/dma-mapping.h>
#include <linux/sh_timer.h>
#include <linux/sh_timer.h>
#include <linux/sh_intc.h>
#include <cpu/dma-register.h>
#include <cpu/dma-register.h>
#include <asm/mmzone.h>
#include <asm/mmzone.h>
#include <asm/dmaengine.h>
#include <asm/dmaengine.h>
@@ -907,6 +908,7 @@ static DECLARE_INTC_DESC(intc_desc_irl4567, "sh7786-irl4567", vectors_irl4567,
#define INTC_INTMSK2	INTMSK2
#define INTC_INTMSK2	INTMSK2
#define INTC_INTMSKCLR1	CnINTMSKCLR1
#define INTC_INTMSKCLR1	CnINTMSKCLR1
#define INTC_INTMSKCLR2	INTMSKCLR2
#define INTC_INTMSKCLR2	INTMSKCLR2
#define INTC_USERIMASK	0xfe411000


void __init plat_irq_setup(void)
void __init plat_irq_setup(void)
{
{
@@ -921,6 +923,7 @@ void __init plat_irq_setup(void)
	__raw_writel(__raw_readl(INTC_ICR0) & ~0x00c00000, INTC_ICR0);
	__raw_writel(__raw_readl(INTC_ICR0) & ~0x00c00000, INTC_ICR0);


	register_intc_controller(&intc_desc);
	register_intc_controller(&intc_desc);
	register_intc_userimask(INTC_USERIMASK);
}
}


void __init plat_irq_setup_pins(int mode)
void __init plat_irq_setup_pins(int mode)

drivers/sh/Kconfig

0 → 100644
+13 −0
Original line number Original line Diff line number Diff line
config INTC_USERIMASK
	bool "Userspace interrupt masking support"
	depends on ARCH_SHMOBILE || (SUPERH && CPU_SH4A)
	help
	  This enables support for hardware-assisted userspace hardirq
	  masking.

	  SH-4A and newer interrupt blocks all support a special shadowed
	  page with all non-masking registers obscured when mapped in to
	  userspace. This is primarily for use by userspace device
	  drivers that are using special priority levels.

	  If in doubt, say N.
+67 −2
Original line number Original line Diff line number Diff line
@@ -27,6 +27,7 @@
#include <linux/topology.h>
#include <linux/topology.h>
#include <linux/bitmap.h>
#include <linux/bitmap.h>
#include <linux/cpumask.h>
#include <linux/cpumask.h>
#include <asm/sizes.h>


#define _INTC_MK(fn, mode, addr_e, addr_d, width, shift) \
#define _INTC_MK(fn, mode, addr_e, addr_d, width, shift) \
	((shift) | ((width) << 5) | ((fn) << 9) | ((mode) << 13) | \
	((shift) | ((width) << 5) | ((fn) << 9) | ((mode) << 13) | \
@@ -95,6 +96,7 @@ static DEFINE_SPINLOCK(vector_lock);
#endif
#endif


static unsigned int intc_prio_level[NR_IRQS];	/* for now */
static unsigned int intc_prio_level[NR_IRQS];	/* for now */
static unsigned int default_prio_level = 2;	/* 2 - 16 */
static unsigned long ack_handle[NR_IRQS];
static unsigned long ack_handle[NR_IRQS];


static inline struct intc_desc_int *get_intc_desc(unsigned int irq)
static inline struct intc_desc_int *get_intc_desc(unsigned int irq)
@@ -787,7 +789,7 @@ static void __init intc_register_irq(struct intc_desc *desc,
	/* set priority level
	/* set priority level
	 * - this needs to be at least 2 for 5-bit priorities on 7780
	 * - this needs to be at least 2 for 5-bit priorities on 7780
	 */
	 */
	intc_prio_level[irq] = 2;
	intc_prio_level[irq] = default_prio_level;


	/* enable secondary masking method if present */
	/* enable secondary masking method if present */
	if (data[!primary])
	if (data[!primary])
@@ -1037,6 +1039,64 @@ int __init register_intc_controller(struct intc_desc *desc)
	return -ENOMEM;
	return -ENOMEM;
}
}


#ifdef CONFIG_INTC_USERIMASK
static void __iomem *uimask;

int register_intc_userimask(unsigned long addr)
{
	if (unlikely(uimask))
		return -EBUSY;

	uimask = ioremap_nocache(addr, SZ_4K);
	if (unlikely(!uimask))
		return -ENOMEM;

	pr_info("intc: userimask support registered for levels 0 -> %d\n",
		default_prio_level - 1);

	return 0;
}

static ssize_t
show_intc_userimask(struct sysdev_class *cls,
		    struct sysdev_class_attribute *attr, char *buf)
{
	return sprintf(buf, "%d\n", (__raw_readl(uimask) >> 4) & 0xf);
}

static ssize_t
store_intc_userimask(struct sysdev_class *cls,
		     struct sysdev_class_attribute *attr,
		     const char *buf, size_t count)
{
	unsigned long level;

	level = simple_strtoul(buf, NULL, 10);

	/*
	 * Minimal acceptable IRQ levels are in the 2 - 16 range, but
	 * these are chomped so as to not interfere with normal IRQs.
	 *
	 * Level 1 is a special case on some CPUs in that it's not
	 * directly settable, but given that USERIMASK cuts off below a
	 * certain level, we don't care about this limitation here.
	 * Level 0 on the other hand equates to user masking disabled.
	 *
	 * We use default_prio_level as a cut off so that only special
	 * case opt-in IRQs can be mangled.
	 */
	if (level >= default_prio_level)
		return -EINVAL;

	__raw_writel(0xa5 << 24 | level << 4, uimask);

	return count;
}

static SYSDEV_CLASS_ATTR(userimask, S_IRUSR | S_IWUSR,
			 show_intc_userimask, store_intc_userimask);
#endif

static ssize_t
static ssize_t
show_intc_name(struct sys_device *dev, struct sysdev_attribute *attr, char *buf)
show_intc_name(struct sys_device *dev, struct sysdev_attribute *attr, char *buf)
{
{
@@ -1108,6 +1168,11 @@ static int __init register_intc_sysdevs(void)
	int id = 0;
	int id = 0;


	error = sysdev_class_register(&intc_sysdev_class);
	error = sysdev_class_register(&intc_sysdev_class);
#ifdef CONFIG_INTC_USERIMASK
	if (!error && uimask)
		error = sysdev_class_create_file(&intc_sysdev_class,
						 &attr_userimask);
#endif
	if (!error) {
	if (!error) {
		list_for_each_entry(d, &intc_list, list) {
		list_for_each_entry(d, &intc_list, list) {
			d->sysdev.id = id;
			d->sysdev.id = id;
+9 −0
Original line number Original line Diff line number Diff line
@@ -99,6 +99,15 @@ struct intc_desc symbol __initdata = { \
int __init register_intc_controller(struct intc_desc *desc);
int __init register_intc_controller(struct intc_desc *desc);
int intc_set_priority(unsigned int irq, unsigned int prio);
int intc_set_priority(unsigned int irq, unsigned int prio);


#ifdef CONFIG_INTC_USERIMASK
int register_intc_userimask(unsigned long addr);
#else
static inline int register_intc_userimask(unsigned long addr)
{
	return 0;
}
#endif

int reserve_irq_vector(unsigned int irq);
int reserve_irq_vector(unsigned int irq);
void reserve_irq_legacy(void);
void reserve_irq_legacy(void);