1/* $Id: niccy.c,v 1.21.2.4 2004/01/13 23:48:39 keil Exp $ 2 * 3 * low level stuff for Dr. Neuhaus NICCY PnP and NICCY PCI and 4 * compatible (SAGEM cybermodem) 5 * 6 * Author Karsten Keil 7 * Copyright by Karsten Keil <keil@isdn4linux.de> 8 * 9 * This software may be used and distributed according to the terms 10 * of the GNU General Public License, incorporated herein by reference. 11 * 12 * Thanks to Dr. Neuhaus and SAGEM for information 13 * 14 */ 15 16#include <linux/init.h> 17#include "hisax.h" 18#include "isac.h" 19#include "hscx.h" 20#include "isdnl1.h" 21#include <linux/pci.h> 22#include <linux/isapnp.h> 23 24static const char *niccy_revision = "$Revision: 1.21.2.4 $"; 25 26#define byteout(addr, val) outb(val, addr) 27#define bytein(addr) inb(addr) 28 29#define ISAC_PCI_DATA 0 30#define HSCX_PCI_DATA 1 31#define ISAC_PCI_ADDR 2 32#define HSCX_PCI_ADDR 3 33#define ISAC_PNP 0 34#define HSCX_PNP 1 35 36/* SUB Types */ 37#define NICCY_PNP 1 38#define NICCY_PCI 2 39 40/* PCI stuff */ 41#define PCI_IRQ_CTRL_REG 0x38 42#define PCI_IRQ_ENABLE 0x1f00 43#define PCI_IRQ_DISABLE 0xff0000 44#define PCI_IRQ_ASSERT 0x800000 45 46static inline u_char readreg(unsigned int ale, unsigned int adr, u_char off) 47{ 48 register u_char ret; 49 50 byteout(ale, off); 51 ret = bytein(adr); 52 return ret; 53} 54 55static inline void readfifo(unsigned int ale, unsigned int adr, u_char off, 56 u_char *data, int size) 57{ 58 byteout(ale, off); 59 insb(adr, data, size); 60} 61 62static inline void writereg(unsigned int ale, unsigned int adr, u_char off, 63 u_char data) 64{ 65 byteout(ale, off); 66 byteout(adr, data); 67} 68 69static inline void writefifo(unsigned int ale, unsigned int adr, u_char off, 70 u_char *data, int size) 71{ 72 byteout(ale, off); 73 outsb(adr, data, size); 74} 75 76/* Interface functions */ 77 78static u_char ReadISAC(struct IsdnCardState *cs, u_char offset) 79{ 80 return readreg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, offset); 81} 82 83static void WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) 84{ 85 writereg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, offset, value); 86} 87 88static void ReadISACfifo(struct IsdnCardState *cs, u_char *data, int size) 89{ 90 readfifo(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, 0, data, size); 91} 92 93static void WriteISACfifo(struct IsdnCardState *cs, u_char *data, int size) 94{ 95 writefifo(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, 0, data, size); 96} 97 98static u_char ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset) 99{ 100 return readreg(cs->hw.niccy.hscx_ale, 101 cs->hw.niccy.hscx, offset + (hscx ? 0x40 : 0)); 102} 103 104static void WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, 105 u_char value) 106{ 107 writereg(cs->hw.niccy.hscx_ale, 108 cs->hw.niccy.hscx, offset + (hscx ? 0x40 : 0), value); 109} 110 111#define READHSCX(cs, nr, reg) readreg(cs->hw.niccy.hscx_ale, \ 112 cs->hw.niccy.hscx, reg + (nr ? 0x40 : 0)) 113#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.niccy.hscx_ale, \ 114 cs->hw.niccy.hscx, reg + (nr ? 0x40 : 0), data) 115 116#define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.niccy.hscx_ale, \ 117 cs->hw.niccy.hscx, (nr ? 0x40 : 0), ptr, cnt) 118 119#define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.niccy.hscx_ale, \ 120 cs->hw.niccy.hscx, (nr ? 0x40 : 0), ptr, cnt) 121 122#include "hscx_irq.c" 123 124static irqreturn_t niccy_interrupt(int intno, void *dev_id) 125{ 126 struct IsdnCardState *cs = dev_id; 127 u_char val; 128 u_long flags; 129 130 spin_lock_irqsave(&cs->lock, flags); 131 if (cs->subtyp == NICCY_PCI) { 132 int ival; 133 ival = inl(cs->hw.niccy.cfg_reg + PCI_IRQ_CTRL_REG); 134 if (!(ival & PCI_IRQ_ASSERT)) { /* IRQ not for us (shared) */ 135 spin_unlock_irqrestore(&cs->lock, flags); 136 return IRQ_NONE; 137 } 138 outl(ival, cs->hw.niccy.cfg_reg + PCI_IRQ_CTRL_REG); 139 } 140 val = readreg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, 141 HSCX_ISTA + 0x40); 142Start_HSCX: 143 if (val) 144 hscx_int_main(cs, val); 145 val = readreg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, ISAC_ISTA); 146Start_ISAC: 147 if (val) 148 isac_interrupt(cs, val); 149 val = readreg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, 150 HSCX_ISTA + 0x40); 151 if (val) { 152 if (cs->debug & L1_DEB_HSCX) 153 debugl1(cs, "HSCX IntStat after IntRoutine"); 154 goto Start_HSCX; 155 } 156 val = readreg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, ISAC_ISTA); 157 if (val) { 158 if (cs->debug & L1_DEB_ISAC) 159 debugl1(cs, "ISAC IntStat after IntRoutine"); 160 goto Start_ISAC; 161 } 162 writereg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_MASK, 0xFF); 163 writereg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_MASK + 0x40, 164 0xFF); 165 writereg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, ISAC_MASK, 0xFF); 166 writereg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, ISAC_MASK, 0); 167 writereg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_MASK, 0); 168 writereg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_MASK + 0x40, 0); 169 spin_unlock_irqrestore(&cs->lock, flags); 170 return IRQ_HANDLED; 171} 172 173static void release_io_niccy(struct IsdnCardState *cs) 174{ 175 if (cs->subtyp == NICCY_PCI) { 176 int val; 177 178 val = inl(cs->hw.niccy.cfg_reg + PCI_IRQ_CTRL_REG); 179 val &= PCI_IRQ_DISABLE; 180 outl(val, cs->hw.niccy.cfg_reg + PCI_IRQ_CTRL_REG); 181 release_region(cs->hw.niccy.cfg_reg, 0x40); 182 release_region(cs->hw.niccy.isac, 4); 183 } else { 184 release_region(cs->hw.niccy.isac, 2); 185 release_region(cs->hw.niccy.isac_ale, 2); 186 } 187} 188 189static void niccy_reset(struct IsdnCardState *cs) 190{ 191 if (cs->subtyp == NICCY_PCI) { 192 int val; 193 194 val = inl(cs->hw.niccy.cfg_reg + PCI_IRQ_CTRL_REG); 195 val |= PCI_IRQ_ENABLE; 196 outl(val, cs->hw.niccy.cfg_reg + PCI_IRQ_CTRL_REG); 197 } 198 inithscxisac(cs, 3); 199} 200 201static int niccy_card_msg(struct IsdnCardState *cs, int mt, void *arg) 202{ 203 u_long flags; 204 205 switch (mt) { 206 case CARD_RESET: 207 spin_lock_irqsave(&cs->lock, flags); 208 niccy_reset(cs); 209 spin_unlock_irqrestore(&cs->lock, flags); 210 return 0; 211 case CARD_RELEASE: 212 release_io_niccy(cs); 213 return 0; 214 case CARD_INIT: 215 spin_lock_irqsave(&cs->lock, flags); 216 niccy_reset(cs); 217 spin_unlock_irqrestore(&cs->lock, flags); 218 return 0; 219 case CARD_TEST: 220 return 0; 221 } 222 return 0; 223} 224 225#ifdef __ISAPNP__ 226static struct pnp_card *pnp_c = NULL; 227#endif 228 229int setup_niccy(struct IsdnCard *card) 230{ 231 struct IsdnCardState *cs = card->cs; 232 char tmp[64]; 233 234 strcpy(tmp, niccy_revision); 235 printk(KERN_INFO "HiSax: Niccy driver Rev. %s\n", HiSax_getrev(tmp)); 236 if (cs->typ != ISDN_CTYPE_NICCY) 237 return 0; 238#ifdef __ISAPNP__ 239 if (!card->para[1] && isapnp_present()) { 240 struct pnp_dev *pnp_d = NULL; 241 int err; 242 243 pnp_c = pnp_find_card(ISAPNP_VENDOR('S', 'D', 'A'), 244 ISAPNP_FUNCTION(0x0150), pnp_c); 245 if (pnp_c) { 246 pnp_d = pnp_find_dev(pnp_c, 247 ISAPNP_VENDOR('S', 'D', 'A'), 248 ISAPNP_FUNCTION(0x0150), pnp_d); 249 if (!pnp_d) { 250 printk(KERN_ERR "NiccyPnP: PnP error card " 251 "found, no device\n"); 252 return 0; 253 } 254 pnp_disable_dev(pnp_d); 255 err = pnp_activate_dev(pnp_d); 256 if (err < 0) { 257 printk(KERN_WARNING "%s: pnp_activate_dev " 258 "ret(%d)\n", __func__, err); 259 return 0; 260 } 261 card->para[1] = pnp_port_start(pnp_d, 0); 262 card->para[2] = pnp_port_start(pnp_d, 1); 263 card->para[0] = pnp_irq(pnp_d, 0); 264 if (!card->para[0] || !card->para[1] || 265 !card->para[2]) { 266 printk(KERN_ERR "NiccyPnP:some resources are " 267 "missing %ld/%lx/%lx\n", 268 card->para[0], card->para[1], 269 card->para[2]); 270 pnp_disable_dev(pnp_d); 271 return 0; 272 } 273 } else 274 printk(KERN_INFO "NiccyPnP: no ISAPnP card found\n"); 275 } 276#endif 277 if (card->para[1]) { 278 cs->hw.niccy.isac = card->para[1] + ISAC_PNP; 279 cs->hw.niccy.hscx = card->para[1] + HSCX_PNP; 280 cs->hw.niccy.isac_ale = card->para[2] + ISAC_PNP; 281 cs->hw.niccy.hscx_ale = card->para[2] + HSCX_PNP; 282 cs->hw.niccy.cfg_reg = 0; 283 cs->subtyp = NICCY_PNP; 284 cs->irq = card->para[0]; 285 if (!request_region(cs->hw.niccy.isac, 2, "niccy data")) { 286 printk(KERN_WARNING "HiSax: NICCY data port %x-%x " 287 "already in use\n", 288 cs->hw.niccy.isac, cs->hw.niccy.isac + 1); 289 return 0; 290 } 291 if (!request_region(cs->hw.niccy.isac_ale, 2, "niccy addr")) { 292 printk(KERN_WARNING "HiSax: NICCY address port %x-%x " 293 "already in use\n", 294 cs->hw.niccy.isac_ale, 295 cs->hw.niccy.isac_ale + 1); 296 release_region(cs->hw.niccy.isac, 2); 297 return 0; 298 } 299 } else { 300#ifdef CONFIG_PCI 301 static struct pci_dev *niccy_dev; 302 303 u_int pci_ioaddr; 304 cs->subtyp = 0; 305 if ((niccy_dev = hisax_find_pci_device(PCI_VENDOR_ID_SATSAGEM, 306 PCI_DEVICE_ID_SATSAGEM_NICCY, 307 niccy_dev))) { 308 if (pci_enable_device(niccy_dev)) 309 return 0; 310 /* get IRQ */ 311 if (!niccy_dev->irq) { 312 printk(KERN_WARNING 313 "Niccy: No IRQ for PCI card found\n"); 314 return 0; 315 } 316 cs->irq = niccy_dev->irq; 317 cs->hw.niccy.cfg_reg = pci_resource_start(niccy_dev, 0); 318 if (!cs->hw.niccy.cfg_reg) { 319 printk(KERN_WARNING 320 "Niccy: No IO-Adr for PCI cfg found\n"); 321 return 0; 322 } 323 pci_ioaddr = pci_resource_start(niccy_dev, 1); 324 if (!pci_ioaddr) { 325 printk(KERN_WARNING 326 "Niccy: No IO-Adr for PCI card found\n"); 327 return 0; 328 } 329 cs->subtyp = NICCY_PCI; 330 } else { 331 printk(KERN_WARNING "Niccy: No PCI card found\n"); 332 return 0; 333 } 334 cs->irq_flags |= IRQF_SHARED; 335 cs->hw.niccy.isac = pci_ioaddr + ISAC_PCI_DATA; 336 cs->hw.niccy.isac_ale = pci_ioaddr + ISAC_PCI_ADDR; 337 cs->hw.niccy.hscx = pci_ioaddr + HSCX_PCI_DATA; 338 cs->hw.niccy.hscx_ale = pci_ioaddr + HSCX_PCI_ADDR; 339 if (!request_region(cs->hw.niccy.isac, 4, "niccy")) { 340 printk(KERN_WARNING 341 "HiSax: NICCY data port %x-%x already in use\n", 342 cs->hw.niccy.isac, cs->hw.niccy.isac + 4); 343 return 0; 344 } 345 if (!request_region(cs->hw.niccy.cfg_reg, 0x40, "niccy pci")) { 346 printk(KERN_WARNING 347 "HiSax: NICCY pci port %x-%x already in use\n", 348 cs->hw.niccy.cfg_reg, 349 cs->hw.niccy.cfg_reg + 0x40); 350 release_region(cs->hw.niccy.isac, 4); 351 return 0; 352 } 353#else 354 printk(KERN_WARNING "Niccy: io0 0 and NO_PCI_BIOS\n"); 355 printk(KERN_WARNING "Niccy: unable to config NICCY PCI\n"); 356 return 0; 357#endif /* CONFIG_PCI */ 358 } 359 printk(KERN_INFO "HiSax: NICCY %s config irq:%d data:0x%X ale:0x%X\n", 360 (cs->subtyp == 1) ? "PnP" : "PCI", 361 cs->irq, cs->hw.niccy.isac, cs->hw.niccy.isac_ale); 362 setup_isac(cs); 363 cs->readisac = &ReadISAC; 364 cs->writeisac = &WriteISAC; 365 cs->readisacfifo = &ReadISACfifo; 366 cs->writeisacfifo = &WriteISACfifo; 367 cs->BC_Read_Reg = &ReadHSCX; 368 cs->BC_Write_Reg = &WriteHSCX; 369 cs->BC_Send_Data = &hscx_fill_fifo; 370 cs->cardmsg = &niccy_card_msg; 371 cs->irq_func = &niccy_interrupt; 372 ISACVersion(cs, "Niccy:"); 373 if (HscxVersion(cs, "Niccy:")) { 374 printk(KERN_WARNING "Niccy: wrong HSCX versions check IO " 375 "address\n"); 376 release_io_niccy(cs); 377 return 0; 378 } 379 return 1; 380} 381