From d6c61ea463c2e46cb2979f99c460f63cac358b78 Mon Sep 17 00:00:00 2001 From: "(no author)" <(no author)@5390a7c7-147a-4af0-8ec9-7488f05a26cb> Date: Sat, 16 Oct 2004 15:23:58 +0000 Subject: This commit was manufactured by cvs2svn to create branch 'v1-0'. git-svn-id: http://svn.digium.com/svn/zaptel/branches/v1-0@474 5390a7c7-147a-4af0-8ec9-7488f05a26cb --- LICENSE | 341 +++++++++++++ wcte11xp.c | 1569 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 1910 insertions(+) create mode 100755 LICENSE create mode 100755 wcte11xp.c diff --git a/LICENSE b/LICENSE new file mode 100755 index 0000000..a52b16e --- /dev/null +++ b/LICENSE @@ -0,0 +1,341 @@ + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/wcte11xp.c b/wcte11xp.c new file mode 100755 index 0000000..4e6e616 --- /dev/null +++ b/wcte11xp.c @@ -0,0 +1,1569 @@ +/* + * Digium, Inc. Wildcard TE110P T1/PRI card Driver + * + * Written by Mark Spencer + * Matthew Fredrickson + * William Meadows + * + * Copyright (C) 2004, Digium, Inc. + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id$ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef STANDALONE_ZAPATA +#include "zaptel.h" +#else +#include +#endif + +#include "wct4xxp.h" /* For certain definitions */ + +#define WC_MAX_CARDS 32 + +/* +#define TEST_REGS +*/ + +/* Define to get more attention-grabbing but slightly more I/O using + alarm status */ +#define FANCY_ALARM + +/* Define to enable the V2.1 errata register settings */ +#if 0 +#define TRUST_INFINEON_ERRATA +#endif + +#define DELAY 0x0 /* 30 = 15 cycles, 10 = 8 cycles, 0 = 3 cycles */ + +#define WC_CNTL 0x00 +#define WC_OPER 0x01 +#define WC_AUXC 0x02 +#define WC_AUXD 0x03 +#define WC_MASK0 0x04 +#define WC_MASK1 0x05 +#define WC_INTSTAT 0x06 + +#define WC_DMAWS 0x08 +#define WC_DMAWI 0x0c +#define WC_DMAWE 0x10 +#define WC_DMARS 0x18 +#define WC_DMARI 0x1c +#define WC_DMARE 0x20 +#define WC_CURPOS 0x24 + +#define WC_SERC 0x2d +#define WC_FSCDELAY 0x2f + +#define WC_USERREG 0xc0 + +#define WC_CLOCK 0x0 +#define WC_LEDTEST 0x1 +#define WC_VERSION 0x2 + +/* Offset between transmit and receive */ +#define WC_OFFSET 4 + +#define BIT_CS (1 << 7) +#define BIT_ADDR (0xf << 3) + +#define BIT_LED1 (1 << 0) +#define BIT_LED0 (1 << 1) +#define BIT_TEST (1 << 2) + +#define FLAG_STARTED (1 << 0) +#define FLAG_NMF (1 << 1) +#define FLAG_SENDINGYELLOW (1 << 2) +#define FLAG_FALC12 (1 << 3) + +#define TYPE_T1 1 /* is a T1 card */ +#define TYPE_E1 2 /* is an E1 card */ + +static int chanmap_t1[] = +{ 2,1,0, + 6,5,4, + 10,9,8, + 14,13,12, + 18,17,16, + 22,21,20, + 26,25,24, + 30,29,28 }; + +static int chanmap_e1[] = +{ 2,1,0, + 7,6,5,4, + 11,10,9,8, + 15,14,13,12, + 19,18,17,16, + 23,22,21,20, + 27,26,25,24, + 31,30,29,28 }; + +#ifdef FANCY_ALARM +static int altab[] = { +0, 0, 0, 1, 2, 3, 4, 6, 8, 9, 11, 13, 16, 18, 20, 22, 24, 25, 27, 28, 29, 30, 31, 31, 32, 31, 31, 30, 29, 28, 27, 25, 23, 22, 20, 18, 16, 13, 11, 9, 8, 6, 4, 3, 2, 1, 0, 0, +}; +#endif + +struct t1 { + struct pci_dev *dev; + spinlock_t lock; + int spantype; + int spanflags; /* Span flags */ + unsigned char txsigs[16]; /* Copy of tx sig registers */ + int num; + int alarmcount; /* How much red alarm we've seen */ + int alarmdebounce; + /* Our offset for finding channel 1 */ + int offset; + char *variety; + int intcount; + int usecount; + int clocktimeout; + int sync; + int dead; + int blinktimer; + int alarmtimer; + int checktiming; /* Set >0 to cause the timing source to be checked */ + int loopupcnt; + int loopdowncnt; + int miss; + int misslast; + int *chanmap; +#ifdef FANCY_ALARM + int alarmpos; +#endif + unsigned char ledtestreg; + unsigned char outbyte; + unsigned long ioaddr; + unsigned short canary; + /* T1 signalling */ + dma_addr_t readdma; + dma_addr_t writedma; + volatile unsigned char *writechunk; /* Double-word aligned write memory */ + volatile unsigned char *readchunk; /* Double-word aligned read memory */ + unsigned char ec_chunk1[31][ZT_CHUNKSIZE]; + unsigned char ec_chunk2[31][ZT_CHUNKSIZE]; + unsigned char tempo[32]; + struct zt_span span; /* Span */ + struct zt_chan chans[31]; /* Channels */ +}; + +#define CANARY 0xca1e + +static int debug = 0; /* doesnt do anything */ +static int alarmdebounce = 0; +static int loopback = 0; +static int clockextra = 0; +static int t1e1override = -1; + +static struct t1 *cards[WC_MAX_CARDS]; + +static inline void start_alarm(struct t1 *wc) +{ +#ifdef FANCY_ALARM + wc->alarmpos = 0; +#endif + wc->blinktimer = 0; +} + +static inline void stop_alarm(struct t1 *wc) +{ +#ifdef FANCY_ALARM + wc->alarmpos = 0; +#endif + wc->blinktimer = 0; +} + +static inline void __select_framer(struct t1 *wc, int reg) +{ + /* Top four bits of address from AUX 6-3 */ + wc->outbyte &= ~BIT_CS; + wc->outbyte &= ~BIT_ADDR; + wc->outbyte |= (reg & 0xf0) >> 1; + outb(wc->outbyte, wc->ioaddr + WC_AUXD); +} + +static inline void __select_control(struct t1 *wc) +{ + if (!(wc->outbyte & BIT_CS)) { + wc->outbyte |= BIT_CS; + outb(wc->outbyte, wc->ioaddr + WC_AUXD); + } +} + +static int t1xxp_open(struct zt_chan *chan) +{ + struct t1 *wc = chan->pvt; + if (wc->dead) + return -ENODEV; + wc->usecount++; +#ifndef LINUX26 + MOD_INC_USE_COUNT; +#endif + return 0; +} + +static int __control_set_reg(struct t1 *wc, int reg, unsigned char val) +{ + __select_control(wc); + outb(val, wc->ioaddr + WC_USERREG + ((reg & 0xf) << 2)); + return 0; +} + +static int control_set_reg(struct t1 *wc, int reg, unsigned char val) +{ + unsigned long flags; + int res; + spin_lock_irqsave(&wc->lock, flags); + res = __control_set_reg(wc, reg, val); + spin_unlock_irqrestore(&wc->lock, flags); + return res; +} + +static int __control_get_reg(struct t1 *wc, int reg) +{ + unsigned char res; + /* The following makes UTTERLY no sense, but what was happening + was that reads in some cases were not actually happening + on the physical bus. Why, we dunno. But in debugging, we found + that writing before reading (in this case to an unused position) + seems to get rid of the problem */ + __control_set_reg(wc,3,0x69); /* do magic here */ + /* now get the read byte from the Xilinx part */ + res = inb(wc->ioaddr + WC_USERREG + ((reg & 0xf) << 2)); + return res; +} + +static int control_get_reg(struct t1 *wc, int reg) +{ + unsigned long flags; + int res; + spin_lock_irqsave(&wc->lock, flags); + res = __control_get_reg(wc, reg); + spin_unlock_irqrestore(&wc->lock, flags); + return res; +} + +static inline unsigned int __t1_framer_in(struct t1 *wc, const unsigned int reg) +{ + unsigned char res; + __select_framer(wc, reg); + /* Get value */ + res = inb(wc->ioaddr + WC_USERREG + ((reg & 0xf) << 2)); + return res; +#if 0 + unsigned int ret; + __t1_pci_out(wc, WC_LADDR, (unit << 8) | (addr & 0xff)); + __t1_pci_out(wc, WC_LADDR, (unit << 8) | (addr & 0xff) | ( 1 << 10) | WC_LREAD); + ret = __t1_pci_in(wc, WC_LDATA); + __t1_pci_out(wc, WC_LADDR, 0); + return ret & 0xff; +#endif +} + +static inline unsigned int t1_framer_in(struct t1 *wc, const unsigned int addr) +{ + unsigned long flags; + unsigned int ret; + spin_lock_irqsave(&wc->lock, flags); + ret = __t1_framer_in(wc, addr); + spin_unlock_irqrestore(&wc->lock, flags); + return ret; + +} + +static inline void __t1_framer_out(struct t1 *wc, const unsigned int reg, const unsigned int val) +{ + if (debug > 1) + printk("Writing %02x to address %02x\n", val, reg); + __select_framer(wc, reg); + /* Send address */ + outb(val, wc->ioaddr + WC_USERREG + ((reg & 0xf) << 2)); +#if 0 + __t1_pci_out(wc, WC_LADDR, (unit << 8) | (addr & 0xff)); + __t1_pci_out(wc, WC_LDATA, value); + __t1_pci_out(wc, WC_LADDR, (unit << 8) | (addr & 0xff) | (1 << 10)); + __t1_pci_out(wc, WC_LADDR, (unit << 8) | (addr & 0xff) | (1 << 10) | WC_LWRITE); + __t1_pci_out(wc, WC_LADDR, (unit << 8) | (addr & 0xff) | (1 << 10)); + __t1_pci_out(wc, WC_LADDR, (unit << 8) | (addr & 0xff)); + __t1_pci_out(wc, WC_LADDR, 0); + if (debug) printk("Write complete\n"); +#endif +#if 0 + { unsigned int tmp; + tmp = t1_framer_in(wc, unit, addr); + if (tmp != value) { + printk("Expected %d from unit %d register %d but got %d instead\n", value, unit, addr, tmp); + } } +#endif +} + +static inline void t1_framer_out(struct t1 *wc, const unsigned int addr, const unsigned int value) +{ + unsigned long flags; + spin_lock_irqsave(&wc->lock, flags); + __t1_framer_out(wc, addr, value); + spin_unlock_irqrestore(&wc->lock, flags); +} + +static void t1xxp_release(struct t1 *wc) +{ + zt_unregister(&wc->span); + kfree(wc); + printk("Freed a Wildcard\n"); +} + +static int t1xxp_close(struct zt_chan *chan) +{ + struct t1 *wc = chan->pvt; + wc->usecount--; +#ifndef LINUX26 + MOD_DEC_USE_COUNT; +#endif + /* If we're dead, release us now */ + if (!wc->usecount && wc->dead) + t1xxp_release(wc); + return 0; +} + +static void t1xxp_enable_interrupts(struct t1 *wc) +{ + /* Clear interrupts */ + outb(0xff, wc->ioaddr + WC_INTSTAT); + /* Enable interrupts (we care about all of them) */ + outb(0x3c /* 0x3f */, wc->ioaddr + WC_MASK0); + /* No external interrupts */ + outb(0x00, wc->ioaddr + WC_MASK1); + if (debug) printk("Enabled interrupts!\n"); +} + +static void t1xxp_start_dma(struct t1 *wc) +{ + /* Reset Master and TDM */ + outb(DELAY | 0x0f, wc->ioaddr + WC_CNTL); + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(1); + outb(DELAY | 0x01, wc->ioaddr + WC_CNTL); + outb(0x01, wc->ioaddr + WC_OPER); + if (debug) printk("Started DMA\n"); + outb(0x03, wc->ioaddr + WC_OPER); + outb(0x01, wc->ioaddr + WC_OPER); +} + +static void __t1xxp_stop_dma(struct t1 *wc) +{ + outb(0x00, wc->ioaddr + WC_OPER); +} + +static void __t1xxp_disable_interrupts(struct t1 *wc) +{ + outb(0x00, wc->ioaddr + WC_MASK0); + outb(0x00, wc->ioaddr + WC_MASK1); +} + +static void __t1xxp_set_clear(struct t1 *wc) +{ + int i,j; + unsigned short val=0; + for (i=0;i<24;i++) { + j = (i/8); + if (wc->span.chans[i].flags & ZT_FLAG_CLEAR) + val |= 1 << (7 - (i % 8)); + if ((i % 8)==7) { + if (debug > 1) + printk("Putting %d in register %02x\n", + val, 0x2f + j); + __t1_framer_out(wc, 0x2f + j, val); + val = 0; + } + } +} + +static int t1xxp_ioctl(struct zt_chan *chan, unsigned int cmd, unsigned long data) +{ + struct t4_regs regs; + int x; + struct t1 *wc; + switch(cmd) { + case WCT4_GET_REGS: + wc = chan->pvt; + for (x=0;xioaddr + (x << 2))) | + (inb(wc->ioaddr + (x << 2) + 1) << 8) | + (inb(wc->ioaddr + (x << 2) + 2) << 16) | + (inb(wc->ioaddr + (x << 2) + 3) << 24); +#else + regs.pci[x] = (inb(wc->ioaddr + x)); +#endif + + for (x=0;xpvt; + + if (wc->spantype == TYPE_E1) { + switch(cmd) { + case ZT_MAINT_NONE: + printk("XXX Turn off local and remote loops E1 XXX\n"); + break; + case ZT_MAINT_LOCALLOOP: + printk("XXX Turn on local loopback E1 XXX\n"); + break; + case ZT_MAINT_REMOTELOOP: + printk("XXX Turn on remote loopback E1 XXX\n"); + break; + case ZT_MAINT_LOOPUP: + printk("XXX Send loopup code E1 XXX\n"); + break; + case ZT_MAINT_LOOPDOWN: + printk("XXX Send loopdown code E1 XXX\n"); + break; + case ZT_MAINT_LOOPSTOP: + printk("XXX Stop sending loop codes E1 XXX\n"); + break; + default: + printk("TE110P: Unknown E1 maint command: %d\n", cmd); + break; + } + } else { + switch(cmd) { + case ZT_MAINT_NONE: + printk("XXX Turn off local and remote loops T1 XXX\n"); + break; + case ZT_MAINT_LOCALLOOP: + printk("XXX Turn on local loop and no remote loop XXX\n"); + break; + case ZT_MAINT_REMOTELOOP: + printk("XXX Turn on remote loopup XXX\n"); + break; + case ZT_MAINT_LOOPUP: + t1_framer_out(wc, 0x21, 0x50); /* FMR5: Nothing but RBS mode */ + break; + case ZT_MAINT_LOOPDOWN: + t1_framer_out(wc, 0x21, 0x60); /* FMR5: Nothing but RBS mode */ + break; + case ZT_MAINT_LOOPSTOP: + t1_framer_out(wc, 0x21, 0x40); /* FMR5: Nothing but RBS mode */ + break; + default: + printk("TE110P: Unknown T1 maint command: %d\n", cmd); + break; + } + } + return 0; +} + +static int t1xxp_rbsbits(struct zt_chan *chan, int bits) +{ + u_char m,c; + int n,b; + struct t1 *wc = chan->pvt; + unsigned long flags; + + if(debug > 1) printk("Setting bits to %d on channel %s\n", bits, chan->name); + spin_lock_irqsave(&wc->lock, flags); + if (wc->spantype == TYPE_E1) { /* do it E1 way */ + if (chan->chanpos == 16) { + spin_unlock_irqrestore(&wc->lock, flags); + return 0; + } + n = chan->chanpos - 1; + if (chan->chanpos > 15) n--; + b = (n % 15); + c = wc->txsigs[b]; + m = (n / 15) << 2; /* nibble selector */ + c &= (0xf << m); /* keep the other nibble */ + c |= (bits & 0xf) << (4 - m); /* put our new nibble here */ + wc->txsigs[b] = c; + /* output them to the chip */ + __t1_framer_out(wc,0x71 + b,c); + } else if (wc->span.lineconfig & ZT_CONFIG_D4) { + n = chan->chanpos - 1; + b = (n/4); + c = wc->txsigs[b]; + m = ((3 - (n % 4)) << 1); /* nibble selector */ + c &= ~(0x3 << m); /* keep the other nibble */ + c |= ((bits >> 2) & 0x3) << m; /* put our new nibble here */ + wc->txsigs[b] = c; + /* output them to the chip */ + __t1_framer_out(wc,0x70 + b,c); + __t1_framer_out(wc,0x70 + b + 6,c); + } else if (wc->span.lineconfig & ZT_CONFIG_ESF) { + n = chan->chanpos - 1; + b = (n/2); + c = wc->txsigs[b]; + m = ((n % 2) << 2); /* nibble selector */ + c &= (0xf << m); /* keep the other nibble */ + c |= (bits & 0xf) << (4 - m); /* put our new nibble here */ + wc->txsigs[b] = c; + /* output them to the chip */ + __t1_framer_out(wc,0x70 + b,c); + } + spin_unlock_irqrestore(&wc->lock, flags); + if (debug > 1) + printk("Finished setting RBS bits\n"); + return 0; +} + +static void __t1_check_sigbits(struct t1 *wc) +{ + int a,i,rxs; + if (!(wc->span.flags & ZT_FLAG_RUNNING)) + return; + if (wc->spantype == TYPE_E1) { + for (i = 0; i < 15; i++) { + a = __t1_framer_in(wc, 0x71 + i); + /* Get high channel in low bits */ + rxs = (a & 0xf); + if (!(wc->span.chans[i+16].sig & ZT_SIG_CLEAR)) { + if (wc->span.chans[i+16].rxsig != rxs) + zt_rbsbits(&wc->span.chans[i+16], rxs); + } + rxs = (a >> 4) & 0xf; + if (!(wc->span.chans[i].sig & ZT_SIG_CLEAR)) { + if (wc->span.chans[i].rxsig != rxs) + zt_rbsbits(&wc->span.chans[i], rxs); + } + } + } else if (wc->span.lineconfig & ZT_CONFIG_D4) { + for (i = 0; i < 24; i+=4) { + a = __t1_framer_in(wc, 0x70 + (i>>2)); + /* Get high channel in low bits */ + rxs = (a & 0x3) << 2; + if (!(wc->span.chans[i+3].sig & ZT_SIG_CLEAR)) { + if (wc->span.chans[i+3].rxsig != rxs) + zt_rbsbits(&wc->span.chans[i+3], rxs); + } + rxs = (a & 0xc); + if (!(wc->span.chans[i+2].sig & ZT_SIG_CLEAR)) { + if (wc->span.chans[i+2].rxsig != rxs) + zt_rbsbits(&wc->span.chans[i+2], rxs); + } + rxs = (a >> 2) & 0xc; + if (!(wc->span.chans[i+1].sig & ZT_SIG_CLEAR)) { + if (wc->span.chans[i+1].rxsig != rxs) + zt_rbsbits(&wc->span.chans[i+1], rxs); + } + rxs = (a >> 4) & 0xc; + if (!(wc->span.chans[i].sig & ZT_SIG_CLEAR)) { + if (wc->span.chans[i].rxsig != rxs) + zt_rbsbits(&wc->span.chans[i], rxs); + } + } + } else { + for (i = 0; i < 24; i+=2) { + a = __t1_framer_in(wc, 0x70 + (i>>1)); + /* Get high channel in low bits */ + rxs = (a & 0xf); + if (!(wc->span.chans[i+1].sig & ZT_SIG_CLEAR)) { + if (wc->span.chans[i+1].rxsig != rxs) + zt_rbsbits(&wc->span.chans[i+1], rxs); + } + rxs = (a >> 4) & 0xf; + if (!(wc->span.chans[i].sig & ZT_SIG_CLEAR)) { + if (wc->span.chans[i].rxsig != rxs) + zt_rbsbits(&wc->span.chans[i], rxs); + } + } + } +} + +static void t4_serial_setup(struct t1 *wc) +{ + printk("TE110P: Setting up global serial parameters for %s %s\n", + wc->spantype == TYPE_E1 ? "E1" : "T1", + wc->spanflags & FLAG_FALC12 ? "FALC V1.2" : "FALC V2.1"); + t1_framer_out(wc, 0x85, 0xe0); /* GPC1: Multiplex mode enabled, FSC is output, active low, RCLK from channel 0 */ + t1_framer_out(wc, 0x08, 0x05); /* IPC: Interrupt push/pull active low */ + if (wc->spanflags & FLAG_FALC12) { + t1_framer_out(wc, 0x92, 0x00); + t1_framer_out(wc, 0x93, 0x58); + t1_framer_out(wc, 0x94, 0xd2); + t1_framer_out(wc, 0x95, 0xc2); + t1_framer_out(wc, 0x96, 0x03); + t1_framer_out(wc, 0x97, 0x10); + } else { +#ifdef TRUST_INFINEON_ERRATA + if (wc->spantype == TYPE_E1) { + /* Global clocks (8.192 Mhz CLK) */ + t1_framer_out(wc, 0x92, 0x00); + t1_framer_out(wc, 0x93, 0x00); + t1_framer_out(wc, 0x94, 0x00); + t1_framer_out(wc, 0x95, 0x00); + t1_framer_out(wc, 0x96, 0x00); + t1_framer_out(wc, 0x97, 0x0F); + t1_framer_out(wc, 0x98, 0x80); + t1_framer_out(wc, 0x99, 0x00); + } else { + /* Global clocks (8.192 Mhz CLK) */ + t1_framer_out(wc, 0x92, 0x00); + t1_framer_out(wc, 0x93, 0x10); + t1_framer_out(wc, 0x94, 0xfb); + t1_framer_out(wc, 0x95, 0x03); + t1_framer_out(wc, 0x96, 0x00); + t1_framer_out(wc, 0x97, 0x0b); + t1_framer_out(wc, 0x98, 0x8c); + t1_framer_out(wc, 0x99, 0x80); + } +#else + t1_framer_out(wc, 0x92, 0x66); + t1_framer_out(wc, 0x93, 0x0e); + t1_framer_out(wc, 0x94, 0x3f); + t1_framer_out(wc, 0x95, 0x0f); + t1_framer_out(wc, 0x96, 0x04); + t1_framer_out(wc, 0x97, 0x3c); + t1_framer_out(wc, 0x98, 0x9c); + t1_framer_out(wc, 0x99, 0x90); +#endif + } + /* Configure interrupts */ + t1_framer_out(wc, 0x46, 0x40); /* GCR: Interrupt on Activation/Deactivation of AIX, LOS */ + + /* Configure system interface */ + t1_framer_out(wc, 0x3e, 0x02); /* SIC1: 4.096 Mhz clock/bus, double buffer receive / transmit, byte interleaved */ + t1_framer_out(wc, 0x3f, 0x00); /* SIC2: No FFS, no center receive eliastic buffer, phase 0 */ + t1_framer_out(wc, 0x40, 0x04); /* SIC3: Edges for capture */ + t1_framer_out(wc, 0x44, 0x30); /* CMR1: RCLK is at 8.192 Mhz dejittered */ + t1_framer_out(wc, 0x45, 0x00); /* CMR2: We provide sync and clock for tx and rx. */ + t1_framer_out(wc, 0x22, 0x00); /* XC0: Normal operation of Sa-bits */ + t1_framer_out(wc, 0x23, 0x04); /* XC1: 0 offset */ + t1_framer_out(wc, 0x24, 0x07); /* RC0: Just shy of 255 */ + if (wc->spanflags & FLAG_FALC12) + t1_framer_out(wc, 0x25, 0x04); /* RC1: The rest of RC0 */ + else + t1_framer_out(wc, 0x25, 0x06); /* RC1: The rest of RC0 */ + + /* Configure ports */ + t1_framer_out(wc, 0x80, 0x00); /* PC1: SPYR/SPYX input on RPA/XPA */ + t1_framer_out(wc, 0x81, 0x22); /* PC2: RMFB/XSIG output/input on RPB/XPB */ + t1_framer_out(wc, 0x82, 0x65); /* PC3: Some unused stuff */ + t1_framer_out(wc, 0x83, 0x35); /* PC4: Some more unused stuff */ + t1_framer_out(wc, 0x84, 0x31); /* PC5: XMFS active low, SCLKR is input, RCLK is output */ + t1_framer_out(wc, 0x86, 0x03); /* PC6: CLK1 is Tx Clock output, CLK2 is 8.192 Mhz from DCO-R */ + t1_framer_out(wc, 0x3b, 0x00); /* Clear LCR1 */ + printk("TE110P: Successfully initialized serial bus for card\n"); +} + +static void __t1_configure_t1(struct t1 *wc, int lineconfig, int txlevel) +{ + unsigned int fmr4, fmr2, fmr1, fmr0, lim2; + char *framing, *line; + int mytxlevel; + if ((txlevel > 7) || (txlevel < 4)) + mytxlevel = 0; + else + mytxlevel = txlevel - 4; + fmr1 = 0x1c; /* FMR1: Mode 0, T1 mode, CRC on for ESF, 2.048 Mhz system data rate, no XAIS */ + fmr2 = 0x22; /* FMR2: no payload loopback, auto send yellow alarm */ + if (loopback) + fmr2 |= 0x4; + fmr4 = 0x0c; /* FMR4: Lose sync on 2 out of 5 framing bits, auto resync */ + lim2 = 0x21; /* LIM2: 50% peak is a "1", Advanced Loss recovery */ + lim2 |= (mytxlevel << 6); /* LIM2: Add line buildout */ + __t1_framer_out(wc, 0x1d, fmr1); + __t1_framer_out(wc, 0x1e, fmr2); + + /* Configure line interface */ + if (lineconfig & ZT_CONFIG_AMI) { + line = "AMI"; + fmr0 = 0xa0; + } else { + line = "B8ZS"; + fmr0 = 0xf0; + } + if (lineconfig & ZT_CONFIG_D4) { + framing = "D4"; + } else { + framing = "ESF"; + fmr4 |= 0x2; + fmr2 |= 0xc0; + } + __t1_framer_out(wc, 0x1c, fmr0); + __t1_framer_out(wc, 0x20, fmr4); + __t1_framer_out(wc, 0x21, 0x40); /* FMR5: Enable RBS mode */ + + __t1_framer_out(wc, 0x37, 0xf8); /* LIM1: Clear data in case of LOS, Set receiver threshold (0.5V), No remote loop, no DRS */ + __t1_framer_out(wc, 0x36, 0x08); /* LIM0: Enable auto long haul mode, no local loop (must be after LIM1) */ + + __t1_framer_out(wc, 0x02, 0x50); /* CMDR: Reset the receiver and transmitter line interface */ + __t1_framer_out(wc, 0x02, 0x00); /* CMDR: Reset the receiver and transmitter line interface */ + + __t1_framer_out(wc, 0x3a, lim2); /* LIM2: 50% peak amplitude is a "1" */ + __t1_framer_out(wc, 0x38, 0x0a); /* PCD: LOS after 176 consecutive "zeros" */ + __t1_framer_out(wc, 0x39, 0x15); /* PCR: 22 "ones" clear LOS */ + + /* Generate pulse mask for T1 */ + switch(mytxlevel) { + case 3: + __t1_framer_out(wc, 0x26, 0x07); /* XPM0 */ + __t1_framer_out(wc, 0x27, 0x01); /* XPM1 */ + __t1_framer_out(wc, 0x28, 0x00); /* XPM2 */ + break; + case 2: + __t1_framer_out(wc, 0x26, 0x8c); /* XPM0 */ + __t1_framer_out(wc, 0x27, 0x11); /* XPM1 */ + __t1_framer_out(wc, 0x28, 0x01); /* XPM2 */ + break; + case 1: + __t1_framer_out(wc, 0x26, 0x8c); /* XPM0 */ + __t1_framer_out(wc, 0x27, 0x01); /* XPM1 */ + __t1_framer_out(wc, 0x28, 0x00); /* XPM2 */ + break; + case 0: + default: + __t1_framer_out(wc, 0x26, 0xd7); /* XPM0 */ + __t1_framer_out(wc, 0x27, 0x22); /* XPM1 */ + __t1_framer_out(wc, 0x28, 0x01); /* XPM2 */ + break; + } + printk("TE110P: Span configured for %s/%s\n", framing, line); +} + +static void __t1_configure_e1(struct t1 *wc, int lineconfig) +{ + unsigned int fmr2, fmr1, fmr0; + unsigned int cas = 0; + char *crc4 = ""; + char *framing, *line; + fmr1 = 0x44; /* FMR1: E1 mode, Automatic force resync, PCM30 mode, 8.192 Mhz backplane, no XAIS */ + fmr2 = 0x03; /* FMR2: Auto transmit remote alarm, auto loss of multiframe recovery, no payload loopback */ + if (loopback) + fmr2 |= 0x4; + if (lineconfig & ZT_CONFIG_CRC4) { + fmr1 |= 0x08; /* CRC4 transmit */ + fmr2 |= 0xc0; /* CRC4 receive */ + crc4 = "/CRC4"; + } + __t1_framer_out(wc, 0x1d, fmr1); + __t1_framer_out(wc, 0x1e, fmr2); + + /* Configure line interface */ + if (lineconfig & ZT_CONFIG_AMI) { + line = "AMI"; + fmr0 = 0xa0; + } else { + line = "HDB3"; + fmr0 = 0xf0; + } + if (lineconfig & ZT_CONFIG_CCS) { + framing = "CCS"; + } else { + framing = "CAS"; + cas = 0x40; + } + __t1_framer_out(wc, 0x1c, fmr0); + + __t1_framer_out(wc, 0x37, 0xf0 /*| 0x6 */ ); /* LIM1: Clear data in case of LOS, Set receiver threshold (0.5V), No remote loop, no DRS */ + __t1_framer_out(wc, 0x36, 0x08); /* LIM0: Enable auto long haul mode, no local loop (must be after LIM1) */ + + __t1_framer_out(wc, 0x02, 0x50); /* CMDR: Reset the receiver and transmitter line interface */ + __t1_framer_out(wc, 0x02, 0x00); /* CMDR: Reset the receiver and transmitter line interface */ + + /* Condition receive line interface for E1 after reset */ + __t1_framer_out(wc, 0xbb, 0x17); + __t1_framer_out(wc, 0xbc, 0x55); + __t1_framer_out(wc, 0xbb, 0x97); + __t1_framer_out(wc, 0xbb, 0x11); + __t1_framer_out(wc, 0xbc, 0xaa); + __t1_framer_out(wc, 0xbb, 0x91); + __t1_framer_out(wc, 0xbb, 0x12); + __t1_framer_out(wc, 0xbc, 0x55); + __t1_framer_out(wc, 0xbb, 0x92); + __t1_framer_out(wc, 0xbb, 0x0c); + __t1_framer_out(wc, 0xbb, 0x00); + __t1_framer_out(wc, 0xbb, 0x8c); + + __t1_framer_out(wc, 0x3a, 0x20); /* LIM2: 50% peak amplitude is a "1" */ + __t1_framer_out(wc, 0x38, 0x0a); /* PCD: LOS after 176 consecutive "zeros" */ + __t1_framer_out(wc, 0x39, 0x15); /* PCR: 22 "ones" clear LOS */ + + __t1_framer_out(wc, 0x20, 0x9f); /* XSW: Spare bits all to 1 */ + __t1_framer_out(wc, 0x21, 0x1c|cas); /* XSP: E-bit set when async. AXS auto, XSIF to 1 */ + + + /* Generate pulse mask for E1 */ + __t1_framer_out(wc, 0x26, 0x54); /* XPM0 */ + __t1_framer_out(wc, 0x27, 0x02); /* XPM1 */ + __t1_framer_out(wc, 0x28, 0x00); /* XPM2 */ + printk("TE110P: Span configured for %s/%s%s\n", framing, line, crc4); +} + +static void t1xxp_framer_start(struct t1 *wc, struct zt_span *span) +{ + int alreadyrunning = wc->span.flags & ZT_FLAG_RUNNING; + unsigned long flags; + + spin_lock_irqsave(&wc->lock, flags); + + if (wc->spantype == TYPE_E1) { /* if this is an E1 card */ + __t1_configure_e1(wc, span->lineconfig); + } else { /* is a T1 card */ + __t1_configure_t1(wc, span->lineconfig, span->txlevel); + __t1xxp_set_clear(wc); + } + + if (!alreadyrunning) + wc->span.flags |= ZT_FLAG_RUNNING; + + spin_unlock_irqrestore(&wc->lock, flags); +} + + +static int t1xxp_startup(struct zt_span *span) +{ + struct t1 *wc = span->pvt; + + int i,alreadyrunning = span->flags & ZT_FLAG_RUNNING; + + /* initialize the start value for the entire chunk of last ec buffer */ + for(i = 0; i < span->channels; i++) + { + memset(wc->ec_chunk1[i], + ZT_LIN2X(0,&span->chans[i]),ZT_CHUNKSIZE); + memset(wc->ec_chunk2[i], + ZT_LIN2X(0,&span->chans[i]),ZT_CHUNKSIZE); + } + + /* Reset framer with proper parameters and start */ + t1xxp_framer_start(wc, span); + printk("Calling startup (flags is %d)\n", span->flags); + + if (!alreadyrunning) { + /* Only if we're not already going */ + t1xxp_enable_interrupts(wc); + t1xxp_start_dma(wc); + span->flags |= ZT_FLAG_RUNNING; + } + return 0; +} + +static int t1xxp_shutdown(struct zt_span *span) +{ + struct t1 *wc = span->pvt; + unsigned long flags; + + spin_lock_irqsave(&wc->lock, flags); + t1_framer_out(wc, 0x46, 0x41); /* GCR: Interrupt on Activation/Deactivation of AIX, LOS */ + __t1xxp_stop_dma(wc); + __t1xxp_disable_interrupts(wc); + span->flags &= ~ZT_FLAG_RUNNING; + spin_unlock_irqrestore(&wc->lock, flags); + return 0; +} + + +static int t1xxp_chanconfig(struct zt_chan *chan, int sigtype) +{ + struct t1 *wc = chan->pvt; + unsigned long flags; + int alreadyrunning = chan->span->flags & ZT_FLAG_RUNNING; + + spin_lock_irqsave(&wc->lock, flags); + + if (alreadyrunning && (wc->spantype != TYPE_E1)) + __t1xxp_set_clear(wc); + + spin_unlock_irqrestore(&wc->lock, flags); + return 0; +} + +static int t1xxp_spanconfig(struct zt_span *span, struct zt_lineconfig *lc) +{ + struct t1 *wc = span->pvt; + span->lineconfig = lc->lineconfig; + span->txlevel = lc->lbo; + span->rxlevel = 0; + /* Do we want to SYNC on receive or not */ + wc->sync = lc->sync; + /* If already running, apply changes immediately */ + if (span->flags & ZT_FLAG_RUNNING) + return t1xxp_startup(span); + return 0; +} + +static int t1xxp_software_init(struct t1 *wc) +{ + int x; + /* Find position */ + for (x=0;x= WC_MAX_CARDS) + return -1; + t4_serial_setup(wc); + wc->num = x; + sprintf(wc->span.name, "WCT1/%d", wc->num); + sprintf(wc->span.desc, "%s Card %d", wc->variety, wc->num); + wc->span.spanconfig = t1xxp_spanconfig; + wc->span.chanconfig = t1xxp_chanconfig; + wc->span.startup = t1xxp_startup; + wc->span.shutdown = t1xxp_shutdown; + wc->span.rbsbits = t1xxp_rbsbits; + wc->span.maint = t1xxp_maint; + wc->span.open = t1xxp_open; + wc->span.close = t1xxp_close; + if (wc->spantype == TYPE_E1) + wc->span.channels = 31; + else + wc->span.channels = 24; + wc->span.chans = wc->chans; + wc->span.flags = ZT_FLAG_RBS; + wc->span.linecompat = ZT_CONFIG_AMI | ZT_CONFIG_B8ZS | ZT_CONFIG_D4 | ZT_CONFIG_ESF; + wc->span.ioctl = t1xxp_ioctl; + wc->span.pvt = wc; + if (wc->spantype == TYPE_E1) + wc->span.deflaw = ZT_LAW_ALAW; + else + wc->span.deflaw = ZT_LAW_MULAW; + init_waitqueue_head(&wc->span.maintq); + for (x=0;xspan.channels;x++) { + sprintf(wc->chans[x].name, "WCT1/%d/%d", wc->num, x + 1); + wc->chans[x].sigcap = ZT_SIG_EM | ZT_SIG_CLEAR | ZT_SIG_EM_E1 | + ZT_SIG_FXSLS | ZT_SIG_FXSGS | + ZT_SIG_FXSKS | ZT_SIG_FXOLS | + ZT_SIG_FXOGS | ZT_SIG_FXOKS | ZT_SIG_CAS | ZT_SIG_SF; + wc->chans[x].pvt = wc; + wc->chans[x].chanpos = x + 1; + } + if (zt_register(&wc->span, 0)) { + printk("Unable to register span with zaptel\n"); + return -1; + } + return 0; +} + +static inline void __handle_leds(struct t1 *wc) +{ + int oldreg; + + if (wc->span.alarms & (ZT_ALARM_RED | ZT_ALARM_BLUE)) { + /* Red/Blue alarm */ + wc->blinktimer++; +#ifdef FANCY_ALARM + if (wc->blinktimer == (altab[wc->alarmpos] >> 1)) { + wc->ledtestreg = (wc->ledtestreg | BIT_LED1) & ~BIT_LED0; + __control_set_reg(wc, WC_LEDTEST, wc->ledtestreg); + } + if (wc->blinktimer >= 0xf) { + wc->ledtestreg = wc->ledtestreg & ~(BIT_LED0 | BIT_LED1); + __control_set_reg(wc, WC_LEDTEST, wc->ledtestreg); + wc->blinktimer = -1; + wc->alarmpos++; + if (wc->alarmpos >= (sizeof(altab) / sizeof(altab[0]))) + wc->alarmpos = 0; + } +#else + if (wc->blinktimer == 160) { + wc->ledtestreg = (wc->ledtestreg | BIT_LED1) & ~BIT_LED0; + __control_set_reg(wc, WC_LEDTEST, wc->ledtestreg); + } else if (wc->blinktimer == 480) { + wc->ledtestreg = wc->ledtestreg & ~(BIT_LED0 | BIT_LED1); + __control_set_reg(wc, WC_LEDTEST, wc->ledtestreg); + wc->blinktimer = 0; + } +#endif + } else if (wc->span.alarms & ZT_ALARM_YELLOW) { + /* Yellow Alarm */ + if (!(wc->blinktimer % 2)) + wc->ledtestreg = (wc->ledtestreg | BIT_LED1) & ~BIT_LED0; + else + wc->ledtestreg = (wc->ledtestreg | BIT_LED0) & ~BIT_LED1; + __control_set_reg(wc, WC_LEDTEST, wc->ledtestreg); + } else { + /* No Alarm */ + oldreg = wc->ledtestreg; + if (wc->span.maintstat != ZT_MAINT_NONE) + wc->ledtestreg |= BIT_TEST; + else + wc->ledtestreg &= ~BIT_TEST; + if (wc->span.flags & ZT_FLAG_RUNNING) + wc->ledtestreg = (wc->ledtestreg | BIT_LED0) & ~BIT_LED1; + else + wc->ledtestreg = wc->ledtestreg & ~(BIT_LED0 | BIT_LED1); + if (oldreg != wc->ledtestreg) + __control_set_reg(wc, WC_LEDTEST, wc->ledtestreg); + } +} + +static void t1xxp_transmitprep(struct t1 *wc, int ints) +{ + volatile unsigned char *txbuf; + int x,y; + int pos; + if (ints & 0x04 /* 0x01 */) { + /* We just finished sending the first buffer, start filling it + now */ + txbuf = wc->writechunk; + } else { + /* Just finished sending second buffer, fill it now */ + txbuf = wc->writechunk + 32 * ZT_CHUNKSIZE; + } + zt_transmit(&wc->span); + for (x=0;xoffset;x++) + txbuf[x] = wc->tempo[x]; + for (y=0;yspan.channels;x++) { + pos = y * 32 + wc->chanmap[x] + wc->offset; + /* Put channel number as outgoing data */ + if (pos < 32 * ZT_CHUNKSIZE) + txbuf[pos] = wc->chans[x].writechunk[y]; + else + wc->tempo[pos - 32 * ZT_CHUNKSIZE] = wc->chans[x].writechunk[y]; + } + } +} + +static void t1xxp_receiveprep(struct t1 *wc, int ints) +{ + volatile unsigned char *rxbuf; + volatile unsigned int *canary; + int x; + int y; + unsigned int oldcan; + if (ints & 0x04) { + /* Just received first buffer */ + rxbuf = wc->readchunk; + canary = (unsigned int *)(wc->readchunk + ZT_CHUNKSIZE * 64 - 4); + } else { + rxbuf = wc->readchunk + ZT_CHUNKSIZE * 32; + canary = (unsigned int *)(wc->readchunk + ZT_CHUNKSIZE * 32 - 4); + } + oldcan = *canary; + if (((oldcan & 0xffff0000) >> 16) != CANARY) { + /* Check top part */ + if (debug) printk("Expecting top %04x, got %04x\n", CANARY, (oldcan & 0xffff0000) >> 16); + wc->span.irqmisses++; + } else if ((oldcan & 0xffff) != ((wc->canary - 1) & 0xffff)) { + if (debug) printk("Expecting bottom %d, got %d\n", wc->canary - 1, oldcan & 0xffff); + wc->span.irqmisses++; + } + for (y=0;yspan.channels;x++) { + /* XXX Optimize, remove * and + XXX */ + /* Must map received channels into appropriate data */ + wc->chans[x].readchunk[y] = + rxbuf[32 * y + ((wc->chanmap[x] + WC_OFFSET + wc->offset) & 0x1f)]; + } + if (wc->spantype != TYPE_E1) { + for (x=3;x<32;x+=4) { + if (rxbuf[32 * y + ((x + WC_OFFSET) & 0x1f)] == 0x7f) { + if (wc->offset != (x-3)) { + /* Resync */ + control_set_reg(wc, WC_CLOCK, 0x06 | wc->sync | clockextra); + wc->clocktimeout = 100; +#if 1 + if (debug) printk("T1: Lost our place, resyncing\n"); +#endif + } + } + } + } else { + if (!wc->clocktimeout && !wc->span.alarms) { + if ((rxbuf[32 * y + ((3 + WC_OFFSET + wc->offset) & 0x1f)] & 0x7f) != 0x1b) { + if (wc->miss) { + if (debug) printk("Double miss (%d, %d)...\n", wc->misslast, rxbuf[32 * y + ((3 + WC_OFFSET + wc->offset) & 0x1f)]); + control_set_reg(wc, WC_CLOCK, 0x06 | wc->sync | clockextra); + wc->clocktimeout = 100; + } else { + wc->miss = 1; + wc->misslast = rxbuf[32 * y + ((3 + WC_OFFSET + wc->offset) & 0x1f)]; + } + } else { + wc->miss = 0; + } + } else { + wc->miss = 0; + } + } + } + /* Store the next canary */ + canary = (unsigned int *)(rxbuf + ZT_CHUNKSIZE * 32 - 4); + *canary = (wc->canary++) | (CANARY << 16); + for (x=0;xspan.channels;x++) { + zt_ec_chunk(&wc->chans[x], wc->chans[x].readchunk, + wc->ec_chunk2[x]); + memcpy(wc->ec_chunk2[x],wc->ec_chunk1[x],ZT_CHUNKSIZE); + memcpy(wc->ec_chunk1[x],wc->chans[x].writechunk,ZT_CHUNKSIZE); + } + zt_receive(&wc->span); +} + +static void __t1_check_alarms(struct t1 *wc) +{ + unsigned char c,d; + int alarms; + int x,j; + + if (!(wc->span.flags & ZT_FLAG_RUNNING)) + return; + + c = __t1_framer_in(wc, 0x4c); + if (wc->spanflags & FLAG_FALC12) + d = __t1_framer_in(wc, 0x4f); + else + d = __t1_framer_in(wc, 0x4d); + + /* Assume no alarms */ + alarms = 0; + + /* And consider only carrier alarms */ + wc->span.alarms &= (ZT_ALARM_RED | ZT_ALARM_BLUE | ZT_ALARM_NOTOPEN); + + if (wc->spantype == TYPE_E1) { + if (c & 0x04) { + /* No multiframe found, force RAI high after 400ms only if + we haven't found a multiframe since last loss + of frame */ + if (!(wc->spanflags & FLAG_NMF)) { + __t1_framer_out(wc, 0x20, 0x9f | 0x20); /* LIM0: Force RAI High */ + wc->spanflags |= FLAG_NMF; + printk("NMF workaround on!\n"); + } + __t1_framer_out(wc, 0x1e, 0xc3); /* Reset to CRC4 mode */ + __t1_framer_out(wc, 0x1c, 0xf2); /* Force Resync */ + __t1_framer_out(wc, 0x1c, 0xf0); /* Force Resync */ + } else if (!(c & 0x02)) { + if ((wc->spanflags & FLAG_NMF)) { + __t1_framer_out(wc, 0x20, 0x9f); /* LIM0: Clear forced RAI */ + wc->spanflags &= ~FLAG_NMF; + printk("NMF workaround off!\n"); + } + } + } else { + /* Detect loopup code if we're not sending one */ + if ((!wc->span.mainttimer) && (d & 0x08)) { + /* Loop-up code detected */ + if ((wc->loopupcnt++ > 80) && (wc->span.maintstat != ZT_MAINT_REMOTELOOP)) { + __t1_framer_out(wc, 0x36, 0x08); /* LIM0: Disable any local loop */ + __t1_framer_out(wc, 0x37, 0xf6 ); /* LIM1: Enable remote loop */ + wc->span.maintstat = ZT_MAINT_REMOTELOOP; + } + } else + wc->loopupcnt = 0; + /* Same for loopdown code */ + if ((!wc->span.mainttimer) && (d & 0x10)) { + /* Loop-down code detected */ + if ((wc->loopdowncnt++ > 80) && (wc->span.maintstat == ZT_MAINT_REMOTELOOP)) { + __t1_framer_out(wc, 0x36, 0x08); /* LIM0: Disable any local loop */ + __t1_framer_out(wc, 0x37, 0xf0 ); /* LIM1: Disable remote loop */ + wc->span.maintstat = ZT_MAINT_NONE; + } + } else + wc->loopdowncnt = 0; + } + + if (wc->span.lineconfig & ZT_CONFIG_NOTOPEN) { + for (x=0,j=0;x < wc->span.channels;x++) + if ((wc->span.chans[x].flags & ZT_FLAG_OPEN) || + (wc->span.chans[x].flags & ZT_FLAG_NETDEV)) + j++; + if (!j) + alarms |= ZT_ALARM_NOTOPEN; + } + + if (c & 0xa0) { + if (wc->alarmcount >= alarmdebounce) + alarms |= ZT_ALARM_RED; + else + wc->alarmcount++; + } else + wc->alarmcount = 0; + if (c & 0x4) + alarms |= ZT_ALARM_BLUE; + + if (((!wc->span.alarms) && alarms) || + (wc->span.alarms && (!alarms))) + wc->checktiming = 1; + + /* Keep track of recovering */ + if ((!alarms) && wc->span.alarms) + wc->alarmtimer = ZT_ALARMSETTLE_TIME; + if (wc->alarmtimer) + alarms |= ZT_ALARM_RECOVER; + + /* If receiving alarms, go into Yellow alarm state */ + if (alarms && !(wc->spanflags & FLAG_SENDINGYELLOW)) { + unsigned char fmr4; +#if 1 + printk("wcte1xxp: Setting yellow alarm\n"); +#endif + /* We manually do yellow alarm to handle RECOVER and NOTOPEN, otherwise it's auto anyway */ + fmr4 = __t1_framer_in(wc, 0x20); + __t1_framer_out(wc, 0x20, fmr4 | 0x20); + wc->spanflags |= FLAG_SENDINGYELLOW; + } else if ((!alarms) && (wc->spanflags & FLAG_SENDINGYELLOW)) { + unsigned char fmr4; +#if 1 + printk("wcte1xxp: Clearing yellow alarm\n"); +#endif + /* We manually do yellow alarm to handle RECOVER */ + fmr4 = __t1_framer_in(wc, 0x20); + __t1_framer_out(wc, 0x20, fmr4 & ~0x20); + wc->spanflags &= ~FLAG_SENDINGYELLOW; + } + + /* Re-check the timing source when we enter/leave alarm, not withstanding + yellow alarm */ + if (c & 0x10) + alarms |= ZT_ALARM_YELLOW; + if (wc->span.mainttimer || wc->span.maintstat) + alarms |= ZT_ALARM_LOOPBACK; + wc->span.alarms = alarms; + zt_alarm_notify(&wc->span); +} + + +static void __t1_do_counters(struct t1 *wc) +{ + if (wc->alarmtimer) { + if (!--wc->alarmtimer) { + wc->span.alarms &= ~(ZT_ALARM_RECOVER); + zt_alarm_notify(&wc->span); + } + } +} + +#ifdef LINUX26 +static irqreturn_t t1xxp_interrupt(int irq, void *dev_id, struct pt_regs *regs) +#else +static void t1xxp_interrupt(int irq, void *dev_id, struct pt_regs *regs) +#endif +{ + struct t1 *wc = dev_id; + unsigned char ints; + unsigned long flags; + int x; + + ints = inb(wc->ioaddr + WC_INTSTAT); + outb(ints, wc->ioaddr + WC_INTSTAT); + + if (!ints) +#ifdef LINUX26 + return IRQ_NONE; +#else + return; +#endif + + if (!wc->intcount) { + if (debug) printk("Got interrupt: 0x%04x\n", ints); + } + wc->intcount++; + + if (wc->clocktimeout && !--wc->clocktimeout) + control_set_reg(wc, WC_CLOCK, 0x04 | wc->sync | clockextra); + + if (ints & 0x0f) { + t1xxp_receiveprep(wc, ints); + t1xxp_transmitprep(wc, ints); + } + spin_lock_irqsave(&wc->lock, flags); + +#if 1 + __handle_leds(wc); +#endif + + /* Count down timers */ + __t1_do_counters(wc); + + /* Do some things that we don't have to do very often */ + x = wc->intcount & 15 /* 63 */; + switch(x) { + case 0: + case 1: + break; + case 2: + __t1_check_sigbits(wc); + break; + case 4: + /* Check alarms 1/4 as frequently */ + if (!(wc->intcount & 0x30)) + __t1_check_alarms(wc); + break; + } + + spin_unlock_irqrestore(&wc->lock, flags); + + if (ints & 0x10) + printk("PCI Master abort\n"); + + if (ints & 0x20) + printk("PCI Target abort\n"); + +#ifdef LINUX26 + return IRQ_RETVAL(1); +#endif +} + +static int t1xxp_hardware_init(struct t1 *wc) +{ + unsigned int falcver; + unsigned int x; + /* Hardware PCI stuff */ + /* Reset chip and registers */ + outb(DELAY | 0x0e, wc->ioaddr + WC_CNTL); + /* Set all outputs to 0 */ + outb(0x00, wc->ioaddr + WC_AUXD); + /* Set all to outputs except AUX1 (TDO). */ + outb(0xfd, wc->ioaddr + WC_AUXC); + /* Configure the serial port: double clock, 20ns width, no inversion, + MSB first */ + outb(0xc8, wc->ioaddr + WC_SERC); + + /* Internally delay FSC by one */ + outb(0x01, wc->ioaddr + WC_FSCDELAY); + + /* Back to normal, with automatic DMA wrap around */ + outb(DELAY | 0x01, wc->ioaddr + WC_CNTL); + + /* Make sure serial port and DMA are out of reset */ + outb(inb(wc->ioaddr + WC_CNTL) & 0xf9, WC_CNTL); + + /* Setup DMA Addresses */ + /* Start at writedma */ + outl(wc->writedma, wc->ioaddr + WC_DMAWS); /* Write start */ + /* First frame */ + outl(wc->writedma + ZT_CHUNKSIZE * 32 - 4, wc->ioaddr + WC_DMAWI); /* Middle (interrupt) */ + /* Second frame */ + outl(wc->writedma + ZT_CHUNKSIZE * 32 * 2 - 4, wc->ioaddr + WC_DMAWE); /* End */ + + outl(wc->readdma, wc->ioaddr + WC_DMARS); /* Read start */ + /* First frame */ + outl(wc->readdma + ZT_CHUNKSIZE * 32 - 4, wc->ioaddr + WC_DMARI); /* Middle (interrupt) */ + /* Second frame */ + outl(wc->readdma + ZT_CHUNKSIZE * 32 * 2 - 4, wc->ioaddr + WC_DMARE); /* End */ + + if (debug) printk("Setting up DMA (write/read = %08lx/%08lx)\n", (long)wc->writedma, (long)wc->readdma); + + if (t1e1override > -1) { + if (t1e1override) + wc->spantype = TYPE_E1; + else + wc->spantype = TYPE_T1; + } else { + if (control_get_reg(wc, WC_CLOCK) & 0x20) + wc->spantype = TYPE_T1; + else + wc->spantype = TYPE_E1; + } + + /* Check out the controller */ + if (debug); printk("Controller version: %02x\n", control_get_reg(wc, WC_VERSION)); + + + control_set_reg(wc, WC_LEDTEST, 0x00); + + if (wc->spantype == TYPE_E1) + wc->chanmap = chanmap_e1; + else + wc->chanmap = chanmap_t1; + /* Setup clock appropriately */ + control_set_reg(wc, WC_CLOCK, 0x06 | wc->sync | clockextra); + wc->clocktimeout = 100; + + /* Perform register test on FALC */ + for (x=0;x<256;x++) { + t1_framer_out(wc, 0x14, x); + if ((falcver = t1_framer_in(wc, 0x14)) != x) + printk("Wrote '%x' but read '%x'\n", x, falcver); + } + + t1_framer_out(wc, 0x4a, 0xaa); + falcver = t1_framer_in(wc ,0x4a); + printk("FALC version: %08x\n", falcver); + if (!falcver) + wc->spanflags |= FLAG_FALC12; + + + start_alarm(wc); + return 0; + +} + +static int __devinit t1xxp_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + int res; + struct t1 *wc; + unsigned int *canary; + + if (pci_enable_device(pdev)) { + res = -EIO; + } else { + wc = kmalloc(sizeof(struct t1), GFP_KERNEL); + if (wc) { + memset(wc, 0x0, sizeof(struct t1)); + spin_lock_init(&wc->lock); + wc->ioaddr = pci_resource_start(pdev, 0); + wc->dev = pdev; + wc->offset = 28; /* And you thought 42 was the answer */ + + wc->writechunk = + /* 32 channels, Double-buffer, Read/Write */ + (unsigned char *)pci_alloc_consistent(pdev, ZT_MAX_CHUNKSIZE * 32 * 2 * 2, &wc->writedma); + if (!wc->writechunk) { + printk("wcte11xp: Unable to allocate DMA-able memory\n"); + return -ENOMEM; + } + + /* Read is after the whole write piece (in bytes) */ + wc->readchunk = wc->writechunk + ZT_CHUNKSIZE * 32 * 2; + + /* Same thing... */ + wc->readdma = wc->writedma + ZT_CHUNKSIZE * 32 * 2; + + /* Initialize Write/Buffers to all blank data */ + memset((void *)wc->writechunk,0x00,ZT_MAX_CHUNKSIZE * 2 * 2 * 32); + /* Initialize canary */ + canary = (unsigned int *)(wc->readchunk + ZT_CHUNKSIZE * 64 - 4); + *canary = (CANARY << 16) | (0xffff); + + /* Enable bus mastering */ + pci_set_master(pdev); + + /* Keep track of which device we are */ + pci_set_drvdata(pdev, wc); + + if (request_irq(pdev->irq, t1xxp_interrupt, SA_INTERRUPT | SA_SHIRQ, "t1xxp", wc)) { + printk("t1xxp: Unable to request IRQ %d\n", pdev->irq); + kfree(wc); + return -EIO; + } + /* Initialize hardware */ + t1xxp_hardware_init(wc); + + /* We now know which version of card we have */ + wc->variety = "Digium Wildcard TE110P T1/E1"; + + /* Misc. software stuff */ + t1xxp_software_init(wc); + + printk("Found a Wildcard: %s\n", wc->variety); + res = 0; + } else + res = -ENOMEM; + } + return res; +} + +static void t1xxp_stop_stuff(struct t1 *wc) +{ + /* Kill clock */ + control_set_reg(wc, WC_CLOCK, 0); + + /* Turn off LED's */ + control_set_reg(wc, WC_LEDTEST, 0); + +} + +static void __devexit t1xxp_remove_one(struct pci_dev *pdev) +{ + struct t1 *wc = pci_get_drvdata(pdev); + if (wc) { + + /* Stop any DMA */ + __t1xxp_stop_dma(wc); + + /* In case hardware is still there */ + __t1xxp_disable_interrupts(wc); + + t1xxp_stop_stuff(wc); + + /* Immediately free resources */ + pci_free_consistent(pdev, ZT_MAX_CHUNKSIZE * 2 * 2 * 32 * 4, (void *)wc->writechunk, wc->writedma); + free_irq(pdev->irq, wc); + + /* Reset PCI chip and registers */ + outb(DELAY | 0x0e, wc->ioaddr + WC_CNTL); + + /* Release span, possibly delayed */ + if (!wc->usecount) + t1xxp_release(wc); + else + wc->dead = 1; + } +} + +static struct pci_device_id t1xxp_pci_tbl[] = { + { 0xe159, 0x0001, 0x79fe, PCI_ANY_ID, 0, 0, (unsigned long) "Digium Wildcard TE110P T1/E1 Board" }, + { 0 } +}; + +MODULE_DEVICE_TABLE(pci,t1xxp_pci_tbl); + +static struct pci_driver t1xxp_driver = { + name: "t1xxp", + probe: t1xxp_init_one, +#ifdef LINUX26 + remove: __devexit_p(t1xxp_remove_one), +#else + remove: t1xxp_remove_one, +#endif + suspend: NULL, + resume: NULL, + id_table: t1xxp_pci_tbl, +}; + +static int __init t1xxp_init(void) +{ + int res; + res = pci_module_init(&t1xxp_driver); + if (res) + return -ENODEV; + return 0; +} + +static void __exit t1xxp_cleanup(void) +{ + pci_unregister_driver(&t1xxp_driver); +} + +MODULE_PARM(alarmdebounce, "i"); +MODULE_PARM(loopback, "i"); +MODULE_PARM(t1e1override, "i"); +MODULE_PARM(clockextra, "i"); +MODULE_PARM(debug, "i"); +MODULE_DESCRIPTION("Wildcard T100P/E100P Zaptel Driver"); +MODULE_AUTHOR("Mark Spencer "); +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +module_init(t1xxp_init); +module_exit(t1xxp_cleanup); -- cgit v1.2.3