162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Platform information definitions. 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copied from arch/ppc/syslib/cpm2_pic.c with minor subsequent updates 562306a36Sopenharmony_ci * to make in work in arch/powerpc/. Original (c) belongs to Dan Malek. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Author: Vitaly Bordug <vbordug@ru.mvista.com> 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * 1999-2001 (c) Dan Malek <dan@embeddedalley.com> 1062306a36Sopenharmony_ci * 2006 (c) MontaVista Software, Inc. 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * This file is licensed under the terms of the GNU General Public License 1362306a36Sopenharmony_ci * version 2. This program is licensed "as is" without any warranty of any 1462306a36Sopenharmony_ci * kind, whether express or implied. 1562306a36Sopenharmony_ci */ 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci/* The CPM2 internal interrupt controller. It is usually 1862306a36Sopenharmony_ci * the only interrupt controller. 1962306a36Sopenharmony_ci * There are two 32-bit registers (high/low) for up to 64 2062306a36Sopenharmony_ci * possible interrupts. 2162306a36Sopenharmony_ci * 2262306a36Sopenharmony_ci * Now, the fun starts.....Interrupt Numbers DO NOT MAP 2362306a36Sopenharmony_ci * in a simple arithmetic fashion to mask or pending registers. 2462306a36Sopenharmony_ci * That is, interrupt 4 does not map to bit position 4. 2562306a36Sopenharmony_ci * We create two tables, indexed by vector number, to indicate 2662306a36Sopenharmony_ci * which register to use and which bit in the register to use. 2762306a36Sopenharmony_ci */ 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#include <linux/stddef.h> 3062306a36Sopenharmony_ci#include <linux/sched.h> 3162306a36Sopenharmony_ci#include <linux/signal.h> 3262306a36Sopenharmony_ci#include <linux/irq.h> 3362306a36Sopenharmony_ci#include <linux/irqdomain.h> 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#include <asm/immap_cpm2.h> 3662306a36Sopenharmony_ci#include <asm/io.h> 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#include "cpm2_pic.h" 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci/* External IRQS */ 4162306a36Sopenharmony_ci#define CPM2_IRQ_EXT1 19 4262306a36Sopenharmony_ci#define CPM2_IRQ_EXT7 25 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci/* Port C IRQS */ 4562306a36Sopenharmony_ci#define CPM2_IRQ_PORTC15 48 4662306a36Sopenharmony_ci#define CPM2_IRQ_PORTC0 63 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic intctl_cpm2_t __iomem *cpm2_intctl; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic struct irq_domain *cpm2_pic_host; 5162306a36Sopenharmony_cistatic unsigned long ppc_cached_irq_mask[2]; /* 2 32-bit registers */ 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistatic const u_char irq_to_siureg[] = { 5462306a36Sopenharmony_ci 1, 1, 1, 1, 1, 1, 1, 1, 5562306a36Sopenharmony_ci 1, 1, 1, 1, 1, 1, 1, 1, 5662306a36Sopenharmony_ci 0, 0, 0, 0, 0, 0, 0, 0, 5762306a36Sopenharmony_ci 0, 0, 0, 0, 0, 0, 0, 0, 5862306a36Sopenharmony_ci 1, 1, 1, 1, 1, 1, 1, 1, 5962306a36Sopenharmony_ci 1, 1, 1, 1, 1, 1, 1, 1, 6062306a36Sopenharmony_ci 0, 0, 0, 0, 0, 0, 0, 0, 6162306a36Sopenharmony_ci 0, 0, 0, 0, 0, 0, 0, 0 6262306a36Sopenharmony_ci}; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci/* bit numbers do not match the docs, these are precomputed so the bit for 6562306a36Sopenharmony_ci * a given irq is (1 << irq_to_siubit[irq]) */ 6662306a36Sopenharmony_cistatic const u_char irq_to_siubit[] = { 6762306a36Sopenharmony_ci 0, 15, 14, 13, 12, 11, 10, 9, 6862306a36Sopenharmony_ci 8, 7, 6, 5, 4, 3, 2, 1, 6962306a36Sopenharmony_ci 2, 1, 0, 14, 13, 12, 11, 10, 7062306a36Sopenharmony_ci 9, 8, 7, 6, 5, 4, 3, 0, 7162306a36Sopenharmony_ci 31, 30, 29, 28, 27, 26, 25, 24, 7262306a36Sopenharmony_ci 23, 22, 21, 20, 19, 18, 17, 16, 7362306a36Sopenharmony_ci 16, 17, 18, 19, 20, 21, 22, 23, 7462306a36Sopenharmony_ci 24, 25, 26, 27, 28, 29, 30, 31, 7562306a36Sopenharmony_ci}; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistatic void cpm2_mask_irq(struct irq_data *d) 7862306a36Sopenharmony_ci{ 7962306a36Sopenharmony_ci int bit, word; 8062306a36Sopenharmony_ci unsigned int irq_nr = irqd_to_hwirq(d); 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci bit = irq_to_siubit[irq_nr]; 8362306a36Sopenharmony_ci word = irq_to_siureg[irq_nr]; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci ppc_cached_irq_mask[word] &= ~(1 << bit); 8662306a36Sopenharmony_ci out_be32(&cpm2_intctl->ic_simrh + word, ppc_cached_irq_mask[word]); 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic void cpm2_unmask_irq(struct irq_data *d) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci int bit, word; 9262306a36Sopenharmony_ci unsigned int irq_nr = irqd_to_hwirq(d); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci bit = irq_to_siubit[irq_nr]; 9562306a36Sopenharmony_ci word = irq_to_siureg[irq_nr]; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci ppc_cached_irq_mask[word] |= 1 << bit; 9862306a36Sopenharmony_ci out_be32(&cpm2_intctl->ic_simrh + word, ppc_cached_irq_mask[word]); 9962306a36Sopenharmony_ci} 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_cistatic void cpm2_ack(struct irq_data *d) 10262306a36Sopenharmony_ci{ 10362306a36Sopenharmony_ci int bit, word; 10462306a36Sopenharmony_ci unsigned int irq_nr = irqd_to_hwirq(d); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci bit = irq_to_siubit[irq_nr]; 10762306a36Sopenharmony_ci word = irq_to_siureg[irq_nr]; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci out_be32(&cpm2_intctl->ic_sipnrh + word, 1 << bit); 11062306a36Sopenharmony_ci} 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_cistatic void cpm2_end_irq(struct irq_data *d) 11362306a36Sopenharmony_ci{ 11462306a36Sopenharmony_ci int bit, word; 11562306a36Sopenharmony_ci unsigned int irq_nr = irqd_to_hwirq(d); 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci bit = irq_to_siubit[irq_nr]; 11862306a36Sopenharmony_ci word = irq_to_siureg[irq_nr]; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci ppc_cached_irq_mask[word] |= 1 << bit; 12162306a36Sopenharmony_ci out_be32(&cpm2_intctl->ic_simrh + word, ppc_cached_irq_mask[word]); 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci /* 12462306a36Sopenharmony_ci * Work around large numbers of spurious IRQs on PowerPC 82xx 12562306a36Sopenharmony_ci * systems. 12662306a36Sopenharmony_ci */ 12762306a36Sopenharmony_ci mb(); 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cistatic int cpm2_set_irq_type(struct irq_data *d, unsigned int flow_type) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci unsigned int src = irqd_to_hwirq(d); 13362306a36Sopenharmony_ci unsigned int vold, vnew, edibit; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci /* Port C interrupts are either IRQ_TYPE_EDGE_FALLING or 13662306a36Sopenharmony_ci * IRQ_TYPE_EDGE_BOTH (default). All others are IRQ_TYPE_EDGE_FALLING 13762306a36Sopenharmony_ci * or IRQ_TYPE_LEVEL_LOW (default) 13862306a36Sopenharmony_ci */ 13962306a36Sopenharmony_ci if (src >= CPM2_IRQ_PORTC15 && src <= CPM2_IRQ_PORTC0) { 14062306a36Sopenharmony_ci if (flow_type == IRQ_TYPE_NONE) 14162306a36Sopenharmony_ci flow_type = IRQ_TYPE_EDGE_BOTH; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci if (flow_type != IRQ_TYPE_EDGE_BOTH && 14462306a36Sopenharmony_ci flow_type != IRQ_TYPE_EDGE_FALLING) 14562306a36Sopenharmony_ci goto err_sense; 14662306a36Sopenharmony_ci } else { 14762306a36Sopenharmony_ci if (flow_type == IRQ_TYPE_NONE) 14862306a36Sopenharmony_ci flow_type = IRQ_TYPE_LEVEL_LOW; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci if (flow_type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_LEVEL_HIGH)) 15162306a36Sopenharmony_ci goto err_sense; 15262306a36Sopenharmony_ci } 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci irqd_set_trigger_type(d, flow_type); 15562306a36Sopenharmony_ci if (flow_type & IRQ_TYPE_LEVEL_LOW) 15662306a36Sopenharmony_ci irq_set_handler_locked(d, handle_level_irq); 15762306a36Sopenharmony_ci else 15862306a36Sopenharmony_ci irq_set_handler_locked(d, handle_edge_irq); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci /* internal IRQ senses are LEVEL_LOW 16162306a36Sopenharmony_ci * EXT IRQ and Port C IRQ senses are programmable 16262306a36Sopenharmony_ci */ 16362306a36Sopenharmony_ci if (src >= CPM2_IRQ_EXT1 && src <= CPM2_IRQ_EXT7) 16462306a36Sopenharmony_ci edibit = (14 - (src - CPM2_IRQ_EXT1)); 16562306a36Sopenharmony_ci else 16662306a36Sopenharmony_ci if (src >= CPM2_IRQ_PORTC15 && src <= CPM2_IRQ_PORTC0) 16762306a36Sopenharmony_ci edibit = (31 - (CPM2_IRQ_PORTC0 - src)); 16862306a36Sopenharmony_ci else 16962306a36Sopenharmony_ci return (flow_type & IRQ_TYPE_LEVEL_LOW) ? 17062306a36Sopenharmony_ci IRQ_SET_MASK_OK_NOCOPY : -EINVAL; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci vold = in_be32(&cpm2_intctl->ic_siexr); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci if ((flow_type & IRQ_TYPE_SENSE_MASK) == IRQ_TYPE_EDGE_FALLING) 17562306a36Sopenharmony_ci vnew = vold | (1 << edibit); 17662306a36Sopenharmony_ci else 17762306a36Sopenharmony_ci vnew = vold & ~(1 << edibit); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci if (vold != vnew) 18062306a36Sopenharmony_ci out_be32(&cpm2_intctl->ic_siexr, vnew); 18162306a36Sopenharmony_ci return IRQ_SET_MASK_OK_NOCOPY; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_cierr_sense: 18462306a36Sopenharmony_ci pr_err("CPM2 PIC: sense type 0x%x not supported\n", flow_type); 18562306a36Sopenharmony_ci return -EINVAL; 18662306a36Sopenharmony_ci} 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_cistatic struct irq_chip cpm2_pic = { 18962306a36Sopenharmony_ci .name = "CPM2 SIU", 19062306a36Sopenharmony_ci .irq_mask = cpm2_mask_irq, 19162306a36Sopenharmony_ci .irq_unmask = cpm2_unmask_irq, 19262306a36Sopenharmony_ci .irq_ack = cpm2_ack, 19362306a36Sopenharmony_ci .irq_eoi = cpm2_end_irq, 19462306a36Sopenharmony_ci .irq_set_type = cpm2_set_irq_type, 19562306a36Sopenharmony_ci .flags = IRQCHIP_EOI_IF_HANDLED, 19662306a36Sopenharmony_ci}; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ciunsigned int cpm2_get_irq(void) 19962306a36Sopenharmony_ci{ 20062306a36Sopenharmony_ci int irq; 20162306a36Sopenharmony_ci unsigned long bits; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci /* For CPM2, read the SIVEC register and shift the bits down 20462306a36Sopenharmony_ci * to get the irq number. */ 20562306a36Sopenharmony_ci bits = in_be32(&cpm2_intctl->ic_sivec); 20662306a36Sopenharmony_ci irq = bits >> 26; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci if (irq == 0) 20962306a36Sopenharmony_ci return(-1); 21062306a36Sopenharmony_ci return irq_linear_revmap(cpm2_pic_host, irq); 21162306a36Sopenharmony_ci} 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_cistatic int cpm2_pic_host_map(struct irq_domain *h, unsigned int virq, 21462306a36Sopenharmony_ci irq_hw_number_t hw) 21562306a36Sopenharmony_ci{ 21662306a36Sopenharmony_ci pr_debug("cpm2_pic_host_map(%d, 0x%lx)\n", virq, hw); 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci irq_set_status_flags(virq, IRQ_LEVEL); 21962306a36Sopenharmony_ci irq_set_chip_and_handler(virq, &cpm2_pic, handle_level_irq); 22062306a36Sopenharmony_ci return 0; 22162306a36Sopenharmony_ci} 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_cistatic const struct irq_domain_ops cpm2_pic_host_ops = { 22462306a36Sopenharmony_ci .map = cpm2_pic_host_map, 22562306a36Sopenharmony_ci .xlate = irq_domain_xlate_onetwocell, 22662306a36Sopenharmony_ci}; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_civoid cpm2_pic_init(struct device_node *node) 22962306a36Sopenharmony_ci{ 23062306a36Sopenharmony_ci int i; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci cpm2_intctl = &cpm2_immr->im_intctl; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci /* Clear the CPM IRQ controller, in case it has any bits set 23562306a36Sopenharmony_ci * from the bootloader 23662306a36Sopenharmony_ci */ 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci /* Mask out everything */ 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci out_be32(&cpm2_intctl->ic_simrh, 0x00000000); 24162306a36Sopenharmony_ci out_be32(&cpm2_intctl->ic_simrl, 0x00000000); 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci wmb(); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci /* Ack everything */ 24662306a36Sopenharmony_ci out_be32(&cpm2_intctl->ic_sipnrh, 0xffffffff); 24762306a36Sopenharmony_ci out_be32(&cpm2_intctl->ic_sipnrl, 0xffffffff); 24862306a36Sopenharmony_ci wmb(); 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci /* Dummy read of the vector */ 25162306a36Sopenharmony_ci i = in_be32(&cpm2_intctl->ic_sivec); 25262306a36Sopenharmony_ci rmb(); 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci /* Initialize the default interrupt mapping priorities, 25562306a36Sopenharmony_ci * in case the boot rom changed something on us. 25662306a36Sopenharmony_ci */ 25762306a36Sopenharmony_ci out_be16(&cpm2_intctl->ic_sicr, 0); 25862306a36Sopenharmony_ci out_be32(&cpm2_intctl->ic_scprrh, 0x05309770); 25962306a36Sopenharmony_ci out_be32(&cpm2_intctl->ic_scprrl, 0x05309770); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci /* create a legacy host */ 26262306a36Sopenharmony_ci cpm2_pic_host = irq_domain_add_linear(node, 64, &cpm2_pic_host_ops, NULL); 26362306a36Sopenharmony_ci if (cpm2_pic_host == NULL) { 26462306a36Sopenharmony_ci printk(KERN_ERR "CPM2 PIC: failed to allocate irq host!\n"); 26562306a36Sopenharmony_ci return; 26662306a36Sopenharmony_ci } 26762306a36Sopenharmony_ci} 268