Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* $Id: telespci.c,v 2.23.2.3 2004/01/13 14:31:26 keil Exp $ |
2 | * | |
3 | * low level stuff for Teles PCI isdn cards | |
4 | * | |
5 | * Author Ton van Rosmalen | |
6 | * Karsten Keil | |
7 | * Copyright by Ton van Rosmalen | |
8 | * by Karsten Keil <keil@isdn4linux.de> | |
9 | * | |
10 | * This software may be used and distributed according to the terms | |
11 | * of the GNU General Public License, incorporated herein by reference. | |
12 | * | |
13 | */ | |
14 | ||
15 | #include <linux/init.h> | |
16 | #include <linux/config.h> | |
17 | #include "hisax.h" | |
18 | #include "isac.h" | |
19 | #include "hscx.h" | |
20 | #include "isdnl1.h" | |
21 | #include <linux/pci.h> | |
22 | ||
23 | extern const char *CardType[]; | |
672c3fd9 | 24 | static const char *telespci_revision = "$Revision: 2.23.2.3 $"; |
1da177e4 LT |
25 | |
26 | #define ZORAN_PO_RQ_PEN 0x02000000 | |
27 | #define ZORAN_PO_WR 0x00800000 | |
28 | #define ZORAN_PO_GID0 0x00000000 | |
29 | #define ZORAN_PO_GID1 0x00100000 | |
30 | #define ZORAN_PO_GREG0 0x00000000 | |
31 | #define ZORAN_PO_GREG1 0x00010000 | |
32 | #define ZORAN_PO_DMASK 0xFF | |
33 | ||
34 | #define WRITE_ADDR_ISAC (ZORAN_PO_WR | ZORAN_PO_GID0 | ZORAN_PO_GREG0) | |
35 | #define READ_DATA_ISAC (ZORAN_PO_GID0 | ZORAN_PO_GREG1) | |
36 | #define WRITE_DATA_ISAC (ZORAN_PO_WR | ZORAN_PO_GID0 | ZORAN_PO_GREG1) | |
37 | #define WRITE_ADDR_HSCX (ZORAN_PO_WR | ZORAN_PO_GID1 | ZORAN_PO_GREG0) | |
38 | #define READ_DATA_HSCX (ZORAN_PO_GID1 | ZORAN_PO_GREG1) | |
39 | #define WRITE_DATA_HSCX (ZORAN_PO_WR | ZORAN_PO_GID1 | ZORAN_PO_GREG1) | |
40 | ||
41 | #define ZORAN_WAIT_NOBUSY do { \ | |
42 | portdata = readl(adr + 0x200); \ | |
43 | } while (portdata & ZORAN_PO_RQ_PEN) | |
44 | ||
45 | static inline u_char | |
46 | readisac(void __iomem *adr, u_char off) | |
47 | { | |
48 | register unsigned int portdata; | |
49 | ||
50 | ZORAN_WAIT_NOBUSY; | |
51 | ||
52 | /* set address for ISAC */ | |
53 | writel(WRITE_ADDR_ISAC | off, adr + 0x200); | |
54 | ZORAN_WAIT_NOBUSY; | |
55 | ||
56 | /* read data from ISAC */ | |
57 | writel(READ_DATA_ISAC, adr + 0x200); | |
58 | ZORAN_WAIT_NOBUSY; | |
59 | return((u_char)(portdata & ZORAN_PO_DMASK)); | |
60 | } | |
61 | ||
62 | static inline void | |
63 | writeisac(void __iomem *adr, u_char off, u_char data) | |
64 | { | |
65 | register unsigned int portdata; | |
66 | ||
67 | ZORAN_WAIT_NOBUSY; | |
68 | ||
69 | /* set address for ISAC */ | |
70 | writel(WRITE_ADDR_ISAC | off, adr + 0x200); | |
71 | ZORAN_WAIT_NOBUSY; | |
72 | ||
73 | /* write data to ISAC */ | |
74 | writel(WRITE_DATA_ISAC | data, adr + 0x200); | |
75 | ZORAN_WAIT_NOBUSY; | |
76 | } | |
77 | ||
78 | static inline u_char | |
79 | readhscx(void __iomem *adr, int hscx, u_char off) | |
80 | { | |
81 | register unsigned int portdata; | |
82 | ||
83 | ZORAN_WAIT_NOBUSY; | |
84 | /* set address for HSCX */ | |
85 | writel(WRITE_ADDR_HSCX | ((hscx ? 0x40:0) + off), adr + 0x200); | |
86 | ZORAN_WAIT_NOBUSY; | |
87 | ||
88 | /* read data from HSCX */ | |
89 | writel(READ_DATA_HSCX, adr + 0x200); | |
90 | ZORAN_WAIT_NOBUSY; | |
91 | return ((u_char)(portdata & ZORAN_PO_DMASK)); | |
92 | } | |
93 | ||
94 | static inline void | |
95 | writehscx(void __iomem *adr, int hscx, u_char off, u_char data) | |
96 | { | |
97 | register unsigned int portdata; | |
98 | ||
99 | ZORAN_WAIT_NOBUSY; | |
100 | /* set address for HSCX */ | |
101 | writel(WRITE_ADDR_HSCX | ((hscx ? 0x40:0) + off), adr + 0x200); | |
102 | ZORAN_WAIT_NOBUSY; | |
103 | ||
104 | /* write data to HSCX */ | |
105 | writel(WRITE_DATA_HSCX | data, adr + 0x200); | |
106 | ZORAN_WAIT_NOBUSY; | |
107 | } | |
108 | ||
109 | static inline void | |
110 | read_fifo_isac(void __iomem *adr, u_char * data, int size) | |
111 | { | |
112 | register unsigned int portdata; | |
113 | register int i; | |
114 | ||
115 | ZORAN_WAIT_NOBUSY; | |
116 | /* read data from ISAC */ | |
117 | for (i = 0; i < size; i++) { | |
118 | /* set address for ISAC fifo */ | |
119 | writel(WRITE_ADDR_ISAC | 0x1E, adr + 0x200); | |
120 | ZORAN_WAIT_NOBUSY; | |
121 | writel(READ_DATA_ISAC, adr + 0x200); | |
122 | ZORAN_WAIT_NOBUSY; | |
123 | data[i] = (u_char)(portdata & ZORAN_PO_DMASK); | |
124 | } | |
125 | } | |
126 | ||
127 | static void | |
128 | write_fifo_isac(void __iomem *adr, u_char * data, int size) | |
129 | { | |
130 | register unsigned int portdata; | |
131 | register int i; | |
132 | ||
133 | ZORAN_WAIT_NOBUSY; | |
134 | /* write data to ISAC */ | |
135 | for (i = 0; i < size; i++) { | |
136 | /* set address for ISAC fifo */ | |
137 | writel(WRITE_ADDR_ISAC | 0x1E, adr + 0x200); | |
138 | ZORAN_WAIT_NOBUSY; | |
139 | writel(WRITE_DATA_ISAC | data[i], adr + 0x200); | |
140 | ZORAN_WAIT_NOBUSY; | |
141 | } | |
142 | } | |
143 | ||
144 | static inline void | |
145 | read_fifo_hscx(void __iomem *adr, int hscx, u_char * data, int size) | |
146 | { | |
147 | register unsigned int portdata; | |
148 | register int i; | |
149 | ||
150 | ZORAN_WAIT_NOBUSY; | |
151 | /* read data from HSCX */ | |
152 | for (i = 0; i < size; i++) { | |
153 | /* set address for HSCX fifo */ | |
154 | writel(WRITE_ADDR_HSCX |(hscx ? 0x5F:0x1F), adr + 0x200); | |
155 | ZORAN_WAIT_NOBUSY; | |
156 | writel(READ_DATA_HSCX, adr + 0x200); | |
157 | ZORAN_WAIT_NOBUSY; | |
158 | data[i] = (u_char) (portdata & ZORAN_PO_DMASK); | |
159 | } | |
160 | } | |
161 | ||
162 | static inline void | |
163 | write_fifo_hscx(void __iomem *adr, int hscx, u_char * data, int size) | |
164 | { | |
165 | unsigned int portdata; | |
166 | register int i; | |
167 | ||
168 | ZORAN_WAIT_NOBUSY; | |
169 | /* write data to HSCX */ | |
170 | for (i = 0; i < size; i++) { | |
171 | /* set address for HSCX fifo */ | |
172 | writel(WRITE_ADDR_HSCX |(hscx ? 0x5F:0x1F), adr + 0x200); | |
173 | ZORAN_WAIT_NOBUSY; | |
174 | writel(WRITE_DATA_HSCX | data[i], adr + 0x200); | |
175 | ZORAN_WAIT_NOBUSY; | |
176 | udelay(10); | |
177 | } | |
178 | } | |
179 | ||
180 | /* Interface functions */ | |
181 | ||
182 | static u_char | |
183 | ReadISAC(struct IsdnCardState *cs, u_char offset) | |
184 | { | |
185 | return (readisac(cs->hw.teles0.membase, offset)); | |
186 | } | |
187 | ||
188 | static void | |
189 | WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) | |
190 | { | |
191 | writeisac(cs->hw.teles0.membase, offset, value); | |
192 | } | |
193 | ||
194 | static void | |
195 | ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size) | |
196 | { | |
197 | read_fifo_isac(cs->hw.teles0.membase, data, size); | |
198 | } | |
199 | ||
200 | static void | |
201 | WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size) | |
202 | { | |
203 | write_fifo_isac(cs->hw.teles0.membase, data, size); | |
204 | } | |
205 | ||
206 | static u_char | |
207 | ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset) | |
208 | { | |
209 | return (readhscx(cs->hw.teles0.membase, hscx, offset)); | |
210 | } | |
211 | ||
212 | static void | |
213 | WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value) | |
214 | { | |
215 | writehscx(cs->hw.teles0.membase, hscx, offset, value); | |
216 | } | |
217 | ||
218 | /* | |
219 | * fast interrupt HSCX stuff goes here | |
220 | */ | |
221 | ||
222 | #define READHSCX(cs, nr, reg) readhscx(cs->hw.teles0.membase, nr, reg) | |
223 | #define WRITEHSCX(cs, nr, reg, data) writehscx(cs->hw.teles0.membase, nr, reg, data) | |
224 | #define READHSCXFIFO(cs, nr, ptr, cnt) read_fifo_hscx(cs->hw.teles0.membase, nr, ptr, cnt) | |
225 | #define WRITEHSCXFIFO(cs, nr, ptr, cnt) write_fifo_hscx(cs->hw.teles0.membase, nr, ptr, cnt) | |
226 | ||
227 | #include "hscx_irq.c" | |
228 | ||
229 | static irqreturn_t | |
230 | telespci_interrupt(int intno, void *dev_id, struct pt_regs *regs) | |
231 | { | |
232 | struct IsdnCardState *cs = dev_id; | |
233 | u_char hval, ival; | |
234 | u_long flags; | |
235 | ||
236 | spin_lock_irqsave(&cs->lock, flags); | |
237 | hval = readhscx(cs->hw.teles0.membase, 1, HSCX_ISTA); | |
238 | if (hval) | |
239 | hscx_int_main(cs, hval); | |
240 | ival = readisac(cs->hw.teles0.membase, ISAC_ISTA); | |
241 | if ((hval | ival) == 0) { | |
242 | spin_unlock_irqrestore(&cs->lock, flags); | |
243 | return IRQ_NONE; | |
244 | } | |
245 | if (ival) | |
246 | isac_interrupt(cs, ival); | |
247 | /* Clear interrupt register for Zoran PCI controller */ | |
248 | writel(0x70000000, cs->hw.teles0.membase + 0x3C); | |
249 | ||
250 | writehscx(cs->hw.teles0.membase, 0, HSCX_MASK, 0xFF); | |
251 | writehscx(cs->hw.teles0.membase, 1, HSCX_MASK, 0xFF); | |
252 | writeisac(cs->hw.teles0.membase, ISAC_MASK, 0xFF); | |
253 | writeisac(cs->hw.teles0.membase, ISAC_MASK, 0x0); | |
254 | writehscx(cs->hw.teles0.membase, 0, HSCX_MASK, 0x0); | |
255 | writehscx(cs->hw.teles0.membase, 1, HSCX_MASK, 0x0); | |
256 | spin_unlock_irqrestore(&cs->lock, flags); | |
257 | return IRQ_HANDLED; | |
258 | } | |
259 | ||
672c3fd9 | 260 | static void |
1da177e4 LT |
261 | release_io_telespci(struct IsdnCardState *cs) |
262 | { | |
263 | iounmap(cs->hw.teles0.membase); | |
264 | } | |
265 | ||
266 | static int | |
267 | TelesPCI_card_msg(struct IsdnCardState *cs, int mt, void *arg) | |
268 | { | |
269 | u_long flags; | |
270 | ||
271 | switch (mt) { | |
272 | case CARD_RESET: | |
273 | return(0); | |
274 | case CARD_RELEASE: | |
275 | release_io_telespci(cs); | |
276 | return(0); | |
277 | case CARD_INIT: | |
278 | spin_lock_irqsave(&cs->lock, flags); | |
279 | inithscxisac(cs, 3); | |
280 | spin_unlock_irqrestore(&cs->lock, flags); | |
281 | return(0); | |
282 | case CARD_TEST: | |
283 | return(0); | |
284 | } | |
285 | return(0); | |
286 | } | |
287 | ||
288 | static struct pci_dev *dev_tel __initdata = NULL; | |
289 | ||
290 | int __init | |
291 | setup_telespci(struct IsdnCard *card) | |
292 | { | |
293 | struct IsdnCardState *cs = card->cs; | |
294 | char tmp[64]; | |
295 | ||
296 | #ifdef __BIG_ENDIAN | |
297 | #error "not running on big endian machines now" | |
298 | #endif | |
299 | strcpy(tmp, telespci_revision); | |
300 | printk(KERN_INFO "HiSax: Teles/PCI driver Rev. %s\n", HiSax_getrev(tmp)); | |
301 | if (cs->typ != ISDN_CTYPE_TELESPCI) | |
302 | return (0); | |
303 | #ifdef CONFIG_PCI | |
304 | if ((dev_tel = pci_find_device (PCI_VENDOR_ID_ZORAN, PCI_DEVICE_ID_ZORAN_36120, dev_tel))) { | |
305 | if (pci_enable_device(dev_tel)) | |
306 | return(0); | |
307 | cs->irq = dev_tel->irq; | |
308 | if (!cs->irq) { | |
309 | printk(KERN_WARNING "Teles: No IRQ for PCI card found\n"); | |
310 | return(0); | |
311 | } | |
312 | cs->hw.teles0.membase = ioremap(pci_resource_start(dev_tel, 0), | |
313 | PAGE_SIZE); | |
314 | printk(KERN_INFO "Found: Zoran, base-address: 0x%lx, irq: 0x%x\n", | |
315 | pci_resource_start(dev_tel, 0), dev_tel->irq); | |
316 | } else { | |
317 | printk(KERN_WARNING "TelesPCI: No PCI card found\n"); | |
318 | return(0); | |
319 | } | |
320 | #else | |
321 | printk(KERN_WARNING "HiSax: Teles/PCI and NO_PCI_BIOS\n"); | |
322 | printk(KERN_WARNING "HiSax: Teles/PCI unable to config\n"); | |
323 | return (0); | |
324 | #endif /* CONFIG_PCI */ | |
325 | ||
326 | /* Initialize Zoran PCI controller */ | |
327 | writel(0x00000000, cs->hw.teles0.membase + 0x28); | |
328 | writel(0x01000000, cs->hw.teles0.membase + 0x28); | |
329 | writel(0x01000000, cs->hw.teles0.membase + 0x28); | |
330 | writel(0x7BFFFFFF, cs->hw.teles0.membase + 0x2C); | |
331 | writel(0x70000000, cs->hw.teles0.membase + 0x3C); | |
332 | writel(0x61000000, cs->hw.teles0.membase + 0x40); | |
333 | /* writel(0x00800000, cs->hw.teles0.membase + 0x200); */ | |
334 | ||
335 | printk(KERN_INFO | |
336 | "HiSax: %s config irq:%d mem:%p\n", | |
337 | CardType[cs->typ], cs->irq, | |
338 | cs->hw.teles0.membase); | |
339 | ||
340 | setup_isac(cs); | |
341 | cs->readisac = &ReadISAC; | |
342 | cs->writeisac = &WriteISAC; | |
343 | cs->readisacfifo = &ReadISACfifo; | |
344 | cs->writeisacfifo = &WriteISACfifo; | |
345 | cs->BC_Read_Reg = &ReadHSCX; | |
346 | cs->BC_Write_Reg = &WriteHSCX; | |
347 | cs->BC_Send_Data = &hscx_fill_fifo; | |
348 | cs->cardmsg = &TelesPCI_card_msg; | |
349 | cs->irq_func = &telespci_interrupt; | |
350 | cs->irq_flags |= SA_SHIRQ; | |
351 | ISACVersion(cs, "TelesPCI:"); | |
352 | if (HscxVersion(cs, "TelesPCI:")) { | |
353 | printk(KERN_WARNING | |
354 | "TelesPCI: wrong HSCX versions check IO/MEM addresses\n"); | |
355 | release_io_telespci(cs); | |
356 | return (0); | |
357 | } | |
358 | return (1); | |
359 | } |