Commit f432f271 authored by Jing Li's avatar Jing Li Committed by guzitao
Browse files

sw64: irqchip: fix irq_enable/disable callback for MCU controller

Sunway inclusion
category: bugfix
bugzilla: https://gitee.com/openeuler/kernel/issues/IB73UR



--------------------------------

Enable or disable the corresponding IRQ instead of all IRQs in
irq_enable/disable callback of MCU controller. In addition, spin locks
are added to protect access to registers.

Signed-off-by: default avatarJing Li <jingli@wxiat.com>
Reviewed-by: default avatarHe Sheng <hesheng@wxiat.com>
Signed-off-by: default avatarGu Zitao <guzitao@wxiat.com>
parent e3c511da
Loading
Loading
Loading
Loading
+89 −61
Original line number Diff line number Diff line
@@ -84,6 +84,8 @@ struct pintc_chip_data {
	void __iomem *mcu_base;   /* MCU/SPBU base address */
	struct irq_chip *mcu_chip;
	u32 mcu_irq_num;
	raw_spinlock_t pintc_lock;
	raw_spinlock_t mcu_lock;
};

static struct pintc_chip_data *chip_datas[MAX_NUMNODES];
@@ -116,59 +118,82 @@ static void pintc_free_chip_data(struct pintc_chip_data *chip_data)
	kfree(chip_data);
}

static DEFINE_RAW_SPINLOCK(pintc_lock);
static void lock_dev_lock(void)
{
	raw_spin_lock(&pintc_lock);
}

static void unlock_dev_lock(void)
{
	raw_spin_unlock(&pintc_lock);
}

static void mcu_irq_mask(struct irq_data *data)
static void mcu_irq_disable(struct irq_data *data)
{
	struct pintc_chip_data *chip_data = data->chip_data;
	unsigned long mask;
	unsigned long mask, flags;
	int hwirq = data->hwirq;

	raw_spin_lock_irqsave(&chip_data->mcu_lock, flags);

	mask = readq(chip_data->mcu_base + OFFSET_MCU_DVC_INT_EN);
	mask &= ~(0x1UL << hwirq);
	writeq(mask, chip_data->mcu_base + OFFSET_MCU_DVC_INT_EN);

	raw_spin_unlock_irqrestore(&chip_data->mcu_lock, flags);
}

static void mcu_irq_unmask(struct irq_data *data)
static void mcu_irq_enable(struct irq_data *data)
{
	struct pintc_chip_data *chip_data = data->chip_data;
	unsigned long mask;
	unsigned long mask, flags;
	int hwirq = data->hwirq;

	raw_spin_lock_irqsave(&chip_data->mcu_lock, flags);

	mask = readq(chip_data->mcu_base + OFFSET_MCU_DVC_INT_EN);
	mask |= (0x1UL << hwirq);
	writeq(mask, chip_data->mcu_base + OFFSET_MCU_DVC_INT_EN);

	raw_spin_unlock_irqrestore(&chip_data->mcu_lock, flags);
}

static void mcu_irq_enable(struct irq_data *irq_data)
static void pintc_mcu_enable(void __iomem *pintc_base)
{
	struct pintc_chip_data *chip_data = irq_data->chip_data;
	unsigned long devint_conf;

	devint_conf = readq(chip_data->pintc_base + OFFSET_DEV_INT_CONFIG);
	devint_conf = readq(pintc_base + OFFSET_DEV_INT_CONFIG);
	devint_conf |= (1UL << 8);
	writeq(devint_conf, chip_data->pintc_base + OFFSET_DEV_INT_CONFIG);
	mcu_irq_unmask(irq_data);
	writeq(devint_conf, pintc_base + OFFSET_DEV_INT_CONFIG);
}

static void mcu_irq_disable(struct irq_data *irq_data)
static void pintc_mcu_disable(void __iomem *pintc_base)
{
	struct pintc_chip_data *chip_data = irq_data->chip_data;
	unsigned long devint_conf;

	devint_conf = readq(chip_data->pintc_base + OFFSET_DEV_INT_CONFIG);
	devint_conf = readq(pintc_base + OFFSET_DEV_INT_CONFIG);
	devint_conf &= ~(1UL << 8);
	writeq(devint_conf, chip_data->pintc_base + OFFSET_DEV_INT_CONFIG);
	mcu_irq_mask(irq_data);
	writeq(devint_conf, pintc_base + OFFSET_DEV_INT_CONFIG);
}

static unsigned long
pintc_mcu_disable_and_save(struct pintc_chip_data *chip_data)
{
	unsigned long val;

	raw_spin_lock(&chip_data->pintc_lock);

	val = readq(chip_data->pintc_base + OFFSET_DEV_INT_CONFIG);
	pintc_mcu_disable(chip_data->pintc_base);

	raw_spin_unlock(&chip_data->pintc_lock);

	return val & (1UL << 8);
}

static void
pintc_mcu_restore(struct pintc_chip_data *chip_data, unsigned long val)
{
	unsigned long current_val;

	raw_spin_lock(&chip_data->pintc_lock);

	current_val = readq(chip_data->pintc_base + OFFSET_DEV_INT_CONFIG);
	current_val &= ~(1UL << 8);
	current_val |= val;
	writeq(current_val, chip_data->pintc_base + OFFSET_DEV_INT_CONFIG);

	raw_spin_unlock(&chip_data->pintc_lock);
}

static unsigned long make_pintc_int_target(u32 version, int rcid)
@@ -195,10 +220,30 @@ static unsigned long make_pintc_int_target(u32 version, int rcid)
	return target;
}

static int __assign_mcu_irq_config(const struct pintc_chip_data *chip_data,
static void update_pintc_mcu_target(struct pintc_chip_data *chip_data,
		unsigned long target)
{
	unsigned long val, flags;

	raw_spin_lock_irqsave(&chip_data->pintc_lock, flags);

	val = readq(chip_data->pintc_base + OFFSET_DEV_INT_CONFIG);

	/* Disable MCU irqs until affinity setting is completed */
	pintc_mcu_disable(chip_data->pintc_base);

	val &= 0xffff;
	val |= (target << 16);

	writeq(val, chip_data->pintc_base + OFFSET_DEV_INT_CONFIG);

	raw_spin_unlock_irqrestore(&chip_data->pintc_lock, flags);
}

static int assign_mcu_irq_config(struct pintc_chip_data *chip_data,
		cpumask_t *targets)
{
	unsigned long dev_int_tar, val;
	unsigned long dev_int_tar;
	unsigned int cpu;
	int rcid;

@@ -219,52 +264,32 @@ static int __assign_mcu_irq_config(const struct pintc_chip_data *chip_data,

	rcid = cpu_to_rcid(cpu);

	val = readq(chip_data->pintc_base + OFFSET_DEV_INT_CONFIG);
	dev_int_tar = make_pintc_int_target(chip_data->version, rcid);
	val &= 0xffff;
	val |= dev_int_tar << 16;
	writeq(val, chip_data->pintc_base + OFFSET_DEV_INT_CONFIG);
	update_pintc_mcu_target(chip_data, dev_int_tar);

	return 0;
}

static int assign_mcu_irq_config(const struct pintc_chip_data *chip_data,
		cpumask_t *targets)
{
	int ret;

	lock_dev_lock();
	ret = __assign_mcu_irq_config(chip_data, targets);
	unlock_dev_lock();

	return ret;
}

static int mcu_irq_set_affinity(struct irq_data *irq_data,
				 const struct cpumask *dest, bool force)
{
	struct pintc_chip_data *chip_data = irq_data->chip_data;
	cpumask_t targets;
	int ret = 0;

	if (cpumask_any_and(dest, cpu_online_mask) >= nr_cpu_ids)
		return -EINVAL;

	cpumask_and(&targets, dest, cpu_online_mask);

	mcu_irq_disable(irq_data);
	ret = assign_mcu_irq_config(chip_data, &targets);
	mcu_irq_enable(irq_data);

	return ret;
	return assign_mcu_irq_config(chip_data, &targets);
}

static struct irq_chip pintc_mcu_chip = {
	.name			= "MCU-INT",
	.irq_enable		= mcu_irq_enable,
	.irq_disable		= mcu_irq_disable,
	.irq_mask		= mcu_irq_mask,
	.irq_unmask		= mcu_irq_unmask,
	.irq_mask		= mcu_irq_disable,
	.irq_unmask		= mcu_irq_enable,
	.irq_set_affinity	= mcu_irq_set_affinity,
};

@@ -378,6 +403,10 @@ static int __init pintc_init_mcu(struct pintc_chip_data *chip_data,
				&pintc_mcu_domain_ops, chip_data);
		/* Mask all interrupts for now */
		writeq(0x0, chip_data->mcu_base + OFFSET_MCU_DVC_INT_EN);

		/* When building the root domain, move it to a better location */
		if (mcu_irq_domain)
			pintc_mcu_enable(chip_data->pintc_base);
	}

	if (!mcu_irq_domain) {
@@ -385,26 +414,25 @@ static int __init pintc_init_mcu(struct pintc_chip_data *chip_data,
		return -ENOMEM;
	}

	raw_spin_lock_init(&chip_data->pintc_lock);
	raw_spin_lock_init(&chip_data->mcu_lock);

	pr_info(PREFIX "MCU version [%u] on node [%u] initialized\n",
			chip_data->version, chip_data->node);

	return 0;
}

/* Currently, only MCU controller on node 0 is supported */
void handle_dev_int(struct pt_regs *regs)
{
	void __iomem *mcu_base, *intpu_base;
	unsigned long config_val, val, stat;
	unsigned long stat, val;
	unsigned int hwirq;

	/* Currently, only MCU controller on node 0 is supported */
	mcu_base = chip_datas[0]->mcu_base;
	intpu_base = chip_datas[0]->pintc_base;
	/* Disable global irq of MCU due to some hardware reasons */
	val = pintc_mcu_disable_and_save(chip_datas[0]);

	config_val = readq(intpu_base + OFFSET_DEV_INT_CONFIG);
	val = config_val & (~(1UL << 8));
	writeq(val, intpu_base + OFFSET_DEV_INT_CONFIG);
	stat = readq(mcu_base + OFFSET_MCU_DVC_INT);
	stat = readq(chip_datas[0]->mcu_base + OFFSET_MCU_DVC_INT);

	while (stat) {
		hwirq = ffs(stat) - 1;
@@ -412,7 +440,7 @@ void handle_dev_int(struct pt_regs *regs)
		stat &= ~(1UL << hwirq);
	}

	writeq(config_val, intpu_base + OFFSET_DEV_INT_CONFIG);
	pintc_mcu_restore(chip_datas[0], val);
}

void handle_fault_int(void)