162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Support for hardware-managed IRQ auto-distribution. 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 2010 Paul Mundt 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 762306a36Sopenharmony_ci * License. See the file "COPYING" in the main directory of this archive 862306a36Sopenharmony_ci * for more details. 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci#include "internals.h" 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_cistatic unsigned long dist_handle[INTC_NR_IRQS]; 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_civoid intc_balancing_enable(unsigned int irq) 1562306a36Sopenharmony_ci{ 1662306a36Sopenharmony_ci struct intc_desc_int *d = get_intc_desc(irq); 1762306a36Sopenharmony_ci unsigned long handle = dist_handle[irq]; 1862306a36Sopenharmony_ci unsigned long addr; 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci if (irq_balancing_disabled(irq) || !handle) 2162306a36Sopenharmony_ci return; 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci addr = INTC_REG(d, _INTC_ADDR_D(handle), 0); 2462306a36Sopenharmony_ci intc_reg_fns[_INTC_FN(handle)](addr, handle, 1); 2562306a36Sopenharmony_ci} 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_civoid intc_balancing_disable(unsigned int irq) 2862306a36Sopenharmony_ci{ 2962306a36Sopenharmony_ci struct intc_desc_int *d = get_intc_desc(irq); 3062306a36Sopenharmony_ci unsigned long handle = dist_handle[irq]; 3162306a36Sopenharmony_ci unsigned long addr; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci if (irq_balancing_disabled(irq) || !handle) 3462306a36Sopenharmony_ci return; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci addr = INTC_REG(d, _INTC_ADDR_D(handle), 0); 3762306a36Sopenharmony_ci intc_reg_fns[_INTC_FN(handle)](addr, handle, 0); 3862306a36Sopenharmony_ci} 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistatic unsigned int intc_dist_data(struct intc_desc *desc, 4162306a36Sopenharmony_ci struct intc_desc_int *d, 4262306a36Sopenharmony_ci intc_enum enum_id) 4362306a36Sopenharmony_ci{ 4462306a36Sopenharmony_ci struct intc_mask_reg *mr = desc->hw.mask_regs; 4562306a36Sopenharmony_ci unsigned int i, j, fn, mode; 4662306a36Sopenharmony_ci unsigned long reg_e, reg_d; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci for (i = 0; mr && enum_id && i < desc->hw.nr_mask_regs; i++) { 4962306a36Sopenharmony_ci mr = desc->hw.mask_regs + i; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci /* 5262306a36Sopenharmony_ci * Skip this entry if there's no auto-distribution 5362306a36Sopenharmony_ci * register associated with it. 5462306a36Sopenharmony_ci */ 5562306a36Sopenharmony_ci if (!mr->dist_reg) 5662306a36Sopenharmony_ci continue; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci for (j = 0; j < ARRAY_SIZE(mr->enum_ids); j++) { 5962306a36Sopenharmony_ci if (mr->enum_ids[j] != enum_id) 6062306a36Sopenharmony_ci continue; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci fn = REG_FN_MODIFY_BASE; 6362306a36Sopenharmony_ci mode = MODE_ENABLE_REG; 6462306a36Sopenharmony_ci reg_e = mr->dist_reg; 6562306a36Sopenharmony_ci reg_d = mr->dist_reg; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci fn += (mr->reg_width >> 3) - 1; 6862306a36Sopenharmony_ci return _INTC_MK(fn, mode, 6962306a36Sopenharmony_ci intc_get_reg(d, reg_e), 7062306a36Sopenharmony_ci intc_get_reg(d, reg_d), 7162306a36Sopenharmony_ci 1, 7262306a36Sopenharmony_ci (mr->reg_width - 1) - j); 7362306a36Sopenharmony_ci } 7462306a36Sopenharmony_ci } 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci /* 7762306a36Sopenharmony_ci * It's possible we've gotten here with no distribution options 7862306a36Sopenharmony_ci * available for the IRQ in question, so we just skip over those. 7962306a36Sopenharmony_ci */ 8062306a36Sopenharmony_ci return 0; 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_civoid intc_set_dist_handle(unsigned int irq, struct intc_desc *desc, 8462306a36Sopenharmony_ci struct intc_desc_int *d, intc_enum id) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci unsigned long flags; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci /* 8962306a36Sopenharmony_ci * Nothing to do for this IRQ. 9062306a36Sopenharmony_ci */ 9162306a36Sopenharmony_ci if (!desc->hw.mask_regs) 9262306a36Sopenharmony_ci return; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci raw_spin_lock_irqsave(&intc_big_lock, flags); 9562306a36Sopenharmony_ci dist_handle[irq] = intc_dist_data(desc, d, id); 9662306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&intc_big_lock, flags); 9762306a36Sopenharmony_ci} 98