Path: blob/master/arch/mips/alchemy/devboards/bcsr.c
10818 views
/*1* bcsr.h -- Db1xxx/Pb1xxx Devboard CPLD registers ("BCSR") abstraction.2*3* All Alchemy development boards (except, of course, the weird PB1000)4* have a few registers in a CPLD with standardised layout; they mostly5* only differ in base address.6* All registers are 16bits wide with 32bit spacing.7*/89#include <linux/interrupt.h>10#include <linux/module.h>11#include <linux/spinlock.h>12#include <linux/irq.h>13#include <asm/addrspace.h>14#include <asm/io.h>15#include <asm/mach-db1x00/bcsr.h>1617static struct bcsr_reg {18void __iomem *raddr;19spinlock_t lock;20} bcsr_regs[BCSR_CNT];2122static void __iomem *bcsr_virt; /* KSEG1 addr of BCSR base */23static int bcsr_csc_base; /* linux-irq of first cascaded irq */2425void __init bcsr_init(unsigned long bcsr1_phys, unsigned long bcsr2_phys)26{27int i;2829bcsr1_phys = KSEG1ADDR(CPHYSADDR(bcsr1_phys));30bcsr2_phys = KSEG1ADDR(CPHYSADDR(bcsr2_phys));3132bcsr_virt = (void __iomem *)bcsr1_phys;3334for (i = 0; i < BCSR_CNT; i++) {35if (i >= BCSR_HEXLEDS)36bcsr_regs[i].raddr = (void __iomem *)bcsr2_phys +37(0x04 * (i - BCSR_HEXLEDS));38else39bcsr_regs[i].raddr = (void __iomem *)bcsr1_phys +40(0x04 * i);4142spin_lock_init(&bcsr_regs[i].lock);43}44}4546unsigned short bcsr_read(enum bcsr_id reg)47{48unsigned short r;49unsigned long flags;5051spin_lock_irqsave(&bcsr_regs[reg].lock, flags);52r = __raw_readw(bcsr_regs[reg].raddr);53spin_unlock_irqrestore(&bcsr_regs[reg].lock, flags);54return r;55}56EXPORT_SYMBOL_GPL(bcsr_read);5758void bcsr_write(enum bcsr_id reg, unsigned short val)59{60unsigned long flags;6162spin_lock_irqsave(&bcsr_regs[reg].lock, flags);63__raw_writew(val, bcsr_regs[reg].raddr);64wmb();65spin_unlock_irqrestore(&bcsr_regs[reg].lock, flags);66}67EXPORT_SYMBOL_GPL(bcsr_write);6869void bcsr_mod(enum bcsr_id reg, unsigned short clr, unsigned short set)70{71unsigned short r;72unsigned long flags;7374spin_lock_irqsave(&bcsr_regs[reg].lock, flags);75r = __raw_readw(bcsr_regs[reg].raddr);76r &= ~clr;77r |= set;78__raw_writew(r, bcsr_regs[reg].raddr);79wmb();80spin_unlock_irqrestore(&bcsr_regs[reg].lock, flags);81}82EXPORT_SYMBOL_GPL(bcsr_mod);8384/*85* DB1200/PB1200 CPLD IRQ muxer86*/87static void bcsr_csc_handler(unsigned int irq, struct irq_desc *d)88{89unsigned short bisr = __raw_readw(bcsr_virt + BCSR_REG_INTSTAT);9091for ( ; bisr; bisr &= bisr - 1)92generic_handle_irq(bcsr_csc_base + __ffs(bisr));93}9495/* NOTE: both the enable and mask bits must be cleared, otherwise the96* CPLD generates tons of spurious interrupts (at least on my DB1200).97* -- mlau98*/99static void bcsr_irq_mask(struct irq_data *d)100{101unsigned short v = 1 << (d->irq - bcsr_csc_base);102__raw_writew(v, bcsr_virt + BCSR_REG_INTCLR);103__raw_writew(v, bcsr_virt + BCSR_REG_MASKCLR);104wmb();105}106107static void bcsr_irq_maskack(struct irq_data *d)108{109unsigned short v = 1 << (d->irq - 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 */113wmb();114}115116static void bcsr_irq_unmask(struct irq_data *d)117{118unsigned short v = 1 << (d->irq - bcsr_csc_base);119__raw_writew(v, bcsr_virt + BCSR_REG_INTSET);120__raw_writew(v, bcsr_virt + BCSR_REG_MASKSET);121wmb();122}123124static struct irq_chip bcsr_irq_type = {125.name = "CPLD",126.irq_mask = bcsr_irq_mask,127.irq_mask_ack = bcsr_irq_maskack,128.irq_unmask = bcsr_irq_unmask,129};130131void __init bcsr_init_irq(int csc_start, int csc_end, int hook_irq)132{133unsigned int irq;134135/* 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);139wmb();140141bcsr_csc_base = csc_start;142143for (irq = csc_start; irq <= csc_end; irq++)144irq_set_chip_and_handler_name(irq, &bcsr_irq_type,145handle_level_irq, "level");146147irq_set_chained_handler(hook_irq, bcsr_csc_handler);148}149150151