162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * w6692.c mISDN driver for Winbond w6692 based cards 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Author Karsten Keil <kkeil@suse.de> 662306a36Sopenharmony_ci * based on the w6692 I4L driver from Petr Novak <petr.novak@i.cz> 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Copyright 2009 by Karsten Keil <keil@isdn4linux.de> 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/interrupt.h> 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/pci.h> 1462306a36Sopenharmony_ci#include <linux/delay.h> 1562306a36Sopenharmony_ci#include <linux/mISDNhw.h> 1662306a36Sopenharmony_ci#include <linux/slab.h> 1762306a36Sopenharmony_ci#include "w6692.h" 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#define W6692_REV "2.0" 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#define DBUSY_TIMER_VALUE 80 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cienum { 2462306a36Sopenharmony_ci W6692_ASUS, 2562306a36Sopenharmony_ci W6692_WINBOND, 2662306a36Sopenharmony_ci W6692_USR 2762306a36Sopenharmony_ci}; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci/* private data in the PCI devices list */ 3062306a36Sopenharmony_cistruct w6692map { 3162306a36Sopenharmony_ci u_int subtype; 3262306a36Sopenharmony_ci char *name; 3362306a36Sopenharmony_ci}; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic const struct w6692map w6692_map[] = 3662306a36Sopenharmony_ci{ 3762306a36Sopenharmony_ci {W6692_ASUS, "Dynalink/AsusCom IS64PH"}, 3862306a36Sopenharmony_ci {W6692_WINBOND, "Winbond W6692"}, 3962306a36Sopenharmony_ci {W6692_USR, "USR W6692"} 4062306a36Sopenharmony_ci}; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci#define PCI_DEVICE_ID_USR_6692 0x3409 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistruct w6692_ch { 4562306a36Sopenharmony_ci struct bchannel bch; 4662306a36Sopenharmony_ci u32 addr; 4762306a36Sopenharmony_ci struct timer_list timer; 4862306a36Sopenharmony_ci u8 b_mode; 4962306a36Sopenharmony_ci}; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cistruct w6692_hw { 5262306a36Sopenharmony_ci struct list_head list; 5362306a36Sopenharmony_ci struct pci_dev *pdev; 5462306a36Sopenharmony_ci char name[MISDN_MAX_IDLEN]; 5562306a36Sopenharmony_ci u32 irq; 5662306a36Sopenharmony_ci u32 irqcnt; 5762306a36Sopenharmony_ci u32 addr; 5862306a36Sopenharmony_ci u32 fmask; /* feature mask - bit set per card nr */ 5962306a36Sopenharmony_ci int subtype; 6062306a36Sopenharmony_ci spinlock_t lock; /* hw lock */ 6162306a36Sopenharmony_ci u8 imask; 6262306a36Sopenharmony_ci u8 pctl; 6362306a36Sopenharmony_ci u8 xaddr; 6462306a36Sopenharmony_ci u8 xdata; 6562306a36Sopenharmony_ci u8 state; 6662306a36Sopenharmony_ci struct w6692_ch bc[2]; 6762306a36Sopenharmony_ci struct dchannel dch; 6862306a36Sopenharmony_ci char log[64]; 6962306a36Sopenharmony_ci}; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistatic LIST_HEAD(Cards); 7262306a36Sopenharmony_cistatic DEFINE_RWLOCK(card_lock); /* protect Cards */ 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cistatic int w6692_cnt; 7562306a36Sopenharmony_cistatic int debug; 7662306a36Sopenharmony_cistatic u32 led; 7762306a36Sopenharmony_cistatic u32 pots; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cistatic void 8062306a36Sopenharmony_ci_set_debug(struct w6692_hw *card) 8162306a36Sopenharmony_ci{ 8262306a36Sopenharmony_ci card->dch.debug = debug; 8362306a36Sopenharmony_ci card->bc[0].bch.debug = debug; 8462306a36Sopenharmony_ci card->bc[1].bch.debug = debug; 8562306a36Sopenharmony_ci} 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistatic int 8862306a36Sopenharmony_ciset_debug(const char *val, const struct kernel_param *kp) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci int ret; 9162306a36Sopenharmony_ci struct w6692_hw *card; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci ret = param_set_uint(val, kp); 9462306a36Sopenharmony_ci if (!ret) { 9562306a36Sopenharmony_ci read_lock(&card_lock); 9662306a36Sopenharmony_ci list_for_each_entry(card, &Cards, list) 9762306a36Sopenharmony_ci _set_debug(card); 9862306a36Sopenharmony_ci read_unlock(&card_lock); 9962306a36Sopenharmony_ci } 10062306a36Sopenharmony_ci return ret; 10162306a36Sopenharmony_ci} 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ciMODULE_AUTHOR("Karsten Keil"); 10462306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 10562306a36Sopenharmony_ciMODULE_VERSION(W6692_REV); 10662306a36Sopenharmony_cimodule_param_call(debug, set_debug, param_get_uint, &debug, S_IRUGO | S_IWUSR); 10762306a36Sopenharmony_ciMODULE_PARM_DESC(debug, "W6692 debug mask"); 10862306a36Sopenharmony_cimodule_param(led, uint, S_IRUGO | S_IWUSR); 10962306a36Sopenharmony_ciMODULE_PARM_DESC(led, "W6692 LED support bitmask (one bit per card)"); 11062306a36Sopenharmony_cimodule_param(pots, uint, S_IRUGO | S_IWUSR); 11162306a36Sopenharmony_ciMODULE_PARM_DESC(pots, "W6692 POTS support bitmask (one bit per card)"); 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_cistatic inline u8 11462306a36Sopenharmony_ciReadW6692(struct w6692_hw *card, u8 offset) 11562306a36Sopenharmony_ci{ 11662306a36Sopenharmony_ci return inb(card->addr + offset); 11762306a36Sopenharmony_ci} 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistatic inline void 12062306a36Sopenharmony_ciWriteW6692(struct w6692_hw *card, u8 offset, u8 value) 12162306a36Sopenharmony_ci{ 12262306a36Sopenharmony_ci outb(value, card->addr + offset); 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_cistatic inline u8 12662306a36Sopenharmony_ciReadW6692B(struct w6692_ch *bc, u8 offset) 12762306a36Sopenharmony_ci{ 12862306a36Sopenharmony_ci return inb(bc->addr + offset); 12962306a36Sopenharmony_ci} 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cistatic inline void 13262306a36Sopenharmony_ciWriteW6692B(struct w6692_ch *bc, u8 offset, u8 value) 13362306a36Sopenharmony_ci{ 13462306a36Sopenharmony_ci outb(value, bc->addr + offset); 13562306a36Sopenharmony_ci} 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_cistatic void 13862306a36Sopenharmony_cienable_hwirq(struct w6692_hw *card) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci WriteW6692(card, W_IMASK, card->imask); 14162306a36Sopenharmony_ci} 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_cistatic void 14462306a36Sopenharmony_cidisable_hwirq(struct w6692_hw *card) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci WriteW6692(card, W_IMASK, 0xff); 14762306a36Sopenharmony_ci} 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_cistatic const char *W6692Ver[] = {"V00", "V01", "V10", "V11"}; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_cistatic void 15262306a36Sopenharmony_ciW6692Version(struct w6692_hw *card) 15362306a36Sopenharmony_ci{ 15462306a36Sopenharmony_ci int val; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci val = ReadW6692(card, W_D_RBCH); 15762306a36Sopenharmony_ci pr_notice("%s: Winbond W6692 version: %s\n", card->name, 15862306a36Sopenharmony_ci W6692Ver[(val >> 6) & 3]); 15962306a36Sopenharmony_ci} 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_cistatic void 16262306a36Sopenharmony_ciw6692_led_handler(struct w6692_hw *card, int on) 16362306a36Sopenharmony_ci{ 16462306a36Sopenharmony_ci if ((!(card->fmask & led)) || card->subtype == W6692_USR) 16562306a36Sopenharmony_ci return; 16662306a36Sopenharmony_ci if (on) { 16762306a36Sopenharmony_ci card->xdata &= 0xfb; /* LED ON */ 16862306a36Sopenharmony_ci WriteW6692(card, W_XDATA, card->xdata); 16962306a36Sopenharmony_ci } else { 17062306a36Sopenharmony_ci card->xdata |= 0x04; /* LED OFF */ 17162306a36Sopenharmony_ci WriteW6692(card, W_XDATA, card->xdata); 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci} 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_cistatic void 17662306a36Sopenharmony_ciph_command(struct w6692_hw *card, u8 cmd) 17762306a36Sopenharmony_ci{ 17862306a36Sopenharmony_ci pr_debug("%s: ph_command %x\n", card->name, cmd); 17962306a36Sopenharmony_ci WriteW6692(card, W_CIX, cmd); 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cistatic void 18362306a36Sopenharmony_ciW6692_new_ph(struct w6692_hw *card) 18462306a36Sopenharmony_ci{ 18562306a36Sopenharmony_ci if (card->state == W_L1CMD_RST) 18662306a36Sopenharmony_ci ph_command(card, W_L1CMD_DRC); 18762306a36Sopenharmony_ci schedule_event(&card->dch, FLG_PHCHANGE); 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_cistatic void 19162306a36Sopenharmony_ciW6692_ph_bh(struct dchannel *dch) 19262306a36Sopenharmony_ci{ 19362306a36Sopenharmony_ci struct w6692_hw *card = dch->hw; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci switch (card->state) { 19662306a36Sopenharmony_ci case W_L1CMD_RST: 19762306a36Sopenharmony_ci dch->state = 0; 19862306a36Sopenharmony_ci l1_event(dch->l1, HW_RESET_IND); 19962306a36Sopenharmony_ci break; 20062306a36Sopenharmony_ci case W_L1IND_CD: 20162306a36Sopenharmony_ci dch->state = 3; 20262306a36Sopenharmony_ci l1_event(dch->l1, HW_DEACT_CNF); 20362306a36Sopenharmony_ci break; 20462306a36Sopenharmony_ci case W_L1IND_DRD: 20562306a36Sopenharmony_ci dch->state = 3; 20662306a36Sopenharmony_ci l1_event(dch->l1, HW_DEACT_IND); 20762306a36Sopenharmony_ci break; 20862306a36Sopenharmony_ci case W_L1IND_CE: 20962306a36Sopenharmony_ci dch->state = 4; 21062306a36Sopenharmony_ci l1_event(dch->l1, HW_POWERUP_IND); 21162306a36Sopenharmony_ci break; 21262306a36Sopenharmony_ci case W_L1IND_LD: 21362306a36Sopenharmony_ci if (dch->state <= 5) { 21462306a36Sopenharmony_ci dch->state = 5; 21562306a36Sopenharmony_ci l1_event(dch->l1, ANYSIGNAL); 21662306a36Sopenharmony_ci } else { 21762306a36Sopenharmony_ci dch->state = 8; 21862306a36Sopenharmony_ci l1_event(dch->l1, LOSTFRAMING); 21962306a36Sopenharmony_ci } 22062306a36Sopenharmony_ci break; 22162306a36Sopenharmony_ci case W_L1IND_ARD: 22262306a36Sopenharmony_ci dch->state = 6; 22362306a36Sopenharmony_ci l1_event(dch->l1, INFO2); 22462306a36Sopenharmony_ci break; 22562306a36Sopenharmony_ci case W_L1IND_AI8: 22662306a36Sopenharmony_ci dch->state = 7; 22762306a36Sopenharmony_ci l1_event(dch->l1, INFO4_P8); 22862306a36Sopenharmony_ci break; 22962306a36Sopenharmony_ci case W_L1IND_AI10: 23062306a36Sopenharmony_ci dch->state = 7; 23162306a36Sopenharmony_ci l1_event(dch->l1, INFO4_P10); 23262306a36Sopenharmony_ci break; 23362306a36Sopenharmony_ci default: 23462306a36Sopenharmony_ci pr_debug("%s: TE unknown state %02x dch state %02x\n", 23562306a36Sopenharmony_ci card->name, card->state, dch->state); 23662306a36Sopenharmony_ci break; 23762306a36Sopenharmony_ci } 23862306a36Sopenharmony_ci pr_debug("%s: TE newstate %02x\n", card->name, dch->state); 23962306a36Sopenharmony_ci} 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_cistatic void 24262306a36Sopenharmony_ciW6692_empty_Dfifo(struct w6692_hw *card, int count) 24362306a36Sopenharmony_ci{ 24462306a36Sopenharmony_ci struct dchannel *dch = &card->dch; 24562306a36Sopenharmony_ci u8 *ptr; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci pr_debug("%s: empty_Dfifo %d\n", card->name, count); 24862306a36Sopenharmony_ci if (!dch->rx_skb) { 24962306a36Sopenharmony_ci dch->rx_skb = mI_alloc_skb(card->dch.maxlen, GFP_ATOMIC); 25062306a36Sopenharmony_ci if (!dch->rx_skb) { 25162306a36Sopenharmony_ci pr_info("%s: D receive out of memory\n", card->name); 25262306a36Sopenharmony_ci WriteW6692(card, W_D_CMDR, W_D_CMDR_RACK); 25362306a36Sopenharmony_ci return; 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci } 25662306a36Sopenharmony_ci if ((dch->rx_skb->len + count) >= dch->maxlen) { 25762306a36Sopenharmony_ci pr_debug("%s: empty_Dfifo overrun %d\n", card->name, 25862306a36Sopenharmony_ci dch->rx_skb->len + count); 25962306a36Sopenharmony_ci WriteW6692(card, W_D_CMDR, W_D_CMDR_RACK); 26062306a36Sopenharmony_ci return; 26162306a36Sopenharmony_ci } 26262306a36Sopenharmony_ci ptr = skb_put(dch->rx_skb, count); 26362306a36Sopenharmony_ci insb(card->addr + W_D_RFIFO, ptr, count); 26462306a36Sopenharmony_ci WriteW6692(card, W_D_CMDR, W_D_CMDR_RACK); 26562306a36Sopenharmony_ci if (debug & DEBUG_HW_DFIFO) { 26662306a36Sopenharmony_ci snprintf(card->log, 63, "D-recv %s %d ", 26762306a36Sopenharmony_ci card->name, count); 26862306a36Sopenharmony_ci print_hex_dump_bytes(card->log, DUMP_PREFIX_OFFSET, ptr, count); 26962306a36Sopenharmony_ci } 27062306a36Sopenharmony_ci} 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_cistatic void 27362306a36Sopenharmony_ciW6692_fill_Dfifo(struct w6692_hw *card) 27462306a36Sopenharmony_ci{ 27562306a36Sopenharmony_ci struct dchannel *dch = &card->dch; 27662306a36Sopenharmony_ci int count; 27762306a36Sopenharmony_ci u8 *ptr; 27862306a36Sopenharmony_ci u8 cmd = W_D_CMDR_XMS; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci pr_debug("%s: fill_Dfifo\n", card->name); 28162306a36Sopenharmony_ci if (!dch->tx_skb) 28262306a36Sopenharmony_ci return; 28362306a36Sopenharmony_ci count = dch->tx_skb->len - dch->tx_idx; 28462306a36Sopenharmony_ci if (count <= 0) 28562306a36Sopenharmony_ci return; 28662306a36Sopenharmony_ci if (count > W_D_FIFO_THRESH) 28762306a36Sopenharmony_ci count = W_D_FIFO_THRESH; 28862306a36Sopenharmony_ci else 28962306a36Sopenharmony_ci cmd |= W_D_CMDR_XME; 29062306a36Sopenharmony_ci ptr = dch->tx_skb->data + dch->tx_idx; 29162306a36Sopenharmony_ci dch->tx_idx += count; 29262306a36Sopenharmony_ci outsb(card->addr + W_D_XFIFO, ptr, count); 29362306a36Sopenharmony_ci WriteW6692(card, W_D_CMDR, cmd); 29462306a36Sopenharmony_ci if (test_and_set_bit(FLG_BUSY_TIMER, &dch->Flags)) { 29562306a36Sopenharmony_ci pr_debug("%s: fill_Dfifo dbusytimer running\n", card->name); 29662306a36Sopenharmony_ci del_timer(&dch->timer); 29762306a36Sopenharmony_ci } 29862306a36Sopenharmony_ci dch->timer.expires = jiffies + ((DBUSY_TIMER_VALUE * HZ) / 1000); 29962306a36Sopenharmony_ci add_timer(&dch->timer); 30062306a36Sopenharmony_ci if (debug & DEBUG_HW_DFIFO) { 30162306a36Sopenharmony_ci snprintf(card->log, 63, "D-send %s %d ", 30262306a36Sopenharmony_ci card->name, count); 30362306a36Sopenharmony_ci print_hex_dump_bytes(card->log, DUMP_PREFIX_OFFSET, ptr, count); 30462306a36Sopenharmony_ci } 30562306a36Sopenharmony_ci} 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_cistatic void 30862306a36Sopenharmony_cid_retransmit(struct w6692_hw *card) 30962306a36Sopenharmony_ci{ 31062306a36Sopenharmony_ci struct dchannel *dch = &card->dch; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci if (test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags)) 31362306a36Sopenharmony_ci del_timer(&dch->timer); 31462306a36Sopenharmony_ci#ifdef FIXME 31562306a36Sopenharmony_ci if (test_and_clear_bit(FLG_L1_BUSY, &dch->Flags)) 31662306a36Sopenharmony_ci dchannel_sched_event(dch, D_CLEARBUSY); 31762306a36Sopenharmony_ci#endif 31862306a36Sopenharmony_ci if (test_bit(FLG_TX_BUSY, &dch->Flags)) { 31962306a36Sopenharmony_ci /* Restart frame */ 32062306a36Sopenharmony_ci dch->tx_idx = 0; 32162306a36Sopenharmony_ci W6692_fill_Dfifo(card); 32262306a36Sopenharmony_ci } else if (dch->tx_skb) { /* should not happen */ 32362306a36Sopenharmony_ci pr_info("%s: %s without TX_BUSY\n", card->name, __func__); 32462306a36Sopenharmony_ci test_and_set_bit(FLG_TX_BUSY, &dch->Flags); 32562306a36Sopenharmony_ci dch->tx_idx = 0; 32662306a36Sopenharmony_ci W6692_fill_Dfifo(card); 32762306a36Sopenharmony_ci } else { 32862306a36Sopenharmony_ci pr_info("%s: XDU no TX_BUSY\n", card->name); 32962306a36Sopenharmony_ci if (get_next_dframe(dch)) 33062306a36Sopenharmony_ci W6692_fill_Dfifo(card); 33162306a36Sopenharmony_ci } 33262306a36Sopenharmony_ci} 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_cistatic void 33562306a36Sopenharmony_cihandle_rxD(struct w6692_hw *card) { 33662306a36Sopenharmony_ci u8 stat; 33762306a36Sopenharmony_ci int count; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci stat = ReadW6692(card, W_D_RSTA); 34062306a36Sopenharmony_ci if (stat & (W_D_RSTA_RDOV | W_D_RSTA_CRCE | W_D_RSTA_RMB)) { 34162306a36Sopenharmony_ci if (stat & W_D_RSTA_RDOV) { 34262306a36Sopenharmony_ci pr_debug("%s: D-channel RDOV\n", card->name); 34362306a36Sopenharmony_ci#ifdef ERROR_STATISTIC 34462306a36Sopenharmony_ci card->dch.err_rx++; 34562306a36Sopenharmony_ci#endif 34662306a36Sopenharmony_ci } 34762306a36Sopenharmony_ci if (stat & W_D_RSTA_CRCE) { 34862306a36Sopenharmony_ci pr_debug("%s: D-channel CRC error\n", card->name); 34962306a36Sopenharmony_ci#ifdef ERROR_STATISTIC 35062306a36Sopenharmony_ci card->dch.err_crc++; 35162306a36Sopenharmony_ci#endif 35262306a36Sopenharmony_ci } 35362306a36Sopenharmony_ci if (stat & W_D_RSTA_RMB) { 35462306a36Sopenharmony_ci pr_debug("%s: D-channel ABORT\n", card->name); 35562306a36Sopenharmony_ci#ifdef ERROR_STATISTIC 35662306a36Sopenharmony_ci card->dch.err_rx++; 35762306a36Sopenharmony_ci#endif 35862306a36Sopenharmony_ci } 35962306a36Sopenharmony_ci dev_kfree_skb(card->dch.rx_skb); 36062306a36Sopenharmony_ci card->dch.rx_skb = NULL; 36162306a36Sopenharmony_ci WriteW6692(card, W_D_CMDR, W_D_CMDR_RACK | W_D_CMDR_RRST); 36262306a36Sopenharmony_ci } else { 36362306a36Sopenharmony_ci count = ReadW6692(card, W_D_RBCL) & (W_D_FIFO_THRESH - 1); 36462306a36Sopenharmony_ci if (count == 0) 36562306a36Sopenharmony_ci count = W_D_FIFO_THRESH; 36662306a36Sopenharmony_ci W6692_empty_Dfifo(card, count); 36762306a36Sopenharmony_ci recv_Dchannel(&card->dch); 36862306a36Sopenharmony_ci } 36962306a36Sopenharmony_ci} 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_cistatic void 37262306a36Sopenharmony_cihandle_txD(struct w6692_hw *card) { 37362306a36Sopenharmony_ci if (test_and_clear_bit(FLG_BUSY_TIMER, &card->dch.Flags)) 37462306a36Sopenharmony_ci del_timer(&card->dch.timer); 37562306a36Sopenharmony_ci if (card->dch.tx_skb && card->dch.tx_idx < card->dch.tx_skb->len) { 37662306a36Sopenharmony_ci W6692_fill_Dfifo(card); 37762306a36Sopenharmony_ci } else { 37862306a36Sopenharmony_ci dev_kfree_skb(card->dch.tx_skb); 37962306a36Sopenharmony_ci if (get_next_dframe(&card->dch)) 38062306a36Sopenharmony_ci W6692_fill_Dfifo(card); 38162306a36Sopenharmony_ci } 38262306a36Sopenharmony_ci} 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_cistatic void 38562306a36Sopenharmony_cihandle_statusD(struct w6692_hw *card) 38662306a36Sopenharmony_ci{ 38762306a36Sopenharmony_ci struct dchannel *dch = &card->dch; 38862306a36Sopenharmony_ci u8 exval, v1, cir; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci exval = ReadW6692(card, W_D_EXIR); 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci pr_debug("%s: D_EXIR %02x\n", card->name, exval); 39362306a36Sopenharmony_ci if (exval & (W_D_EXI_XDUN | W_D_EXI_XCOL)) { 39462306a36Sopenharmony_ci /* Transmit underrun/collision */ 39562306a36Sopenharmony_ci pr_debug("%s: D-channel underrun/collision\n", card->name); 39662306a36Sopenharmony_ci#ifdef ERROR_STATISTIC 39762306a36Sopenharmony_ci dch->err_tx++; 39862306a36Sopenharmony_ci#endif 39962306a36Sopenharmony_ci d_retransmit(card); 40062306a36Sopenharmony_ci } 40162306a36Sopenharmony_ci if (exval & W_D_EXI_RDOV) { /* RDOV */ 40262306a36Sopenharmony_ci pr_debug("%s: D-channel RDOV\n", card->name); 40362306a36Sopenharmony_ci WriteW6692(card, W_D_CMDR, W_D_CMDR_RRST); 40462306a36Sopenharmony_ci } 40562306a36Sopenharmony_ci if (exval & W_D_EXI_TIN2) /* TIN2 - never */ 40662306a36Sopenharmony_ci pr_debug("%s: spurious TIN2 interrupt\n", card->name); 40762306a36Sopenharmony_ci if (exval & W_D_EXI_MOC) { /* MOC - not supported */ 40862306a36Sopenharmony_ci v1 = ReadW6692(card, W_MOSR); 40962306a36Sopenharmony_ci pr_debug("%s: spurious MOC interrupt MOSR %02x\n", 41062306a36Sopenharmony_ci card->name, v1); 41162306a36Sopenharmony_ci } 41262306a36Sopenharmony_ci if (exval & W_D_EXI_ISC) { /* ISC - Level1 change */ 41362306a36Sopenharmony_ci cir = ReadW6692(card, W_CIR); 41462306a36Sopenharmony_ci pr_debug("%s: ISC CIR %02X\n", card->name, cir); 41562306a36Sopenharmony_ci if (cir & W_CIR_ICC) { 41662306a36Sopenharmony_ci v1 = cir & W_CIR_COD_MASK; 41762306a36Sopenharmony_ci pr_debug("%s: ph_state_change %x -> %x\n", card->name, 41862306a36Sopenharmony_ci dch->state, v1); 41962306a36Sopenharmony_ci card->state = v1; 42062306a36Sopenharmony_ci if (card->fmask & led) { 42162306a36Sopenharmony_ci switch (v1) { 42262306a36Sopenharmony_ci case W_L1IND_AI8: 42362306a36Sopenharmony_ci case W_L1IND_AI10: 42462306a36Sopenharmony_ci w6692_led_handler(card, 1); 42562306a36Sopenharmony_ci break; 42662306a36Sopenharmony_ci default: 42762306a36Sopenharmony_ci w6692_led_handler(card, 0); 42862306a36Sopenharmony_ci break; 42962306a36Sopenharmony_ci } 43062306a36Sopenharmony_ci } 43162306a36Sopenharmony_ci W6692_new_ph(card); 43262306a36Sopenharmony_ci } 43362306a36Sopenharmony_ci if (cir & W_CIR_SCC) { 43462306a36Sopenharmony_ci v1 = ReadW6692(card, W_SQR); 43562306a36Sopenharmony_ci pr_debug("%s: SCC SQR %02X\n", card->name, v1); 43662306a36Sopenharmony_ci } 43762306a36Sopenharmony_ci } 43862306a36Sopenharmony_ci if (exval & W_D_EXI_WEXP) 43962306a36Sopenharmony_ci pr_debug("%s: spurious WEXP interrupt!\n", card->name); 44062306a36Sopenharmony_ci if (exval & W_D_EXI_TEXP) 44162306a36Sopenharmony_ci pr_debug("%s: spurious TEXP interrupt!\n", card->name); 44262306a36Sopenharmony_ci} 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_cistatic void 44562306a36Sopenharmony_ciW6692_empty_Bfifo(struct w6692_ch *wch, int count) 44662306a36Sopenharmony_ci{ 44762306a36Sopenharmony_ci struct w6692_hw *card = wch->bch.hw; 44862306a36Sopenharmony_ci u8 *ptr; 44962306a36Sopenharmony_ci int maxlen; 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci pr_debug("%s: empty_Bfifo %d\n", card->name, count); 45262306a36Sopenharmony_ci if (unlikely(wch->bch.state == ISDN_P_NONE)) { 45362306a36Sopenharmony_ci pr_debug("%s: empty_Bfifo ISDN_P_NONE\n", card->name); 45462306a36Sopenharmony_ci WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RACT); 45562306a36Sopenharmony_ci if (wch->bch.rx_skb) 45662306a36Sopenharmony_ci skb_trim(wch->bch.rx_skb, 0); 45762306a36Sopenharmony_ci return; 45862306a36Sopenharmony_ci } 45962306a36Sopenharmony_ci if (test_bit(FLG_RX_OFF, &wch->bch.Flags)) { 46062306a36Sopenharmony_ci wch->bch.dropcnt += count; 46162306a36Sopenharmony_ci WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RACT); 46262306a36Sopenharmony_ci return; 46362306a36Sopenharmony_ci } 46462306a36Sopenharmony_ci maxlen = bchannel_get_rxbuf(&wch->bch, count); 46562306a36Sopenharmony_ci if (maxlen < 0) { 46662306a36Sopenharmony_ci WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RACT); 46762306a36Sopenharmony_ci if (wch->bch.rx_skb) 46862306a36Sopenharmony_ci skb_trim(wch->bch.rx_skb, 0); 46962306a36Sopenharmony_ci pr_warn("%s.B%d: No bufferspace for %d bytes\n", 47062306a36Sopenharmony_ci card->name, wch->bch.nr, count); 47162306a36Sopenharmony_ci return; 47262306a36Sopenharmony_ci } 47362306a36Sopenharmony_ci ptr = skb_put(wch->bch.rx_skb, count); 47462306a36Sopenharmony_ci insb(wch->addr + W_B_RFIFO, ptr, count); 47562306a36Sopenharmony_ci WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RACT); 47662306a36Sopenharmony_ci if (debug & DEBUG_HW_DFIFO) { 47762306a36Sopenharmony_ci snprintf(card->log, 63, "B%1d-recv %s %d ", 47862306a36Sopenharmony_ci wch->bch.nr, card->name, count); 47962306a36Sopenharmony_ci print_hex_dump_bytes(card->log, DUMP_PREFIX_OFFSET, ptr, count); 48062306a36Sopenharmony_ci } 48162306a36Sopenharmony_ci} 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_cistatic void 48462306a36Sopenharmony_ciW6692_fill_Bfifo(struct w6692_ch *wch) 48562306a36Sopenharmony_ci{ 48662306a36Sopenharmony_ci struct w6692_hw *card = wch->bch.hw; 48762306a36Sopenharmony_ci int count, fillempty = 0; 48862306a36Sopenharmony_ci u8 *ptr, cmd = W_B_CMDR_RACT | W_B_CMDR_XMS; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci pr_debug("%s: fill Bfifo\n", card->name); 49162306a36Sopenharmony_ci if (!wch->bch.tx_skb) { 49262306a36Sopenharmony_ci if (!test_bit(FLG_TX_EMPTY, &wch->bch.Flags)) 49362306a36Sopenharmony_ci return; 49462306a36Sopenharmony_ci ptr = wch->bch.fill; 49562306a36Sopenharmony_ci count = W_B_FIFO_THRESH; 49662306a36Sopenharmony_ci fillempty = 1; 49762306a36Sopenharmony_ci } else { 49862306a36Sopenharmony_ci count = wch->bch.tx_skb->len - wch->bch.tx_idx; 49962306a36Sopenharmony_ci if (count <= 0) 50062306a36Sopenharmony_ci return; 50162306a36Sopenharmony_ci ptr = wch->bch.tx_skb->data + wch->bch.tx_idx; 50262306a36Sopenharmony_ci } 50362306a36Sopenharmony_ci if (count > W_B_FIFO_THRESH) 50462306a36Sopenharmony_ci count = W_B_FIFO_THRESH; 50562306a36Sopenharmony_ci else if (test_bit(FLG_HDLC, &wch->bch.Flags)) 50662306a36Sopenharmony_ci cmd |= W_B_CMDR_XME; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci pr_debug("%s: fill Bfifo%d/%d\n", card->name, 50962306a36Sopenharmony_ci count, wch->bch.tx_idx); 51062306a36Sopenharmony_ci wch->bch.tx_idx += count; 51162306a36Sopenharmony_ci if (fillempty) { 51262306a36Sopenharmony_ci while (count > 0) { 51362306a36Sopenharmony_ci outsb(wch->addr + W_B_XFIFO, ptr, MISDN_BCH_FILL_SIZE); 51462306a36Sopenharmony_ci count -= MISDN_BCH_FILL_SIZE; 51562306a36Sopenharmony_ci } 51662306a36Sopenharmony_ci } else { 51762306a36Sopenharmony_ci outsb(wch->addr + W_B_XFIFO, ptr, count); 51862306a36Sopenharmony_ci } 51962306a36Sopenharmony_ci WriteW6692B(wch, W_B_CMDR, cmd); 52062306a36Sopenharmony_ci if ((debug & DEBUG_HW_BFIFO) && !fillempty) { 52162306a36Sopenharmony_ci snprintf(card->log, 63, "B%1d-send %s %d ", 52262306a36Sopenharmony_ci wch->bch.nr, card->name, count); 52362306a36Sopenharmony_ci print_hex_dump_bytes(card->log, DUMP_PREFIX_OFFSET, ptr, count); 52462306a36Sopenharmony_ci } 52562306a36Sopenharmony_ci} 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci#if 0 52862306a36Sopenharmony_cistatic int 52962306a36Sopenharmony_cisetvolume(struct w6692_ch *wch, int mic, struct sk_buff *skb) 53062306a36Sopenharmony_ci{ 53162306a36Sopenharmony_ci struct w6692_hw *card = wch->bch.hw; 53262306a36Sopenharmony_ci u16 *vol = (u16 *)skb->data; 53362306a36Sopenharmony_ci u8 val; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci if ((!(card->fmask & pots)) || 53662306a36Sopenharmony_ci !test_bit(FLG_TRANSPARENT, &wch->bch.Flags)) 53762306a36Sopenharmony_ci return -ENODEV; 53862306a36Sopenharmony_ci if (skb->len < 2) 53962306a36Sopenharmony_ci return -EINVAL; 54062306a36Sopenharmony_ci if (*vol > 7) 54162306a36Sopenharmony_ci return -EINVAL; 54262306a36Sopenharmony_ci val = *vol & 7; 54362306a36Sopenharmony_ci val = 7 - val; 54462306a36Sopenharmony_ci if (mic) { 54562306a36Sopenharmony_ci val <<= 3; 54662306a36Sopenharmony_ci card->xaddr &= 0xc7; 54762306a36Sopenharmony_ci } else { 54862306a36Sopenharmony_ci card->xaddr &= 0xf8; 54962306a36Sopenharmony_ci } 55062306a36Sopenharmony_ci card->xaddr |= val; 55162306a36Sopenharmony_ci WriteW6692(card, W_XADDR, card->xaddr); 55262306a36Sopenharmony_ci return 0; 55362306a36Sopenharmony_ci} 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_cistatic int 55662306a36Sopenharmony_cienable_pots(struct w6692_ch *wch) 55762306a36Sopenharmony_ci{ 55862306a36Sopenharmony_ci struct w6692_hw *card = wch->bch.hw; 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci if ((!(card->fmask & pots)) || 56162306a36Sopenharmony_ci !test_bit(FLG_TRANSPARENT, &wch->bch.Flags)) 56262306a36Sopenharmony_ci return -ENODEV; 56362306a36Sopenharmony_ci wch->b_mode |= W_B_MODE_EPCM | W_B_MODE_BSW0; 56462306a36Sopenharmony_ci WriteW6692B(wch, W_B_MODE, wch->b_mode); 56562306a36Sopenharmony_ci WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_XRST); 56662306a36Sopenharmony_ci card->pctl |= ((wch->bch.nr & 2) ? W_PCTL_PCX : 0); 56762306a36Sopenharmony_ci WriteW6692(card, W_PCTL, card->pctl); 56862306a36Sopenharmony_ci return 0; 56962306a36Sopenharmony_ci} 57062306a36Sopenharmony_ci#endif 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_cistatic int 57362306a36Sopenharmony_cidisable_pots(struct w6692_ch *wch) 57462306a36Sopenharmony_ci{ 57562306a36Sopenharmony_ci struct w6692_hw *card = wch->bch.hw; 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci if (!(card->fmask & pots)) 57862306a36Sopenharmony_ci return -ENODEV; 57962306a36Sopenharmony_ci wch->b_mode &= ~(W_B_MODE_EPCM | W_B_MODE_BSW0); 58062306a36Sopenharmony_ci WriteW6692B(wch, W_B_MODE, wch->b_mode); 58162306a36Sopenharmony_ci WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_RACT | 58262306a36Sopenharmony_ci W_B_CMDR_XRST); 58362306a36Sopenharmony_ci return 0; 58462306a36Sopenharmony_ci} 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_cistatic int 58762306a36Sopenharmony_ciw6692_mode(struct w6692_ch *wch, u32 pr) 58862306a36Sopenharmony_ci{ 58962306a36Sopenharmony_ci struct w6692_hw *card; 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci card = wch->bch.hw; 59262306a36Sopenharmony_ci pr_debug("%s: B%d protocol %x-->%x\n", card->name, 59362306a36Sopenharmony_ci wch->bch.nr, wch->bch.state, pr); 59462306a36Sopenharmony_ci switch (pr) { 59562306a36Sopenharmony_ci case ISDN_P_NONE: 59662306a36Sopenharmony_ci if ((card->fmask & pots) && (wch->b_mode & W_B_MODE_EPCM)) 59762306a36Sopenharmony_ci disable_pots(wch); 59862306a36Sopenharmony_ci wch->b_mode = 0; 59962306a36Sopenharmony_ci mISDN_clear_bchannel(&wch->bch); 60062306a36Sopenharmony_ci WriteW6692B(wch, W_B_MODE, wch->b_mode); 60162306a36Sopenharmony_ci WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_XRST); 60262306a36Sopenharmony_ci test_and_clear_bit(FLG_HDLC, &wch->bch.Flags); 60362306a36Sopenharmony_ci test_and_clear_bit(FLG_TRANSPARENT, &wch->bch.Flags); 60462306a36Sopenharmony_ci break; 60562306a36Sopenharmony_ci case ISDN_P_B_RAW: 60662306a36Sopenharmony_ci wch->b_mode = W_B_MODE_MMS; 60762306a36Sopenharmony_ci WriteW6692B(wch, W_B_MODE, wch->b_mode); 60862306a36Sopenharmony_ci WriteW6692B(wch, W_B_EXIM, 0); 60962306a36Sopenharmony_ci WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_RACT | 61062306a36Sopenharmony_ci W_B_CMDR_XRST); 61162306a36Sopenharmony_ci test_and_set_bit(FLG_TRANSPARENT, &wch->bch.Flags); 61262306a36Sopenharmony_ci break; 61362306a36Sopenharmony_ci case ISDN_P_B_HDLC: 61462306a36Sopenharmony_ci wch->b_mode = W_B_MODE_ITF; 61562306a36Sopenharmony_ci WriteW6692B(wch, W_B_MODE, wch->b_mode); 61662306a36Sopenharmony_ci WriteW6692B(wch, W_B_ADM1, 0xff); 61762306a36Sopenharmony_ci WriteW6692B(wch, W_B_ADM2, 0xff); 61862306a36Sopenharmony_ci WriteW6692B(wch, W_B_EXIM, 0); 61962306a36Sopenharmony_ci WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_RACT | 62062306a36Sopenharmony_ci W_B_CMDR_XRST); 62162306a36Sopenharmony_ci test_and_set_bit(FLG_HDLC, &wch->bch.Flags); 62262306a36Sopenharmony_ci break; 62362306a36Sopenharmony_ci default: 62462306a36Sopenharmony_ci pr_info("%s: protocol %x not known\n", card->name, pr); 62562306a36Sopenharmony_ci return -ENOPROTOOPT; 62662306a36Sopenharmony_ci } 62762306a36Sopenharmony_ci wch->bch.state = pr; 62862306a36Sopenharmony_ci return 0; 62962306a36Sopenharmony_ci} 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_cistatic void 63262306a36Sopenharmony_cisend_next(struct w6692_ch *wch) 63362306a36Sopenharmony_ci{ 63462306a36Sopenharmony_ci if (wch->bch.tx_skb && wch->bch.tx_idx < wch->bch.tx_skb->len) { 63562306a36Sopenharmony_ci W6692_fill_Bfifo(wch); 63662306a36Sopenharmony_ci } else { 63762306a36Sopenharmony_ci dev_kfree_skb(wch->bch.tx_skb); 63862306a36Sopenharmony_ci if (get_next_bframe(&wch->bch)) { 63962306a36Sopenharmony_ci W6692_fill_Bfifo(wch); 64062306a36Sopenharmony_ci test_and_clear_bit(FLG_TX_EMPTY, &wch->bch.Flags); 64162306a36Sopenharmony_ci } else if (test_bit(FLG_TX_EMPTY, &wch->bch.Flags)) { 64262306a36Sopenharmony_ci W6692_fill_Bfifo(wch); 64362306a36Sopenharmony_ci } 64462306a36Sopenharmony_ci } 64562306a36Sopenharmony_ci} 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_cistatic void 64862306a36Sopenharmony_ciW6692B_interrupt(struct w6692_hw *card, int ch) 64962306a36Sopenharmony_ci{ 65062306a36Sopenharmony_ci struct w6692_ch *wch = &card->bc[ch]; 65162306a36Sopenharmony_ci int count; 65262306a36Sopenharmony_ci u8 stat, star = 0; 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci stat = ReadW6692B(wch, W_B_EXIR); 65562306a36Sopenharmony_ci pr_debug("%s: B%d EXIR %02x\n", card->name, wch->bch.nr, stat); 65662306a36Sopenharmony_ci if (stat & W_B_EXI_RME) { 65762306a36Sopenharmony_ci star = ReadW6692B(wch, W_B_STAR); 65862306a36Sopenharmony_ci if (star & (W_B_STAR_RDOV | W_B_STAR_CRCE | W_B_STAR_RMB)) { 65962306a36Sopenharmony_ci if ((star & W_B_STAR_RDOV) && 66062306a36Sopenharmony_ci test_bit(FLG_ACTIVE, &wch->bch.Flags)) { 66162306a36Sopenharmony_ci pr_debug("%s: B%d RDOV proto=%x\n", card->name, 66262306a36Sopenharmony_ci wch->bch.nr, wch->bch.state); 66362306a36Sopenharmony_ci#ifdef ERROR_STATISTIC 66462306a36Sopenharmony_ci wch->bch.err_rdo++; 66562306a36Sopenharmony_ci#endif 66662306a36Sopenharmony_ci } 66762306a36Sopenharmony_ci if (test_bit(FLG_HDLC, &wch->bch.Flags)) { 66862306a36Sopenharmony_ci if (star & W_B_STAR_CRCE) { 66962306a36Sopenharmony_ci pr_debug("%s: B%d CRC error\n", 67062306a36Sopenharmony_ci card->name, wch->bch.nr); 67162306a36Sopenharmony_ci#ifdef ERROR_STATISTIC 67262306a36Sopenharmony_ci wch->bch.err_crc++; 67362306a36Sopenharmony_ci#endif 67462306a36Sopenharmony_ci } 67562306a36Sopenharmony_ci if (star & W_B_STAR_RMB) { 67662306a36Sopenharmony_ci pr_debug("%s: B%d message abort\n", 67762306a36Sopenharmony_ci card->name, wch->bch.nr); 67862306a36Sopenharmony_ci#ifdef ERROR_STATISTIC 67962306a36Sopenharmony_ci wch->bch.err_inv++; 68062306a36Sopenharmony_ci#endif 68162306a36Sopenharmony_ci } 68262306a36Sopenharmony_ci } 68362306a36Sopenharmony_ci WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RACK | 68462306a36Sopenharmony_ci W_B_CMDR_RRST | W_B_CMDR_RACT); 68562306a36Sopenharmony_ci if (wch->bch.rx_skb) 68662306a36Sopenharmony_ci skb_trim(wch->bch.rx_skb, 0); 68762306a36Sopenharmony_ci } else { 68862306a36Sopenharmony_ci count = ReadW6692B(wch, W_B_RBCL) & 68962306a36Sopenharmony_ci (W_B_FIFO_THRESH - 1); 69062306a36Sopenharmony_ci if (count == 0) 69162306a36Sopenharmony_ci count = W_B_FIFO_THRESH; 69262306a36Sopenharmony_ci W6692_empty_Bfifo(wch, count); 69362306a36Sopenharmony_ci recv_Bchannel(&wch->bch, 0, false); 69462306a36Sopenharmony_ci } 69562306a36Sopenharmony_ci } 69662306a36Sopenharmony_ci if (stat & W_B_EXI_RMR) { 69762306a36Sopenharmony_ci if (!(stat & W_B_EXI_RME)) 69862306a36Sopenharmony_ci star = ReadW6692B(wch, W_B_STAR); 69962306a36Sopenharmony_ci if (star & W_B_STAR_RDOV) { 70062306a36Sopenharmony_ci pr_debug("%s: B%d RDOV proto=%x\n", card->name, 70162306a36Sopenharmony_ci wch->bch.nr, wch->bch.state); 70262306a36Sopenharmony_ci#ifdef ERROR_STATISTIC 70362306a36Sopenharmony_ci wch->bch.err_rdo++; 70462306a36Sopenharmony_ci#endif 70562306a36Sopenharmony_ci WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RACK | 70662306a36Sopenharmony_ci W_B_CMDR_RRST | W_B_CMDR_RACT); 70762306a36Sopenharmony_ci } else { 70862306a36Sopenharmony_ci W6692_empty_Bfifo(wch, W_B_FIFO_THRESH); 70962306a36Sopenharmony_ci if (test_bit(FLG_TRANSPARENT, &wch->bch.Flags)) 71062306a36Sopenharmony_ci recv_Bchannel(&wch->bch, 0, false); 71162306a36Sopenharmony_ci } 71262306a36Sopenharmony_ci } 71362306a36Sopenharmony_ci if (stat & W_B_EXI_RDOV) { 71462306a36Sopenharmony_ci /* only if it is not handled yet */ 71562306a36Sopenharmony_ci if (!(star & W_B_STAR_RDOV)) { 71662306a36Sopenharmony_ci pr_debug("%s: B%d RDOV IRQ proto=%x\n", card->name, 71762306a36Sopenharmony_ci wch->bch.nr, wch->bch.state); 71862306a36Sopenharmony_ci#ifdef ERROR_STATISTIC 71962306a36Sopenharmony_ci wch->bch.err_rdo++; 72062306a36Sopenharmony_ci#endif 72162306a36Sopenharmony_ci WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RACK | 72262306a36Sopenharmony_ci W_B_CMDR_RRST | W_B_CMDR_RACT); 72362306a36Sopenharmony_ci } 72462306a36Sopenharmony_ci } 72562306a36Sopenharmony_ci if (stat & W_B_EXI_XFR) { 72662306a36Sopenharmony_ci if (!(stat & (W_B_EXI_RME | W_B_EXI_RMR))) { 72762306a36Sopenharmony_ci star = ReadW6692B(wch, W_B_STAR); 72862306a36Sopenharmony_ci pr_debug("%s: B%d star %02x\n", card->name, 72962306a36Sopenharmony_ci wch->bch.nr, star); 73062306a36Sopenharmony_ci } 73162306a36Sopenharmony_ci if (star & W_B_STAR_XDOW) { 73262306a36Sopenharmony_ci pr_warn("%s: B%d XDOW proto=%x\n", card->name, 73362306a36Sopenharmony_ci wch->bch.nr, wch->bch.state); 73462306a36Sopenharmony_ci#ifdef ERROR_STATISTIC 73562306a36Sopenharmony_ci wch->bch.err_xdu++; 73662306a36Sopenharmony_ci#endif 73762306a36Sopenharmony_ci WriteW6692B(wch, W_B_CMDR, W_B_CMDR_XRST | 73862306a36Sopenharmony_ci W_B_CMDR_RACT); 73962306a36Sopenharmony_ci /* resend */ 74062306a36Sopenharmony_ci if (wch->bch.tx_skb) { 74162306a36Sopenharmony_ci if (!test_bit(FLG_TRANSPARENT, &wch->bch.Flags)) 74262306a36Sopenharmony_ci wch->bch.tx_idx = 0; 74362306a36Sopenharmony_ci } 74462306a36Sopenharmony_ci } 74562306a36Sopenharmony_ci send_next(wch); 74662306a36Sopenharmony_ci if (star & W_B_STAR_XDOW) 74762306a36Sopenharmony_ci return; /* handle XDOW only once */ 74862306a36Sopenharmony_ci } 74962306a36Sopenharmony_ci if (stat & W_B_EXI_XDUN) { 75062306a36Sopenharmony_ci pr_warn("%s: B%d XDUN proto=%x\n", card->name, 75162306a36Sopenharmony_ci wch->bch.nr, wch->bch.state); 75262306a36Sopenharmony_ci#ifdef ERROR_STATISTIC 75362306a36Sopenharmony_ci wch->bch.err_xdu++; 75462306a36Sopenharmony_ci#endif 75562306a36Sopenharmony_ci /* resend - no XRST needed */ 75662306a36Sopenharmony_ci if (wch->bch.tx_skb) { 75762306a36Sopenharmony_ci if (!test_bit(FLG_TRANSPARENT, &wch->bch.Flags)) 75862306a36Sopenharmony_ci wch->bch.tx_idx = 0; 75962306a36Sopenharmony_ci } else if (test_bit(FLG_FILLEMPTY, &wch->bch.Flags)) { 76062306a36Sopenharmony_ci test_and_set_bit(FLG_TX_EMPTY, &wch->bch.Flags); 76162306a36Sopenharmony_ci } 76262306a36Sopenharmony_ci send_next(wch); 76362306a36Sopenharmony_ci } 76462306a36Sopenharmony_ci} 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_cistatic irqreturn_t 76762306a36Sopenharmony_ciw6692_irq(int intno, void *dev_id) 76862306a36Sopenharmony_ci{ 76962306a36Sopenharmony_ci struct w6692_hw *card = dev_id; 77062306a36Sopenharmony_ci u8 ista; 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci spin_lock(&card->lock); 77362306a36Sopenharmony_ci ista = ReadW6692(card, W_ISTA); 77462306a36Sopenharmony_ci if ((ista | card->imask) == card->imask) { 77562306a36Sopenharmony_ci /* possible a shared IRQ reqest */ 77662306a36Sopenharmony_ci spin_unlock(&card->lock); 77762306a36Sopenharmony_ci return IRQ_NONE; 77862306a36Sopenharmony_ci } 77962306a36Sopenharmony_ci card->irqcnt++; 78062306a36Sopenharmony_ci pr_debug("%s: ista %02x\n", card->name, ista); 78162306a36Sopenharmony_ci ista &= ~card->imask; 78262306a36Sopenharmony_ci if (ista & W_INT_B1_EXI) 78362306a36Sopenharmony_ci W6692B_interrupt(card, 0); 78462306a36Sopenharmony_ci if (ista & W_INT_B2_EXI) 78562306a36Sopenharmony_ci W6692B_interrupt(card, 1); 78662306a36Sopenharmony_ci if (ista & W_INT_D_RME) 78762306a36Sopenharmony_ci handle_rxD(card); 78862306a36Sopenharmony_ci if (ista & W_INT_D_RMR) 78962306a36Sopenharmony_ci W6692_empty_Dfifo(card, W_D_FIFO_THRESH); 79062306a36Sopenharmony_ci if (ista & W_INT_D_XFR) 79162306a36Sopenharmony_ci handle_txD(card); 79262306a36Sopenharmony_ci if (ista & W_INT_D_EXI) 79362306a36Sopenharmony_ci handle_statusD(card); 79462306a36Sopenharmony_ci if (ista & (W_INT_XINT0 | W_INT_XINT1)) /* XINT0/1 - never */ 79562306a36Sopenharmony_ci pr_debug("%s: W6692 spurious XINT!\n", card->name); 79662306a36Sopenharmony_ci/* End IRQ Handler */ 79762306a36Sopenharmony_ci spin_unlock(&card->lock); 79862306a36Sopenharmony_ci return IRQ_HANDLED; 79962306a36Sopenharmony_ci} 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_cistatic void 80262306a36Sopenharmony_cidbusy_timer_handler(struct timer_list *t) 80362306a36Sopenharmony_ci{ 80462306a36Sopenharmony_ci struct dchannel *dch = from_timer(dch, t, timer); 80562306a36Sopenharmony_ci struct w6692_hw *card = dch->hw; 80662306a36Sopenharmony_ci int rbch, star; 80762306a36Sopenharmony_ci u_long flags; 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci if (test_bit(FLG_BUSY_TIMER, &dch->Flags)) { 81062306a36Sopenharmony_ci spin_lock_irqsave(&card->lock, flags); 81162306a36Sopenharmony_ci rbch = ReadW6692(card, W_D_RBCH); 81262306a36Sopenharmony_ci star = ReadW6692(card, W_D_STAR); 81362306a36Sopenharmony_ci pr_debug("%s: D-Channel Busy RBCH %02x STAR %02x\n", 81462306a36Sopenharmony_ci card->name, rbch, star); 81562306a36Sopenharmony_ci if (star & W_D_STAR_XBZ) /* D-Channel Busy */ 81662306a36Sopenharmony_ci test_and_set_bit(FLG_L1_BUSY, &dch->Flags); 81762306a36Sopenharmony_ci else { 81862306a36Sopenharmony_ci /* discard frame; reset transceiver */ 81962306a36Sopenharmony_ci test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags); 82062306a36Sopenharmony_ci if (dch->tx_idx) 82162306a36Sopenharmony_ci dch->tx_idx = 0; 82262306a36Sopenharmony_ci else 82362306a36Sopenharmony_ci pr_info("%s: W6692 D-Channel Busy no tx_idx\n", 82462306a36Sopenharmony_ci card->name); 82562306a36Sopenharmony_ci /* Transmitter reset */ 82662306a36Sopenharmony_ci WriteW6692(card, W_D_CMDR, W_D_CMDR_XRST); 82762306a36Sopenharmony_ci } 82862306a36Sopenharmony_ci spin_unlock_irqrestore(&card->lock, flags); 82962306a36Sopenharmony_ci } 83062306a36Sopenharmony_ci} 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_cistatic void initW6692(struct w6692_hw *card) 83362306a36Sopenharmony_ci{ 83462306a36Sopenharmony_ci u8 val; 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci timer_setup(&card->dch.timer, dbusy_timer_handler, 0); 83762306a36Sopenharmony_ci w6692_mode(&card->bc[0], ISDN_P_NONE); 83862306a36Sopenharmony_ci w6692_mode(&card->bc[1], ISDN_P_NONE); 83962306a36Sopenharmony_ci WriteW6692(card, W_D_CTL, 0x00); 84062306a36Sopenharmony_ci disable_hwirq(card); 84162306a36Sopenharmony_ci WriteW6692(card, W_D_SAM, 0xff); 84262306a36Sopenharmony_ci WriteW6692(card, W_D_TAM, 0xff); 84362306a36Sopenharmony_ci WriteW6692(card, W_D_MODE, W_D_MODE_RACT); 84462306a36Sopenharmony_ci card->state = W_L1CMD_RST; 84562306a36Sopenharmony_ci ph_command(card, W_L1CMD_RST); 84662306a36Sopenharmony_ci ph_command(card, W_L1CMD_ECK); 84762306a36Sopenharmony_ci /* enable all IRQ but extern */ 84862306a36Sopenharmony_ci card->imask = 0x18; 84962306a36Sopenharmony_ci WriteW6692(card, W_D_EXIM, 0x00); 85062306a36Sopenharmony_ci WriteW6692B(&card->bc[0], W_B_EXIM, 0); 85162306a36Sopenharmony_ci WriteW6692B(&card->bc[1], W_B_EXIM, 0); 85262306a36Sopenharmony_ci /* Reset D-chan receiver and transmitter */ 85362306a36Sopenharmony_ci WriteW6692(card, W_D_CMDR, W_D_CMDR_RRST | W_D_CMDR_XRST); 85462306a36Sopenharmony_ci /* Reset B-chan receiver and transmitter */ 85562306a36Sopenharmony_ci WriteW6692B(&card->bc[0], W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_XRST); 85662306a36Sopenharmony_ci WriteW6692B(&card->bc[1], W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_XRST); 85762306a36Sopenharmony_ci /* enable peripheral */ 85862306a36Sopenharmony_ci if (card->subtype == W6692_USR) { 85962306a36Sopenharmony_ci /* seems that USR implemented some power control features 86062306a36Sopenharmony_ci * Pin 79 is connected to the oscilator circuit so we 86162306a36Sopenharmony_ci * have to handle it here 86262306a36Sopenharmony_ci */ 86362306a36Sopenharmony_ci card->pctl = 0x80; 86462306a36Sopenharmony_ci card->xdata = 0; 86562306a36Sopenharmony_ci WriteW6692(card, W_PCTL, card->pctl); 86662306a36Sopenharmony_ci WriteW6692(card, W_XDATA, card->xdata); 86762306a36Sopenharmony_ci } else { 86862306a36Sopenharmony_ci card->pctl = W_PCTL_OE5 | W_PCTL_OE4 | W_PCTL_OE2 | 86962306a36Sopenharmony_ci W_PCTL_OE1 | W_PCTL_OE0; 87062306a36Sopenharmony_ci card->xaddr = 0x00;/* all sw off */ 87162306a36Sopenharmony_ci if (card->fmask & pots) 87262306a36Sopenharmony_ci card->xdata |= 0x06; /* POWER UP/ LED OFF / ALAW */ 87362306a36Sopenharmony_ci if (card->fmask & led) 87462306a36Sopenharmony_ci card->xdata |= 0x04; /* LED OFF */ 87562306a36Sopenharmony_ci if ((card->fmask & pots) || (card->fmask & led)) { 87662306a36Sopenharmony_ci WriteW6692(card, W_PCTL, card->pctl); 87762306a36Sopenharmony_ci WriteW6692(card, W_XADDR, card->xaddr); 87862306a36Sopenharmony_ci WriteW6692(card, W_XDATA, card->xdata); 87962306a36Sopenharmony_ci val = ReadW6692(card, W_XADDR); 88062306a36Sopenharmony_ci if (debug & DEBUG_HW) 88162306a36Sopenharmony_ci pr_notice("%s: W_XADDR=%02x\n", 88262306a36Sopenharmony_ci card->name, val); 88362306a36Sopenharmony_ci } 88462306a36Sopenharmony_ci } 88562306a36Sopenharmony_ci} 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_cistatic void 88862306a36Sopenharmony_cireset_w6692(struct w6692_hw *card) 88962306a36Sopenharmony_ci{ 89062306a36Sopenharmony_ci WriteW6692(card, W_D_CTL, W_D_CTL_SRST); 89162306a36Sopenharmony_ci mdelay(10); 89262306a36Sopenharmony_ci WriteW6692(card, W_D_CTL, 0); 89362306a36Sopenharmony_ci} 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_cistatic int 89662306a36Sopenharmony_ciinit_card(struct w6692_hw *card) 89762306a36Sopenharmony_ci{ 89862306a36Sopenharmony_ci int cnt = 3; 89962306a36Sopenharmony_ci u_long flags; 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci spin_lock_irqsave(&card->lock, flags); 90262306a36Sopenharmony_ci disable_hwirq(card); 90362306a36Sopenharmony_ci spin_unlock_irqrestore(&card->lock, flags); 90462306a36Sopenharmony_ci if (request_irq(card->irq, w6692_irq, IRQF_SHARED, card->name, card)) { 90562306a36Sopenharmony_ci pr_info("%s: couldn't get interrupt %d\n", card->name, 90662306a36Sopenharmony_ci card->irq); 90762306a36Sopenharmony_ci return -EIO; 90862306a36Sopenharmony_ci } 90962306a36Sopenharmony_ci while (cnt--) { 91062306a36Sopenharmony_ci spin_lock_irqsave(&card->lock, flags); 91162306a36Sopenharmony_ci initW6692(card); 91262306a36Sopenharmony_ci enable_hwirq(card); 91362306a36Sopenharmony_ci spin_unlock_irqrestore(&card->lock, flags); 91462306a36Sopenharmony_ci /* Timeout 10ms */ 91562306a36Sopenharmony_ci msleep_interruptible(10); 91662306a36Sopenharmony_ci if (debug & DEBUG_HW) 91762306a36Sopenharmony_ci pr_notice("%s: IRQ %d count %d\n", card->name, 91862306a36Sopenharmony_ci card->irq, card->irqcnt); 91962306a36Sopenharmony_ci if (!card->irqcnt) { 92062306a36Sopenharmony_ci pr_info("%s: IRQ(%d) getting no IRQs during init %d\n", 92162306a36Sopenharmony_ci card->name, card->irq, 3 - cnt); 92262306a36Sopenharmony_ci reset_w6692(card); 92362306a36Sopenharmony_ci } else 92462306a36Sopenharmony_ci return 0; 92562306a36Sopenharmony_ci } 92662306a36Sopenharmony_ci free_irq(card->irq, card); 92762306a36Sopenharmony_ci return -EIO; 92862306a36Sopenharmony_ci} 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_cistatic int 93162306a36Sopenharmony_ciw6692_l2l1B(struct mISDNchannel *ch, struct sk_buff *skb) 93262306a36Sopenharmony_ci{ 93362306a36Sopenharmony_ci struct bchannel *bch = container_of(ch, struct bchannel, ch); 93462306a36Sopenharmony_ci struct w6692_ch *bc = container_of(bch, struct w6692_ch, bch); 93562306a36Sopenharmony_ci struct w6692_hw *card = bch->hw; 93662306a36Sopenharmony_ci int ret = -EINVAL; 93762306a36Sopenharmony_ci struct mISDNhead *hh = mISDN_HEAD_P(skb); 93862306a36Sopenharmony_ci unsigned long flags; 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci switch (hh->prim) { 94162306a36Sopenharmony_ci case PH_DATA_REQ: 94262306a36Sopenharmony_ci spin_lock_irqsave(&card->lock, flags); 94362306a36Sopenharmony_ci ret = bchannel_senddata(bch, skb); 94462306a36Sopenharmony_ci if (ret > 0) { /* direct TX */ 94562306a36Sopenharmony_ci ret = 0; 94662306a36Sopenharmony_ci W6692_fill_Bfifo(bc); 94762306a36Sopenharmony_ci } 94862306a36Sopenharmony_ci spin_unlock_irqrestore(&card->lock, flags); 94962306a36Sopenharmony_ci return ret; 95062306a36Sopenharmony_ci case PH_ACTIVATE_REQ: 95162306a36Sopenharmony_ci spin_lock_irqsave(&card->lock, flags); 95262306a36Sopenharmony_ci if (!test_and_set_bit(FLG_ACTIVE, &bch->Flags)) 95362306a36Sopenharmony_ci ret = w6692_mode(bc, ch->protocol); 95462306a36Sopenharmony_ci else 95562306a36Sopenharmony_ci ret = 0; 95662306a36Sopenharmony_ci spin_unlock_irqrestore(&card->lock, flags); 95762306a36Sopenharmony_ci if (!ret) 95862306a36Sopenharmony_ci _queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY, 0, 95962306a36Sopenharmony_ci NULL, GFP_KERNEL); 96062306a36Sopenharmony_ci break; 96162306a36Sopenharmony_ci case PH_DEACTIVATE_REQ: 96262306a36Sopenharmony_ci spin_lock_irqsave(&card->lock, flags); 96362306a36Sopenharmony_ci mISDN_clear_bchannel(bch); 96462306a36Sopenharmony_ci w6692_mode(bc, ISDN_P_NONE); 96562306a36Sopenharmony_ci spin_unlock_irqrestore(&card->lock, flags); 96662306a36Sopenharmony_ci _queue_data(ch, PH_DEACTIVATE_IND, MISDN_ID_ANY, 0, 96762306a36Sopenharmony_ci NULL, GFP_KERNEL); 96862306a36Sopenharmony_ci ret = 0; 96962306a36Sopenharmony_ci break; 97062306a36Sopenharmony_ci default: 97162306a36Sopenharmony_ci pr_info("%s: %s unknown prim(%x,%x)\n", 97262306a36Sopenharmony_ci card->name, __func__, hh->prim, hh->id); 97362306a36Sopenharmony_ci ret = -EINVAL; 97462306a36Sopenharmony_ci } 97562306a36Sopenharmony_ci if (!ret) 97662306a36Sopenharmony_ci dev_kfree_skb(skb); 97762306a36Sopenharmony_ci return ret; 97862306a36Sopenharmony_ci} 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_cistatic int 98162306a36Sopenharmony_cichannel_bctrl(struct bchannel *bch, struct mISDN_ctrl_req *cq) 98262306a36Sopenharmony_ci{ 98362306a36Sopenharmony_ci return mISDN_ctrl_bchannel(bch, cq); 98462306a36Sopenharmony_ci} 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_cistatic int 98762306a36Sopenharmony_ciopen_bchannel(struct w6692_hw *card, struct channel_req *rq) 98862306a36Sopenharmony_ci{ 98962306a36Sopenharmony_ci struct bchannel *bch; 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci if (rq->adr.channel == 0 || rq->adr.channel > 2) 99262306a36Sopenharmony_ci return -EINVAL; 99362306a36Sopenharmony_ci if (rq->protocol == ISDN_P_NONE) 99462306a36Sopenharmony_ci return -EINVAL; 99562306a36Sopenharmony_ci bch = &card->bc[rq->adr.channel - 1].bch; 99662306a36Sopenharmony_ci if (test_and_set_bit(FLG_OPEN, &bch->Flags)) 99762306a36Sopenharmony_ci return -EBUSY; /* b-channel can be only open once */ 99862306a36Sopenharmony_ci bch->ch.protocol = rq->protocol; 99962306a36Sopenharmony_ci rq->ch = &bch->ch; 100062306a36Sopenharmony_ci return 0; 100162306a36Sopenharmony_ci} 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_cistatic int 100462306a36Sopenharmony_cichannel_ctrl(struct w6692_hw *card, struct mISDN_ctrl_req *cq) 100562306a36Sopenharmony_ci{ 100662306a36Sopenharmony_ci int ret = 0; 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_ci switch (cq->op) { 100962306a36Sopenharmony_ci case MISDN_CTRL_GETOP: 101062306a36Sopenharmony_ci cq->op = MISDN_CTRL_L1_TIMER3; 101162306a36Sopenharmony_ci break; 101262306a36Sopenharmony_ci case MISDN_CTRL_L1_TIMER3: 101362306a36Sopenharmony_ci ret = l1_event(card->dch.l1, HW_TIMER3_VALUE | (cq->p1 & 0xff)); 101462306a36Sopenharmony_ci break; 101562306a36Sopenharmony_ci default: 101662306a36Sopenharmony_ci pr_info("%s: unknown CTRL OP %x\n", card->name, cq->op); 101762306a36Sopenharmony_ci ret = -EINVAL; 101862306a36Sopenharmony_ci break; 101962306a36Sopenharmony_ci } 102062306a36Sopenharmony_ci return ret; 102162306a36Sopenharmony_ci} 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_cistatic int 102462306a36Sopenharmony_ciw6692_bctrl(struct mISDNchannel *ch, u32 cmd, void *arg) 102562306a36Sopenharmony_ci{ 102662306a36Sopenharmony_ci struct bchannel *bch = container_of(ch, struct bchannel, ch); 102762306a36Sopenharmony_ci struct w6692_ch *bc = container_of(bch, struct w6692_ch, bch); 102862306a36Sopenharmony_ci struct w6692_hw *card = bch->hw; 102962306a36Sopenharmony_ci int ret = -EINVAL; 103062306a36Sopenharmony_ci u_long flags; 103162306a36Sopenharmony_ci 103262306a36Sopenharmony_ci pr_debug("%s: %s cmd:%x %p\n", card->name, __func__, cmd, arg); 103362306a36Sopenharmony_ci switch (cmd) { 103462306a36Sopenharmony_ci case CLOSE_CHANNEL: 103562306a36Sopenharmony_ci test_and_clear_bit(FLG_OPEN, &bch->Flags); 103662306a36Sopenharmony_ci cancel_work_sync(&bch->workq); 103762306a36Sopenharmony_ci spin_lock_irqsave(&card->lock, flags); 103862306a36Sopenharmony_ci mISDN_clear_bchannel(bch); 103962306a36Sopenharmony_ci w6692_mode(bc, ISDN_P_NONE); 104062306a36Sopenharmony_ci spin_unlock_irqrestore(&card->lock, flags); 104162306a36Sopenharmony_ci ch->protocol = ISDN_P_NONE; 104262306a36Sopenharmony_ci ch->peer = NULL; 104362306a36Sopenharmony_ci module_put(THIS_MODULE); 104462306a36Sopenharmony_ci ret = 0; 104562306a36Sopenharmony_ci break; 104662306a36Sopenharmony_ci case CONTROL_CHANNEL: 104762306a36Sopenharmony_ci ret = channel_bctrl(bch, arg); 104862306a36Sopenharmony_ci break; 104962306a36Sopenharmony_ci default: 105062306a36Sopenharmony_ci pr_info("%s: %s unknown prim(%x)\n", 105162306a36Sopenharmony_ci card->name, __func__, cmd); 105262306a36Sopenharmony_ci } 105362306a36Sopenharmony_ci return ret; 105462306a36Sopenharmony_ci} 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_cistatic int 105762306a36Sopenharmony_ciw6692_l2l1D(struct mISDNchannel *ch, struct sk_buff *skb) 105862306a36Sopenharmony_ci{ 105962306a36Sopenharmony_ci struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D); 106062306a36Sopenharmony_ci struct dchannel *dch = container_of(dev, struct dchannel, dev); 106162306a36Sopenharmony_ci struct w6692_hw *card = container_of(dch, struct w6692_hw, dch); 106262306a36Sopenharmony_ci int ret = -EINVAL; 106362306a36Sopenharmony_ci struct mISDNhead *hh = mISDN_HEAD_P(skb); 106462306a36Sopenharmony_ci u32 id; 106562306a36Sopenharmony_ci u_long flags; 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_ci switch (hh->prim) { 106862306a36Sopenharmony_ci case PH_DATA_REQ: 106962306a36Sopenharmony_ci spin_lock_irqsave(&card->lock, flags); 107062306a36Sopenharmony_ci ret = dchannel_senddata(dch, skb); 107162306a36Sopenharmony_ci if (ret > 0) { /* direct TX */ 107262306a36Sopenharmony_ci id = hh->id; /* skb can be freed */ 107362306a36Sopenharmony_ci W6692_fill_Dfifo(card); 107462306a36Sopenharmony_ci ret = 0; 107562306a36Sopenharmony_ci spin_unlock_irqrestore(&card->lock, flags); 107662306a36Sopenharmony_ci queue_ch_frame(ch, PH_DATA_CNF, id, NULL); 107762306a36Sopenharmony_ci } else 107862306a36Sopenharmony_ci spin_unlock_irqrestore(&card->lock, flags); 107962306a36Sopenharmony_ci return ret; 108062306a36Sopenharmony_ci case PH_ACTIVATE_REQ: 108162306a36Sopenharmony_ci ret = l1_event(dch->l1, hh->prim); 108262306a36Sopenharmony_ci break; 108362306a36Sopenharmony_ci case PH_DEACTIVATE_REQ: 108462306a36Sopenharmony_ci test_and_clear_bit(FLG_L2_ACTIVATED, &dch->Flags); 108562306a36Sopenharmony_ci ret = l1_event(dch->l1, hh->prim); 108662306a36Sopenharmony_ci break; 108762306a36Sopenharmony_ci } 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci if (!ret) 109062306a36Sopenharmony_ci dev_kfree_skb(skb); 109162306a36Sopenharmony_ci return ret; 109262306a36Sopenharmony_ci} 109362306a36Sopenharmony_ci 109462306a36Sopenharmony_cistatic int 109562306a36Sopenharmony_ciw6692_l1callback(struct dchannel *dch, u32 cmd) 109662306a36Sopenharmony_ci{ 109762306a36Sopenharmony_ci struct w6692_hw *card = container_of(dch, struct w6692_hw, dch); 109862306a36Sopenharmony_ci u_long flags; 109962306a36Sopenharmony_ci 110062306a36Sopenharmony_ci pr_debug("%s: cmd(%x) state(%02x)\n", card->name, cmd, card->state); 110162306a36Sopenharmony_ci switch (cmd) { 110262306a36Sopenharmony_ci case INFO3_P8: 110362306a36Sopenharmony_ci spin_lock_irqsave(&card->lock, flags); 110462306a36Sopenharmony_ci ph_command(card, W_L1CMD_AR8); 110562306a36Sopenharmony_ci spin_unlock_irqrestore(&card->lock, flags); 110662306a36Sopenharmony_ci break; 110762306a36Sopenharmony_ci case INFO3_P10: 110862306a36Sopenharmony_ci spin_lock_irqsave(&card->lock, flags); 110962306a36Sopenharmony_ci ph_command(card, W_L1CMD_AR10); 111062306a36Sopenharmony_ci spin_unlock_irqrestore(&card->lock, flags); 111162306a36Sopenharmony_ci break; 111262306a36Sopenharmony_ci case HW_RESET_REQ: 111362306a36Sopenharmony_ci spin_lock_irqsave(&card->lock, flags); 111462306a36Sopenharmony_ci if (card->state != W_L1IND_DRD) 111562306a36Sopenharmony_ci ph_command(card, W_L1CMD_RST); 111662306a36Sopenharmony_ci ph_command(card, W_L1CMD_ECK); 111762306a36Sopenharmony_ci spin_unlock_irqrestore(&card->lock, flags); 111862306a36Sopenharmony_ci break; 111962306a36Sopenharmony_ci case HW_DEACT_REQ: 112062306a36Sopenharmony_ci skb_queue_purge(&dch->squeue); 112162306a36Sopenharmony_ci if (dch->tx_skb) { 112262306a36Sopenharmony_ci dev_kfree_skb(dch->tx_skb); 112362306a36Sopenharmony_ci dch->tx_skb = NULL; 112462306a36Sopenharmony_ci } 112562306a36Sopenharmony_ci dch->tx_idx = 0; 112662306a36Sopenharmony_ci if (dch->rx_skb) { 112762306a36Sopenharmony_ci dev_kfree_skb(dch->rx_skb); 112862306a36Sopenharmony_ci dch->rx_skb = NULL; 112962306a36Sopenharmony_ci } 113062306a36Sopenharmony_ci test_and_clear_bit(FLG_TX_BUSY, &dch->Flags); 113162306a36Sopenharmony_ci if (test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags)) 113262306a36Sopenharmony_ci del_timer(&dch->timer); 113362306a36Sopenharmony_ci break; 113462306a36Sopenharmony_ci case HW_POWERUP_REQ: 113562306a36Sopenharmony_ci spin_lock_irqsave(&card->lock, flags); 113662306a36Sopenharmony_ci ph_command(card, W_L1CMD_ECK); 113762306a36Sopenharmony_ci spin_unlock_irqrestore(&card->lock, flags); 113862306a36Sopenharmony_ci break; 113962306a36Sopenharmony_ci case PH_ACTIVATE_IND: 114062306a36Sopenharmony_ci test_and_set_bit(FLG_ACTIVE, &dch->Flags); 114162306a36Sopenharmony_ci _queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL, 114262306a36Sopenharmony_ci GFP_ATOMIC); 114362306a36Sopenharmony_ci break; 114462306a36Sopenharmony_ci case PH_DEACTIVATE_IND: 114562306a36Sopenharmony_ci test_and_clear_bit(FLG_ACTIVE, &dch->Flags); 114662306a36Sopenharmony_ci _queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL, 114762306a36Sopenharmony_ci GFP_ATOMIC); 114862306a36Sopenharmony_ci break; 114962306a36Sopenharmony_ci default: 115062306a36Sopenharmony_ci pr_debug("%s: %s unknown command %x\n", card->name, 115162306a36Sopenharmony_ci __func__, cmd); 115262306a36Sopenharmony_ci return -1; 115362306a36Sopenharmony_ci } 115462306a36Sopenharmony_ci return 0; 115562306a36Sopenharmony_ci} 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_cistatic int 115862306a36Sopenharmony_ciopen_dchannel(struct w6692_hw *card, struct channel_req *rq, void *caller) 115962306a36Sopenharmony_ci{ 116062306a36Sopenharmony_ci pr_debug("%s: %s dev(%d) open from %p\n", card->name, __func__, 116162306a36Sopenharmony_ci card->dch.dev.id, caller); 116262306a36Sopenharmony_ci if (rq->protocol != ISDN_P_TE_S0) 116362306a36Sopenharmony_ci return -EINVAL; 116462306a36Sopenharmony_ci if (rq->adr.channel == 1) 116562306a36Sopenharmony_ci /* E-Channel not supported */ 116662306a36Sopenharmony_ci return -EINVAL; 116762306a36Sopenharmony_ci rq->ch = &card->dch.dev.D; 116862306a36Sopenharmony_ci rq->ch->protocol = rq->protocol; 116962306a36Sopenharmony_ci if (card->dch.state == 7) 117062306a36Sopenharmony_ci _queue_data(rq->ch, PH_ACTIVATE_IND, MISDN_ID_ANY, 117162306a36Sopenharmony_ci 0, NULL, GFP_KERNEL); 117262306a36Sopenharmony_ci return 0; 117362306a36Sopenharmony_ci} 117462306a36Sopenharmony_ci 117562306a36Sopenharmony_cistatic int 117662306a36Sopenharmony_ciw6692_dctrl(struct mISDNchannel *ch, u32 cmd, void *arg) 117762306a36Sopenharmony_ci{ 117862306a36Sopenharmony_ci struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D); 117962306a36Sopenharmony_ci struct dchannel *dch = container_of(dev, struct dchannel, dev); 118062306a36Sopenharmony_ci struct w6692_hw *card = container_of(dch, struct w6692_hw, dch); 118162306a36Sopenharmony_ci struct channel_req *rq; 118262306a36Sopenharmony_ci int err = 0; 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_ci pr_debug("%s: DCTRL: %x %p\n", card->name, cmd, arg); 118562306a36Sopenharmony_ci switch (cmd) { 118662306a36Sopenharmony_ci case OPEN_CHANNEL: 118762306a36Sopenharmony_ci rq = arg; 118862306a36Sopenharmony_ci if (rq->protocol == ISDN_P_TE_S0) 118962306a36Sopenharmony_ci err = open_dchannel(card, rq, __builtin_return_address(0)); 119062306a36Sopenharmony_ci else 119162306a36Sopenharmony_ci err = open_bchannel(card, rq); 119262306a36Sopenharmony_ci if (err) 119362306a36Sopenharmony_ci break; 119462306a36Sopenharmony_ci if (!try_module_get(THIS_MODULE)) 119562306a36Sopenharmony_ci pr_info("%s: cannot get module\n", card->name); 119662306a36Sopenharmony_ci break; 119762306a36Sopenharmony_ci case CLOSE_CHANNEL: 119862306a36Sopenharmony_ci pr_debug("%s: dev(%d) close from %p\n", card->name, 119962306a36Sopenharmony_ci dch->dev.id, __builtin_return_address(0)); 120062306a36Sopenharmony_ci module_put(THIS_MODULE); 120162306a36Sopenharmony_ci break; 120262306a36Sopenharmony_ci case CONTROL_CHANNEL: 120362306a36Sopenharmony_ci err = channel_ctrl(card, arg); 120462306a36Sopenharmony_ci break; 120562306a36Sopenharmony_ci default: 120662306a36Sopenharmony_ci pr_debug("%s: unknown DCTRL command %x\n", card->name, cmd); 120762306a36Sopenharmony_ci return -EINVAL; 120862306a36Sopenharmony_ci } 120962306a36Sopenharmony_ci return err; 121062306a36Sopenharmony_ci} 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_cistatic int 121362306a36Sopenharmony_cisetup_w6692(struct w6692_hw *card) 121462306a36Sopenharmony_ci{ 121562306a36Sopenharmony_ci u32 val; 121662306a36Sopenharmony_ci 121762306a36Sopenharmony_ci if (!request_region(card->addr, 256, card->name)) { 121862306a36Sopenharmony_ci pr_info("%s: config port %x-%x already in use\n", card->name, 121962306a36Sopenharmony_ci card->addr, card->addr + 255); 122062306a36Sopenharmony_ci return -EIO; 122162306a36Sopenharmony_ci } 122262306a36Sopenharmony_ci W6692Version(card); 122362306a36Sopenharmony_ci card->bc[0].addr = card->addr; 122462306a36Sopenharmony_ci card->bc[1].addr = card->addr + 0x40; 122562306a36Sopenharmony_ci val = ReadW6692(card, W_ISTA); 122662306a36Sopenharmony_ci if (debug & DEBUG_HW) 122762306a36Sopenharmony_ci pr_notice("%s ISTA=%02x\n", card->name, val); 122862306a36Sopenharmony_ci val = ReadW6692(card, W_IMASK); 122962306a36Sopenharmony_ci if (debug & DEBUG_HW) 123062306a36Sopenharmony_ci pr_notice("%s IMASK=%02x\n", card->name, val); 123162306a36Sopenharmony_ci val = ReadW6692(card, W_D_EXIR); 123262306a36Sopenharmony_ci if (debug & DEBUG_HW) 123362306a36Sopenharmony_ci pr_notice("%s D_EXIR=%02x\n", card->name, val); 123462306a36Sopenharmony_ci val = ReadW6692(card, W_D_EXIM); 123562306a36Sopenharmony_ci if (debug & DEBUG_HW) 123662306a36Sopenharmony_ci pr_notice("%s D_EXIM=%02x\n", card->name, val); 123762306a36Sopenharmony_ci val = ReadW6692(card, W_D_RSTA); 123862306a36Sopenharmony_ci if (debug & DEBUG_HW) 123962306a36Sopenharmony_ci pr_notice("%s D_RSTA=%02x\n", card->name, val); 124062306a36Sopenharmony_ci return 0; 124162306a36Sopenharmony_ci} 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_cistatic void 124462306a36Sopenharmony_cirelease_card(struct w6692_hw *card) 124562306a36Sopenharmony_ci{ 124662306a36Sopenharmony_ci u_long flags; 124762306a36Sopenharmony_ci 124862306a36Sopenharmony_ci spin_lock_irqsave(&card->lock, flags); 124962306a36Sopenharmony_ci disable_hwirq(card); 125062306a36Sopenharmony_ci w6692_mode(&card->bc[0], ISDN_P_NONE); 125162306a36Sopenharmony_ci w6692_mode(&card->bc[1], ISDN_P_NONE); 125262306a36Sopenharmony_ci if ((card->fmask & led) || card->subtype == W6692_USR) { 125362306a36Sopenharmony_ci card->xdata |= 0x04; /* LED OFF */ 125462306a36Sopenharmony_ci WriteW6692(card, W_XDATA, card->xdata); 125562306a36Sopenharmony_ci } 125662306a36Sopenharmony_ci spin_unlock_irqrestore(&card->lock, flags); 125762306a36Sopenharmony_ci free_irq(card->irq, card); 125862306a36Sopenharmony_ci l1_event(card->dch.l1, CLOSE_CHANNEL); 125962306a36Sopenharmony_ci mISDN_unregister_device(&card->dch.dev); 126062306a36Sopenharmony_ci release_region(card->addr, 256); 126162306a36Sopenharmony_ci mISDN_freebchannel(&card->bc[1].bch); 126262306a36Sopenharmony_ci mISDN_freebchannel(&card->bc[0].bch); 126362306a36Sopenharmony_ci mISDN_freedchannel(&card->dch); 126462306a36Sopenharmony_ci write_lock_irqsave(&card_lock, flags); 126562306a36Sopenharmony_ci list_del(&card->list); 126662306a36Sopenharmony_ci write_unlock_irqrestore(&card_lock, flags); 126762306a36Sopenharmony_ci pci_disable_device(card->pdev); 126862306a36Sopenharmony_ci pci_set_drvdata(card->pdev, NULL); 126962306a36Sopenharmony_ci kfree(card); 127062306a36Sopenharmony_ci} 127162306a36Sopenharmony_ci 127262306a36Sopenharmony_cistatic int 127362306a36Sopenharmony_cisetup_instance(struct w6692_hw *card) 127462306a36Sopenharmony_ci{ 127562306a36Sopenharmony_ci int i, err; 127662306a36Sopenharmony_ci u_long flags; 127762306a36Sopenharmony_ci 127862306a36Sopenharmony_ci snprintf(card->name, MISDN_MAX_IDLEN - 1, "w6692.%d", w6692_cnt + 1); 127962306a36Sopenharmony_ci write_lock_irqsave(&card_lock, flags); 128062306a36Sopenharmony_ci list_add_tail(&card->list, &Cards); 128162306a36Sopenharmony_ci write_unlock_irqrestore(&card_lock, flags); 128262306a36Sopenharmony_ci card->fmask = (1 << w6692_cnt); 128362306a36Sopenharmony_ci _set_debug(card); 128462306a36Sopenharmony_ci spin_lock_init(&card->lock); 128562306a36Sopenharmony_ci mISDN_initdchannel(&card->dch, MAX_DFRAME_LEN_L1, W6692_ph_bh); 128662306a36Sopenharmony_ci card->dch.dev.Dprotocols = (1 << ISDN_P_TE_S0); 128762306a36Sopenharmony_ci card->dch.dev.D.send = w6692_l2l1D; 128862306a36Sopenharmony_ci card->dch.dev.D.ctrl = w6692_dctrl; 128962306a36Sopenharmony_ci card->dch.dev.Bprotocols = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) | 129062306a36Sopenharmony_ci (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK)); 129162306a36Sopenharmony_ci card->dch.hw = card; 129262306a36Sopenharmony_ci card->dch.dev.nrbchan = 2; 129362306a36Sopenharmony_ci for (i = 0; i < 2; i++) { 129462306a36Sopenharmony_ci mISDN_initbchannel(&card->bc[i].bch, MAX_DATA_MEM, 129562306a36Sopenharmony_ci W_B_FIFO_THRESH); 129662306a36Sopenharmony_ci card->bc[i].bch.hw = card; 129762306a36Sopenharmony_ci card->bc[i].bch.nr = i + 1; 129862306a36Sopenharmony_ci card->bc[i].bch.ch.nr = i + 1; 129962306a36Sopenharmony_ci card->bc[i].bch.ch.send = w6692_l2l1B; 130062306a36Sopenharmony_ci card->bc[i].bch.ch.ctrl = w6692_bctrl; 130162306a36Sopenharmony_ci set_channelmap(i + 1, card->dch.dev.channelmap); 130262306a36Sopenharmony_ci list_add(&card->bc[i].bch.ch.list, &card->dch.dev.bchannels); 130362306a36Sopenharmony_ci } 130462306a36Sopenharmony_ci err = setup_w6692(card); 130562306a36Sopenharmony_ci if (err) 130662306a36Sopenharmony_ci goto error_setup; 130762306a36Sopenharmony_ci err = mISDN_register_device(&card->dch.dev, &card->pdev->dev, 130862306a36Sopenharmony_ci card->name); 130962306a36Sopenharmony_ci if (err) 131062306a36Sopenharmony_ci goto error_reg; 131162306a36Sopenharmony_ci err = init_card(card); 131262306a36Sopenharmony_ci if (err) 131362306a36Sopenharmony_ci goto error_init; 131462306a36Sopenharmony_ci err = create_l1(&card->dch, w6692_l1callback); 131562306a36Sopenharmony_ci if (!err) { 131662306a36Sopenharmony_ci w6692_cnt++; 131762306a36Sopenharmony_ci pr_notice("W6692 %d cards installed\n", w6692_cnt); 131862306a36Sopenharmony_ci return 0; 131962306a36Sopenharmony_ci } 132062306a36Sopenharmony_ci 132162306a36Sopenharmony_ci free_irq(card->irq, card); 132262306a36Sopenharmony_cierror_init: 132362306a36Sopenharmony_ci mISDN_unregister_device(&card->dch.dev); 132462306a36Sopenharmony_cierror_reg: 132562306a36Sopenharmony_ci release_region(card->addr, 256); 132662306a36Sopenharmony_cierror_setup: 132762306a36Sopenharmony_ci mISDN_freebchannel(&card->bc[1].bch); 132862306a36Sopenharmony_ci mISDN_freebchannel(&card->bc[0].bch); 132962306a36Sopenharmony_ci mISDN_freedchannel(&card->dch); 133062306a36Sopenharmony_ci write_lock_irqsave(&card_lock, flags); 133162306a36Sopenharmony_ci list_del(&card->list); 133262306a36Sopenharmony_ci write_unlock_irqrestore(&card_lock, flags); 133362306a36Sopenharmony_ci kfree(card); 133462306a36Sopenharmony_ci return err; 133562306a36Sopenharmony_ci} 133662306a36Sopenharmony_ci 133762306a36Sopenharmony_cistatic int 133862306a36Sopenharmony_ciw6692_probe(struct pci_dev *pdev, const struct pci_device_id *ent) 133962306a36Sopenharmony_ci{ 134062306a36Sopenharmony_ci int err = -ENOMEM; 134162306a36Sopenharmony_ci struct w6692_hw *card; 134262306a36Sopenharmony_ci struct w6692map *m = (struct w6692map *)ent->driver_data; 134362306a36Sopenharmony_ci 134462306a36Sopenharmony_ci card = kzalloc(sizeof(struct w6692_hw), GFP_KERNEL); 134562306a36Sopenharmony_ci if (!card) { 134662306a36Sopenharmony_ci pr_info("No kmem for w6692 card\n"); 134762306a36Sopenharmony_ci return err; 134862306a36Sopenharmony_ci } 134962306a36Sopenharmony_ci card->pdev = pdev; 135062306a36Sopenharmony_ci card->subtype = m->subtype; 135162306a36Sopenharmony_ci err = pci_enable_device(pdev); 135262306a36Sopenharmony_ci if (err) { 135362306a36Sopenharmony_ci kfree(card); 135462306a36Sopenharmony_ci return err; 135562306a36Sopenharmony_ci } 135662306a36Sopenharmony_ci 135762306a36Sopenharmony_ci printk(KERN_INFO "mISDN_w6692: found adapter %s at %s\n", 135862306a36Sopenharmony_ci m->name, pci_name(pdev)); 135962306a36Sopenharmony_ci 136062306a36Sopenharmony_ci card->addr = pci_resource_start(pdev, 1); 136162306a36Sopenharmony_ci card->irq = pdev->irq; 136262306a36Sopenharmony_ci pci_set_drvdata(pdev, card); 136362306a36Sopenharmony_ci err = setup_instance(card); 136462306a36Sopenharmony_ci if (err) 136562306a36Sopenharmony_ci pci_set_drvdata(pdev, NULL); 136662306a36Sopenharmony_ci return err; 136762306a36Sopenharmony_ci} 136862306a36Sopenharmony_ci 136962306a36Sopenharmony_cistatic void 137062306a36Sopenharmony_ciw6692_remove_pci(struct pci_dev *pdev) 137162306a36Sopenharmony_ci{ 137262306a36Sopenharmony_ci struct w6692_hw *card = pci_get_drvdata(pdev); 137362306a36Sopenharmony_ci 137462306a36Sopenharmony_ci if (card) 137562306a36Sopenharmony_ci release_card(card); 137662306a36Sopenharmony_ci else 137762306a36Sopenharmony_ci if (debug) 137862306a36Sopenharmony_ci pr_notice("%s: drvdata already removed\n", __func__); 137962306a36Sopenharmony_ci} 138062306a36Sopenharmony_ci 138162306a36Sopenharmony_cistatic const struct pci_device_id w6692_ids[] = { 138262306a36Sopenharmony_ci { PCI_VENDOR_ID_DYNALINK, PCI_DEVICE_ID_DYNALINK_IS64PH, 138362306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, (ulong)&w6692_map[0]}, 138462306a36Sopenharmony_ci { PCI_VENDOR_ID_WINBOND2, PCI_DEVICE_ID_WINBOND2_6692, 138562306a36Sopenharmony_ci PCI_VENDOR_ID_USR, PCI_DEVICE_ID_USR_6692, 0, 0, 138662306a36Sopenharmony_ci (ulong)&w6692_map[2]}, 138762306a36Sopenharmony_ci { PCI_VENDOR_ID_WINBOND2, PCI_DEVICE_ID_WINBOND2_6692, 138862306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, (ulong)&w6692_map[1]}, 138962306a36Sopenharmony_ci { } 139062306a36Sopenharmony_ci}; 139162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, w6692_ids); 139262306a36Sopenharmony_ci 139362306a36Sopenharmony_cistatic struct pci_driver w6692_driver = { 139462306a36Sopenharmony_ci .name = "w6692", 139562306a36Sopenharmony_ci .probe = w6692_probe, 139662306a36Sopenharmony_ci .remove = w6692_remove_pci, 139762306a36Sopenharmony_ci .id_table = w6692_ids, 139862306a36Sopenharmony_ci}; 139962306a36Sopenharmony_ci 140062306a36Sopenharmony_cistatic int __init w6692_init(void) 140162306a36Sopenharmony_ci{ 140262306a36Sopenharmony_ci int err; 140362306a36Sopenharmony_ci 140462306a36Sopenharmony_ci pr_notice("Winbond W6692 PCI driver Rev. %s\n", W6692_REV); 140562306a36Sopenharmony_ci 140662306a36Sopenharmony_ci err = pci_register_driver(&w6692_driver); 140762306a36Sopenharmony_ci return err; 140862306a36Sopenharmony_ci} 140962306a36Sopenharmony_ci 141062306a36Sopenharmony_cistatic void __exit w6692_cleanup(void) 141162306a36Sopenharmony_ci{ 141262306a36Sopenharmony_ci pci_unregister_driver(&w6692_driver); 141362306a36Sopenharmony_ci} 141462306a36Sopenharmony_ci 141562306a36Sopenharmony_cimodule_init(w6692_init); 141662306a36Sopenharmony_cimodule_exit(w6692_cleanup); 1417