Files
openloongarch-linux-6.12/drivers/irqchip/irq-loongarch-2k-iointc.c
T
maoxiaochuan 7ffd18ab9d Update to 14fc3074cfe039
1. 添加带irq处理的gpio驱动
2. 龙芯2k系列添加mmc驱动支持
3. 添加看门狗驱动
4. dtb支持从command line中的board_name字段判断
5. 支持内存分布描述数据从固件传入的数据获取
6. 支持从bootcmdline获取ramdisk启动的地址和所用大小
7. 添加2k300的i2c总线驱动
8. 2k300使用新的spi 和 spi-io 驱动
9. CPU芯片温度传感器支持2k300的计算方式
10. 添加2k300的adc驱动
11. pwm: update default pwm freq to 50Mhz
12. loongson pwm 驱动支持dts使用clock-frequency形式声明频率
13. loongson pwm 确保disable时输出的电平为空闲电平
14. 2k300编译配置使能pwm leds功能
15. 2k300的gmac的phy-mode默认使用rgmii-id
16. 2k300先锋派设置YT8531的内部延迟
17. YT8531 phy 支持设置led的工作模式
18. LCD屏幕 支持cmdline的panel属性选择某一款板卡的dts配置,实现屏幕切换
19. 添加脚本用于快速选择板卡配置文件
20. 添加CAN驱动
21. 添加音频驱动
22. 优化OTG兼容性

Signed-off-by: maoxiaochuan <maoxiaochuan@loongson.cn>
2025-02-13 16:03:32 +08:00

387 lines
11 KiB
C

/* SPDX-License-Identifier: GPL-2.0 */
/*
* =====================================================================================
*
* Filename: irq.c
*
* Description: irq handle
*
* Version: 1.0
* Revision: none
* Compiler: gcc
*
* Company: Loongson Corp.
*
* =====================================================================================
*/
#include <linux/irq.h>
#include <linux/irqchip.h>
#include <linux/spinlock_types.h>
#include <linux/of_irq.h>
#include <linux/of_address.h>
#include <linux/interrupt.h>
#define LS2K_INTISR_REG(i) (priv->regbase + 0x20 + (((i) > 31) ? 0x40 : 0))
#define LS2K_INTEN_REG(i) (priv->regbase + 0x24 + (((i) > 31) ? 0x40 : 0))
#define LS2K_INTENSET_REG(i) (priv->regbase + 0x28 + (((i) > 31) ? 0x40 : 0))
#define LS2K_INTENCLR_REG(i) (priv->regbase + 0x2c + (((i) > 31) ? 0x40 : 0))
#define LS2K_INTPOL_REG(i) (priv->regbase + 0x30 + (((i) > 31) ? 0x40 : 0))
#define LS2K_INTEDGE_REG(i) (priv->regbase + 0x34 + (((i) > 31) ? 0x40 : 0))
#define LS2K_IRQ_ROUTE_REG(i) (priv->regbase + 0x00 + (((i) > 31) ? 0x40 : 0) + ((i) % 32))
#define LS2K_COREISR_REG0(i) (priv->isrreg + (i) * 0x100)
#define LS2K_COREISR_REG1(i) (priv->isrreg + 8 + (i) * 0x100)
/* 2k1000 */
#define LS2K_BOUNCE_REG(i) (priv->regbase + 0x38 + (((i) > 31) ? 0x40 : 0))
#define LS2K_INTAUTO_REG(i) (priv->regbase + 0x3c + (((i) > 31) ? 0x40 : 0))
struct iointc {
struct irq_domain *domain;
int cascade;
void *regbase;
void *isrreg;
raw_spinlock_t lock;
};
static void mask_ls2k_iointc_irq(struct irq_data *data)
{
struct iointc *priv = (struct iointc *)data->domain->host_data;
unsigned long hwirq = data->hwirq;
void *reg = LS2K_INTENCLR_REG(hwirq);
unsigned long flags;
raw_spin_lock_irqsave(&priv->lock, flags);
writel(1<<(hwirq%32), (void *)reg);
raw_spin_unlock_irqrestore(&priv->lock, flags);
}
static void unmask_ls2k_iointc_irq(struct irq_data *data)
{
struct iointc *priv = (struct iointc *)data->domain->host_data;
unsigned long hwirq = data->hwirq;
void *reg = LS2K_INTENSET_REG(hwirq);
unsigned long flags;
raw_spin_lock_irqsave(&priv->lock, flags);
writel(1<<(hwirq % 32), (void *)reg);
raw_spin_unlock_irqrestore(&priv->lock, flags);
}
static int ls2k_iointc_set_type(struct irq_data *d, unsigned int type)
{
struct iointc *priv = (struct iointc *)d->domain->host_data;
unsigned long hwirq = d->hwirq;
unsigned long flags;
unsigned int pol_val, edge_val;
raw_spin_lock_irqsave(&priv->lock, flags);
pol_val = readl((void *)LS2K_INTPOL_REG(hwirq));
edge_val = readl((void *)LS2K_INTEDGE_REG(hwirq));
switch (type) {
case IRQ_TYPE_EDGE_RISING:
edge_val |= (1<<(hwirq%32));
pol_val &= ~(1<<(hwirq%32));
break;
case IRQ_TYPE_EDGE_FALLING:
edge_val |= (1<<(hwirq%32));
pol_val |= (1<<(hwirq%32));
break;
case IRQ_TYPE_LEVEL_LOW:
edge_val &= ~(1<<(hwirq%32));
pol_val |= (1<<(hwirq%32));
break;
case IRQ_TYPE_LEVEL_HIGH:
edge_val &= ~(1<<(hwirq%32));
pol_val &= ~(1<<(hwirq%32));
break;
case IRQ_TYPE_EDGE_BOTH:
pr_warn("Not support both_edge interrupt,\
but requester can simulate it\n");
break;
case IRQ_TYPE_NONE:
default:
raw_spin_unlock_irqrestore(&priv->lock, flags);
return -EINVAL;
}
writel(pol_val, (void *)LS2K_INTPOL_REG(hwirq));
writel(edge_val, (void *)LS2K_INTEDGE_REG(hwirq));
raw_spin_unlock_irqrestore(&priv->lock, flags);
return 0;
}
static void ls2k_iointc_irq_ack(struct irq_data *d)
{
struct iointc *priv = (struct iointc *)d->domain->host_data;
unsigned long hwirq = d->hwirq;
void *reg;
unsigned long flags;
unsigned int reg_val;
raw_spin_lock_irqsave(&priv->lock, flags);
reg = LS2K_INTEN_REG(hwirq);
reg_val = readl((void *)reg);
reg = LS2K_INTENCLR_REG(hwirq);
writel(1 << (hwirq%32), (void *)reg);
if (reg_val & (1 << (hwirq%32)))
reg = LS2K_INTENSET_REG(hwirq);
else
reg = LS2K_INTENCLR_REG(hwirq);
writel(1 << (hwirq%32), (void *)reg);
raw_spin_unlock_irqrestore(&priv->lock, flags);
}
static int ls2k_iointc_irq_domain_xlate(struct irq_domain *d, struct device_node *node,
const u32 *intspec, unsigned int intsize,
unsigned long *out_hwirq, unsigned int *out_type)
{
if (WARN_ON(intsize < 1))
return -EINVAL;
*out_hwirq = intspec[0];
if (intsize > 1)
*out_type = intspec[1] & IRQ_TYPE_SENSE_MASK;
else
*out_type = IRQ_TYPE_NONE;
return 0;
}
static void ls2k500_iointc_set_irq_route(struct iointc *priv, int irq, struct cpumask *core_mask, int line)
{
if (irq > 63) {
printk(KERN_ERR "%s: interrupt number error\n", __func__);
return;
}
writeb((1 << (line + 4)), (void *)LS2K_IRQ_ROUTE_REG(irq));
}
static int ls2k500_iointc_set_affinity(struct irq_data *d, const struct cpumask *affinity,
bool force)
{
struct iointc *priv = (struct iointc *)d->domain->host_data;
unsigned long hwirq = d->hwirq;
cpumask_t tmask;
unsigned long *mask;
unsigned long flags;
unsigned long irqline_parent = irq_get_irq_data(priv->cascade)->hwirq;
cpumask_and(&tmask, affinity, cpu_online_mask);
cpumask_copy(d->common->affinity, &tmask);
mask = cpumask_bits(&tmask);
raw_spin_lock_irqsave(&priv->lock, flags);
ls2k500_iointc_set_irq_route(priv, hwirq, &tmask, irqline_parent - 2 /* 2 soft IP */);
raw_spin_unlock_irqrestore(&priv->lock, flags);
irq_data_update_effective_affinity(d, &tmask);
return IRQ_SET_MASK_OK_NOCOPY;
}
static struct irq_chip ls2k500_iointc = {
.name = "2k-iointc",
.irq_mask = mask_ls2k_iointc_irq,
.irq_unmask = unmask_ls2k_iointc_irq,
.irq_set_affinity = ls2k500_iointc_set_affinity,
.irq_ack = ls2k_iointc_irq_ack,
.irq_set_type = ls2k_iointc_set_type,
.flags = IRQCHIP_SKIP_SET_WAKE,
};
static void ls2k500_iointc_irq_handler(struct irq_desc *desc)
{
struct iointc *priv = irq_desc_get_handler_data(desc);
unsigned long pending;
u32 reg_val;
int irq;
unsigned int virq;
pending = readl((void *)LS2K_COREISR_REG0(0));
reg_val = readl((void *)LS2K_COREISR_REG1(0));
pending |= (unsigned long)reg_val << 32;
if (!pending)
spurious_interrupt();
while (pending) {
irq = fls64(pending) - 1;
virq = irq_linear_revmap(priv->domain, irq);
generic_handle_irq(virq);
pending &= ~BIT(irq);
}
}
static int ls2k500_iointc_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw)
{
irq_set_chip_and_handler(irq, &ls2k500_iointc, handle_level_irq);
return 0;
}
static const struct irq_domain_ops ls2k500_iointc_irq_domain_ops = {
.map = ls2k500_iointc_map,
.xlate = ls2k_iointc_irq_domain_xlate,
};
static int ls2k500_iointc_irq_of_init(struct device_node *of_node,
struct device_node *parent)
{
u32 cascade;
struct fwnode_handle *fwnode;
struct iointc *priv = kzalloc(sizeof(struct iointc), GFP_KERNEL);
raw_spin_lock_init(&priv->lock);
priv->cascade = cascade = of_irq_get_byname(of_node, "cascade");
priv->regbase = of_iomap(of_node, 0);
priv->isrreg = of_iomap(of_node, 1);
fwnode = of_node_to_fwnode(of_node);
priv->domain = irq_domain_create_linear(fwnode, 64,
&ls2k500_iointc_irq_domain_ops, priv);
irq_set_chained_handler_and_data(cascade,
ls2k500_iointc_irq_handler,
priv);
if (!priv->domain)
panic("Failed to add irqdomain for ls2k io intc");
return 0;
}
IRQCHIP_DECLARE(ls2k500_iointc, "loongson,2k500-iointc", ls2k500_iointc_irq_of_init);
IRQCHIP_DECLARE(ls2k500_icu, "loongson,2k500-icu", ls2k500_iointc_irq_of_init);
static void ls2k1000_iointc_set_irq_route(struct iointc *priv, int irq, struct cpumask *core_mask, int line)
{
void *auto_reg = LS2K_INTAUTO_REG(irq);
void *bounce_reg = LS2K_BOUNCE_REG(irq);
unsigned long *mask;
unsigned int weight;
unsigned int reg_val;
if (irq > 63) {
printk(KERN_ERR "%s: interrupt number error\n", __func__);
return;
}
mask = cpumask_bits(core_mask);
writeb((1 << (line + 4)) | (*mask & 0xf), (void *)LS2K_IRQ_ROUTE_REG(irq));
reg_val = readl((void *)auto_reg);
reg_val &= ~(1 << (irq % 32)); /* auto always set to 0 */
writel(reg_val, (void *)auto_reg);
reg_val = readl((void *)bounce_reg);
weight = cpumask_weight(core_mask);
if (weight > 1)
reg_val |= (1 << (irq % 32));
else
reg_val &= ~(1 << (irq % 32));
writel(reg_val, (void *)bounce_reg);
}
static int ls2k1000_iointc_set_affinity(struct irq_data *d, const struct cpumask *affinity,
bool force)
{
struct iointc *priv = (struct iointc *)d->domain->host_data;
unsigned long hwirq = d->hwirq;
cpumask_t tmask;
unsigned long *mask;
unsigned long flags;
unsigned long irqline_parent = irq_get_irq_data(priv->cascade)->hwirq;
cpumask_and(&tmask, affinity, cpu_online_mask);
cpumask_copy(d->common->affinity, &tmask);
mask = cpumask_bits(&tmask);
raw_spin_lock_irqsave(&priv->lock, flags);
ls2k1000_iointc_set_irq_route(priv, hwirq, &tmask, irqline_parent - 2 /* 2 soft IP */);
raw_spin_unlock_irqrestore(&priv->lock, flags);
irq_data_update_effective_affinity(d, &tmask);
return IRQ_SET_MASK_OK_NOCOPY;
}
static struct irq_chip ls2k1000_iointc = {
.name = "2k-iointc",
.irq_mask = mask_ls2k_iointc_irq,
.irq_unmask = unmask_ls2k_iointc_irq,
.irq_set_affinity = ls2k1000_iointc_set_affinity,
.irq_ack = ls2k_iointc_irq_ack,
.irq_set_type = ls2k_iointc_set_type,
.flags = IRQCHIP_SKIP_SET_WAKE,
};
static void ls2k1000_iointc_irq_handler(struct irq_desc *desc)
{
struct iointc *priv = irq_desc_get_handler_data(desc);
unsigned long pending;
int irq, virq;
int core = cpu_logical_map(smp_processor_id());
pending = readl((void *)LS2K_COREISR_REG1(core));
pending <<= 32;
pending |= readl((void *)LS2K_COREISR_REG0(core));
if (!pending)
spurious_interrupt();
while (pending) {
irq = fls64(pending) - 1;
virq = irq_linear_revmap(priv->domain, irq);
generic_handle_irq(virq);
pending &= ~BIT(irq);
}
}
static int ls2k1000_iointc_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw)
{
irq_set_chip_and_handler(irq, &ls2k1000_iointc, handle_level_irq);
return 0;
}
static const struct irq_domain_ops ls2k1000_iointc_irq_domain_ops = {
.map = ls2k1000_iointc_map,
.xlate = ls2k_iointc_irq_domain_xlate,
};
static int ls2k1000_iointc_irq_of_init(struct device_node *of_node,
struct device_node *parent)
{
u32 cascade;
struct fwnode_handle *fwnode;
struct iointc *priv = kzalloc(sizeof(struct iointc), GFP_KERNEL);
raw_spin_lock_init(&priv->lock);
priv->cascade = cascade = of_irq_get_byname(of_node, "cascade");
priv->regbase = of_iomap(of_node, 0);
priv->isrreg = of_iomap(of_node, 1);
fwnode = of_node_to_fwnode(of_node);
priv->domain = irq_domain_create_linear(fwnode, 64,
&ls2k1000_iointc_irq_domain_ops, priv);
irq_set_chained_handler_and_data(cascade,
ls2k1000_iointc_irq_handler,
priv);
if (!priv->domain)
panic("Failed to add irqdomain for ls2k io intc");
irq_set_default_host(priv->domain);
return 0;
}
IRQCHIP_DECLARE(ls2k1000_iointc, "loongson,2k1000-iointc", ls2k1000_iointc_irq_of_init);
IRQCHIP_DECLARE(ls2k1000_icu, "loongson,2k1000-icu", ls2k1000_iointc_irq_of_init);