diff options
Diffstat (limited to 'arch/mips/alchemy/devboards/bcsr.c')
-rw-r--r-- | arch/mips/alchemy/devboards/bcsr.c | 148 |
1 files changed, 148 insertions, 0 deletions
diff --git a/arch/mips/alchemy/devboards/bcsr.c b/arch/mips/alchemy/devboards/bcsr.c new file mode 100644 index 000000000000..3bc4fd2155d7 --- /dev/null +++ b/arch/mips/alchemy/devboards/bcsr.c | |||
@@ -0,0 +1,148 @@ | |||
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 | } | ||