Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

irqchip/loongson-pch-pic: Update interrupt registration policy

The current code is using a fixed mapping between the LS7A interrupt source
and the HT interrupt vector. This prevents the utilization of the full
interrupt vector space and therefore limits the number of interrupt source
in a system.

Replace the fixed mapping with a dynamic mapping which allocates a
vector when an interrupt source is set up. This avoids that unused
sources prevent vectors from being used for other devices.

Introduce a mapping table in struct pch_pic, where each interrupt source
will allocate an index as a 'hwirq' number from the table in the order of
application and set table value as interrupt source number. This hwirq
number will be configured as vector in the HT interrupt controller. For an
interrupt source, the validity period of the obtained hwirq will last until
the system reset.

Co-developed-by: Biao Dong <dongbiao@loongson.cn>
Signed-off-by: Biao Dong <dongbiao@loongson.cn>
Co-developed-by: Tianyang Zhang <zhangtianyang@loongson.cn>
Signed-off-by: Tianyang Zhang <zhangtianyang@loongson.cn>
Signed-off-by: Baoqi Zhang <zhangbaoqi@loongson.cn>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Link: https://lore.kernel.org/r/20240422093830.27212-1-zhangtianyang@loongson.cn

authored by

Baoqi Zhang and committed by
Thomas Gleixner
234a557e bb58c1ba

+59 -17
+59 -17
drivers/irqchip/irq-loongson-pch-pic.c
··· 33 33 #define PIC_COUNT (PIC_COUNT_PER_REG * PIC_REG_COUNT) 34 34 #define PIC_REG_IDX(irq_id) ((irq_id) / PIC_COUNT_PER_REG) 35 35 #define PIC_REG_BIT(irq_id) ((irq_id) % PIC_COUNT_PER_REG) 36 + #define PIC_UNDEF_VECTOR 255 36 37 37 38 static int nr_pics; 38 39 ··· 47 46 u32 saved_vec_en[PIC_REG_COUNT]; 48 47 u32 saved_vec_pol[PIC_REG_COUNT]; 49 48 u32 saved_vec_edge[PIC_REG_COUNT]; 49 + u8 table[PIC_COUNT]; 50 + int inuse; 50 51 }; 51 52 52 53 static struct pch_pic *pch_pic_priv[MAX_IO_PICS]; 53 54 54 55 struct fwnode_handle *pch_pic_handle[MAX_IO_PICS]; 56 + 57 + static inline u8 hwirq_to_bit(struct pch_pic *priv, int hirq) 58 + { 59 + return priv->table[hirq]; 60 + } 55 61 56 62 static void pch_pic_bitset(struct pch_pic *priv, int offset, int bit) 57 63 { ··· 88 80 { 89 81 struct pch_pic *priv = irq_data_get_irq_chip_data(d); 90 82 91 - pch_pic_bitset(priv, PCH_PIC_MASK, d->hwirq); 83 + pch_pic_bitset(priv, PCH_PIC_MASK, hwirq_to_bit(priv, d->hwirq)); 92 84 irq_chip_mask_parent(d); 93 85 } 94 86 95 87 static void pch_pic_unmask_irq(struct irq_data *d) 96 88 { 97 89 struct pch_pic *priv = irq_data_get_irq_chip_data(d); 90 + int bit = hwirq_to_bit(priv, d->hwirq); 98 91 99 - writel(BIT(PIC_REG_BIT(d->hwirq)), 100 - priv->base + PCH_PIC_CLR + PIC_REG_IDX(d->hwirq) * 4); 92 + writel(BIT(PIC_REG_BIT(bit)), 93 + priv->base + PCH_PIC_CLR + PIC_REG_IDX(bit) * 4); 101 94 102 95 irq_chip_unmask_parent(d); 103 - pch_pic_bitclr(priv, PCH_PIC_MASK, d->hwirq); 96 + pch_pic_bitclr(priv, PCH_PIC_MASK, bit); 104 97 } 105 98 106 99 static int pch_pic_set_type(struct irq_data *d, unsigned int type) 107 100 { 108 101 struct pch_pic *priv = irq_data_get_irq_chip_data(d); 102 + int bit = hwirq_to_bit(priv, d->hwirq); 109 103 int ret = 0; 110 104 111 105 switch (type) { 112 106 case IRQ_TYPE_EDGE_RISING: 113 - pch_pic_bitset(priv, PCH_PIC_EDGE, d->hwirq); 114 - pch_pic_bitclr(priv, PCH_PIC_POL, d->hwirq); 107 + pch_pic_bitset(priv, PCH_PIC_EDGE, bit); 108 + pch_pic_bitclr(priv, PCH_PIC_POL, bit); 115 109 irq_set_handler_locked(d, handle_edge_irq); 116 110 break; 117 111 case IRQ_TYPE_EDGE_FALLING: 118 - pch_pic_bitset(priv, PCH_PIC_EDGE, d->hwirq); 119 - pch_pic_bitset(priv, PCH_PIC_POL, d->hwirq); 112 + pch_pic_bitset(priv, PCH_PIC_EDGE, bit); 113 + pch_pic_bitset(priv, PCH_PIC_POL, bit); 120 114 irq_set_handler_locked(d, handle_edge_irq); 121 115 break; 122 116 case IRQ_TYPE_LEVEL_HIGH: 123 - pch_pic_bitclr(priv, PCH_PIC_EDGE, d->hwirq); 124 - pch_pic_bitclr(priv, PCH_PIC_POL, d->hwirq); 117 + pch_pic_bitclr(priv, PCH_PIC_EDGE, bit); 118 + pch_pic_bitclr(priv, PCH_PIC_POL, bit); 125 119 irq_set_handler_locked(d, handle_level_irq); 126 120 break; 127 121 case IRQ_TYPE_LEVEL_LOW: 128 - pch_pic_bitclr(priv, PCH_PIC_EDGE, d->hwirq); 129 - pch_pic_bitset(priv, PCH_PIC_POL, d->hwirq); 122 + pch_pic_bitclr(priv, PCH_PIC_EDGE, bit); 123 + pch_pic_bitset(priv, PCH_PIC_POL, bit); 130 124 irq_set_handler_locked(d, handle_level_irq); 131 125 break; 132 126 default: ··· 143 133 { 144 134 unsigned int reg; 145 135 struct pch_pic *priv = irq_data_get_irq_chip_data(d); 136 + int bit = hwirq_to_bit(priv, d->hwirq); 146 137 147 - reg = readl(priv->base + PCH_PIC_EDGE + PIC_REG_IDX(d->hwirq) * 4); 148 - if (reg & BIT(PIC_REG_BIT(d->hwirq))) { 149 - writel(BIT(PIC_REG_BIT(d->hwirq)), 150 - priv->base + PCH_PIC_CLR + PIC_REG_IDX(d->hwirq) * 4); 138 + reg = readl(priv->base + PCH_PIC_EDGE + PIC_REG_IDX(bit) * 4); 139 + if (reg & BIT(PIC_REG_BIT(bit))) { 140 + writel(BIT(PIC_REG_BIT(bit)), 141 + priv->base + PCH_PIC_CLR + PIC_REG_IDX(bit) * 4); 151 142 } 152 143 irq_chip_ack_parent(d); 153 144 } ··· 170 159 { 171 160 struct pch_pic *priv = d->host_data; 172 161 struct device_node *of_node = to_of_node(fwspec->fwnode); 162 + unsigned long flags; 163 + int i; 173 164 174 165 if (of_node) { 175 166 if (fwspec->param_count < 2) ··· 184 171 return -EINVAL; 185 172 186 173 *hwirq = fwspec->param[0] - priv->gsi_base; 174 + 187 175 if (fwspec->param_count > 1) 188 176 *type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK; 189 177 else 190 178 *type = IRQ_TYPE_NONE; 191 179 } 180 + 181 + raw_spin_lock_irqsave(&priv->pic_lock, flags); 182 + /* Check pic-table to confirm if the hwirq has been assigned */ 183 + for (i = 0; i < priv->inuse; i++) { 184 + if (priv->table[i] == *hwirq) { 185 + *hwirq = i; 186 + break; 187 + } 188 + } 189 + if (i == priv->inuse) { 190 + /* Assign a new hwirq in pic-table */ 191 + if (priv->inuse >= PIC_COUNT) { 192 + pr_err("pch-pic domain has no free vectors\n"); 193 + raw_spin_unlock_irqrestore(&priv->pic_lock, flags); 194 + return -EINVAL; 195 + } 196 + priv->table[priv->inuse] = *hwirq; 197 + *hwirq = priv->inuse++; 198 + } 199 + raw_spin_unlock_irqrestore(&priv->pic_lock, flags); 192 200 193 201 return 0; 194 202 } ··· 227 193 err = pch_pic_domain_translate(domain, fwspec, &hwirq, &type); 228 194 if (err) 229 195 return err; 196 + 197 + /* Write vector ID */ 198 + writeb(priv->ht_vec_base + hwirq, priv->base + PCH_INT_HTVEC(hwirq_to_bit(priv, hwirq))); 230 199 231 200 parent_fwspec.fwnode = domain->parent->fwnode; 232 201 parent_fwspec.param_count = 1; ··· 259 222 260 223 for (i = 0; i < PIC_COUNT; i++) { 261 224 /* Write vector ID */ 262 - writeb(priv->ht_vec_base + i, priv->base + PCH_INT_HTVEC(i)); 225 + writeb(priv->ht_vec_base + i, priv->base + PCH_INT_HTVEC(hwirq_to_bit(priv, i))); 263 226 /* Hardcode route to HT0 Lo */ 264 227 writeb(1, priv->base + PCH_INT_ROUTE(i)); 265 228 } ··· 321 284 u32 gsi_base) 322 285 { 323 286 struct pch_pic *priv; 287 + int i; 324 288 325 289 priv = kzalloc(sizeof(*priv), GFP_KERNEL); 326 290 if (!priv) ··· 331 293 priv->base = ioremap(addr, size); 332 294 if (!priv->base) 333 295 goto free_priv; 296 + 297 + priv->inuse = 0; 298 + for (i = 0; i < PIC_COUNT; i++) 299 + priv->table[i] = PIC_UNDEF_VECTOR; 334 300 335 301 priv->ht_vec_base = vec_base; 336 302 priv->vec_count = ((readq(priv->base) >> 48) & 0xff) + 1;