Path: blob/master/drivers/isdn/hardware/mISDN/w6692.c
15112 views
/*1* w6692.c mISDN driver for Winbond w6692 based cards2*3* Author Karsten Keil <[email protected]>4* based on the w6692 I4L driver from Petr Novak <[email protected]>5*6* Copyright 2009 by Karsten Keil <[email protected]>7*8* This program is free software; you can redistribute it and/or modify9* it under the terms of the GNU General Public License version 2 as10* published by the Free Software Foundation.11*12* This program is distributed in the hope that it will be useful,13* but WITHOUT ANY WARRANTY; without even the implied warranty of14* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the15* GNU General Public License for more details.16*17* You should have received a copy of the GNU General Public License18* along with this program; if not, write to the Free Software19* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.20*21*/2223#include <linux/module.h>24#include <linux/pci.h>25#include <linux/delay.h>26#include <linux/mISDNhw.h>27#include <linux/slab.h>28#include "w6692.h"2930#define W6692_REV "2.0"3132#define DBUSY_TIMER_VALUE 803334enum {35W6692_ASUS,36W6692_WINBOND,37W6692_USR38};3940/* private data in the PCI devices list */41struct w6692map {42u_int subtype;43char *name;44};4546static const struct w6692map w6692_map[] =47{48{W6692_ASUS, "Dynalink/AsusCom IS64PH"},49{W6692_WINBOND, "Winbond W6692"},50{W6692_USR, "USR W6692"}51};5253#ifndef PCI_VENDOR_ID_USR54#define PCI_VENDOR_ID_USR 0x16ec55#define PCI_DEVICE_ID_USR_6692 0x340956#endif5758struct w6692_ch {59struct bchannel bch;60u32 addr;61struct timer_list timer;62u8 b_mode;63};6465struct w6692_hw {66struct list_head list;67struct pci_dev *pdev;68char name[MISDN_MAX_IDLEN];69u32 irq;70u32 irqcnt;71u32 addr;72u32 fmask; /* feature mask - bit set per card nr */73int subtype;74spinlock_t lock; /* hw lock */75u8 imask;76u8 pctl;77u8 xaddr;78u8 xdata;79u8 state;80struct w6692_ch bc[2];81struct dchannel dch;82char log[64];83};8485static LIST_HEAD(Cards);86static DEFINE_RWLOCK(card_lock); /* protect Cards */8788static int w6692_cnt;89static int debug;90static u32 led;91static u32 pots;9293static void94_set_debug(struct w6692_hw *card)95{96card->dch.debug = debug;97card->bc[0].bch.debug = debug;98card->bc[1].bch.debug = debug;99}100101static int102set_debug(const char *val, struct kernel_param *kp)103{104int ret;105struct w6692_hw *card;106107ret = param_set_uint(val, kp);108if (!ret) {109read_lock(&card_lock);110list_for_each_entry(card, &Cards, list)111_set_debug(card);112read_unlock(&card_lock);113}114return ret;115}116117MODULE_AUTHOR("Karsten Keil");118MODULE_LICENSE("GPL v2");119MODULE_VERSION(W6692_REV);120module_param_call(debug, set_debug, param_get_uint, &debug, S_IRUGO | S_IWUSR);121MODULE_PARM_DESC(debug, "W6692 debug mask");122module_param(led, uint, S_IRUGO | S_IWUSR);123MODULE_PARM_DESC(led, "W6692 LED support bitmask (one bit per card)");124module_param(pots, uint, S_IRUGO | S_IWUSR);125MODULE_PARM_DESC(pots, "W6692 POTS support bitmask (one bit per card)");126127static inline u8128ReadW6692(struct w6692_hw *card, u8 offset)129{130return inb(card->addr + offset);131}132133static inline void134WriteW6692(struct w6692_hw *card, u8 offset, u8 value)135{136outb(value, card->addr + offset);137}138139static inline u8140ReadW6692B(struct w6692_ch *bc, u8 offset)141{142return inb(bc->addr + offset);143}144145static inline void146WriteW6692B(struct w6692_ch *bc, u8 offset, u8 value)147{148outb(value, bc->addr + offset);149}150151static void152enable_hwirq(struct w6692_hw *card)153{154WriteW6692(card, W_IMASK, card->imask);155}156157static void158disable_hwirq(struct w6692_hw *card)159{160WriteW6692(card, W_IMASK, 0xff);161}162163static const char *W6692Ver[] = {"V00", "V01", "V10", "V11"};164165static void166W6692Version(struct w6692_hw *card)167{168int val;169170val = ReadW6692(card, W_D_RBCH);171pr_notice("%s: Winbond W6692 version: %s\n", card->name,172W6692Ver[(val >> 6) & 3]);173}174175static void176w6692_led_handler(struct w6692_hw *card, int on)177{178if ((!(card->fmask & led)) || card->subtype == W6692_USR)179return;180if (on) {181card->xdata &= 0xfb; /* LED ON */182WriteW6692(card, W_XDATA, card->xdata);183} else {184card->xdata |= 0x04; /* LED OFF */185WriteW6692(card, W_XDATA, card->xdata);186}187}188189static void190ph_command(struct w6692_hw *card, u8 cmd)191{192pr_debug("%s: ph_command %x\n", card->name, cmd);193WriteW6692(card, W_CIX, cmd);194}195196static void197W6692_new_ph(struct w6692_hw *card)198{199if (card->state == W_L1CMD_RST)200ph_command(card, W_L1CMD_DRC);201schedule_event(&card->dch, FLG_PHCHANGE);202}203204static void205W6692_ph_bh(struct dchannel *dch)206{207struct w6692_hw *card = dch->hw;208209switch (card->state) {210case W_L1CMD_RST:211dch->state = 0;212l1_event(dch->l1, HW_RESET_IND);213break;214case W_L1IND_CD:215dch->state = 3;216l1_event(dch->l1, HW_DEACT_CNF);217break;218case W_L1IND_DRD:219dch->state = 3;220l1_event(dch->l1, HW_DEACT_IND);221break;222case W_L1IND_CE:223dch->state = 4;224l1_event(dch->l1, HW_POWERUP_IND);225break;226case W_L1IND_LD:227if (dch->state <= 5) {228dch->state = 5;229l1_event(dch->l1, ANYSIGNAL);230} else {231dch->state = 8;232l1_event(dch->l1, LOSTFRAMING);233}234break;235case W_L1IND_ARD:236dch->state = 6;237l1_event(dch->l1, INFO2);238break;239case W_L1IND_AI8:240dch->state = 7;241l1_event(dch->l1, INFO4_P8);242break;243case W_L1IND_AI10:244dch->state = 7;245l1_event(dch->l1, INFO4_P10);246break;247default:248pr_debug("%s: TE unknown state %02x dch state %02x\n",249card->name, card->state, dch->state);250break;251}252pr_debug("%s: TE newstate %02x\n", card->name, dch->state);253}254255static void256W6692_empty_Dfifo(struct w6692_hw *card, int count)257{258struct dchannel *dch = &card->dch;259u8 *ptr;260261pr_debug("%s: empty_Dfifo %d\n", card->name, count);262if (!dch->rx_skb) {263dch->rx_skb = mI_alloc_skb(card->dch.maxlen, GFP_ATOMIC);264if (!dch->rx_skb) {265pr_info("%s: D receive out of memory\n", card->name);266WriteW6692(card, W_D_CMDR, W_D_CMDR_RACK);267return;268}269}270if ((dch->rx_skb->len + count) >= dch->maxlen) {271pr_debug("%s: empty_Dfifo overrun %d\n", card->name,272dch->rx_skb->len + count);273WriteW6692(card, W_D_CMDR, W_D_CMDR_RACK);274return;275}276ptr = skb_put(dch->rx_skb, count);277insb(card->addr + W_D_RFIFO, ptr, count);278WriteW6692(card, W_D_CMDR, W_D_CMDR_RACK);279if (debug & DEBUG_HW_DFIFO) {280snprintf(card->log, 63, "D-recv %s %d ",281card->name, count);282print_hex_dump_bytes(card->log, DUMP_PREFIX_OFFSET, ptr, count);283}284}285286static void287W6692_fill_Dfifo(struct w6692_hw *card)288{289struct dchannel *dch = &card->dch;290int count;291u8 *ptr;292u8 cmd = W_D_CMDR_XMS;293294pr_debug("%s: fill_Dfifo\n", card->name);295if (!dch->tx_skb)296return;297count = dch->tx_skb->len - dch->tx_idx;298if (count <= 0)299return;300if (count > W_D_FIFO_THRESH)301count = W_D_FIFO_THRESH;302else303cmd |= W_D_CMDR_XME;304ptr = dch->tx_skb->data + dch->tx_idx;305dch->tx_idx += count;306outsb(card->addr + W_D_XFIFO, ptr, count);307WriteW6692(card, W_D_CMDR, cmd);308if (test_and_set_bit(FLG_BUSY_TIMER, &dch->Flags)) {309pr_debug("%s: fill_Dfifo dbusytimer running\n", card->name);310del_timer(&dch->timer);311}312init_timer(&dch->timer);313dch->timer.expires = jiffies + ((DBUSY_TIMER_VALUE * HZ)/1000);314add_timer(&dch->timer);315if (debug & DEBUG_HW_DFIFO) {316snprintf(card->log, 63, "D-send %s %d ",317card->name, count);318print_hex_dump_bytes(card->log, DUMP_PREFIX_OFFSET, ptr, count);319}320}321322static void323d_retransmit(struct w6692_hw *card)324{325struct dchannel *dch = &card->dch;326327if (test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags))328del_timer(&dch->timer);329#ifdef FIXME330if (test_and_clear_bit(FLG_L1_BUSY, &dch->Flags))331dchannel_sched_event(dch, D_CLEARBUSY);332#endif333if (test_bit(FLG_TX_BUSY, &dch->Flags)) {334/* Restart frame */335dch->tx_idx = 0;336W6692_fill_Dfifo(card);337} else if (dch->tx_skb) { /* should not happen */338pr_info("%s: %s without TX_BUSY\n", card->name, __func__);339test_and_set_bit(FLG_TX_BUSY, &dch->Flags);340dch->tx_idx = 0;341W6692_fill_Dfifo(card);342} else {343pr_info("%s: XDU no TX_BUSY\n", card->name);344if (get_next_dframe(dch))345W6692_fill_Dfifo(card);346}347}348349static void350handle_rxD(struct w6692_hw *card) {351u8 stat;352int count;353354stat = ReadW6692(card, W_D_RSTA);355if (stat & (W_D_RSTA_RDOV | W_D_RSTA_CRCE | W_D_RSTA_RMB)) {356if (stat & W_D_RSTA_RDOV) {357pr_debug("%s: D-channel RDOV\n", card->name);358#ifdef ERROR_STATISTIC359card->dch.err_rx++;360#endif361}362if (stat & W_D_RSTA_CRCE) {363pr_debug("%s: D-channel CRC error\n", card->name);364#ifdef ERROR_STATISTIC365card->dch.err_crc++;366#endif367}368if (stat & W_D_RSTA_RMB) {369pr_debug("%s: D-channel ABORT\n", card->name);370#ifdef ERROR_STATISTIC371card->dch.err_rx++;372#endif373}374if (card->dch.rx_skb)375dev_kfree_skb(card->dch.rx_skb);376card->dch.rx_skb = NULL;377WriteW6692(card, W_D_CMDR, W_D_CMDR_RACK | W_D_CMDR_RRST);378} else {379count = ReadW6692(card, W_D_RBCL) & (W_D_FIFO_THRESH - 1);380if (count == 0)381count = W_D_FIFO_THRESH;382W6692_empty_Dfifo(card, count);383recv_Dchannel(&card->dch);384}385}386387static void388handle_txD(struct w6692_hw *card) {389if (test_and_clear_bit(FLG_BUSY_TIMER, &card->dch.Flags))390del_timer(&card->dch.timer);391if (card->dch.tx_skb && card->dch.tx_idx < card->dch.tx_skb->len) {392W6692_fill_Dfifo(card);393} else {394if (card->dch.tx_skb)395dev_kfree_skb(card->dch.tx_skb);396if (get_next_dframe(&card->dch))397W6692_fill_Dfifo(card);398}399}400401static void402handle_statusD(struct w6692_hw *card)403{404struct dchannel *dch = &card->dch;405u8 exval, v1, cir;406407exval = ReadW6692(card, W_D_EXIR);408409pr_debug("%s: D_EXIR %02x\n", card->name, exval);410if (exval & (W_D_EXI_XDUN | W_D_EXI_XCOL)) {411/* Transmit underrun/collision */412pr_debug("%s: D-channel underrun/collision\n", card->name);413#ifdef ERROR_STATISTIC414dch->err_tx++;415#endif416d_retransmit(card);417}418if (exval & W_D_EXI_RDOV) { /* RDOV */419pr_debug("%s: D-channel RDOV\n", card->name);420WriteW6692(card, W_D_CMDR, W_D_CMDR_RRST);421}422if (exval & W_D_EXI_TIN2) /* TIN2 - never */423pr_debug("%s: spurious TIN2 interrupt\n", card->name);424if (exval & W_D_EXI_MOC) { /* MOC - not supported */425v1 = ReadW6692(card, W_MOSR);426pr_debug("%s: spurious MOC interrupt MOSR %02x\n",427card->name, v1);428}429if (exval & W_D_EXI_ISC) { /* ISC - Level1 change */430cir = ReadW6692(card, W_CIR);431pr_debug("%s: ISC CIR %02X\n", card->name, cir);432if (cir & W_CIR_ICC) {433v1 = cir & W_CIR_COD_MASK;434pr_debug("%s: ph_state_change %x -> %x\n", card->name,435dch->state, v1);436card->state = v1;437if (card->fmask & led) {438switch (v1) {439case W_L1IND_AI8:440case W_L1IND_AI10:441w6692_led_handler(card, 1);442break;443default:444w6692_led_handler(card, 0);445break;446}447}448W6692_new_ph(card);449}450if (cir & W_CIR_SCC) {451v1 = ReadW6692(card, W_SQR);452pr_debug("%s: SCC SQR %02X\n", card->name, v1);453}454}455if (exval & W_D_EXI_WEXP)456pr_debug("%s: spurious WEXP interrupt!\n", card->name);457if (exval & W_D_EXI_TEXP)458pr_debug("%s: spurious TEXP interrupt!\n", card->name);459}460461static void462W6692_empty_Bfifo(struct w6692_ch *wch, int count)463{464struct w6692_hw *card = wch->bch.hw;465u8 *ptr;466467pr_debug("%s: empty_Bfifo %d\n", card->name, count);468if (unlikely(wch->bch.state == ISDN_P_NONE)) {469pr_debug("%s: empty_Bfifo ISDN_P_NONE\n", card->name);470WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RACT);471if (wch->bch.rx_skb)472skb_trim(wch->bch.rx_skb, 0);473return;474}475if (!wch->bch.rx_skb) {476wch->bch.rx_skb = mI_alloc_skb(wch->bch.maxlen, GFP_ATOMIC);477if (unlikely(!wch->bch.rx_skb)) {478pr_info("%s: B receive out of memory\n", card->name);479WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RACK |480W_B_CMDR_RACT);481return;482}483}484if (wch->bch.rx_skb->len + count > wch->bch.maxlen) {485pr_debug("%s: empty_Bfifo incoming packet too large\n",486card->name);487WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RACT);488skb_trim(wch->bch.rx_skb, 0);489return;490}491ptr = skb_put(wch->bch.rx_skb, count);492insb(wch->addr + W_B_RFIFO, ptr, count);493WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RACT);494if (debug & DEBUG_HW_DFIFO) {495snprintf(card->log, 63, "B%1d-recv %s %d ",496wch->bch.nr, card->name, count);497print_hex_dump_bytes(card->log, DUMP_PREFIX_OFFSET, ptr, count);498}499}500501static void502W6692_fill_Bfifo(struct w6692_ch *wch)503{504struct w6692_hw *card = wch->bch.hw;505int count;506u8 *ptr, cmd = W_B_CMDR_RACT | W_B_CMDR_XMS;507508pr_debug("%s: fill Bfifo\n", card->name);509if (!wch->bch.tx_skb)510return;511count = wch->bch.tx_skb->len - wch->bch.tx_idx;512if (count <= 0)513return;514ptr = wch->bch.tx_skb->data + wch->bch.tx_idx;515if (count > W_B_FIFO_THRESH)516count = W_B_FIFO_THRESH;517else if (test_bit(FLG_HDLC, &wch->bch.Flags))518cmd |= W_B_CMDR_XME;519520pr_debug("%s: fill Bfifo%d/%d\n", card->name,521count, wch->bch.tx_idx);522wch->bch.tx_idx += count;523outsb(wch->addr + W_B_XFIFO, ptr, count);524WriteW6692B(wch, W_B_CMDR, cmd);525if (debug & DEBUG_HW_DFIFO) {526snprintf(card->log, 63, "B%1d-send %s %d ",527wch->bch.nr, card->name, count);528print_hex_dump_bytes(card->log, DUMP_PREFIX_OFFSET, ptr, count);529}530}531532#if 0533static int534setvolume(struct w6692_ch *wch, int mic, struct sk_buff *skb)535{536struct w6692_hw *card = wch->bch.hw;537u16 *vol = (u16 *)skb->data;538u8 val;539540if ((!(card->fmask & pots)) ||541!test_bit(FLG_TRANSPARENT, &wch->bch.Flags))542return -ENODEV;543if (skb->len < 2)544return -EINVAL;545if (*vol > 7)546return -EINVAL;547val = *vol & 7;548val = 7 - val;549if (mic) {550val <<= 3;551card->xaddr &= 0xc7;552} else {553card->xaddr &= 0xf8;554}555card->xaddr |= val;556WriteW6692(card, W_XADDR, card->xaddr);557return 0;558}559560static int561enable_pots(struct w6692_ch *wch)562{563struct w6692_hw *card = wch->bch.hw;564565if ((!(card->fmask & pots)) ||566!test_bit(FLG_TRANSPARENT, &wch->bch.Flags))567return -ENODEV;568wch->b_mode |= W_B_MODE_EPCM | W_B_MODE_BSW0;569WriteW6692B(wch, W_B_MODE, wch->b_mode);570WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_XRST);571card->pctl |= ((wch->bch.nr & 2) ? W_PCTL_PCX : 0);572WriteW6692(card, W_PCTL, card->pctl);573return 0;574}575#endif576577static int578disable_pots(struct w6692_ch *wch)579{580struct w6692_hw *card = wch->bch.hw;581582if (!(card->fmask & pots))583return -ENODEV;584wch->b_mode &= ~(W_B_MODE_EPCM | W_B_MODE_BSW0);585WriteW6692B(wch, W_B_MODE, wch->b_mode);586WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_RACT |587W_B_CMDR_XRST);588return 0;589}590591static int592w6692_mode(struct w6692_ch *wch, u32 pr)593{594struct w6692_hw *card;595596card = wch->bch.hw;597pr_debug("%s: B%d protocol %x-->%x\n", card->name,598wch->bch.nr, wch->bch.state, pr);599switch (pr) {600case ISDN_P_NONE:601if ((card->fmask & pots) && (wch->b_mode & W_B_MODE_EPCM))602disable_pots(wch);603wch->b_mode = 0;604mISDN_clear_bchannel(&wch->bch);605WriteW6692B(wch, W_B_MODE, wch->b_mode);606WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_XRST);607test_and_clear_bit(FLG_HDLC, &wch->bch.Flags);608test_and_clear_bit(FLG_TRANSPARENT, &wch->bch.Flags);609break;610case ISDN_P_B_RAW:611wch->b_mode = W_B_MODE_MMS;612WriteW6692B(wch, W_B_MODE, wch->b_mode);613WriteW6692B(wch, W_B_EXIM, 0);614WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_RACT |615W_B_CMDR_XRST);616test_and_set_bit(FLG_TRANSPARENT, &wch->bch.Flags);617break;618case ISDN_P_B_HDLC:619wch->b_mode = W_B_MODE_ITF;620WriteW6692B(wch, W_B_MODE, wch->b_mode);621WriteW6692B(wch, W_B_ADM1, 0xff);622WriteW6692B(wch, W_B_ADM2, 0xff);623WriteW6692B(wch, W_B_EXIM, 0);624WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_RACT |625W_B_CMDR_XRST);626test_and_set_bit(FLG_HDLC, &wch->bch.Flags);627break;628default:629pr_info("%s: protocol %x not known\n", card->name, pr);630return -ENOPROTOOPT;631}632wch->bch.state = pr;633return 0;634}635636static void637send_next(struct w6692_ch *wch)638{639if (wch->bch.tx_skb && wch->bch.tx_idx < wch->bch.tx_skb->len)640W6692_fill_Bfifo(wch);641else {642if (wch->bch.tx_skb) {643/* send confirm, on trans, free on hdlc. */644if (test_bit(FLG_TRANSPARENT, &wch->bch.Flags))645confirm_Bsend(&wch->bch);646dev_kfree_skb(wch->bch.tx_skb);647}648if (get_next_bframe(&wch->bch))649W6692_fill_Bfifo(wch);650}651}652653static void654W6692B_interrupt(struct w6692_hw *card, int ch)655{656struct w6692_ch *wch = &card->bc[ch];657int count;658u8 stat, star = 0;659660stat = ReadW6692B(wch, W_B_EXIR);661pr_debug("%s: B%d EXIR %02x\n", card->name, wch->bch.nr, stat);662if (stat & W_B_EXI_RME) {663star = ReadW6692B(wch, W_B_STAR);664if (star & (W_B_STAR_RDOV | W_B_STAR_CRCE | W_B_STAR_RMB)) {665if ((star & W_B_STAR_RDOV) &&666test_bit(FLG_ACTIVE, &wch->bch.Flags)) {667pr_debug("%s: B%d RDOV proto=%x\n", card->name,668wch->bch.nr, wch->bch.state);669#ifdef ERROR_STATISTIC670wch->bch.err_rdo++;671#endif672}673if (test_bit(FLG_HDLC, &wch->bch.Flags)) {674if (star & W_B_STAR_CRCE) {675pr_debug("%s: B%d CRC error\n",676card->name, wch->bch.nr);677#ifdef ERROR_STATISTIC678wch->bch.err_crc++;679#endif680}681if (star & W_B_STAR_RMB) {682pr_debug("%s: B%d message abort\n",683card->name, wch->bch.nr);684#ifdef ERROR_STATISTIC685wch->bch.err_inv++;686#endif687}688}689WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RACK |690W_B_CMDR_RRST | W_B_CMDR_RACT);691if (wch->bch.rx_skb)692skb_trim(wch->bch.rx_skb, 0);693} else {694count = ReadW6692B(wch, W_B_RBCL) &695(W_B_FIFO_THRESH - 1);696if (count == 0)697count = W_B_FIFO_THRESH;698W6692_empty_Bfifo(wch, count);699recv_Bchannel(&wch->bch, 0);700}701}702if (stat & W_B_EXI_RMR) {703if (!(stat & W_B_EXI_RME))704star = ReadW6692B(wch, W_B_STAR);705if (star & W_B_STAR_RDOV) {706pr_debug("%s: B%d RDOV proto=%x\n", card->name,707wch->bch.nr, wch->bch.state);708#ifdef ERROR_STATISTIC709wch->bch.err_rdo++;710#endif711WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RACK |712W_B_CMDR_RRST | W_B_CMDR_RACT);713} else {714W6692_empty_Bfifo(wch, W_B_FIFO_THRESH);715if (test_bit(FLG_TRANSPARENT, &wch->bch.Flags) &&716wch->bch.rx_skb && (wch->bch.rx_skb->len > 0))717recv_Bchannel(&wch->bch, 0);718}719}720if (stat & W_B_EXI_RDOV) {721/* only if it is not handled yet */722if (!(star & W_B_STAR_RDOV)) {723pr_debug("%s: B%d RDOV IRQ proto=%x\n", card->name,724wch->bch.nr, wch->bch.state);725#ifdef ERROR_STATISTIC726wch->bch.err_rdo++;727#endif728WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RACK |729W_B_CMDR_RRST | W_B_CMDR_RACT);730}731}732if (stat & W_B_EXI_XFR) {733if (!(stat & (W_B_EXI_RME | W_B_EXI_RMR))) {734star = ReadW6692B(wch, W_B_STAR);735pr_debug("%s: B%d star %02x\n", card->name,736wch->bch.nr, star);737}738if (star & W_B_STAR_XDOW) {739pr_debug("%s: B%d XDOW proto=%x\n", card->name,740wch->bch.nr, wch->bch.state);741#ifdef ERROR_STATISTIC742wch->bch.err_xdu++;743#endif744WriteW6692B(wch, W_B_CMDR, W_B_CMDR_XRST |745W_B_CMDR_RACT);746/* resend */747if (wch->bch.tx_skb) {748if (!test_bit(FLG_TRANSPARENT, &wch->bch.Flags))749wch->bch.tx_idx = 0;750}751}752send_next(wch);753if (stat & W_B_EXI_XDUN)754return; /* handle XDOW only once */755}756if (stat & W_B_EXI_XDUN) {757pr_debug("%s: B%d XDUN proto=%x\n", card->name,758wch->bch.nr, wch->bch.state);759#ifdef ERROR_STATISTIC760wch->bch.err_xdu++;761#endif762WriteW6692B(wch, W_B_CMDR, W_B_CMDR_XRST | W_B_CMDR_RACT);763/* resend */764if (wch->bch.tx_skb) {765if (!test_bit(FLG_TRANSPARENT, &wch->bch.Flags))766wch->bch.tx_idx = 0;767}768send_next(wch);769}770}771772static irqreturn_t773w6692_irq(int intno, void *dev_id)774{775struct w6692_hw *card = dev_id;776u8 ista;777778spin_lock(&card->lock);779ista = ReadW6692(card, W_ISTA);780if ((ista | card->imask) == card->imask) {781/* possible a shared IRQ reqest */782spin_unlock(&card->lock);783return IRQ_NONE;784}785card->irqcnt++;786pr_debug("%s: ista %02x\n", card->name, ista);787ista &= ~card->imask;788if (ista & W_INT_B1_EXI)789W6692B_interrupt(card, 0);790if (ista & W_INT_B2_EXI)791W6692B_interrupt(card, 1);792if (ista & W_INT_D_RME)793handle_rxD(card);794if (ista & W_INT_D_RMR)795W6692_empty_Dfifo(card, W_D_FIFO_THRESH);796if (ista & W_INT_D_XFR)797handle_txD(card);798if (ista & W_INT_D_EXI)799handle_statusD(card);800if (ista & (W_INT_XINT0 | W_INT_XINT1)) /* XINT0/1 - never */801pr_debug("%s: W6692 spurious XINT!\n", card->name);802/* End IRQ Handler */803spin_unlock(&card->lock);804return IRQ_HANDLED;805}806807static void808dbusy_timer_handler(struct dchannel *dch)809{810struct w6692_hw *card = dch->hw;811int rbch, star;812u_long flags;813814if (test_bit(FLG_BUSY_TIMER, &dch->Flags)) {815spin_lock_irqsave(&card->lock, flags);816rbch = ReadW6692(card, W_D_RBCH);817star = ReadW6692(card, W_D_STAR);818pr_debug("%s: D-Channel Busy RBCH %02x STAR %02x\n",819card->name, rbch, star);820if (star & W_D_STAR_XBZ) /* D-Channel Busy */821test_and_set_bit(FLG_L1_BUSY, &dch->Flags);822else {823/* discard frame; reset transceiver */824test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags);825if (dch->tx_idx)826dch->tx_idx = 0;827else828pr_info("%s: W6692 D-Channel Busy no tx_idx\n",829card->name);830/* Transmitter reset */831WriteW6692(card, W_D_CMDR, W_D_CMDR_XRST);832}833spin_unlock_irqrestore(&card->lock, flags);834}835}836837void initW6692(struct w6692_hw *card)838{839u8 val;840841card->dch.timer.function = (void *)dbusy_timer_handler;842card->dch.timer.data = (u_long)&card->dch;843init_timer(&card->dch.timer);844w6692_mode(&card->bc[0], ISDN_P_NONE);845w6692_mode(&card->bc[1], ISDN_P_NONE);846WriteW6692(card, W_D_CTL, 0x00);847disable_hwirq(card);848WriteW6692(card, W_D_SAM, 0xff);849WriteW6692(card, W_D_TAM, 0xff);850WriteW6692(card, W_D_MODE, W_D_MODE_RACT);851card->state = W_L1CMD_RST;852ph_command(card, W_L1CMD_RST);853ph_command(card, W_L1CMD_ECK);854/* enable all IRQ but extern */855card->imask = 0x18;856WriteW6692(card, W_D_EXIM, 0x00);857WriteW6692B(&card->bc[0], W_B_EXIM, 0);858WriteW6692B(&card->bc[1], W_B_EXIM, 0);859/* Reset D-chan receiver and transmitter */860WriteW6692(card, W_D_CMDR, W_D_CMDR_RRST | W_D_CMDR_XRST);861/* Reset B-chan receiver and transmitter */862WriteW6692B(&card->bc[0], W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_XRST);863WriteW6692B(&card->bc[1], W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_XRST);864/* enable peripheral */865if (card->subtype == W6692_USR) {866/* seems that USR implemented some power control features867* Pin 79 is connected to the oscilator circuit so we868* have to handle it here869*/870card->pctl = 0x80;871card->xdata = 0;872WriteW6692(card, W_PCTL, card->pctl);873WriteW6692(card, W_XDATA, card->xdata);874} else {875card->pctl = W_PCTL_OE5 | W_PCTL_OE4 | W_PCTL_OE2 |876W_PCTL_OE1 | W_PCTL_OE0;877card->xaddr = 0x00;/* all sw off */878if (card->fmask & pots)879card->xdata |= 0x06; /* POWER UP/ LED OFF / ALAW */880if (card->fmask & led)881card->xdata |= 0x04; /* LED OFF */882if ((card->fmask & pots) || (card->fmask & led)) {883WriteW6692(card, W_PCTL, card->pctl);884WriteW6692(card, W_XADDR, card->xaddr);885WriteW6692(card, W_XDATA, card->xdata);886val = ReadW6692(card, W_XADDR);887if (debug & DEBUG_HW)888pr_notice("%s: W_XADDR=%02x\n",889card->name, val);890}891}892}893894static void895reset_w6692(struct w6692_hw *card)896{897WriteW6692(card, W_D_CTL, W_D_CTL_SRST);898mdelay(10);899WriteW6692(card, W_D_CTL, 0);900}901902static int903init_card(struct w6692_hw *card)904{905int cnt = 3;906u_long flags;907908spin_lock_irqsave(&card->lock, flags);909disable_hwirq(card);910spin_unlock_irqrestore(&card->lock, flags);911if (request_irq(card->irq, w6692_irq, IRQF_SHARED, card->name, card)) {912pr_info("%s: couldn't get interrupt %d\n", card->name,913card->irq);914return -EIO;915}916while (cnt--) {917spin_lock_irqsave(&card->lock, flags);918initW6692(card);919enable_hwirq(card);920spin_unlock_irqrestore(&card->lock, flags);921/* Timeout 10ms */922msleep_interruptible(10);923if (debug & DEBUG_HW)924pr_notice("%s: IRQ %d count %d\n", card->name,925card->irq, card->irqcnt);926if (!card->irqcnt) {927pr_info("%s: IRQ(%d) getting no IRQs during init %d\n",928card->name, card->irq, 3 - cnt);929reset_w6692(card);930} else931return 0;932}933free_irq(card->irq, card);934return -EIO;935}936937static int938w6692_l2l1B(struct mISDNchannel *ch, struct sk_buff *skb)939{940struct bchannel *bch = container_of(ch, struct bchannel, ch);941struct w6692_ch *bc = container_of(bch, struct w6692_ch, bch);942struct w6692_hw *card = bch->hw;943int ret = -EINVAL;944struct mISDNhead *hh = mISDN_HEAD_P(skb);945u32 id;946u_long flags;947948switch (hh->prim) {949case PH_DATA_REQ:950spin_lock_irqsave(&card->lock, flags);951ret = bchannel_senddata(bch, skb);952if (ret > 0) { /* direct TX */953id = hh->id; /* skb can be freed */954ret = 0;955W6692_fill_Bfifo(bc);956spin_unlock_irqrestore(&card->lock, flags);957if (!test_bit(FLG_TRANSPARENT, &bch->Flags))958queue_ch_frame(ch, PH_DATA_CNF, id, NULL);959} else960spin_unlock_irqrestore(&card->lock, flags);961return ret;962case PH_ACTIVATE_REQ:963spin_lock_irqsave(&card->lock, flags);964if (!test_and_set_bit(FLG_ACTIVE, &bch->Flags))965ret = w6692_mode(bc, ch->protocol);966else967ret = 0;968spin_unlock_irqrestore(&card->lock, flags);969if (!ret)970_queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY, 0,971NULL, GFP_KERNEL);972break;973case PH_DEACTIVATE_REQ:974spin_lock_irqsave(&card->lock, flags);975mISDN_clear_bchannel(bch);976w6692_mode(bc, ISDN_P_NONE);977spin_unlock_irqrestore(&card->lock, flags);978_queue_data(ch, PH_DEACTIVATE_IND, MISDN_ID_ANY, 0,979NULL, GFP_KERNEL);980ret = 0;981break;982default:983pr_info("%s: %s unknown prim(%x,%x)\n",984card->name, __func__, hh->prim, hh->id);985ret = -EINVAL;986}987if (!ret)988dev_kfree_skb(skb);989return ret;990}991992static int993channel_bctrl(struct bchannel *bch, struct mISDN_ctrl_req *cq)994{995int ret = 0;996997switch (cq->op) {998case MISDN_CTRL_GETOP:999cq->op = 0;1000break;1001/* Nothing implemented yet */1002case MISDN_CTRL_FILL_EMPTY:1003default:1004pr_info("%s: unknown Op %x\n", __func__, cq->op);1005ret = -EINVAL;1006break;1007}1008return ret;1009}10101011static int1012open_bchannel(struct w6692_hw *card, struct channel_req *rq)1013{1014struct bchannel *bch;10151016if (rq->adr.channel > 2)1017return -EINVAL;1018if (rq->protocol == ISDN_P_NONE)1019return -EINVAL;1020bch = &card->bc[rq->adr.channel - 1].bch;1021if (test_and_set_bit(FLG_OPEN, &bch->Flags))1022return -EBUSY; /* b-channel can be only open once */1023test_and_clear_bit(FLG_FILLEMPTY, &bch->Flags);1024bch->ch.protocol = rq->protocol;1025rq->ch = &bch->ch;1026return 0;1027}10281029static int1030channel_ctrl(struct w6692_hw *card, struct mISDN_ctrl_req *cq)1031{1032int ret = 0;10331034switch (cq->op) {1035case MISDN_CTRL_GETOP:1036cq->op = 0;1037break;1038default:1039pr_info("%s: unknown CTRL OP %x\n", card->name, cq->op);1040ret = -EINVAL;1041break;1042}1043return ret;1044}10451046static int1047w6692_bctrl(struct mISDNchannel *ch, u32 cmd, void *arg)1048{1049struct bchannel *bch = container_of(ch, struct bchannel, ch);1050struct w6692_ch *bc = container_of(bch, struct w6692_ch, bch);1051struct w6692_hw *card = bch->hw;1052int ret = -EINVAL;1053u_long flags;10541055pr_debug("%s: %s cmd:%x %p\n", card->name, __func__, cmd, arg);1056switch (cmd) {1057case CLOSE_CHANNEL:1058test_and_clear_bit(FLG_OPEN, &bch->Flags);1059if (test_bit(FLG_ACTIVE, &bch->Flags)) {1060spin_lock_irqsave(&card->lock, flags);1061mISDN_freebchannel(bch);1062w6692_mode(bc, ISDN_P_NONE);1063spin_unlock_irqrestore(&card->lock, flags);1064} else {1065skb_queue_purge(&bch->rqueue);1066bch->rcount = 0;1067}1068ch->protocol = ISDN_P_NONE;1069ch->peer = NULL;1070module_put(THIS_MODULE);1071ret = 0;1072break;1073case CONTROL_CHANNEL:1074ret = channel_bctrl(bch, arg);1075break;1076default:1077pr_info("%s: %s unknown prim(%x)\n",1078card->name, __func__, cmd);1079}1080return ret;1081}10821083static int1084w6692_l2l1D(struct mISDNchannel *ch, struct sk_buff *skb)1085{1086struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D);1087struct dchannel *dch = container_of(dev, struct dchannel, dev);1088struct w6692_hw *card = container_of(dch, struct w6692_hw, dch);1089int ret = -EINVAL;1090struct mISDNhead *hh = mISDN_HEAD_P(skb);1091u32 id;1092u_long flags;10931094switch (hh->prim) {1095case PH_DATA_REQ:1096spin_lock_irqsave(&card->lock, flags);1097ret = dchannel_senddata(dch, skb);1098if (ret > 0) { /* direct TX */1099id = hh->id; /* skb can be freed */1100W6692_fill_Dfifo(card);1101ret = 0;1102spin_unlock_irqrestore(&card->lock, flags);1103queue_ch_frame(ch, PH_DATA_CNF, id, NULL);1104} else1105spin_unlock_irqrestore(&card->lock, flags);1106return ret;1107case PH_ACTIVATE_REQ:1108ret = l1_event(dch->l1, hh->prim);1109break;1110case PH_DEACTIVATE_REQ:1111test_and_clear_bit(FLG_L2_ACTIVATED, &dch->Flags);1112ret = l1_event(dch->l1, hh->prim);1113break;1114}11151116if (!ret)1117dev_kfree_skb(skb);1118return ret;1119}11201121static int1122w6692_l1callback(struct dchannel *dch, u32 cmd)1123{1124struct w6692_hw *card = container_of(dch, struct w6692_hw, dch);1125u_long flags;11261127pr_debug("%s: cmd(%x) state(%02x)\n", card->name, cmd, card->state);1128switch (cmd) {1129case INFO3_P8:1130spin_lock_irqsave(&card->lock, flags);1131ph_command(card, W_L1CMD_AR8);1132spin_unlock_irqrestore(&card->lock, flags);1133break;1134case INFO3_P10:1135spin_lock_irqsave(&card->lock, flags);1136ph_command(card, W_L1CMD_AR10);1137spin_unlock_irqrestore(&card->lock, flags);1138break;1139case HW_RESET_REQ:1140spin_lock_irqsave(&card->lock, flags);1141if (card->state != W_L1IND_DRD)1142ph_command(card, W_L1CMD_RST);1143ph_command(card, W_L1CMD_ECK);1144spin_unlock_irqrestore(&card->lock, flags);1145break;1146case HW_DEACT_REQ:1147skb_queue_purge(&dch->squeue);1148if (dch->tx_skb) {1149dev_kfree_skb(dch->tx_skb);1150dch->tx_skb = NULL;1151}1152dch->tx_idx = 0;1153if (dch->rx_skb) {1154dev_kfree_skb(dch->rx_skb);1155dch->rx_skb = NULL;1156}1157test_and_clear_bit(FLG_TX_BUSY, &dch->Flags);1158if (test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags))1159del_timer(&dch->timer);1160break;1161case HW_POWERUP_REQ:1162spin_lock_irqsave(&card->lock, flags);1163ph_command(card, W_L1CMD_ECK);1164spin_unlock_irqrestore(&card->lock, flags);1165break;1166case PH_ACTIVATE_IND:1167test_and_set_bit(FLG_ACTIVE, &dch->Flags);1168_queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL,1169GFP_ATOMIC);1170break;1171case PH_DEACTIVATE_IND:1172test_and_clear_bit(FLG_ACTIVE, &dch->Flags);1173_queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL,1174GFP_ATOMIC);1175break;1176default:1177pr_debug("%s: %s unknown command %x\n", card->name,1178__func__, cmd);1179return -1;1180}1181return 0;1182}11831184static int1185open_dchannel(struct w6692_hw *card, struct channel_req *rq)1186{1187pr_debug("%s: %s dev(%d) open from %p\n", card->name, __func__,1188card->dch.dev.id, __builtin_return_address(1));1189if (rq->protocol != ISDN_P_TE_S0)1190return -EINVAL;1191if (rq->adr.channel == 1)1192/* E-Channel not supported */1193return -EINVAL;1194rq->ch = &card->dch.dev.D;1195rq->ch->protocol = rq->protocol;1196if (card->dch.state == 7)1197_queue_data(rq->ch, PH_ACTIVATE_IND, MISDN_ID_ANY,11980, NULL, GFP_KERNEL);1199return 0;1200}12011202static int1203w6692_dctrl(struct mISDNchannel *ch, u32 cmd, void *arg)1204{1205struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D);1206struct dchannel *dch = container_of(dev, struct dchannel, dev);1207struct w6692_hw *card = container_of(dch, struct w6692_hw, dch);1208struct channel_req *rq;1209int err = 0;12101211pr_debug("%s: DCTRL: %x %p\n", card->name, cmd, arg);1212switch (cmd) {1213case OPEN_CHANNEL:1214rq = arg;1215if (rq->protocol == ISDN_P_TE_S0)1216err = open_dchannel(card, rq);1217else1218err = open_bchannel(card, rq);1219if (err)1220break;1221if (!try_module_get(THIS_MODULE))1222pr_info("%s: cannot get module\n", card->name);1223break;1224case CLOSE_CHANNEL:1225pr_debug("%s: dev(%d) close from %p\n", card->name,1226dch->dev.id, __builtin_return_address(0));1227module_put(THIS_MODULE);1228break;1229case CONTROL_CHANNEL:1230err = channel_ctrl(card, arg);1231break;1232default:1233pr_debug("%s: unknown DCTRL command %x\n", card->name, cmd);1234return -EINVAL;1235}1236return err;1237}12381239static int1240setup_w6692(struct w6692_hw *card)1241{1242u32 val;12431244if (!request_region(card->addr, 256, card->name)) {1245pr_info("%s: config port %x-%x already in use\n", card->name,1246card->addr, card->addr + 255);1247return -EIO;1248}1249W6692Version(card);1250card->bc[0].addr = card->addr;1251card->bc[1].addr = card->addr + 0x40;1252val = ReadW6692(card, W_ISTA);1253if (debug & DEBUG_HW)1254pr_notice("%s ISTA=%02x\n", card->name, val);1255val = ReadW6692(card, W_IMASK);1256if (debug & DEBUG_HW)1257pr_notice("%s IMASK=%02x\n", card->name, val);1258val = ReadW6692(card, W_D_EXIR);1259if (debug & DEBUG_HW)1260pr_notice("%s D_EXIR=%02x\n", card->name, val);1261val = ReadW6692(card, W_D_EXIM);1262if (debug & DEBUG_HW)1263pr_notice("%s D_EXIM=%02x\n", card->name, val);1264val = ReadW6692(card, W_D_RSTA);1265if (debug & DEBUG_HW)1266pr_notice("%s D_RSTA=%02x\n", card->name, val);1267return 0;1268}12691270static void1271release_card(struct w6692_hw *card)1272{1273u_long flags;12741275spin_lock_irqsave(&card->lock, flags);1276disable_hwirq(card);1277w6692_mode(&card->bc[0], ISDN_P_NONE);1278w6692_mode(&card->bc[1], ISDN_P_NONE);1279if ((card->fmask & led) || card->subtype == W6692_USR) {1280card->xdata |= 0x04; /* LED OFF */1281WriteW6692(card, W_XDATA, card->xdata);1282}1283spin_unlock_irqrestore(&card->lock, flags);1284free_irq(card->irq, card);1285l1_event(card->dch.l1, CLOSE_CHANNEL);1286mISDN_unregister_device(&card->dch.dev);1287release_region(card->addr, 256);1288mISDN_freebchannel(&card->bc[1].bch);1289mISDN_freebchannel(&card->bc[0].bch);1290mISDN_freedchannel(&card->dch);1291write_lock_irqsave(&card_lock, flags);1292list_del(&card->list);1293write_unlock_irqrestore(&card_lock, flags);1294pci_disable_device(card->pdev);1295pci_set_drvdata(card->pdev, NULL);1296kfree(card);1297}12981299static int1300setup_instance(struct w6692_hw *card)1301{1302int i, err;1303u_long flags;13041305snprintf(card->name, MISDN_MAX_IDLEN - 1, "w6692.%d", w6692_cnt + 1);1306write_lock_irqsave(&card_lock, flags);1307list_add_tail(&card->list, &Cards);1308write_unlock_irqrestore(&card_lock, flags);1309card->fmask = (1 << w6692_cnt);1310_set_debug(card);1311spin_lock_init(&card->lock);1312mISDN_initdchannel(&card->dch, MAX_DFRAME_LEN_L1, W6692_ph_bh);1313card->dch.dev.Dprotocols = (1 << ISDN_P_TE_S0);1314card->dch.dev.D.send = w6692_l2l1D;1315card->dch.dev.D.ctrl = w6692_dctrl;1316card->dch.dev.Bprotocols = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) |1317(1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK));1318card->dch.hw = card;1319card->dch.dev.nrbchan = 2;1320for (i = 0; i < 2; i++) {1321mISDN_initbchannel(&card->bc[i].bch, MAX_DATA_MEM);1322card->bc[i].bch.hw = card;1323card->bc[i].bch.nr = i + 1;1324card->bc[i].bch.ch.nr = i + 1;1325card->bc[i].bch.ch.send = w6692_l2l1B;1326card->bc[i].bch.ch.ctrl = w6692_bctrl;1327set_channelmap(i + 1, card->dch.dev.channelmap);1328list_add(&card->bc[i].bch.ch.list, &card->dch.dev.bchannels);1329}1330err = setup_w6692(card);1331if (err)1332goto error_setup;1333err = mISDN_register_device(&card->dch.dev, &card->pdev->dev,1334card->name);1335if (err)1336goto error_reg;1337err = init_card(card);1338if (err)1339goto error_init;1340err = create_l1(&card->dch, w6692_l1callback);1341if (!err) {1342w6692_cnt++;1343pr_notice("W6692 %d cards installed\n", w6692_cnt);1344return 0;1345}13461347free_irq(card->irq, card);1348error_init:1349mISDN_unregister_device(&card->dch.dev);1350error_reg:1351release_region(card->addr, 256);1352error_setup:1353mISDN_freebchannel(&card->bc[1].bch);1354mISDN_freebchannel(&card->bc[0].bch);1355mISDN_freedchannel(&card->dch);1356write_lock_irqsave(&card_lock, flags);1357list_del(&card->list);1358write_unlock_irqrestore(&card_lock, flags);1359kfree(card);1360return err;1361}13621363static int __devinit1364w6692_probe(struct pci_dev *pdev, const struct pci_device_id *ent)1365{1366int err = -ENOMEM;1367struct w6692_hw *card;1368struct w6692map *m = (struct w6692map *)ent->driver_data;13691370card = kzalloc(sizeof(struct w6692_hw), GFP_KERNEL);1371if (!card) {1372pr_info("No kmem for w6692 card\n");1373return err;1374}1375card->pdev = pdev;1376card->subtype = m->subtype;1377err = pci_enable_device(pdev);1378if (err) {1379kfree(card);1380return err;1381}13821383printk(KERN_INFO "mISDN_w6692: found adapter %s at %s\n",1384m->name, pci_name(pdev));13851386card->addr = pci_resource_start(pdev, 1);1387card->irq = pdev->irq;1388pci_set_drvdata(pdev, card);1389err = setup_instance(card);1390if (err)1391pci_set_drvdata(pdev, NULL);1392return err;1393}13941395static void __devexit1396w6692_remove_pci(struct pci_dev *pdev)1397{1398struct w6692_hw *card = pci_get_drvdata(pdev);13991400if (card)1401release_card(card);1402else1403if (debug)1404pr_notice("%s: drvdata already removed\n", __func__);1405}14061407static struct pci_device_id w6692_ids[] = {1408{ PCI_VENDOR_ID_DYNALINK, PCI_DEVICE_ID_DYNALINK_IS64PH,1409PCI_ANY_ID, PCI_ANY_ID, 0, 0, (ulong)&w6692_map[0]},1410{ PCI_VENDOR_ID_WINBOND2, PCI_DEVICE_ID_WINBOND2_6692,1411PCI_VENDOR_ID_USR, PCI_DEVICE_ID_USR_6692, 0, 0,1412(ulong)&w6692_map[2]},1413{ PCI_VENDOR_ID_WINBOND2, PCI_DEVICE_ID_WINBOND2_6692,1414PCI_ANY_ID, PCI_ANY_ID, 0, 0, (ulong)&w6692_map[1]},1415{ }1416};1417MODULE_DEVICE_TABLE(pci, w6692_ids);14181419static struct pci_driver w6692_driver = {1420.name = "w6692",1421.probe = w6692_probe,1422.remove = __devexit_p(w6692_remove_pci),1423.id_table = w6692_ids,1424};14251426static int __init w6692_init(void)1427{1428int err;14291430pr_notice("Winbond W6692 PCI driver Rev. %s\n", W6692_REV);14311432err = pci_register_driver(&w6692_driver);1433return err;1434}14351436static void __exit w6692_cleanup(void)1437{1438pci_unregister_driver(&w6692_driver);1439}14401441module_init(w6692_init);1442module_exit(w6692_cleanup);144314441445