1/* 2 * bcsr.h -- Db1xxx/Pb1xxx Devboard CPLD registers ("BCSR") abstraction. 3 * 4 * All Alchemy development boards (except, of course, the weird PB1000) 5 * have a few registers in a CPLD with standardised layout; they mostly 6 * only differ in base address. 7 * All registers are 16bits wide with 32bit spacing. 8 */ 9 10#include <linux/interrupt.h> 11#include <linux/module.h> 12#include <linux/spinlock.h> 13#include <asm/addrspace.h> 14#include <asm/io.h> 15#include <asm/mach-db1x00/bcsr.h> 16 17static struct bcsr_reg { 18 void __iomem *raddr; 19 spinlock_t lock; 20} bcsr_regs[BCSR_CNT]; 21 22static void __iomem *bcsr_virt; /* KSEG1 addr of BCSR base */ 23static int bcsr_csc_base; /* linux-irq of first cascaded irq */ 24 25void __init bcsr_init(unsigned long bcsr1_phys, unsigned long bcsr2_phys) 26{ 27 int i; 28 29 bcsr1_phys = KSEG1ADDR(CPHYSADDR(bcsr1_phys)); 30 bcsr2_phys = KSEG1ADDR(CPHYSADDR(bcsr2_phys)); 31 32 bcsr_virt = (void __iomem *)bcsr1_phys; 33 34 for (i = 0; i < BCSR_CNT; i++) { 35 if (i >= BCSR_HEXLEDS) 36 bcsr_regs[i].raddr = (void __iomem *)bcsr2_phys + 37 (0x04 * (i - BCSR_HEXLEDS)); 38 else 39 bcsr_regs[i].raddr = (void __iomem *)bcsr1_phys + 40 (0x04 * i); 41 42 spin_lock_init(&bcsr_regs[i].lock); 43 } 44} 45 46unsigned short bcsr_read(enum bcsr_id reg) 47{ 48 unsigned short r; 49 unsigned long flags; 50 51 spin_lock_irqsave(&bcsr_regs[reg].lock, flags); 52 r = __raw_readw(bcsr_regs[reg].raddr); 53 spin_unlock_irqrestore(&bcsr_regs[reg].lock, flags); 54 return r; 55} 56EXPORT_SYMBOL_GPL(bcsr_read); 57 58void bcsr_write(enum bcsr_id reg, unsigned short val) 59{ 60 unsigned long flags; 61 62 spin_lock_irqsave(&bcsr_regs[reg].lock, flags); 63 __raw_writew(val, bcsr_regs[reg].raddr); 64 wmb(); 65 spin_unlock_irqrestore(&bcsr_regs[reg].lock, flags); 66} 67EXPORT_SYMBOL_GPL(bcsr_write); 68 69void bcsr_mod(enum bcsr_id reg, unsigned short clr, unsigned short set) 70{ 71 unsigned short r; 72 unsigned long flags; 73 74 spin_lock_irqsave(&bcsr_regs[reg].lock, flags); 75 r = __raw_readw(bcsr_regs[reg].raddr); 76 r &= ~clr; 77 r |= set; 78 __raw_writew(r, bcsr_regs[reg].raddr); 79 wmb(); 80 spin_unlock_irqrestore(&bcsr_regs[reg].lock, flags); 81} 82EXPORT_SYMBOL_GPL(bcsr_mod); 83 84/* 85 * DB1200/PB1200 CPLD IRQ muxer 86 */ 87static void bcsr_csc_handler(unsigned int irq, struct irq_desc *d) 88{ 89 unsigned short bisr = __raw_readw(bcsr_virt + BCSR_REG_INTSTAT); 90 91 for ( ; bisr; bisr &= bisr - 1) 92 generic_handle_irq(bcsr_csc_base + __ffs(bisr)); 93} 94 95/* NOTE: both the enable and mask bits must be cleared, otherwise the 96 * CPLD generates tons of spurious interrupts (at least on my DB1200). 97 * -- mlau 98 */ 99static void bcsr_irq_mask(unsigned int irq_nr) 100{ 101 unsigned short v = 1 << (irq_nr - bcsr_csc_base); 102 __raw_writew(v, bcsr_virt + BCSR_REG_INTCLR); 103 __raw_writew(v, bcsr_virt + BCSR_REG_MASKCLR); 104 wmb(); 105} 106 107static void bcsr_irq_maskack(unsigned int irq_nr) 108{ 109 unsigned short v = 1 << (irq_nr - bcsr_csc_base); 110 __raw_writew(v, bcsr_virt + BCSR_REG_INTCLR); 111 __raw_writew(v, bcsr_virt + BCSR_REG_MASKCLR); 112 __raw_writew(v, bcsr_virt + BCSR_REG_INTSTAT); /* ack */ 113 wmb(); 114} 115 116static void bcsr_irq_unmask(unsigned int irq_nr) 117{ 118 unsigned short v = 1 << (irq_nr - bcsr_csc_base); 119 __raw_writew(v, bcsr_virt + BCSR_REG_INTSET); 120 __raw_writew(v, bcsr_virt + BCSR_REG_MASKSET); 121 wmb(); 122} 123 124static struct irq_chip bcsr_irq_type = { 125 .name = "CPLD", 126 .mask = bcsr_irq_mask, 127 .mask_ack = bcsr_irq_maskack, 128 .unmask = bcsr_irq_unmask, 129}; 130 131void __init bcsr_init_irq(int csc_start, int csc_end, int hook_irq) 132{ 133 unsigned int irq; 134 135 /* mask & disable & ack all */ 136 __raw_writew(0xffff, bcsr_virt + BCSR_REG_INTCLR); 137 __raw_writew(0xffff, bcsr_virt + BCSR_REG_MASKCLR); 138 __raw_writew(0xffff, bcsr_virt + BCSR_REG_INTSTAT); 139 wmb(); 140 141 bcsr_csc_base = csc_start; 142 143 for (irq = csc_start; irq <= csc_end; irq++) 144 set_irq_chip_and_handler_name(irq, &bcsr_irq_type, 145 handle_level_irq, "level"); 146 147 set_irq_chained_handler(hook_irq, bcsr_csc_handler); 148} 149