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 17 static struct bcsr_reg { 18 void __iomem *raddr; 19 spinlock_t lock; 20 } bcsr_regs[BCSR_CNT]; 21 22 static void __iomem *bcsr_virt; /* KSEG1 addr of BCSR base */ 23 static int bcsr_csc_base; /* linux-irq of first cascaded irq */ 24 25 void __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 46 unsigned 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 } 56 EXPORT_SYMBOL_GPL(bcsr_read); 57 58 void 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 } 67 EXPORT_SYMBOL_GPL(bcsr_write); 68 69 void 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 } 82 EXPORT_SYMBOL_GPL(bcsr_mod); 83 84 /* 85 * DB1200/PB1200 CPLD IRQ muxer 86 */ 87 static 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 */ 99 static 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 107 static 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 116 static 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 124 static 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 131 void __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