18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Shared interrupt handling code for IPR and INTC2 types of IRQs. 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2007, 2008 Magnus Damm 58c2ecf20Sopenharmony_ci * Copyright (C) 2009, 2010 Paul Mundt 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 88c2ecf20Sopenharmony_ci * License. See the file "COPYING" in the main directory of this archive 98c2ecf20Sopenharmony_ci * for more details. 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci#include <linux/init.h> 128c2ecf20Sopenharmony_ci#include <linux/irq.h> 138c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 148c2ecf20Sopenharmony_ci#include "internals.h" 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_cistatic unsigned long ack_handle[INTC_NR_IRQS]; 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_cistatic intc_enum __init intc_grp_id(struct intc_desc *desc, 198c2ecf20Sopenharmony_ci intc_enum enum_id) 208c2ecf20Sopenharmony_ci{ 218c2ecf20Sopenharmony_ci struct intc_group *g = desc->hw.groups; 228c2ecf20Sopenharmony_ci unsigned int i, j; 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci for (i = 0; g && enum_id && i < desc->hw.nr_groups; i++) { 258c2ecf20Sopenharmony_ci g = desc->hw.groups + i; 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci for (j = 0; g->enum_ids[j]; j++) { 288c2ecf20Sopenharmony_ci if (g->enum_ids[j] != enum_id) 298c2ecf20Sopenharmony_ci continue; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci return g->enum_id; 328c2ecf20Sopenharmony_ci } 338c2ecf20Sopenharmony_ci } 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci return 0; 368c2ecf20Sopenharmony_ci} 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistatic unsigned int __init _intc_mask_data(struct intc_desc *desc, 398c2ecf20Sopenharmony_ci struct intc_desc_int *d, 408c2ecf20Sopenharmony_ci intc_enum enum_id, 418c2ecf20Sopenharmony_ci unsigned int *reg_idx, 428c2ecf20Sopenharmony_ci unsigned int *fld_idx) 438c2ecf20Sopenharmony_ci{ 448c2ecf20Sopenharmony_ci struct intc_mask_reg *mr = desc->hw.mask_regs; 458c2ecf20Sopenharmony_ci unsigned int fn, mode; 468c2ecf20Sopenharmony_ci unsigned long reg_e, reg_d; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci while (mr && enum_id && *reg_idx < desc->hw.nr_mask_regs) { 498c2ecf20Sopenharmony_ci mr = desc->hw.mask_regs + *reg_idx; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci for (; *fld_idx < ARRAY_SIZE(mr->enum_ids); (*fld_idx)++) { 528c2ecf20Sopenharmony_ci if (mr->enum_ids[*fld_idx] != enum_id) 538c2ecf20Sopenharmony_ci continue; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci if (mr->set_reg && mr->clr_reg) { 568c2ecf20Sopenharmony_ci fn = REG_FN_WRITE_BASE; 578c2ecf20Sopenharmony_ci mode = MODE_DUAL_REG; 588c2ecf20Sopenharmony_ci reg_e = mr->clr_reg; 598c2ecf20Sopenharmony_ci reg_d = mr->set_reg; 608c2ecf20Sopenharmony_ci } else { 618c2ecf20Sopenharmony_ci fn = REG_FN_MODIFY_BASE; 628c2ecf20Sopenharmony_ci if (mr->set_reg) { 638c2ecf20Sopenharmony_ci mode = MODE_ENABLE_REG; 648c2ecf20Sopenharmony_ci reg_e = mr->set_reg; 658c2ecf20Sopenharmony_ci reg_d = mr->set_reg; 668c2ecf20Sopenharmony_ci } else { 678c2ecf20Sopenharmony_ci mode = MODE_MASK_REG; 688c2ecf20Sopenharmony_ci reg_e = mr->clr_reg; 698c2ecf20Sopenharmony_ci reg_d = mr->clr_reg; 708c2ecf20Sopenharmony_ci } 718c2ecf20Sopenharmony_ci } 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci fn += (mr->reg_width >> 3) - 1; 748c2ecf20Sopenharmony_ci return _INTC_MK(fn, mode, 758c2ecf20Sopenharmony_ci intc_get_reg(d, reg_e), 768c2ecf20Sopenharmony_ci intc_get_reg(d, reg_d), 778c2ecf20Sopenharmony_ci 1, 788c2ecf20Sopenharmony_ci (mr->reg_width - 1) - *fld_idx); 798c2ecf20Sopenharmony_ci } 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci *fld_idx = 0; 828c2ecf20Sopenharmony_ci (*reg_idx)++; 838c2ecf20Sopenharmony_ci } 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci return 0; 868c2ecf20Sopenharmony_ci} 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ciunsigned int __init 898c2ecf20Sopenharmony_ciintc_get_mask_handle(struct intc_desc *desc, struct intc_desc_int *d, 908c2ecf20Sopenharmony_ci intc_enum enum_id, int do_grps) 918c2ecf20Sopenharmony_ci{ 928c2ecf20Sopenharmony_ci unsigned int i = 0; 938c2ecf20Sopenharmony_ci unsigned int j = 0; 948c2ecf20Sopenharmony_ci unsigned int ret; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci ret = _intc_mask_data(desc, d, enum_id, &i, &j); 978c2ecf20Sopenharmony_ci if (ret) 988c2ecf20Sopenharmony_ci return ret; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci if (do_grps) 1018c2ecf20Sopenharmony_ci return intc_get_mask_handle(desc, d, intc_grp_id(desc, enum_id), 0); 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci return 0; 1048c2ecf20Sopenharmony_ci} 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_cistatic unsigned int __init _intc_prio_data(struct intc_desc *desc, 1078c2ecf20Sopenharmony_ci struct intc_desc_int *d, 1088c2ecf20Sopenharmony_ci intc_enum enum_id, 1098c2ecf20Sopenharmony_ci unsigned int *reg_idx, 1108c2ecf20Sopenharmony_ci unsigned int *fld_idx) 1118c2ecf20Sopenharmony_ci{ 1128c2ecf20Sopenharmony_ci struct intc_prio_reg *pr = desc->hw.prio_regs; 1138c2ecf20Sopenharmony_ci unsigned int fn, n, mode, bit; 1148c2ecf20Sopenharmony_ci unsigned long reg_e, reg_d; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci while (pr && enum_id && *reg_idx < desc->hw.nr_prio_regs) { 1178c2ecf20Sopenharmony_ci pr = desc->hw.prio_regs + *reg_idx; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci for (; *fld_idx < ARRAY_SIZE(pr->enum_ids); (*fld_idx)++) { 1208c2ecf20Sopenharmony_ci if (pr->enum_ids[*fld_idx] != enum_id) 1218c2ecf20Sopenharmony_ci continue; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci if (pr->set_reg && pr->clr_reg) { 1248c2ecf20Sopenharmony_ci fn = REG_FN_WRITE_BASE; 1258c2ecf20Sopenharmony_ci mode = MODE_PCLR_REG; 1268c2ecf20Sopenharmony_ci reg_e = pr->set_reg; 1278c2ecf20Sopenharmony_ci reg_d = pr->clr_reg; 1288c2ecf20Sopenharmony_ci } else { 1298c2ecf20Sopenharmony_ci fn = REG_FN_MODIFY_BASE; 1308c2ecf20Sopenharmony_ci mode = MODE_PRIO_REG; 1318c2ecf20Sopenharmony_ci if (!pr->set_reg) 1328c2ecf20Sopenharmony_ci BUG(); 1338c2ecf20Sopenharmony_ci reg_e = pr->set_reg; 1348c2ecf20Sopenharmony_ci reg_d = pr->set_reg; 1358c2ecf20Sopenharmony_ci } 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci fn += (pr->reg_width >> 3) - 1; 1388c2ecf20Sopenharmony_ci n = *fld_idx + 1; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci BUG_ON(n * pr->field_width > pr->reg_width); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci bit = pr->reg_width - (n * pr->field_width); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci return _INTC_MK(fn, mode, 1458c2ecf20Sopenharmony_ci intc_get_reg(d, reg_e), 1468c2ecf20Sopenharmony_ci intc_get_reg(d, reg_d), 1478c2ecf20Sopenharmony_ci pr->field_width, bit); 1488c2ecf20Sopenharmony_ci } 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci *fld_idx = 0; 1518c2ecf20Sopenharmony_ci (*reg_idx)++; 1528c2ecf20Sopenharmony_ci } 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci return 0; 1558c2ecf20Sopenharmony_ci} 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ciunsigned int __init 1588c2ecf20Sopenharmony_ciintc_get_prio_handle(struct intc_desc *desc, struct intc_desc_int *d, 1598c2ecf20Sopenharmony_ci intc_enum enum_id, int do_grps) 1608c2ecf20Sopenharmony_ci{ 1618c2ecf20Sopenharmony_ci unsigned int i = 0; 1628c2ecf20Sopenharmony_ci unsigned int j = 0; 1638c2ecf20Sopenharmony_ci unsigned int ret; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci ret = _intc_prio_data(desc, d, enum_id, &i, &j); 1668c2ecf20Sopenharmony_ci if (ret) 1678c2ecf20Sopenharmony_ci return ret; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci if (do_grps) 1708c2ecf20Sopenharmony_ci return intc_get_prio_handle(desc, d, intc_grp_id(desc, enum_id), 0); 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci return 0; 1738c2ecf20Sopenharmony_ci} 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_cistatic unsigned int intc_ack_data(struct intc_desc *desc, 1768c2ecf20Sopenharmony_ci struct intc_desc_int *d, intc_enum enum_id) 1778c2ecf20Sopenharmony_ci{ 1788c2ecf20Sopenharmony_ci struct intc_mask_reg *mr = desc->hw.ack_regs; 1798c2ecf20Sopenharmony_ci unsigned int i, j, fn, mode; 1808c2ecf20Sopenharmony_ci unsigned long reg_e, reg_d; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci for (i = 0; mr && enum_id && i < desc->hw.nr_ack_regs; i++) { 1838c2ecf20Sopenharmony_ci mr = desc->hw.ack_regs + i; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci for (j = 0; j < ARRAY_SIZE(mr->enum_ids); j++) { 1868c2ecf20Sopenharmony_ci if (mr->enum_ids[j] != enum_id) 1878c2ecf20Sopenharmony_ci continue; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci fn = REG_FN_MODIFY_BASE; 1908c2ecf20Sopenharmony_ci mode = MODE_ENABLE_REG; 1918c2ecf20Sopenharmony_ci reg_e = mr->set_reg; 1928c2ecf20Sopenharmony_ci reg_d = mr->set_reg; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci fn += (mr->reg_width >> 3) - 1; 1958c2ecf20Sopenharmony_ci return _INTC_MK(fn, mode, 1968c2ecf20Sopenharmony_ci intc_get_reg(d, reg_e), 1978c2ecf20Sopenharmony_ci intc_get_reg(d, reg_d), 1988c2ecf20Sopenharmony_ci 1, 1998c2ecf20Sopenharmony_ci (mr->reg_width - 1) - j); 2008c2ecf20Sopenharmony_ci } 2018c2ecf20Sopenharmony_ci } 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci return 0; 2048c2ecf20Sopenharmony_ci} 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_cistatic void intc_enable_disable(struct intc_desc_int *d, 2078c2ecf20Sopenharmony_ci unsigned long handle, int do_enable) 2088c2ecf20Sopenharmony_ci{ 2098c2ecf20Sopenharmony_ci unsigned long addr; 2108c2ecf20Sopenharmony_ci unsigned int cpu; 2118c2ecf20Sopenharmony_ci unsigned long (*fn)(unsigned long, unsigned long, 2128c2ecf20Sopenharmony_ci unsigned long (*)(unsigned long, unsigned long, 2138c2ecf20Sopenharmony_ci unsigned long), 2148c2ecf20Sopenharmony_ci unsigned int); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci if (do_enable) { 2178c2ecf20Sopenharmony_ci for (cpu = 0; cpu < SMP_NR(d, _INTC_ADDR_E(handle)); cpu++) { 2188c2ecf20Sopenharmony_ci addr = INTC_REG(d, _INTC_ADDR_E(handle), cpu); 2198c2ecf20Sopenharmony_ci fn = intc_enable_noprio_fns[_INTC_MODE(handle)]; 2208c2ecf20Sopenharmony_ci fn(addr, handle, intc_reg_fns[_INTC_FN(handle)], 0); 2218c2ecf20Sopenharmony_ci } 2228c2ecf20Sopenharmony_ci } else { 2238c2ecf20Sopenharmony_ci for (cpu = 0; cpu < SMP_NR(d, _INTC_ADDR_D(handle)); cpu++) { 2248c2ecf20Sopenharmony_ci addr = INTC_REG(d, _INTC_ADDR_D(handle), cpu); 2258c2ecf20Sopenharmony_ci fn = intc_disable_fns[_INTC_MODE(handle)]; 2268c2ecf20Sopenharmony_ci fn(addr, handle, intc_reg_fns[_INTC_FN(handle)], 0); 2278c2ecf20Sopenharmony_ci } 2288c2ecf20Sopenharmony_ci } 2298c2ecf20Sopenharmony_ci} 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_civoid __init intc_enable_disable_enum(struct intc_desc *desc, 2328c2ecf20Sopenharmony_ci struct intc_desc_int *d, 2338c2ecf20Sopenharmony_ci intc_enum enum_id, int enable) 2348c2ecf20Sopenharmony_ci{ 2358c2ecf20Sopenharmony_ci unsigned int i, j, data; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci /* go through and enable/disable all mask bits */ 2388c2ecf20Sopenharmony_ci i = j = 0; 2398c2ecf20Sopenharmony_ci do { 2408c2ecf20Sopenharmony_ci data = _intc_mask_data(desc, d, enum_id, &i, &j); 2418c2ecf20Sopenharmony_ci if (data) 2428c2ecf20Sopenharmony_ci intc_enable_disable(d, data, enable); 2438c2ecf20Sopenharmony_ci j++; 2448c2ecf20Sopenharmony_ci } while (data); 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci /* go through and enable/disable all priority fields */ 2478c2ecf20Sopenharmony_ci i = j = 0; 2488c2ecf20Sopenharmony_ci do { 2498c2ecf20Sopenharmony_ci data = _intc_prio_data(desc, d, enum_id, &i, &j); 2508c2ecf20Sopenharmony_ci if (data) 2518c2ecf20Sopenharmony_ci intc_enable_disable(d, data, enable); 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci j++; 2548c2ecf20Sopenharmony_ci } while (data); 2558c2ecf20Sopenharmony_ci} 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ciunsigned int __init 2588c2ecf20Sopenharmony_ciintc_get_sense_handle(struct intc_desc *desc, struct intc_desc_int *d, 2598c2ecf20Sopenharmony_ci intc_enum enum_id) 2608c2ecf20Sopenharmony_ci{ 2618c2ecf20Sopenharmony_ci struct intc_sense_reg *sr = desc->hw.sense_regs; 2628c2ecf20Sopenharmony_ci unsigned int i, j, fn, bit; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci for (i = 0; sr && enum_id && i < desc->hw.nr_sense_regs; i++) { 2658c2ecf20Sopenharmony_ci sr = desc->hw.sense_regs + i; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci for (j = 0; j < ARRAY_SIZE(sr->enum_ids); j++) { 2688c2ecf20Sopenharmony_ci if (sr->enum_ids[j] != enum_id) 2698c2ecf20Sopenharmony_ci continue; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci fn = REG_FN_MODIFY_BASE; 2728c2ecf20Sopenharmony_ci fn += (sr->reg_width >> 3) - 1; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci BUG_ON((j + 1) * sr->field_width > sr->reg_width); 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci bit = sr->reg_width - ((j + 1) * sr->field_width); 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci return _INTC_MK(fn, 0, intc_get_reg(d, sr->reg), 2798c2ecf20Sopenharmony_ci 0, sr->field_width, bit); 2808c2ecf20Sopenharmony_ci } 2818c2ecf20Sopenharmony_ci } 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci return 0; 2848c2ecf20Sopenharmony_ci} 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_civoid intc_set_ack_handle(unsigned int irq, struct intc_desc *desc, 2888c2ecf20Sopenharmony_ci struct intc_desc_int *d, intc_enum id) 2898c2ecf20Sopenharmony_ci{ 2908c2ecf20Sopenharmony_ci unsigned long flags; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci /* 2938c2ecf20Sopenharmony_ci * Nothing to do for this IRQ. 2948c2ecf20Sopenharmony_ci */ 2958c2ecf20Sopenharmony_ci if (!desc->hw.ack_regs) 2968c2ecf20Sopenharmony_ci return; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&intc_big_lock, flags); 2998c2ecf20Sopenharmony_ci ack_handle[irq] = intc_ack_data(desc, d, id); 3008c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&intc_big_lock, flags); 3018c2ecf20Sopenharmony_ci} 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ciunsigned long intc_get_ack_handle(unsigned int irq) 3048c2ecf20Sopenharmony_ci{ 3058c2ecf20Sopenharmony_ci return ack_handle[irq]; 3068c2ecf20Sopenharmony_ci} 307