diff options
Diffstat (limited to 'drivers')
92 files changed, 12940 insertions, 10383 deletions
diff --git a/drivers/Makefile b/drivers/Makefile index 71ee973cf..def2731d2 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -11,7 +11,7 @@ SUB_DIRS := block char net parport sound misc MOD_SUB_DIRS := $(SUB_DIRS) ALL_SUB_DIRS := $(SUB_DIRS) pci sgi scsi sbus cdrom isdn pnp i2o ieee1394 \ macintosh video dio zorro fc4 usb \ - nubus tc ap1000 atm pcmcia i2c telephony + nubus tc atm pcmcia i2c telephony ifdef CONFIG_DIO SUB_DIRS += dio @@ -137,10 +137,6 @@ SUB_DIRS += atm MOD_SUB_DIRS += atm endif -ifeq ($(CONFIG_AP1000),y) -SUB_DIRS += ap1000 -endif - ifeq ($(CONFIG_FC4),y) SUB_DIRS += fc4 MOD_SUB_DIRS += fc4 diff --git a/drivers/ap1000/Makefile b/drivers/ap1000/Makefile deleted file mode 100644 index 47f37720a..000000000 --- a/drivers/ap1000/Makefile +++ /dev/null @@ -1,29 +0,0 @@ -# File: drivers/ap1000/Makefile -# -# Makefile for the AP1000 drivers -# - -L_TARGET := ap1000.a -L_OBJS := bif.o apfddi.o mac.o plc.o ringbuf.o - -ifeq ($(CONFIG_APBLOCK),y) -L_OBJS += ap.o -else - ifeq ($(CONFIG_APBLOCK),m) - M_OBJS += ap.o - endif -endif - -ifeq ($(CONFIG_DDV),y) -L_OBJS += ddv.o ddv_util.o -else - ifeq ($(CONFIG_DDV),m) - M_OBJS += ddv.o ddv_util.o - endif -endif - -include $(TOPDIR)/Rules.make - -clean: - rm -f core *.o *.a *.s - diff --git a/drivers/ap1000/am79c830.h b/drivers/ap1000/am79c830.h deleted file mode 100644 index f9ba50910..000000000 --- a/drivers/ap1000/am79c830.h +++ /dev/null @@ -1,276 +0,0 @@ - /* - * Copyright 1996 The Australian National University. - * Copyright 1996 Fujitsu Laboratories Limited - * - * This software may be distributed under the terms of the Gnu - * Public License version 2 or later - */ -/* - * Definitions for the AM79C830 FORMAC (Fiber Optic Ring MAC) chip. - */ - -typedef int formac_reg; - -struct formac { - formac_reg cmdreg1; /* command register 1 */ - formac_reg cmdreg2; /* command register 2 */ -#define st1u cmdreg1 /* status reg 1, upper */ -#define st1l cmdreg2 /* status reg 1, lower */ - formac_reg st2u; /* status reg 2, upper */ - formac_reg st2l; /* status reg 2, lower */ - formac_reg imsk1u; /* interrupt mask 1, upper */ - formac_reg imsk1l; /* interrupt mask 1, lower */ - formac_reg imsk2u; /* interrupt mask 2, upper */ - formac_reg imsk2l; /* interrupt mask 2, lower */ - formac_reg said; /* short address, individual */ - formac_reg laim; /* long adrs, indiv, MS word */ - formac_reg laic; /* long adrs, indiv, middle word */ - formac_reg lail; /* long adrs, indiv, LS word */ - formac_reg sagp; /* short address, group */ - formac_reg lagm; /* short adrs, group, MS word */ - formac_reg lagc; /* short adrs, group, middle word */ - formac_reg lagl; /* short adrs, group, LS word */ - formac_reg mdreg1; /* mode reg 1 */ - formac_reg stmchn; /* state machine reg */ - formac_reg mir1; /* MAC information reg, upper */ - formac_reg mir0; /* MAC information reg, lower */ - formac_reg tmax; /* TMax value (2's-comp) */ - formac_reg tvx; /* TVX value (2's-comp) */ - formac_reg trt; /* TRT timer value */ - formac_reg tht; /* THT timer value */ - formac_reg tneg; /* current TNeg (2's-comp) */ - formac_reg tmrs; /* extra bits of tneg, trt, tht; late count */ - formac_reg treq0; /* our TReq (2's-comp), lower */ - formac_reg treq1; /* our TReq (2's-comp), upper */ - formac_reg pri0; /* priority reg for async queue 0 */ - formac_reg pri1; /* priority reg for async queue 1 */ - formac_reg pri2; /* priority reg for async queue 2 */ - formac_reg tsync; /* TSync value (2's-comp) */ - formac_reg mdreg2; /* mode reg 2 */ - formac_reg frmthr; /* frame threshold reg */ - formac_reg eacb; /* end address of claim/beacon area */ - formac_reg earv; /* end address of receive area */ - formac_reg eas; /* end address of sync queue */ - formac_reg eaa0; /* end address of async queue 0 */ - formac_reg eaa1; /* end address of async queue 1 */ - formac_reg eaa2; /* end address of async queue 2 */ - formac_reg sacl; /* start address of claim frame */ - formac_reg sabc; /* start address of beacon frame */ - formac_reg wpxsf; /* write pointer, special frames */ - formac_reg rpxsf; /* read pointer, special frames */ - formac_reg dummy1; /* not used */ - formac_reg rpr; /* read pointer, receive */ - formac_reg wpr; /* write pointer, receive */ - formac_reg swpr; /* shadow write pointer, receive */ - formac_reg wpxs; /* write pointer, sync queue */ - formac_reg wpxa0; /* write pointer, async queue 0 */ - formac_reg wpxa1; /* write pointer, async queue 1 */ - formac_reg wpxa2; /* write pointer, async queue 2 */ - formac_reg swpxs; /* shadow write pointer, sync queue */ - formac_reg swpxa0; /* shadow write pointer, async queue 0 */ - formac_reg swpxa1; /* shadow write pointer, async queue 1 */ - formac_reg swpxa2; /* shadow write pointer, async queue 2 */ - formac_reg rpxs; /* read pointer, sync queue */ - formac_reg rpxa0; /* read pointer, async queue 0 */ - formac_reg rpxa1; /* read pointer, async queue 1 */ - formac_reg rpxa2; /* read pointer, async queue 2 */ - formac_reg marr; /* memory address for random reads */ - formac_reg marw; /* memory address for random writes */ - formac_reg mdru; /* memory data register, upper */ - formac_reg mdrl; /* memory data register, lower */ - formac_reg tmsync; /* TSync timer value */ - formac_reg fcntr; /* frame counter */ - formac_reg lcntr; /* lost counter */ - formac_reg ecntr; /* error counter */ -}; - -/* Values for cmdreg1 */ -#define C1_SOFTWARE_RESET 1 -#define C1_IRMEMWI 2 -#define C1_IRMEMWO 3 -#define C1_IDLE_LISTEN 4 -#define C1_CLAIM_LISTEN 5 -#define C1_BEACON_LISTEN 6 -#define C1_LOAD_TVX 7 -#define C1_SEND_NR_TOKEN 0x0c -#define C1_SEND_R_TOKEN 0x0d -#define C1_ENTER_SI_MODE 0x0e -#define C1_EXIT_SI_MODE 0x0f -#define C1_CLR_SYNCQ_LOCK 0x11 -#define C1_CLR_ASYNCQ0_LOCK 0x12 -#define C1_CLR_ASYNCQ1_LOCK 0x14 -#define C1_CLR_ASYNCQ2_LOCK 0x18 -#define C1_CLR_RECVQ_LOCK 0x20 -#define C1_CLR_ALL_LOCKS 0x3f - -/* Values for cmdreg2 */ -#define C2_XMIT_SYNCQ 1 -#define C2_XMIT_ASYNCQ0 2 -#define C2_XMIT_ASYNCQ1 4 -#define C2_XMIT_ASYNCQ2 8 -#define C2_ABORT_XMIT 0x10 -#define C2_RESET_XMITQS 0x20 -#define C2_SET_TAG 0x30 -#define C2_EN_RECV_FRAME 0x40 - -/* Bits in (st1u << 16) + st1l (and (imsk1u << 16) + imsk1l) */ -#define S1_XMIT_ABORT 0x80000000 -#define S1_XABORT_ASYNC2 0x40000000 -#define S1_XABORT_ASYNC1 0x20000000 -#define S1_XABORT_ASYNC0 0x10000000 -#define S1_XABORT_SYNC 0x08000000 -#define S1_XBUF_FULL_SYNC 0x04000000 -#define S1_XBUF_FULL_ASYNC 0x02000000 -#define S1_XDONE_SYNC 0x01000000 -#define S1_END_CHAIN_ASYNC2 0x00800000 -#define S1_END_CHAIN_ASYNC1 0x00400000 -#define S1_END_CHAIN_ASYNC0 0x00200000 -#define S1_END_CHAIN_SYNC 0x00100000 -#define S1_END_FRAME_ASYNC2 0x00080000 -#define S1_END_FRAME_ASYNC1 0x00040000 -#define S1_END_FRAME_ASYNC0 0x00020000 -#define S1_END_FRAME_SYNC 0x00010000 -#define S1_BUF_UNDERRUN_ASYNC2 0x00008000 -#define S1_BUF_UNDERRUN_ASYNC1 0x00004000 -#define S1_BUF_UNDERRUN_ASYNC0 0x00002000 -#define S1_BUF_UNDERRUN_SYNC 0x00001000 -#define S1_PAR_ERROR_ASYNC2 0x00000800 -#define S1_PAR_ERROR_ASYNC1 0x00000400 -#define S1_PAR_ERROR_ASYNC0 0x00000200 -#define S1_PAR_ERROR_SYNC 0x00000100 -#define S1_XINSTR_FULL_ASYNC2 0x00000080 -#define S1_XINSTR_FULL_ASYNC1 0x00000040 -#define S1_XINSTR_FULL_ASYNC0 0x00000020 -#define S1_XINSTR_FULL_SYNC 0x00000010 -#define S1_QUEUE_LOCK_ASYNC2 0x00000008 -#define S1_QUEUE_LOCK_ASYNC1 0x00000004 -#define S1_QUEUE_LOCK_ASYNC0 0x00000002 -#define S1_QUEUE_LOCK_SYNC 0x00000001 - -/* Bits in (st2u << 16) + st2l (and (imsk2u << 16) + imsk2l) */ -#define S2_RECV_COMPLETE 0x80000000 -#define S2_RECV_BUF_EMPTY 0x40000000 -#define S2_RECV_ABORT 0x20000000 -#define S2_RECV_BUF_FULL 0x10000000 -#define S2_RECV_FIFO_OVF 0x08000000 -#define S2_RECV_FRAME 0x04000000 -#define S2_RECV_FRCT_OVF 0x02000000 -#define S2_NP_SIMULT_LOAD 0x01000000 -#define S2_ERR_SPECIAL_FR 0x00800000 -#define S2_CLAIM_STATE 0x00400000 -#define S2_MY_CLAIM 0x00200000 -#define S2_HIGHER_CLAIM 0x00100000 -#define S2_LOWER_CLAIM 0x00080000 -#define S2_BEACON_STATE 0x00040000 -#define S2_MY_BEACON 0x00020000 -#define S2_OTHER_BEACON 0x00010000 -#define S2_RING_OP 0x00008000 -#define S2_MULTIPLE_DA 0x00004000 -#define S2_TOKEN_ERR 0x00002000 -#define S2_TOKEN_ISSUED 0x00001000 -#define S2_TVX_EXP 0x00000800 -#define S2_TRT_EXP 0x00000400 -#define S2_MISSED_FRAME 0x00000200 -#define S2_ADDRESS_DET 0x00000100 -#define S2_PHY_INVALID 0x00000080 -#define S2_LOST_CTR_OVF 0x00000040 -#define S2_ERR_CTR_OVF 0x00000020 -#define S2_FRAME_CTR_OVF 0x00000010 -#define S2_SHORT_IFG 0x00000008 -#define S2_DUPL_CLAIM 0x00000004 -#define S2_TRT_EXP_RECOV 0x00000002 - -/* Bits in mdreg1 */ -#define M1_SINGLE_FRAME 0x8000 -#define M1_MODE 0x7000 -#define M1_MODE_INITIALIZE 0x0000 -#define M1_MODE_MEMORY 0x1000 -#define M1_MODE_ONLINE_SP 0x2000 -#define M1_MODE_ONLINE 0x3000 -#define M1_MODE_INT_LOOP 0x4000 -#define M1_MODE_EXT_LOOP 0x7000 -#define M1_SHORT_ADRS 0x0800 -#define M1_ADDET 0x0700 -#define M1_ADDET_NORM 0x0000 -#define M1_ADDET_METOO 0x0100 -#define M1_ADDET_NSA_NOTME 0x0200 -#define M1_ADDET_NSA 0x0300 -#define M1_ADDET_DISABLE_RECV 0x0400 -#define M1_ADDET_LIM_PROMISC 0x0600 -#define M1_ADDET_PROMISC 0x0700 -#define M1_SELECT_RA 0x0080 -#define M1_DISABLE_CARRY 0x0040 -#define M1_EXT_GRP 0x0030 -#define M1_EXT_GRP_MYGRP 0x0000 -#define M1_EXT_GRP_SOFT 0x0010 -#define M1_EXT_GRP_UPPER24 0x0020 -#define M1_EXT_GRP_UPPER16 0x0030 -#define M1_LOCK_XMIT_QS 0x0008 -#define M1_FULL_DUPLEX 0x0004 -#define M1_XMTINH_PIN 0x0002 - -/* Bits in mdreg2 */ -#define M2_TAGMODE 0x8000 -#define M2_STRIP_FCS 0x4000 -#define M2_CHECK_PARITY 0x2000 -#define M2_EVEN_PARITY 0x1000 -#define M2_LSB_FIRST 0x0800 -#define M2_RCV_BYTE_BDRY_MASK 0x0600 -#define M2_RCV_BYTE_BDRY 0x0200 -#define M2_ENABLE_HSREQ 0x0100 -#define M2_ENABLE_NPDMA 0x0080 -#define M2_SYNC_NPDMA 0x0040 -#define M2_SYMBOL_CTRL 0x0020 -#define M2_RECV_BAD_FRAMES 0x0010 -#define M2_AFULL_MASK 0x000f -#define M2_AFULL 0x0001 - -/* Bits in stmchn */ -#define SM_REV_MASK 0xe000 -#define SM_REV 0x2000 -#define SM_SEND_IMM_MODE 0x1000 -#define SM_TOKEN_MODE 0x0c00 -#define SM_TOKEN_MODE_NR 0x0000 -#define SM_TOKEN_MODE_ENTER_R 0x0400 -#define SM_TOKEN_MODE_ENTER_NR 0x0800 -#define SM_TOKEN_MODE_R 0x0c00 -#define SM_RCV_STATE 0x0380 -#define SM_XMIT_STATE 0x0070 -#define SM_MDR_PENDING 0x0008 -#define SM_MDR_TAG 0x0004 - -/* Bits in transmit descriptor */ -#define TD_MORE 0x80000000 -#define TD_MAGIC 0x40000000 -#define TD_BYTE_BDRY_MASK 0x18000000 -#define TD_BYTE_BDRY_1 0x08000000 -#define TD_XMIT_DONE 0x04000000 -#define TD_NO_FCS 0x02000000 -#define TD_XMIT_ABORT 0x01000000 -#define TD_BYTE_BDRY_LG 27 - -/* Bits in pointer in buffer memory (nontag mode) */ -#define PT_MAGIC 0xa0000000 - -/* Bits in receive status word */ -#define RS_VALID 0x80000000 -#define RS_ABORTED 0x40000000 -#define RS_SRC_ROUTE 0x10000000 -#define RS_E_INDIC 0x08000000 -#define RS_A_INDIC 0x04000000 -#define RS_C_INDIC 0x02000000 -#define RS_ERROR 0x01000000 -#define RS_ADDR_MATCH 0x00800000 -#define RS_FRAME_TYPE 0x00700000 -#define RS_FT_SMT 0x00000000 -#define RS_FT_LLC 0x00100000 -#define RS_FT_IMPL 0x00200000 -#define RS_FT_MAC 0x00400000 -#define RS_FT_LLC_SYNC 0x00500000 -#define RS_FT_IMPL_SYNC 0x00600000 -#define RS_BYTE_BDRY_MASK 0x00030000 -#define RS_BYTE_BDRY 0x00010000 -#define RS_BYTE_BDRY_LG 16 - -#define RS_LENGTH 0x0000ffff - diff --git a/drivers/ap1000/am79c864.h b/drivers/ap1000/am79c864.h deleted file mode 100644 index 0fef95791..000000000 --- a/drivers/ap1000/am79c864.h +++ /dev/null @@ -1,162 +0,0 @@ - /* - * Copyright 1996 The Australian National University. - * Copyright 1996 Fujitsu Laboratories Limited - * - * This software may be distributed under the terms of the Gnu - * Public License version 2 or later - */ -/* - * Definitions for Am79c864 PLC (Physical Layer Controller) - */ - -typedef int plc_reg; - -struct plc { - plc_reg ctrl_a; - plc_reg ctrl_b; - plc_reg intr_mask; - plc_reg xmit_vector; - plc_reg vec_length; - plc_reg le_threshold; - plc_reg c_min; - plc_reg tl_min; - plc_reg tb_min; - plc_reg t_out; - plc_reg dummy1; - plc_reg lc_length; - plc_reg t_scrub; - plc_reg ns_max; - plc_reg tpc_load; - plc_reg tne_load; - plc_reg status_a; - plc_reg status_b; - plc_reg tpc; - plc_reg tne; - plc_reg clk_div; - plc_reg bist_sig; - plc_reg rcv_vector; - plc_reg intr_event; - plc_reg viol_sym_ct; - plc_reg min_idle_ct; - plc_reg link_err_ct; -}; - -/* Bits in ctrl_a */ -#define CA_NOISE_TIMER 0x4000 -#define CA_TNE_16BIT 0x2000 -#define CA_TPC_16BIT 0x1000 -#define CA_REQ_SCRUB 0x0800 -#define CA_VSYM_INTR_MODE 0x0200 -#define CA_MINI_INTR_MODE 0x0100 -#define CA_LOOPBACK 0x0080 -#define CA_FOT_OFF 0x0040 -#define CA_EB_LOOP 0x0020 -#define CA_LM_LOOP 0x0010 -#define CA_BYPASS 0x0008 -#define CA_REM_LOOP 0x0004 -#define CA_RF_DISABLE 0x0002 -#define CA_RUN_BIST 0x0001 - -/* Bits in ctrl_b */ -#define CB_CONFIG_CTRL 0x8000 -#define CB_MATCH_LS 0x7800 -#define CB_MATCH_LS_ANY 0x0000 -#define CB_MATCH_LS_QLS 0x4000 -#define CB_MATCH_LS_MLS 0x2000 -#define CB_MATCH_LS_HLS 0x1000 -#define CB_MATCH_LS_ILS 0x0800 -#define CB_MAINT_LS 0x0700 -#define CB_MAINT_LS_QLS 0x0000 -#define CB_MAINT_LS_ILS 0x0100 -#define CB_MAINT_LS_HLS 0x0200 -#define CB_MAINT_LS_MLS 0x0300 -#define CB_MAINT_LS_PDR 0x0600 -#define CB_CLASS_S 0x0080 -#define CB_PC_LCT 0x0060 -#define CB_PC_LCT_NONE 0x0000 -#define CB_PC_LCT_PDR 0x0020 -#define CB_PC_LCT_IDLE 0x0040 -#define CB_PC_LCT_LOOP 0x0060 -#define CB_PC_JOIN 0x0010 -#define CB_LONG_LCT 0x0008 -#define CB_PC_MAINT 0x0004 -#define CB_PCM_CTRL 0x0003 -#define CB_PC_START 0x0001 -#define CB_PC_TRACE 0x0002 -#define CB_PC_STOP 0x0003 - -/* Bits in status_a */ -#define SA_SIG_DETECT 0x0400 -#define SA_PREV_LS 0x0300 -#define SA_PREV_LS_QLS 0x0000 -#define SA_PREV_LS_MLS 0x0100 -#define SA_PREV_LS_HLS 0x0200 -#define SA_PREV_LS_ILS 0x0300 -#define SA_LINE_ST 0x00e0 -#define SA_LINE_ST_NLS 0x0000 -#define SA_LINE_ST_ALS 0x0020 -#define SA_LINE_ST_ILS4 0x0060 -#define SA_LINE_ST_QLS 0x0080 -#define SA_LINE_ST_MLS 0x00a0 -#define SA_LINE_ST_HLS 0x00c0 -#define SA_LINE_ST_ILS 0x00e0 -#define SA_LSM_STATE 0x0010 -#define SA_UNKN_LINE_ST 0x0008 -#define SA_SYM_PAIR_CTR 0x0007 - -/* Bits in status_b */ -#define SB_RF_STATE 0xc000 -#define SB_RF_STATE_REPEAT 0x0000 -#define SB_RF_STATE_IDLE 0x4000 -#define SB_RF_STATE_HALT1 0x8000 -#define SB_RF_STATE_HALT2 0xc000 -#define SB_PCI_STATE 0x3000 -#define SB_PCI_STATE_REMOVED 0x0000 -#define SB_PCI_STATE_INS_SCR 0x1000 -#define SB_PCI_STATE_REM_SCR 0x2000 -#define SB_PCI_STATE_INSERTED 0x3000 -#define SB_PCI_SCRUB 0x0800 -#define SB_PCM_STATE 0x0780 -#define SB_PCM_STATE_OFF 0x0000 -#define SB_PCM_STATE_BREAK 0x0080 -#define SB_PCM_STATE_TRACE 0x0100 -#define SB_PCM_STATE_CONNECT 0x0180 -#define SB_PCM_STATE_NEXT 0x0200 -#define SB_PCM_STATE_SIGNAL 0x0280 -#define SB_PCM_STATE_JOIN 0x0300 -#define SB_PCM_STATE_VERIFY 0x0380 -#define SB_PCM_STATE_ACTIVE 0x0400 -#define SB_PCM_STATE_MAIN 0x0480 -#define SB_PCM_SIGNALING 0x0040 -#define SB_LSF 0x0020 -#define SB_RCF 0x0010 -#define SB_TCF 0x0008 -#define SB_BREAK_REASON 0x0007 -#define SB_BREAK_REASON_NONE 0x0000 -#define SB_BREAK_REASON_START 0x0001 -#define SB_BREAK_REASON_T_OUT 0x0002 -#define SB_BREAK_REASON_NS_MAX 0x0003 -#define SB_BREAK_REASON_QLS 0x0004 -#define SB_BREAK_REASON_ILS 0x0005 -#define SB_BREAK_REASON_HLS 0x0006 - -/* Bits in intr_event and intr_mask */ -#define IE_NP_ERROR 0x8000 -#define IE_SIGNAL_OFF 0x4000 -#define IE_LE_CTR 0x2000 -#define IE_MINI_CTR 0x1000 -#define IE_VSYM_CTR 0x0800 -#define IE_PHY_INVALID 0x0400 -#define IE_EBUF_ERR 0x0200 -#define IE_TNE_EXP 0x0100 -#define IE_TPC_EXP 0x0080 -#define IE_PCM_ENABLED 0x0040 -#define IE_PCM_BREAK 0x0020 -#define IE_SELF_TEST 0x0010 -#define IE_TRACE_PROP 0x0008 -#define IE_PCM_CODE 0x0004 -#define IE_LS_MATCH 0x0002 -#define IE_PARITY_ERR 0x0001 - -/* Correct value for BIST signature */ -#define BIST_CORRECT 0x6ecd diff --git a/drivers/ap1000/ap.c b/drivers/ap1000/ap.c deleted file mode 100644 index 6e05d7cb5..000000000 --- a/drivers/ap1000/ap.c +++ /dev/null @@ -1,307 +0,0 @@ - /* - * Copyright 1996 The Australian National University. - * Copyright 1996 Fujitsu Laboratories Limited - * - * This software may be distributed under the terms of the Gnu - * Public License version 2 or later - */ -/* - * ap.c - Single AP1000 block driver. - * - * (C) dwalsh, Pious project, DCS, ANU 1996 - * - * This block driver is designed to simply to perform - * io operations to the hosts file system. - * - * Heavily modified by tridge - * - */ - -#include <linux/errno.h> -#include <linux/types.h> -#include <linux/fs.h> -#include <linux/ext2_fs.h> -#include <linux/kernel.h> -#include <linux/major.h> -#include <linux/module.h> -#include <asm/uaccess.h> -#include <asm/ap1000/apservice.h> - -#define AP_DEBUG 0 - -#define MAJOR_NR APBLOCK_MAJOR -#define AP_DRIVER 1 -#include <linux/blk.h> - -#define NUM_APDEVS 8 -#define MAX_REQUESTS 1 - -static DECLARE_WAIT_QUEUE_HEAD(busy_wait); - -static int ap_blocksizes[NUM_APDEVS]; -static int ap_length[NUM_APDEVS]; -static int ap_fds[NUM_APDEVS]; - -#define SECTOR_BLOCK_SHIFT 9 -#define AP_BLOCK_SHIFT 12 /* 4k blocks */ -#define AP_BLOCK_SIZE (1<<AP_BLOCK_SHIFT) - -static volatile int request_count = 0; - -static void ap_release(struct inode * inode, struct file * filp) -{ - MOD_DEC_USE_COUNT; -} - -static void ap_request(request_queue_t * q) -{ - struct cap_request creq; - unsigned int minor; - int offset, len; - struct request *req; - - if (request_count >= MAX_REQUESTS) return; - -repeat: - - if (!CURRENT) { - return; - } - - if (MAJOR(CURRENT->rq_dev) != MAJOR_NR) { - panic(DEVICE_NAME ": request list destroyed"); - } - if (CURRENT->bh) { - if (!buffer_locked(CURRENT->bh)) { - panic(DEVICE_NAME ": block not locked"); - } - } - - req = CURRENT; - - minor = MINOR(req->rq_dev); - - if (minor >= NUM_APDEVS) { - printk("apblock: request for invalid minor %d\n",minor); - end_request(0); - goto repeat; - } - - offset = req->sector; - len = req->current_nr_sectors; - - if ((offset + len) > ap_length[minor]) { - printk("apblock: request for invalid sectors %d -> %d\n", - offset,offset+len); - end_request(0); - goto repeat; - } - - if (ap_fds[minor] == -1) { - printk("apblock: minor %d not open\n",minor); - end_request(0); - goto repeat; - } - - /* convert to our units */ - offset <<= SECTOR_BLOCK_SHIFT; - len <<= SECTOR_BLOCK_SHIFT; - - /* setup a request for the host */ - creq.cid = mpp_cid(); - creq.size = sizeof(creq); - creq.header = 0; - creq.data[0] = (int)(req); - creq.data[1] = ap_fds[minor]; - creq.data[2] = offset; - creq.data[3] = len; - - switch (req->cmd) { - case READ: -#if AP_DEBUG - printk("apblock: read req=0x%x len=%d offset=%d\n", - req,len,offset); -#endif - creq.type = REQ_BREAD; - if (bif_queue(&creq,0,0)) { - return; - } - break; - - case WRITE: -#if AP_DEBUG - printk("apblock: write req=0x%x len=%d offset=%d\n", - req,len,offset); -#endif - creq.type = REQ_BWRITE; - creq.size += len; - if (bif_queue_nocopy(&creq,req->buffer,creq.size - sizeof(creq))) { - return; - } - break; - - default: - printk("apblock: unknown ap op %d\n",req->cmd); - end_request(0); - return; - } - - if (++request_count < MAX_REQUESTS) - goto repeat; -} - -/* this is called by ap1000/bif.c when a read/write has completed */ -void ap_complete(struct cap_request *creq) -{ -#if AP_DEBUG - struct request *req = (struct request *)(creq->data[0]); - - printk("request 0x%x complete\n",req); -#endif - end_request(1); - request_count--; - ap_request(NULL); -} - - -/* this is called by ap1000/bif.c to find a buffer to put a BREAD into - using DMA */ -char *ap_buffer(struct cap_request *creq) -{ - struct request *req = (struct request *)(creq->data[0]); - - return(req->buffer); -} - - -static int ap_open(struct inode * inode, struct file * filp) -{ - struct cap_request creq; - int minor; - minor = DEVICE_NR(inode->i_rdev); - -#if AP_DEBUG - printk("ap_open: minor=%x\n", minor); -#endif - - if (minor >= NUM_APDEVS) - return -ENODEV; - - /* if its already open then don't do anything */ - if (ap_fds[minor] != -1) - return 0; - - /* send the open request to the front end */ - creq.cid = mpp_cid(); - creq.type = REQ_BOPEN; - creq.header = 0; - creq.size = sizeof(creq); - creq.data[0] = minor; - - bif_queue(&creq,0,0); - - /* wait for the reply */ - while (ap_fds[minor] == -1) - sleep_on(&busy_wait); - - return 0; -} - - -static int ap_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - if (!inode || !inode->i_rdev) - return -EINVAL; - - switch (cmd) { - case BLKGETSIZE: /* Return device size */ - if (put_user(ap_length[MINOR(inode->i_rdev)],(long *) arg)) - return -EFAULT; - return 0; - - default: - break; - }; - - return 0; -} - - -/* this is called by ap1000/bif.c when a open reply comes in */ -void ap_open_reply(struct cap_request *creq) -{ - int minor = creq->data[0]; - - ap_fds[minor] = creq->data[1]; - ap_length[minor] = creq->data[2] >> SECTOR_BLOCK_SHIFT; - -#if AP_DEBUG - printk("ap opened minor %d length=%d fd=%d\n", - minor,ap_length[minor],ap_fds[minor]); -#endif - - wake_up(&busy_wait); -} - -static struct block_device_operations ap_fops = { - open: ap_open, - release: ap_release, - ioctl: ap_ioctl, -}; - - -int ap_init(void) -{ - int i; - static int done = 0; - - if (done) return(1); - - if (register_blkdev(MAJOR_NR,"apblock",&ap_fops)) { - printk("ap: unable to get major %d for ap block dev\n",MAJOR_NR); - return -1; - } - printk("ap_init: register dev %d\n", MAJOR_NR); - blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), &ap_request); - - for (i=0;i<NUM_APDEVS;i++) { - ap_blocksizes[i] = AP_BLOCK_SIZE; - ap_length[i] = 0; - ap_fds[i] = -1; - } - - blksize_size[MAJOR_NR] = ap_blocksizes; - - read_ahead[MAJOR_NR] = 32; /* 16k read ahead */ - for (i=0;i<NUM_APDEVS;i++) - register_disk(NILL, MKDEV(MAJOR_NR,i), 1, &ap_fops, 0); - - return(0); -} - -/* loadable module support */ - -#ifdef MODULE - -int init_module(void) -{ - int error = ap_init(); - if (!error) - printk(KERN_INFO "APBLOCK: Loaded as module.\n"); - return error; -} - -/* Before freeing the module, invalidate all of the protected buffers! */ -void cleanup_module(void) -{ - int i; - - for (i = 0 ; i < NUM_APDEVS; i++) - invalidate_buffers(MKDEV(MAJOR_NR, i)); - - unregister_blkdev( MAJOR_NR, "apblock" ); - blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR)); -} - -#endif /* MODULE */ diff --git a/drivers/ap1000/apfddi-reg.h b/drivers/ap1000/apfddi-reg.h deleted file mode 100644 index b3f25fb2a..000000000 --- a/drivers/ap1000/apfddi-reg.h +++ /dev/null @@ -1,14 +0,0 @@ - /* - * Copyright 1996 The Australian National University. - * Copyright 1996 Fujitsu Laboratories Limited - * - * This software may be distributed under the terms of the Gnu - * Public License version 2 or later - */ -/* FDDI register pointers */ -extern volatile struct formac *mac; -extern volatile struct plc *plc; -extern volatile int *csr0; -extern volatile int *csr1; -extern volatile int *buffer_mem; -extern volatile int *fifo; diff --git a/drivers/ap1000/apfddi.c b/drivers/ap1000/apfddi.c deleted file mode 100644 index b215e8d03..000000000 --- a/drivers/ap1000/apfddi.c +++ /dev/null @@ -1,702 +0,0 @@ - /* - * Copyright 1996 The Australian National University. - * Copyright 1996 Fujitsu Laboratories Limited - * - * This software may be distributed under the terms of the Gnu - * Public License version 2 or later - */ -/* - * $Id: apfddi.c,v 1.6 1996/12/18 01:45:51 tridge Exp $ - * - * Network interface definitions for AP1000 fddi device. - */ - -#include <linux/kernel.h> -#include <linux/sched.h> -#include <linux/interrupt.h> -#include <linux/fs.h> -#include <linux/types.h> -#include <linux/string.h> -#include <linux/socket.h> -#include <linux/errno.h> -#include <linux/fcntl.h> -#include <linux/in.h> -#include <linux/if_ether.h> /* For the statistics structure. */ -#include <linux/netdevice.h> -#include <linux/if_arp.h> - -#include <asm/system.h> -#include <asm/segment.h> -#include <asm/io.h> - -#include <linux/inet.h> -#include <linux/etherdevice.h> -#include <linux/skbuff.h> -#include <net/sock.h> - -#include <asm/ap1000/apservice.h> -#include <asm/ap1000/apreg.h> -#include <asm/irq.h> - -#include <net/arp.h> - -#include "apfddi.h" -#include "smt-types.h" -#include "mac.h" -#include "plc.h" -#include "am79c830.h" -#include "apfddi-reg.h" - -volatile struct formac *mac; -volatile struct plc *plc; -volatile int *csr0; -volatile int *csr1; -volatile int *buffer_mem; -volatile int *fifo; - -#define APFDDI_DEBUG 0 - -#define APFDDI_IRQ 7 - -#define T(x) (-SECS_TO_FDDI_TIME(x)) - -struct plc_info plc_info = { - pt_s, /* port_type */ - T(1.6e-3), /* c_min */ - T(50e-6), /* tl_min */ - T(5e-3), /* tb_min */ - T(100e-3), /* t_out */ - T(50e-3), /* lc_short */ - T(500e-3), /* lc_medium */ - T(5.0), /* lc_long */ - T(50.0), /* lc_extended */ - T(3.5e-3), /* t_scrub */ - T(1.3e-3), /* ns_max */ -}; - -struct mac_info mac_info = { - T(165e-3), /* tmax */ - T(3.5e-3), /* tvx */ - T(20e-3), /* treq */ - { 0x42, 0x59 }, /* s_address */ - { 0x42, 0x59, 0x10, 0x76, 0x88, 0x82 }, /* l_address */ - { 0 }, /* s_group_adrs */ - { 0 }, /* l_group_adrs */ - 0, /* rcv_own_frames */ - 1, /* only_good_frames */ -}; - -u_char fddi_bitrev[256] = { - 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, - 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0, - 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, - 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, - 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, - 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4, - 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, - 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc, - 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, - 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, - 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, - 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, - 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, - 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6, - 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, - 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, - 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, - 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, - 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, - 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9, - 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, - 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, - 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, - 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, - 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, - 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3, - 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, - 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, - 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, - 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, - 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, - 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff, -}; - -/* XXX our hardware address, canonical bit order */ -static u_char apfddi_saddr[6] = { 0x42, 0x9a, 0x08, 0x6e, 0x11, 0x41 }; - -struct net_device *apfddi_device = NULL; -struct net_device_stats *apfddi_stats = NULL; - -volatile struct apfddi_queue *apfddi_queue_top = NULL; - -void map_regs(void) -{ - unsigned long reg_base_addr = 0xfbf00000; - - mac = (volatile struct formac *) (reg_base_addr + FORMAC); - plc = (volatile struct plc *) (reg_base_addr + PLC); - csr0 = (volatile int *) (reg_base_addr + CSR0); - csr1 = (volatile int *) (reg_base_addr + CSR1); - buffer_mem = (volatile int *) (reg_base_addr + BUFFER_MEM); - fifo = (volatile int *) (reg_base_addr + FIFO); -} - -int ring_op; - -void apfddi_startup(void) -{ - int reason; - -#if APFDDI_DEBUG - printk("In apfddi_startup\n"); -#endif - - *csr0 = CS0_LED0; - ring_op = 0; - if (*csr1 & 0xf078) { - *csr1 = CS1_RESET_MAC | CS1_RESET_FIFO; - *csr1 = 0; - reason = 1; - printk("resetting after power-on\n"); - } else { - *csr1 = CS1_RESET_FIFO; - *csr1 = 0; - reason = plc_inited(&plc_info); - if (reason) - printk("resetting: plc reason %d\n", reason); - } - if (reason) { -#if APFDDI_DEBUG - printk("Calling plc_init\n"); -#endif - plc_init(&plc_info); -#if APFDDI_DEBUG - printk("Calling mac_init\n"); -#endif - mac_init(&mac_info); - *csr0 |= CS0_LED1; - pc_start(loop_none); - - } else { - *csr0 |= CS0_LED2 | CS0_LED1; - reason = mac_inited(&mac_info); - if (reason) { - printk("resetting mac: reason %d\n", reason); - mac_init(&mac_info); - mac_reset(loop_none); - mac_claim(); - } else { - ring_op = 1; - *csr0 &= ~(CS0_LED0 | CS0_LED1 | CS0_LED2); - } - } -} - -void apfddi_off(void) -{ - *csr0 &= ~CS0_LED1; - pc_stop(); -} - -void apfddi_sleep(void) -{ - mac_sleep(); - plc_sleep(); -} - -void apfddi_poll(void) -{ - if (*csr0 & CS0_PHY_IRQ) - plc_poll(); - if (*csr0 & CS0_MAC_IRQ) - mac_poll(); -} - -void set_cf_join(int on) -{ - if (on) { -#if APFDDI_DEBUG - printk("apfddi: joined the ring!\n"); -#endif - mac_reset(loop_none); - *csr0 |= CS0_LED2; - mac_claim(); - } else { - mac_disable(); - ring_op = 0; - *csr0 = (*csr0 & ~CS0_LED2) | CS0_LED1 | CS0_LED0; - } -} - -void set_ring_op(int up) -{ - ring_op = up; - if (up) { -#if APFDDI_DEBUG - printk("apfddi: ring operational!\n"); -#endif - *csr0 &= ~(CS0_LED2 | CS0_LED1 | CS0_LED0); - } else - *csr0 |= CS0_LED2 | CS0_LED1 | CS0_LED0; -} - -void rmt_event(int st) -{ - if (st & (S2_BEACON_STATE|S2_MULTIPLE_DA|S2_TOKEN_ERR - |S2_DUPL_CLAIM|S2_TRT_EXP_RECOV)) { - printk("st2 = %x\n", st); - } -} - - -int apfddi_init(struct net_device *dev); -static void apfddi_interrupt(int irq, void *dev_id, struct pt_regs *regs); -static int apfddi_xmit(struct sk_buff *skb, struct net_device *dev); -int apfddi_rx(struct mac_buf *mbuf); -static struct net_device_stats *apfddi_get_stats(struct net_device *dev); -#if APFDDI_DEBUG -void dump_packet(char *action, char *buf, int len, int seq); -#endif - -/* - * Create FDDI header for an arbitrary protocol layer - * - * saddr=NULL means use device source address (always will anyway) - * daddr=NULL means leave destination address (eg unresolved arp) - */ -static int apfddi_hard_header(struct sk_buff *skb, struct net_device *dev, - unsigned short type, void *daddr, - void *saddr, unsigned len) -{ - struct fddi_header *fh; - struct llc_header *lh; - u_char *base_header; - u_char *fd_daddr = (u_char *)daddr; - int i; - -#if APFDDI_DEBUG - printk("In apfddi_hard_header\n"); -#endif - - if (skb == NULL) { - printk("Null skb in apfddi_hard_header... returning...\n"); - return 0; - } - - switch(type) { - case ETH_P_IP: -#if APFDDI_DEBUG - printk("apfddi_hard_header: Processing IP packet\n"); -#endif - break; - case ETH_P_ARP: -#if APFDDI_DEBUG - printk("apfddi_hard_header: Processing ARP packet\n"); -#endif - break; - case ETH_P_RARP: -#if APFDDI_DEBUG - printk("apfddi_hard_header: Processing RARP packet\n"); -#endif - break; - default: - printk("apfddi_hard_header: I don't understand protocol %d (0x%x)\n", - type, type); - apfddi_stats->tx_errors++; - return 0; - } - - base_header = (u_char *)skb_push(skb, FDDI_HARDHDR_LEN-4); - if (base_header == NULL) { - printk("apfddi_hard_header: Memory squeeze, dropping packet.\n"); - apfddi_stats->tx_dropped++; - return 0; - } - fh = (struct fddi_header *)(base_header + 3); - lh = (struct llc_header *)((char *)fh + FDDI_HDRLEN); - - lh->llc_dsap = lh->llc_ssap = LLC_SNAP_LSAP; - lh->snap_control = LLC_UI; - lh->snap_org_code[0] = 0; - lh->snap_org_code[1] = 0; - lh->snap_org_code[2] = 0; - lh->snap_ether_type = htons(type); - -#if APFDDI_DEBUG - printk("snap_ether_type is %d (0x%x)\n", lh->snap_ether_type, - lh->snap_ether_type); -#endif - - fh->fddi_fc = FDDI_FC_LLC; - - /* - * Fill in the source address. - */ - for (i = 0; i < 6; i++) - fh->fddi_shost[i] = fddi_bitrev[apfddi_saddr[i]]; - - /* - * Fill in the destination address. - */ - if (daddr) { -#if APFDDI_DEBUG - printk("daddr is: "); -#endif - for (i = 0; i < 6; i++) { - fh->fddi_dhost[i] = fddi_bitrev[fd_daddr[i]]; -#if APFDDI_DEBUG - printk("%x(%x):",fh->fddi_dhost[i], fd_daddr[i]); -#endif - } -#if APFDDI_DEBUG - printk("\n"); -#endif - return(FDDI_HARDHDR_LEN-4); - } - else { -#if APFDDI_DEBUG - printk("apfddi_hard_header, daddr was NULL\n"); -#endif - return -(FDDI_HARDHDR_LEN-4); - } -} - -/* - * Rebuild the FDDI header. This is called after an ARP (or in future - * other address resolution) has completed on this sk_buff. We now let - * ARP fill in the other fields. - */ -static int apfddi_rebuild_header(void *buff, struct net_device *dev, - unsigned long raddr, struct sk_buff *skb) -{ - int i, status; - struct fddi_header *fh = (struct fddi_header *)(buff+3); - -#if APFDDI_DEBUG - printk("In apfddi_rebuild_header, dev is %x apfddi_device is %x\n", dev, - apfddi_device); - printk("rebuild header for fc 0x%x\n", fh->fddi_fc); - printk("dest address is:\n"); - for (i = 0; i < 6; i++) printk("%x:", fh->fddi_dhost[i]); -#endif - status = arp_find(raddr, skb) ? 1 : 0; - - if (!status) { -#if APFDDI_DEBUG - printk("dest address is now:\n"); - for (i = 0; i < 6; i++) printk("%x:", fh->fddi_dhost[i]); - printk("status is %d\n", status); -#endif - /* - * Bit reverse the dest_address. - */ - for (i = 0; i < 6; i++) - fh->fddi_dhost[i] = fddi_bitrev[fh->fddi_dhost[i]]; - } -#if APFDDI_DEBUG - printk("\n"); -#endif - return(status); -} - -static int apfddi_set_mac_address(struct net_device *dev, void *addr) -{ -#if APFDDI_DEBUG - printk("In apfddi_set_mac_address\n"); -#endif - return (0); -} - -static void apfddi_set_multicast_list(struct net_device *dev) -{ -#if APFDDI_DEBUG - printk("In apfddi_set_multicast_list\n"); -#endif -} - -static int apfddi_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) -{ -#if APFDDI_DEBUG - printk("In apfddi_do_ioctl\n"); -#endif - return (0); -} - -static int apfddi_set_config(struct net_device *dev, struct ifmap *map) -{ -#if APFDDI_DEBUG - printk("In apfddi_set_config\n"); -#endif - return (0); -} - -/* - * Opening the fddi device through ifconfig. - */ -int apfddi_open(struct net_device *dev) -{ - static int already_run = 0; - unsigned flags; - int res; - - if (already_run) { - apfddi_startup(); - *csr0 |= CS0_INT_ENABLE; - return 0; - } - already_run = 1; - - map_regs(); - apfddi_startup(); - - save_flags(flags); cli(); - if ((res = request_irq(APFDDI_IRQ, apfddi_interrupt, SA_INTERRUPT, - "apfddi", dev))) { - printk("Failed to install apfddi handler error=%d\n", res); - restore_flags(flags); - return(0); - } - enable_irq(APFDDI_IRQ); - restore_flags(flags); - -#if APFDDI_DEBUG - printk("Installed apfddi interrupt handler\n"); -#endif - *csr0 |= CS0_INT_ENABLE; -#if APFDDI_DEBUG - printk("Enabled fddi interrupts\n"); -#endif - - return 0; -} - -/* - * Stop the fddi device through ifconfig. - */ -int apfddi_stop(struct net_device *dev) -{ - *csr0 &= ~CS0_INT_ENABLE; - apfddi_sleep(); - return 0; -} - - -/* - * Initialise fddi network interface. - */ -int apfddi_init(struct net_device *dev) -{ - int i; - - /* - * Check if this thing has already been initialised. - */ - if (apfddi_device != NULL) - return -ENODEV; - - printk("apfddi_init(): Initialising fddi interface\n"); - - apfddi_device = dev; - - dev->open = apfddi_open; - dev->stop = apfddi_stop; - dev->hard_start_xmit = apfddi_xmit; - dev->get_stats = apfddi_get_stats; - dev->priv = kmalloc(sizeof(struct net_device_stats), GFP_ATOMIC); - if (dev->priv == NULL) - return -ENOMEM; - memset(dev->priv, 0, sizeof(struct net_device_stats)); - apfddi_stats = (struct net_device_stats *)apfddi_device->priv; - - /* Initialise the fddi device structure */ - for (i = 0; i < DEV_NUMBUFFS; i++) - skb_queue_head_init(&dev->buffs[i]); - - dev->hard_header = apfddi_hard_header; - dev->rebuild_header = apfddi_rebuild_header; - dev->set_mac_address = apfddi_set_mac_address; - dev->header_cache_update = NULL; - dev->do_ioctl = apfddi_do_ioctl; - dev->set_config = apfddi_set_config; - dev->set_multicast_list = apfddi_set_multicast_list; - dev->type = ARPHRD_ETHER; - dev->hard_header_len = FDDI_HARDHDR_LEN; - dev->mtu = FDDIMTU; - dev->addr_len = 6; - memcpy(dev->dev_addr, apfddi_saddr, sizeof(apfddi_saddr)); - dev->tx_queue_len = 100; /* XXX What should this be? */ - dev->irq = APFDDI_IRQ; - - memset(dev->broadcast, 0xFF, ETH_ALEN); - - return(0); -} - -static void apfddi_interrupt(int irq, void *dev_id, struct pt_regs *regs) -{ -#if APFDDI_DEBUG - static int times = 0; -#endif - unsigned flags; - save_flags(flags); cli(); - -#if APFDDI_DEBUG - printk("In apfddi_interrupt irq %d dev_id %p times %d\n", - irq, dev_id, ++times); -#endif - - apfddi_poll(); - restore_flags(flags); -} - -#if APFDDI_DEBUG -static char *flagbits[8] = { - "fin", "syn", "rst", "push", "ack", "urg", "6", "7" -}; - -void dump_packet(action, buf, len, seq) - char *action, *buf; - int len, seq; -{ - int i, flags; - char *sep; - - printk("%s packet %d of %d bytes at %d:\n", action, seq, - len, jiffies); - printk(" from %x to %x pktid=%d ttl=%d pcol=%d len=%d\n", - *(long *)(buf+12), *(long *)(buf+16), *(u_short *)(buf+4), - *(unsigned char *)(buf+8), buf[9], *(u_short *)(buf+2)); - if( buf[9] == 6 || buf[9] == 17 ){ - /* TCP or UDP */ - printk(" sport=%d dport=%d", - *(u_short *)(buf+20), *(u_short *)(buf+22)); - if( buf[9] == 6 ){ - printk(" seq=%d ack=%d win=%d flags=<", - *(long *)(buf+24), *(long *)(buf+28), - *(unsigned short *)(buf+34)); - flags = buf[33]; - sep = ""; - for (i = 7; i >= 0; --i) { - if (flags & (1 << i)) { - printk("%s%s", sep, flagbits[i]); - sep = "+"; - } - } - printk(">"); - } - printk("\n"); - } -} -#endif - -#if APFDDI_DEBUG -static void apfddi_print_frame(struct sk_buff *skb) -{ - int i; - struct llc_header *lh; - static int seq = 0; - -#if 0 - printk("skb->len is %d\n", skb->len); - printk("fc is 0x%x\n", *(u_char *)(skb->data+3)); - printk("dest address is:\n"); - for (i = 0; i < 6; i++) { - printk("%x:", fddi_bitrev[*(u_char *)(skb->data+4+i)]); - } - printk("\n"); - printk("source address is:\n"); - for (i = 0; i < 6; i++) { - printk("%x:", fddi_bitrev[*(u_char *)(skb->data+10+i)]); - } - printk("\n"); -#endif - lh = (struct llc_header *)(skb->data+16); -#if 0 - printk("llc_dsp %d llc_ssap %d snap_control %d org_code [0]=%d [1]=%d [2]=%d ether_type=%d\n", - lh->llc_dsap, lh->llc_ssap, lh->snap_control, - lh->snap_org_code[0], lh->snap_org_code[1], lh->snap_org_code[2], - lh->snap_ether_type); -#endif - if (lh->snap_ether_type == ETH_P_IP) - dump_packet("apfddi_xmit:", skb->data+24, skb->len-24, seq++); -} -#endif - -/* - * Transmitting packet over FDDI. - */ -static int apfddi_xmit(struct sk_buff *skb, struct net_device *dev) -{ - unsigned long flags; - -#if APFDDI_DEBUG - printk("In apfddi_xmit\n"); -#endif - - /* - * Check there is some work to do. - */ - if (skb == NULL || dev == NULL) - return(0); - -#if APFDDI_DEBUG - printk("skb address is for apfddi 0x%x\n", skb); -#endif - - /* - * Check lock variable. - */ - save_flags(flags); cli(); - if (dev->tbusy != 0) { - restore_flags(flags); - printk("apfddi_xmit: device busy\n"); - apfddi_stats->tx_errors++; - return 1; - } - restore_flags(flags); - dev->tbusy = 1; - - dev->trans_start = jiffies; - - skb->mac.raw = skb->data; - - /* - * Append packet onto send queue. - */ - if (mac_queue_append(skb)) { - /* - * No memory. - */ - return 1; - } - - /* - * Process packet queue. - */ - mac_process(); - - apfddi_stats->tx_packets++; - dev->tbusy = 0; - return 0; -} - -#if APFDDI_DEBUG -void print_mbuf(struct mac_buf *mbuf) -{ - printk("mac %p length=%d ptr=%p wraplen=%d wrapptr=%x fr_start=%d fr_end=%d\n", - mbuf, mbuf->length, mbuf->ptr, mbuf->wraplen, mbuf->wrapptr, - mbuf->fr_start, mbuf->fr_end); -} -#endif - -/* - * Return statistics of fddi driver. - */ -static struct net_device_stats *apfddi_get_stats(struct net_device *dev) -{ - return((struct net_device_stats *)dev->priv); -} - - - - diff --git a/drivers/ap1000/apfddi.h b/drivers/ap1000/apfddi.h deleted file mode 100644 index a77dd6160..000000000 --- a/drivers/ap1000/apfddi.h +++ /dev/null @@ -1,142 +0,0 @@ - /* - * Copyright 1996 The Australian National University. - * Copyright 1996 Fujitsu Laboratories Limited - * - * This software may be distributed under the terms of the Gnu - * Public License version 2 or later - */ -#define BUFFER_MEM 0x40000 -#define CSR0 0x60000 -#define CSR1 0x60004 -#define PLC 0x60080 -#define FORMAC 0x60200 -#define FIFO 0x68000 - -/* Size of buffer memory */ -#define BUFFER_SIZE 32768 /* words; 128kB */ - -/* Bits in CSR0 */ -#define CS0_INT_REQ 0x8000 /* board interrupt request asserted */ -#define CS0_MAC_IRQ 0x4000 /* FORMAC is requesting interrupt */ -#define CS0_PHY_IRQ 0x2000 /* PLC is requesting interrupt */ -#define CS0_LED2 0x1000 /* turn on led 2 */ -#define CS0_DO_IRQ 0x0200 /* request interrupt */ -#define CS0_INT_ENABLE 0x0100 /* enable interrupt requests */ -#define CS0_DMA_ENABLE 0x0080 /* enable DMA requests */ -#define CS0_DMA_RECV 0x0040 /* DMA requests are in receive dirn. */ -#define CS0_LED1 0x0010 /* turn on led 1 */ -#define CS0_LED0 0x0008 /* turn on led 0 (red) */ -#define CS0_HREQ 0x0007 /* host request to FORMAC */ -#define CS0_HREQ_WSPEC 0x0002 /* write special frames */ -#define CS0_HREQ_RECV 0x0003 /* read receive queue */ -#define CS0_HREQ_WS 0x0004 /* write synchronous queue */ -#define CS0_HREQ_WA0 0x0005 /* write async queue 0 */ -#define CS0_HREQ_WA1 0x0006 /* write async queue 1 */ -#define CS0_HREQ_WA2 0x0007 /* write async queue 2 */ - -/* Bits in CSR1 */ -#define CS1_THIS_QAF 0x0800 /* this queue almost full */ -#define CS1_FIFO_TAG 0x0400 /* tag of word at head of fifo */ -#define CS1_BUF_RD_TAG 0x0200 /* tag of last word read from buffer */ -#define CS1_BUF_WR_TAG 0x0100 /* tag to write to buffer */ -#define CS1_TAGMODE 0x0080 /* enable tag mode */ -#define CS1_RESET_MAC 0x0040 /* reset FORMAC and PLC */ -#define CS1_RESET_FIFO 0x0020 /* reset FIFO */ -#define CS1_CLEAR_QAF 0x0010 /* clear queue-almost-full bits */ -#define CS1_FIFO_LEVEL 0x0007 /* # words in FIFO (0 - 4) */ - -/* - * FDDI Frame Control values. - */ -#define FDDI_SMT 0x41 -#define FDDI_SMT_NSA 0x4f -#define FDDI_FC_LLC 0x50 -#define FDDI_FC_LLC_MASK 0xf0 - -/* - * Unnumbered LLC format commands - */ -#define LLC_UI 0x3 -#define LLC_UI_P 0x13 -#define LLC_DISC 0x43 -#define LLC_DISC_P 0x53 -#define LLC_UA 0x63 -#define LLC_UA_P 0x73 -#define LLC_TEST 0xe3 -#define LLC_TEST_P 0xf3 -#define LLC_FRMR 0x87 -#define LLC_FRMR_P 0x97 -#define LLC_DM 0x0f -#define LLC_DM_P 0x1f -#define LLC_XID 0xaf -#define LLC_XID_P 0xbf -#define LLC_SABME 0x6f -#define LLC_SABME_P 0x7f - -/* - * Supervisory LLC commands - */ -#define LLC_RR 0x01 -#define LLC_RNR 0x05 -#define LLC_REJ 0x09 - -/* - * Info format - dummy only - */ -#define LLC_INFO 0x00 - -/* - * ISO PDTR 10178 contains among others - */ -#define LLC_X25_LSAP 0x7e -#define LLC_SNAP_LSAP 0xaa -#define LLC_ISO_LSAP 0xfe - -/* - * Structure of the FDDI MAC header. - */ -struct fddi_header { - u_char fddi_fc; /* frame control field */ - u_char fddi_dhost[6]; /* destination address */ - u_char fddi_shost[6]; /* source address */ -}; - -/* - * Structure of LLC/SNAP header. - */ -struct llc_header { - u_char llc_dsap; - u_char llc_ssap; - u_char snap_control; - u_char snap_org_code[3]; - u_short snap_ether_type; -}; - -#define FDDI_HDRLEN 13 /* sizeof(struct fddi_header) */ -#define LLC_SNAPLEN 8 /* bytes for LLC/SNAP header */ -#define FDDI_HARDHDR_LEN 28 /* Hard header size */ - -#define FDDIMTU 4352 - - -/* Types of loopback we can do. */ -typedef enum { - loop_none, - loop_formac, - loop_plc_lm, - loop_plc_eb, - loop_pdx -} LoopbackType; - -/* Offset from fifo for writing word with tag. */ -#define FIFO_TAG 0x80 - -#define MAX_FRAME_LEN 4500 - -void set_ring_op(int up); -void rmt_event(int st); -void set_cf_join(int on); - -extern struct net_device *apfddi_device; -extern struct net_device_stats *apfddi_stats; - diff --git a/drivers/ap1000/bif.c b/drivers/ap1000/bif.c deleted file mode 100644 index 331ee0f26..000000000 --- a/drivers/ap1000/bif.c +++ /dev/null @@ -1,280 +0,0 @@ - /* - * Copyright 1996 The Australian National University. - * Copyright 1996 Fujitsu Laboratories Limited - * - * This software may be distributed under the terms of the Gnu - * Public License version 2 or later - */ -/* - * $Id: bif.c,v 1.13 1996/12/18 01:45:52 tridge Exp $ - * - * Network interface definitions for bif device. - */ - -#include <linux/kernel.h> -#include <linux/sched.h> -#include <linux/interrupt.h> -#include <linux/fs.h> -#include <linux/types.h> -#include <linux/string.h> -#include <linux/socket.h> -#include <linux/errno.h> -#include <linux/fcntl.h> -#include <linux/in.h> -#include <linux/if_ether.h> /* For the statistics structure. */ -#include <linux/netdevice.h> -#include <linux/if_arp.h> /* For ARPHRD_BIF */ - -#include <asm/system.h> -#include <asm/segment.h> -#include <asm/io.h> - -#include <linux/inet.h> -#include <linux/etherdevice.h> -#include <linux/skbuff.h> -#include <net/sock.h> - -#include <asm/ap1000/apservice.h> -#include <asm/ap1000/apreg.h> - -#define BIF_DEBUG 0 -#if BIF_DEBUG -static int seq = 0; -#endif - -#define BIF_MTU 10240 - -static struct net_device *bif_device = 0; -static struct net_device_stats *bif_stats = 0; - -int bif_init(struct net_device *dev); -int bif_open(struct net_device *dev); -static int bif_xmit(struct sk_buff *skb, struct net_device *dev); -int bif_rx(struct sk_buff *skb); -int bif_stop(struct net_device *dev); -static struct net_device_stats *bif_get_stats(struct net_device *dev); - -static int bif_hard_header(struct sk_buff *skb, struct net_device *dev, - unsigned short type, void *daddr, - void *saddr, unsigned len) -{ -#if BIF_DEBUG - printk("bif_hard_header()\n"); -#endif - - skb_push(skb,dev->hard_header_len); - - if (daddr) skb->arp = 1; - - /* tell IP how much space we took */ - return (dev->hard_header_len); -} - -static int bif_rebuild_header(void *buff, struct net_device *dev, - unsigned long raddr, struct sk_buff *skb) -{ - /* this would normally be used to fill in hardware addresses after - an ARP */ -#if BIF_DEBUG - printk("bif_rebuild_header()\n"); -#endif - if (skb) skb->arp = 1; - return(0); -} - -static int bif_set_mac_address(struct net_device *dev, void *addr) -{ - printk("BIF: set_mac_address called\n"); - return (0); -} - -static void bif_set_multicast_list(struct net_device *dev) -{ - return; -} - -static int bif_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) -{ - printk("BIF: Called do_ioctl\n"); - return (0); -} - -static int bif_set_config(struct net_device *dev, struct ifmap *map) -{ - printk("BIF: Called bif_set_config\n"); - return (0); -} - -/* - * Initialise bif network interface. - */ -int bif_init(struct net_device *dev) -{ - int i; - - printk("bif_init(): Initialising bif interface\n"); - bif_device = dev; - - dev->mtu = BIF_MTU; - dev->tbusy = 0; - dev->hard_start_xmit = bif_xmit; - dev->hard_header = bif_hard_header; - dev->hard_header_len = sizeof(struct cap_request); - dev->addr_len = 0; - dev->tx_queue_len = 50000; /* no limit (almost!) */ - dev->type = ARPHRD_BIF; - dev->rebuild_header = bif_rebuild_header; - dev->open = bif_open; - dev->flags = IFF_NOARP; /* Don't use ARP on this device */ - dev->priv = kmalloc(sizeof(struct net_device_stats), GFP_KERNEL); - if (dev->priv == NULL) - return -ENOMEM; - memset(dev->priv, 0, sizeof(struct net_device_stats)); - bif_stats = (struct net_device_stats *)bif_device->priv; - - - dev->stop = bif_stop; - dev->get_stats = bif_get_stats; - - dev->set_mac_address = bif_set_mac_address; - dev->header_cache_update = NULL; - dev->do_ioctl = bif_do_ioctl; - dev->set_config = bif_set_config; - dev->set_multicast_list = bif_set_multicast_list; - - memset(dev->broadcast, 0xFF, ETH_ALEN); - - dev_init_buffers(dev); - - return(0); -} - -int bif_open(struct net_device *dev) -{ - printk("In bif_open\n"); - dev->tbusy = 0; - dev->start = 1; - return 0; -} - -#if BIF_DEBUG -static void dump_packet(char *action, char *buf, int len, int seq) -{ - int flags; - char *sep; - - printk("%s packet %d of %d bytes at %d:\n", action, seq, - len, (int)jiffies); - printk(" from %x to %x pktid=%d ttl=%d pcol=%d len=%d\n", - *(long *)(buf+12), *(long *)(buf+16), *(u_short *)(buf+4), - *(unsigned char *)(buf+8), buf[9], *(u_short *)(buf+2)); - if( buf[9] == 6 || buf[9] == 17 ){ - /* TCP or UDP */ - printk(" sport=%d dport=%d", - *(u_short *)(buf+20), *(u_short *)(buf+22)); - if( buf[9] == 6 ){ - printk(" seq=%d ack=%d win=%d flags=<", - *(long *)(buf+24), *(long *)(buf+28), - *(unsigned short *)(buf+34)); - flags = buf[33]; - sep = ""; - printk(">"); - } - printk("\n"); - } - else { - printk(" protocol = %d\n", buf[9]); - } -} -#endif - - -static int bif_xmit(struct sk_buff *skb, struct net_device *dev) -{ - extern int bif_send_ip(int cid,struct sk_buff *skb); - extern int tnet_send_ip(int cid,struct sk_buff *skb); - extern int msc_blocked, tnet_ip_enabled; - u_long destip; - int cid; - - if (skb == NULL || dev == NULL) - return(0); - - destip = *(u_long *)(skb->data+sizeof(struct cap_request)+16); - cid = ap_ip_to_cid(destip); - - skb->dev = dev; - skb->mac.raw = skb->data; - - if (cid != -1 && tnet_ip_enabled && !msc_blocked) { - tnet_send_ip(cid,skb); - } else { - bif_send_ip(cid, skb); - } - - dev->tbusy = 0; - - bif_stats->tx_packets++; - - mark_bh(NET_BH); - - return 0; -} - - -/* - * Receive a packet from the BIF - called from interrupt handler. - */ -int bif_rx(struct sk_buff *skb) -{ -#if BIF_DEBUG - dump_packet("bif_rx:", skb->data, skb->len, seq++); -#endif - - if (bif_device == NULL) { - printk("bif: bif_device is NULL in bif_rx\n"); - dev_kfree_skb(skb); - return 0; - } - skb->dev = bif_device; - skb->protocol = ETH_P_IP; - -#if 1 - /* try disabling checksums on receive */ - if (ap_ip_to_cid(*(u_long *)(((char *)skb->data)+12)) != -1) - skb->ip_summed = CHECKSUM_UNNECESSARY; -#endif - - /* - * Inform the network layer of the new packet. - */ - skb->mac.raw = skb->data; - netif_rx(skb); - - if (bif_stats == NULL) { - printk("bif: bif_stats is NULL is bif_rx\n"); - return 0; - } - bif_stats->rx_packets++; - - return 0; -} - -int bif_stop(struct net_device *dev) -{ - printk("in bif_close\n"); - - dev->tbusy = 1; - dev->start = 0; - - return 0; -} - -/* - * Return statistics of bif driver. - */ -static struct net_device_stats *bif_get_stats(struct net_device *dev) -{ - return((struct net_device_stats *)dev->priv); -} - diff --git a/drivers/ap1000/ddv.c b/drivers/ap1000/ddv.c deleted file mode 100644 index 3e32c9c83..000000000 --- a/drivers/ap1000/ddv.c +++ /dev/null @@ -1,1008 +0,0 @@ - /* - * Copyright 1996 The Australian National University. - * Copyright 1996 Fujitsu Laboratories Limited - * - * This software may be distributed under the terms of the Gnu - * Public License version 2 or later - */ -/* - * ddv.c - Single AP1000 block driver. - * - * This block driver performs io operations to the ddv option - * board. (Hopefully:) - * - */ - -#include <linux/errno.h> -#include <linux/types.h> -#include <linux/fs.h> -#include <linux/ext2_fs.h> -#include <linux/kernel.h> -#include <linux/major.h> -#include <linux/mm.h> -#include <linux/malloc.h> -#define __KERNEL_SYSCALLS__ -#include <linux/unistd.h> -#include <linux/sched.h> -#include <asm/pgtable.h> -#include <asm/uaccess.h> -#include <linux/module.h> -#include <asm/ap1000/apreg.h> -#include <asm/ap1000/DdvReqTable.h> - -#define MAJOR_NR DDV_MAJOR - -#include <linux/blk.h> -#include <linux/genhd.h> -#include <linux/hdreg.h> - -#define DDV_DEBUG 0 -#define AIR_DISK 1 - -#define SECTOR_SIZE 512 - -/* we can have lots of partitions */ -#define PARTN_BITS 6 -#define NUM_DDVDEVS (1<<PARTN_BITS) - -#define PARDISK_BASE (1<<5) /* partitions above this number are - striped across all the cells */ -#define STRIPE_SHIFT 6 -#define STRIPE_SECTORS (1<<STRIPE_SHIFT) /* number of sectors per stripe */ - -#define MAX_BNUM 16 -#define MAX_REQUEST (TABLE_SIZE - 2) -#define REQUEST_LOW 16 -#define REQUEST_HIGH 4 - - -/* we fake up a block size larger than the physical block size to try - to make things a bit more efficient */ -#define SECTOR_BLOCK_SHIFT 9 - -#define SECTOR_MASK ((BLOCK_SIZE >> 9) - 1) - -/* try to read ahead a bit */ -#define DDV_READ_AHEAD 64 - -static int have_ddv_board = 1; -static unsigned num_options = 0; -static unsigned this_option = 0; - -extern int ddv_get_mlist(unsigned mptr[],int bnum); -extern int ddv_set_request(struct request *req, - int request_type,int bnum,int mlist,int len,int offset); -extern void ddv_load_kernel(char *opcodep); -extern int ddv_restart_cpu(void); -extern int ddv_mlist_available(void); -static int ddv_revalidate(kdev_t dev, struct gendisk *gdev); -static void ddv_geninit(void); -static void ddv_release(struct inode * inode, struct file * filp); -static void ddv_request1(void); - - -static char *ddv_opcodep = NULL; -static struct request *next_request = NULL; - -static DECLARE_WAIT_QUEUE_HEAD(busy_wait); - -static int ddv_blocksizes[NUM_DDVDEVS]; /* in bytes */ -int ddv_sect_length[NUM_DDVDEVS]; /* in sectors */ -int ddv_blk_length[NUM_DDVDEVS]; /* in blocks */ - -/* these are used by the ddv_daemon, which services remote disk requests */ -static struct remote_request *rem_queue = NULL; -static struct remote_request *rem_queue_end; -static DECLARE_WAIT_QUEUE_HEAD(ddv_daemon_wait); - -static int opiu_kernel_loaded = 0; - -static struct { - unsigned reads, writes, blocks, rq_started, rq_finished, errors; - unsigned sectors_read, sectors_written; -} ddv_stats; - -static struct hd_struct partition_tables[NUM_DDVDEVS]; - -static struct gendisk ddv_gendisk = { - MAJOR_NR, /* Major number */ - DEVICE_NAME, /* Major name */ - PARTN_BITS, /* Bits to shift to get real from partition */ - 1 << PARTN_BITS, /* Number of partitions per real */ - partition_tables,/* hd struct */ - ddv_blk_length, /* block sizes */ - 1, /* number */ - (void *) NULL, /* internal */ - NULL /* next */ -}; - -struct ddv_geometry { - unsigned char heads; - unsigned char sectors; - unsigned short cylinders; - unsigned long start; -}; - -static struct ddv_geometry ddv_geometry; - - -struct remote_request { - union { - struct remote_request *next; - void (*fn)(void); - } u; - unsigned bnum; /* how many blocks does this contain */ - struct request *reqp; /* pointer to the request on the original cell */ - unsigned cell; /* what cell is the request from */ - struct request req; /* details of the request */ -}; - - -static void ddv_set_optadr(void) -{ - unsigned addr = 0x11000000; - OPT_IO(OBASE) = addr; - MSC_IO(MSC_OPTADR) = - ((addr & 0xff000000)>>16) | - ((OPTION_BASE & 0xf0000000)>>24) | - ((OPTION_BASE + 0x10000000)>>28); - OPT_IO(PRST) = 0; -} - -extern struct RequestTable *RTable; -extern struct OPrintBufArray *PrintBufs; -extern struct OAlignBufArray *AlignBufs; -extern struct DiskInfo *DiskInfo; - -static void ddv_release(struct inode * inode, struct file * filp) -{ -#if DEBUG - printk("ddv_release started\n"); -#endif -#if DEBUG - printk("ddv_release done\n"); -#endif -} - - -static unsigned in_request = 0; -static unsigned req_queued = 0; - -static void ddv_end_request(int uptodate,struct request *req) -{ - struct buffer_head * bh; - - ddv_stats.rq_finished++; - -/* printk("ddv_end_request(%d,%p)\n",uptodate,req); */ - - req->errors = 0; - if (!uptodate) { - printk("end_request: I/O error, dev %s, sector %lu\n", - kdevname(req->rq_dev), req->sector); - req->nr_sectors--; - req->nr_sectors &= ~SECTOR_MASK; - req->sector += (BLOCK_SIZE / SECTOR_SIZE); - req->sector &= ~SECTOR_MASK; - ddv_stats.errors++; - } - - if ((bh = req->bh) != NULL) { - req->bh = bh->b_reqnext; - bh->b_reqnext = NULL; - mark_buffer_uptodate(bh, uptodate); - unlock_buffer(bh); - if ((bh = req->bh) != NULL) { - req->current_nr_sectors = bh->b_size >> 9; - if (req->nr_sectors < req->current_nr_sectors) { - req->nr_sectors = req->current_nr_sectors; - printk("end_request: buffer-list destroyed\n"); - } - req->buffer = bh->b_data; - printk("WARNING: ddv: more sectors!\n"); - ddv_stats.errors++; - return; - } - } - if (req->sem != NULL) - up(req->sem); - req->rq_status = RQ_INACTIVE; - wake_up(&wait_for_request); -} - - -/* check that a request is all OK to process */ -static int request_ok(struct request *req) -{ - int minor; - if (!req) return 0; - - if (MAJOR(req->rq_dev) != MAJOR_NR) - panic(DEVICE_NAME ": bad major number\n"); - if (!buffer_locked(req->bh)) - panic(DEVICE_NAME ": block not locked"); - - minor = MINOR(req->rq_dev); - if (minor >= NUM_DDVDEVS) { - printk("ddv_request: Invalid minor (%d)\n", minor); - return 0; - } - - if ((req->sector + req->current_nr_sectors) > ddv_sect_length[minor]) { - printk("ddv: out of range minor=%d offset=%d len=%d sect_length=%d\n", - minor,(int)req->sector,(int)req->current_nr_sectors, - ddv_sect_length[minor]); - return 0; - } - - if (req->cmd != READ && req->cmd != WRITE) { - printk("unknown request type %d\n",req->cmd); - return 0; - } - - /* it seems to be OK */ - return 1; -} - - -static void complete_request(struct request *req,int bnum) -{ - while (bnum--) { - ddv_end_request(1,req); - req = req->next; - } -} - - -static int completion_pointer = 0; - -static void check_completion(void) -{ - int i,bnum; - struct request *req; - - if (!RTable) return; - - for (; - (i=completion_pointer) != RTable->ddv_pointer && - RTable->async_info[i].status == DDV_REQ_FREE; - completion_pointer = INC_T(completion_pointer)) - { - req = (struct request *)RTable->async_info[i].argv[7]; - bnum = RTable->async_info[i].bnum; - if (!req || !bnum) { - printk("%s(%d)\n",__FILE__,__LINE__); - ddv_stats.errors++; - continue; - } - - RTable->async_info[i].status = 0; - RTable->async_info[i].argv[7] = 0; - - complete_request(req,bnum); - in_request--; - } -} - - -static struct request *get_request_queue(struct request *oldq) -{ - struct request *req,*req2; - - /* skip any non-active or bad requests */ - skip1: - if (!(req = CURRENT)) - return oldq; - - if (req->rq_status != RQ_ACTIVE) { - CURRENT = req->next; - goto skip1; - } - - if (!request_ok(req)) { - ddv_end_request(0,req); - CURRENT = req->next; - goto skip1; - } - - /* now grab as many as we can */ - req_queued++; - - for (req2 = req; - req2->next && - req2->next->rq_status == RQ_ACTIVE && - request_ok(req2->next); - req2 = req2->next) - req_queued++; - - /* leave CURRENT pointing at the bad ones */ - CURRENT = req2->next; - - /* chop our list at that point */ - req2->next = NULL; - - if (!oldq) - return req; - - for (req2=oldq;req2->next;req2=req2->next) ; - - req2->next = req; - - return oldq; -} - - -static void ddv_rem_complete(struct remote_request *rem) -{ - unsigned flags; - int bnum = rem->bnum; - struct request *req = rem->reqp; - - complete_request(req,bnum); - in_request--; - - save_flags(flags); cli(); - ddv_request1(); - restore_flags(flags); -} - - -/* - * The background ddv daemon. This receives remote disk requests - * and processes them via the normal block operations - */ -static int ddv_daemon(void *unused) -{ - current->session = 1; - current->pgrp = 1; - sprintf(current->comm, "ddv_daemon"); - spin_lock_irq(¤t->sigmask_lock); - sigfillset(¤t->blocked); /* block all signals */ - recalc_sigpending(current); - spin_unlock_irq(¤t->sigmask_lock); - - /* Give it a realtime priority. */ - current->policy = SCHED_FIFO; - current->priority = 32; /* Fixme --- we need to standardise our - namings for POSIX.4 realtime scheduling - priorities. */ - - printk("Started ddv_daemon\n"); - - while (1) { - struct remote_request *rem; - unsigned flags; - struct buffer_head *bhlist[MAX_BNUM*4]; - int i,j,minor,len,shift,offset; - - save_flags(flags); cli(); - - while (!rem_queue) { - spin_lock(¤t->sigmask_lock); - flush_signals(current); - spin_unlock(¤t->sigmask_lock); - interruptible_sleep_on(&ddv_daemon_wait); - __sti(); cli(); - } - - rem = rem_queue; - rem_queue = rem->u.next; - restore_flags(flags); - - - minor = MINOR(rem->req.rq_dev); - len = rem->req.current_nr_sectors; - offset = rem->req.sector; - - /* work out the conversion to the local block size from - sectors */ - for (shift=0; - (SECTOR_SIZE<<shift) != ddv_blocksizes[minor]; - shift++) ; - - /* do the request */ - for (i=0; len; i++) { - bhlist[i] = getblk(rem->req.rq_dev, - offset >> shift, - ddv_blocksizes[minor]); - if (!buffer_uptodate(bhlist[i])) - ll_rw_block(READ,1,&bhlist[i]); - offset += 1<<shift; - len -= 1<<shift; - } - - for (j=0;j<i;j++) - if (!buffer_uptodate(bhlist[j])) - wait_on_buffer(bhlist[j]); - - - /* put() the data */ - - - /* release the buffers */ - for (j=0;j<i;j++) - brelse(bhlist[j]); - - /* tell the originator that its done */ - rem->u.fn = ddv_rem_complete; - tnet_rpc(rem->cell,rem,sizeof(int)*3,1); - } -} - - -/* receive a remote disk request */ -static void ddv_rem_queue(char *data,unsigned size) -{ - unsigned flags; - struct remote_request *rem = (struct remote_request *) - kmalloc(size,GFP_ATOMIC); - - if (!rem) { - /* oh bugger! */ - ddv_stats.errors++; - return; - } - - memcpy(rem,data,size); - rem->u.next = NULL; - - save_flags(flags); cli(); - - /* add it to our remote request queue */ - if (!rem_queue) - rem_queue = rem; - else - rem_queue_end->u.next = rem; - rem_queue_end = rem; - - restore_flags(flags); - - wake_up(&ddv_daemon_wait); -} - - -/* which disk should this request go to */ -static inline unsigned pardisk_num(struct request *req) -{ - int minor = MINOR(req->rq_dev); - unsigned stripe; - unsigned cell; - - if (minor < PARDISK_BASE) - return this_option; - - stripe = req->sector >> STRIPE_SHIFT; - cell = stripe % num_options; - - return cell; -} - - -/* check if a 2nd request can be tacked onto the first */ -static inline int contiguous(struct request *req1,struct request *req2) -{ - if (req2->cmd != req1->cmd || - req2->rq_dev != req1->rq_dev || - req2->sector != req1->sector + req1->current_nr_sectors || - req2->current_nr_sectors != req1->current_nr_sectors) - return 0; - if (pardisk_num(req1) != pardisk_num(req2)) - return 0; - return 1; -} - -static void ddv_request1(void) -{ - struct request *req,*req1,*req2; - unsigned offset,len,req_num,mlist,bnum,available=0; - static unsigned mptrs[MAX_BNUM]; - unsigned cell; - - if (in_request > REQUEST_HIGH) - return; - - next_request = get_request_queue(next_request); - - while ((req = next_request)) { - int minor; - - if (in_request >= MAX_REQUEST) - return; - - if (in_request>1 && req_queued<REQUEST_LOW) - return; - - /* make sure we have room for a request */ - available = ddv_mlist_available(); - if (available < 1) return; - if (available > MAX_BNUM) - available = MAX_BNUM; - - offset = req->sector; - len = req->current_nr_sectors; - minor = MINOR(req->rq_dev); - - mptrs[0] = (int)req->buffer; - - for (bnum=1,req1=req,req2=req->next; - req2 && bnum<available && contiguous(req1,req2); - req1=req2,req2=req2->next) { - mptrs[bnum++] = (int)req2->buffer; - } - - next_request = req2; - - - req_queued -= bnum; - ddv_stats.blocks += bnum; - ddv_stats.rq_started += bnum; - - if (req->cmd == READ) { - ddv_stats.reads++; - ddv_stats.sectors_read += len*bnum; - } else { - ddv_stats.writes++; - ddv_stats.sectors_written += len*bnum; - } - - if (minor >= PARDISK_BASE) { - /* translate the request to the normal partition */ - unsigned stripe; - minor -= PARDISK_BASE; - - stripe = offset >> STRIPE_SHIFT; - stripe /= num_options; - offset = (stripe << STRIPE_SHIFT) + - (offset & ((1<<STRIPE_SHIFT)-1)); -#if AIR_DISK - /* like an air-guitar :-) */ - complete_request(req,bnum); - continue; -#endif - } - - if ((cell=pardisk_num(req)) != this_option) { - /* its a remote request */ - struct remote_request *rem; - unsigned *remlist; - unsigned size = sizeof(*rem) + sizeof(int)*bnum; - - rem = (struct remote_request *)kmalloc(size,GFP_ATOMIC); - if (!rem) { - /* hopefully we can get it on the next go */ - return; - } - remlist = (unsigned *)(rem+1); - - rem->u.fn = ddv_rem_queue; - rem->cell = this_option; - rem->bnum = bnum; - rem->req = *req; - rem->reqp = req; - rem->req.rq_dev = MKDEV(MAJOR_NR,minor); - rem->req.sector = offset; - memcpy(remlist,mptrs,sizeof(mptrs[0])*bnum); - - if (tnet_rpc(cell,rem,size,1) != 0) { - kfree_s(rem,size); - return; - } - } else { - /* its a local request */ - if ((mlist = ddv_get_mlist(mptrs,bnum)) == -1) { - ddv_stats.errors++; - panic("ddv: mlist corrupted"); - } - - req_num = RTable->cell_pointer; - RTable->async_info[req_num].status = - req->cmd==READ?DDV_RAWREAD_REQ:DDV_RAWWRITE_REQ; - RTable->async_info[req_num].bnum = bnum; - RTable->async_info[req_num].argv[0] = mlist; - RTable->async_info[req_num].argv[1] = len; - RTable->async_info[req_num].argv[2] = offset + - partition_tables[minor].start_sect; - RTable->async_info[req_num].argv[3] = bnum; - RTable->async_info[req_num].argv[7] = (unsigned)req; - RTable->cell_pointer = INC_T(RTable->cell_pointer); - - } - - in_request++; - } -} - - -static void ddv_request(request_queue_t * q) -{ - cli(); - ddv_request1(); - sti(); -} - - -static void check_printbufs(void) -{ - int i; - - if (!PrintBufs) return; - - while (PrintBufs->option_counter != PrintBufs->cell_counter) { - i = PrintBufs->cell_counter; - printk("opiu (%d): ",i); - if (((unsigned)PrintBufs->bufs[i].fmt) > 0x100000) - printk("Error: bad format in printk at %p\n", - PrintBufs->bufs[i].fmt); - else - printk(PrintBufs->bufs[i].fmt + OPIBUS_BASE, - PrintBufs->bufs[i].args[0], - PrintBufs->bufs[i].args[1], - PrintBufs->bufs[i].args[2], - PrintBufs->bufs[i].args[3], - PrintBufs->bufs[i].args[4], - PrintBufs->bufs[i].args[5]); - if (++PrintBufs->cell_counter == PRINT_BUFS) - PrintBufs->cell_counter = 0; - } -} - -static void ddv_interrupt(int irq, void *dev_id, struct pt_regs *regs) -{ - unsigned long flags; - save_flags(flags); cli(); - OPT_IO(IRC1) = 0x80000000; - - check_printbufs(); - check_completion(); - - ddv_request1(); - restore_flags(flags); -} - -static int ddv_open(struct inode * inode, struct file * filp) -{ - int minor = MINOR(inode->i_rdev); - - if (!have_ddv_board || minor >= NUM_DDVDEVS) - return -ENODEV; - - if (minor >= PARDISK_BASE) { - ddv_sect_length[minor] = ddv_sect_length[minor - PARDISK_BASE]; - ddv_blk_length[minor] = ddv_blk_length[minor - PARDISK_BASE]; - } - - return 0; -} - - -static void ddv_open_reply(struct cap_request *creq) -{ - int size = creq->size - sizeof(*creq); - ddv_opcodep = (char *)kmalloc(size,GFP_ATOMIC); - read_bif(ddv_opcodep, size); -#if DEBUG - printk("received opiu kernel of size %d\n",size); -#endif - if (size == 0) - have_ddv_board = 0; - wake_up(&busy_wait); -} - -extern struct block_device_operations ddv_fops; - -static void ddv_load_opiu(void) -{ - int i; - struct cap_request creq; - - /* if the opiu kernel is already loaded then we don't do anything */ - if (!have_ddv_board || opiu_kernel_loaded) - return; - - bif_register_request(REQ_DDVOPEN,ddv_open_reply); - - /* send the open request to the front end */ - creq.cid = mpp_cid(); - creq.type = REQ_DDVOPEN; - creq.header = 0; - creq.size = sizeof(creq); - - bif_queue(&creq,0,0); - - ddv_set_optadr(); - - while (!ddv_opcodep) - sleep_on(&busy_wait); - - if (!have_ddv_board) - return; - - ddv_load_kernel(ddv_opcodep); - - kfree(ddv_opcodep); - ddv_opcodep = NULL; - - if (ddv_restart_cpu()) - return; - - ddv_sect_length[0] = DiskInfo->blocks; - ddv_blk_length[0] = DiskInfo->blocks >> 1; - ddv_blocksizes[0] = BLOCK_SIZE; - - ddv_geometry.cylinders = ddv_sect_length[0] / - (ddv_geometry.heads*ddv_geometry.sectors); - - register_disk(&ddv_gendisk, MKDEV(MAJOR_NR,0), 1<<PARTN_BITS, - &ddv_fops, ddv_sect_length[0]); - - /* FIXME. The crap below is, well, crap. Pseudo-RAID and unsafe one */ - for (i=0;i<PARDISK_BASE;i++) { - ddv_sect_length[i] = ddv_gendisk.part[i].nr_sects; - ddv_blk_length[i] = ddv_gendisk.part[i].nr_sects >> 1; - } - - /* setup the parallel partitions by multiplying the normal - partition by the number of options */ - for (;i<NUM_DDVDEVS;i++) { - ddv_sect_length[i] = ddv_sect_length[i-PARDISK_BASE]*num_options; - ddv_blk_length[i] = ddv_blk_length[i-PARDISK_BASE]*num_options; - ddv_gendisk.part[i].start_sect = ddv_gendisk.part[i-PARDISK_BASE].start_sect; - ddv_gendisk.part[i].nr_sects = ddv_sect_length[i]; - } - - - opiu_kernel_loaded = 1; -} - - -/* - * This routine is called to flush all partitions and partition tables - * for a changed disk, and then re-read the new partition table. - */ -static int ddv_revalidate(kdev_t dev, struct gendisk *gdev) -{ - int target; - int max_p; - int start; - int i; - - target = DEVICE_NR(dev); - - max_p = gdev->max_p; - start = target << gdev->minor_shift; - - printk("ddv_revalidate dev=%d target=%d max_p=%d start=%d\n", - dev,target,max_p,start); - - for (i=max_p - 1; i >=0 ; i--) { - int minor = start + i; - kdev_t devi = MKDEV(gdev->major, minor); - sync_dev(devi); - invalidate_inodes(devi); - invalidate_buffers(devi); - gdev->part[minor].start_sect = 0; - gdev->part[minor].nr_sects = 0; - }; - - ddv_sect_length[start] = DiskInfo->blocks; - ddv_blk_length[start] = DiskInfo->blocks >> 1; - - grok_partitions(gdev, target, 1<<PARTN_BITS, ddv_sect_length[start]); - - printk("sect_length[%d]=%d blk_length[%d]=%d\n", - start,ddv_sect_length[start], - start,ddv_blk_length[start]); - - for (i=0;i<max_p;i++) { - ddv_sect_length[start+i] = gdev->part[start+i].nr_sects; - ddv_blk_length[start+i] = gdev->part[start+i].nr_sects >> 1; - if (gdev->part[start+i].nr_sects) - printk("partition[%d] start=%d length=%d\n",i, - (int)gdev->part[start+i].start_sect, - (int)gdev->part[start+i].nr_sects); - } - - return 0; -} - - - - -static int ddv_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - int err; - struct ddv_geometry *loc = (struct ddv_geometry *) arg; - int dev; - int minor = MINOR(inode->i_rdev); - - if ((!inode) || !(inode->i_rdev)) - return -EINVAL; - dev = DEVICE_NR(inode->i_rdev); -#if DEBUG - printk("ddv_ioctl: cmd=%x dev=%x minor=%d\n", cmd, dev, minor); -#endif - switch (cmd) { - case HDIO_GETGEO: - printk("\tHDIO_GETGEO\n"); - if (!loc) return -EINVAL; - if (put_user(ddv_geometry.heads, (char *) &loc->heads)) return -EFAULT; - if (put_user(ddv_geometry.sectors, (char *) &loc->sectors)) return -EFAULT; - if (put_user(ddv_geometry.cylinders, (short *) &loc->cylinders)) return -EFAULT; - if (put_user(ddv_geometry.start, (long *) &loc->start)) return -EFAULT; - return 0; - - case HDIO_GET_MULTCOUNT : - printk("\tHDIO_GET_MULTCOUNT\n"); - return -EINVAL; - - case HDIO_GET_IDENTITY : - printk("\tHDIO_GET_IDENTITY\n"); - return -EINVAL; - - case HDIO_GET_NOWERR : - printk("\tHDIO_GET_NOWERR\n"); - return -EINVAL; - - case HDIO_SET_NOWERR : - printk("\tHDIO_SET_NOWERR\n"); - return -EINVAL; - - case BLKRRPART: - printk("\tBLKRRPART\n"); - if (!capable(CAP_SYS_ADMIN)) - return -EACCES; - return ddv_revalidate(inode->i_rdev,&ddv_gendisk); - - case BLKGETSIZE: /* Return device size */ - if (put_user(ddv_sect_length[minor],(long *) arg)) return -EFAULT; -#if DEBUG - printk("BLKGETSIZE gave %d\n",ddv_sect_length[minor]); -#endif - return 0; - - default: - printk("ddv_ioctl: Invalid cmd=%d(0x%x)\n", cmd, cmd); - return -EINVAL; - }; -} - -static struct block_device_operations ddv_fops = { - open: ddv_open, - release: ddv_release, - ioctl: ddv_ioctl, -}; - - -static void ddv_status(void) -{ - if (!have_ddv_board) { - printk("no ddv board\n"); - return; - } - - printk(" -in_request %u req_queued %u -MTable: start=%u end=%u -Requests: started=%u finished=%u -Requests: completion_pointer=%u ddv_pointer=%u cell_pointer=%u -PrintBufs: option_counter=%u cell_counter=%u -ddv_stats: reads=%u writes=%u blocks=%u -ddv_stats: sectors_read=%u sectors_written=%u -CURRENT=%p next_request=%p errors=%u -", - in_request,req_queued, - RTable->start_mtable,RTable->end_mtable, - ddv_stats.rq_started,ddv_stats.rq_finished, - completion_pointer,RTable->ddv_pointer,RTable->cell_pointer, - PrintBufs->option_counter,PrintBufs->cell_counter, - ddv_stats.reads,ddv_stats.writes,ddv_stats.blocks, - ddv_stats.sectors_read,ddv_stats.sectors_written, - CURRENT,next_request, - ddv_stats.errors); -} - - -int ddv_init(void) -{ - int cid; - - cid = mpp_cid(); - - if (register_blkdev(MAJOR_NR,DEVICE_NAME,&ddv_fops)) { - printk("ap: unable to get major %d for ap block dev\n", - MAJOR_NR); - return -1; - } - - printk("ddv_init: register dev %d\n", MAJOR_NR); - blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST); - read_ahead[MAJOR_NR] = DDV_READ_AHEAD; - - bif_add_debug_key('d',ddv_status,"DDV status"); - ddv_gendisk.next = gendisk_head; - gendisk_head = &ddv_gendisk; - - num_options = mpp_num_cells(); - this_option = mpp_cid(); - - kernel_thread(ddv_daemon, NULL, 0); - - ddv_geninit(); - - return(0); -} - - -static void ddv_geninit(void) -{ - int i; - static int done = 0; - - if (done) - printk("ddv_geninit already done!\n"); - - done = 1; - - printk("ddv_geninit\n"); - - /* request interrupt line 2 */ - if (request_irq(APOPT0_IRQ,ddv_interrupt,SA_INTERRUPT,"apddv",NULL)) { - printk("Failed to install ddv interrupt handler\n"); - } - - for (i=0;i<NUM_DDVDEVS;i++) { - ddv_blocksizes[i] = BLOCK_SIZE; - ddv_sect_length[i] = 0; - ddv_blk_length[i] = 0; - } - - ddv_geometry.heads = 32; - ddv_geometry.sectors = 32; - ddv_geometry.cylinders = 1; - ddv_geometry.start = 0; - - blksize_size[MAJOR_NR] = ddv_blocksizes; - - ddv_load_opiu(); -} - - -/* loadable module support */ - -#ifdef MODULE - -int init_module(void) -{ - int error = ddv_init(); - if (!error) - printk(KERN_INFO "DDV: Loaded as module.\n"); - return error; -} - -/* Before freeing the module, invalidate all of the protected buffers! */ -void cleanup_module(void) -{ - int i; - struct gendisk ** gdp; - - for (i = 0 ; i < NUM_DDVDEVS; i++) - invalidate_buffers(MKDEV(MAJOR_NR, i)); - - /* reset the opiu */ - OPT_IO(OPIU_OP) = OPIU_RESET; - OPT_IO(PRST) = PRST_IRST; - - unregister_blkdev( MAJOR_NR, DEVICE_NAME ); - for (gdp = &gendisk_head; *gdp; gdp = &((*gdp)->next)) - if (*gdp == &ddv_gendisk) - break; - if (*gdp) - *gdp = (*gdp)->next; - free_irq(APOPT0_IRQ, NULL); - blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR)); -} - -#endif /* MODULE */ - - diff --git a/drivers/ap1000/ddv_util.c b/drivers/ap1000/ddv_util.c deleted file mode 100644 index d6cf12216..000000000 --- a/drivers/ap1000/ddv_util.c +++ /dev/null @@ -1,116 +0,0 @@ - /* - * Copyright 1996 The Australian National University. - * Copyright 1996 Fujitsu Laboratories Limited - * - * This software may be distributed under the terms of the Gnu - * Public License version 2 or later - */ -#define __NO_VERSION__ -#include <linux/module.h> -#include <linux/blk.h> -#include <linux/genhd.h> -#include <asm/pgtable.h> -#include <asm/ap1000/apreg.h> -#include <asm/ap1000/DdvReqTable.h> - - -#define GENDISK_STRUCT ddv_gendisk - -struct RequestTable *RTable=NULL; -struct OPrintBufArray *PrintBufs=NULL; -struct OAlignBufArray *AlignBufs=NULL; -struct DiskInfo *DiskInfo=NULL; - -extern int ddv_length[]; - -int ddv_mlist_available(void) -{ - int start = RTable->start_mtable; - int end = RTable->end_mtable; - - if (start >= end) - return (MTABLE_SIZE - start); - return (end+1) - start; -} - - -int ddv_get_mlist(unsigned mptr[],int bnum) -{ - int available = ddv_mlist_available(); - int i; - int start = RTable->start_mtable; - - if (available < bnum) { - return -1; - } - - for (i = 0; i < bnum; i++) { - unsigned phys = (unsigned)mmu_v2p((unsigned)mptr[i]); - if (phys == -1) - panic("bad address %x in ddv_get_mlist\n",mptr[i]); - RTable->mtable[RTable->start_mtable] = phys; - RTable->start_mtable = INC_ML(RTable->start_mtable); - } - - return start; -} - - - -void ddv_load_kernel(char *opcodep) -{ - int tsize; - char *p; - struct exec *mhead; - - mhead = (struct exec *)opcodep; - p = opcodep + sizeof(*mhead); - - tsize = (mhead->a_text + (PAGE_SIZE-1)) & ~(PAGE_SIZE-1); - memcpy((char *)OPIBUS_BASE+mhead->a_entry,p,mhead->a_text); - memcpy((char *)OPIBUS_BASE+mhead->a_entry+tsize, - p+mhead->a_text,mhead->a_data); - memset((char *)OPIBUS_BASE+mhead->a_entry+tsize+mhead->a_data,0, - mhead->a_bss+PAGE_SIZE); - -#ifdef DDV_DEBUG - printk("CELL(%d) loaded opiu kernel of size %ld %ld %ld (%ld)\n", - ap_getcid(), - mhead->a_text,mhead->a_data,mhead->a_bss,mhead->a_entry); -#endif -} - - -int ddv_restart_cpu(void) -{ - unsigned long timeout; - - OPT_IO(OPIU_OP) = OPIU_RESET; - OPT_IO(PRST) = PRST_IRST; - if (OPT_IO(PRST) != PRST_IRST) { - printk("_iu_load reset release error.\n"); - return(-1); - } - for (timeout=jiffies + 10; - time_before(jiffies, timeout) || (OPT_IO(PBUF0) == 0); - ) /* wait */ ; - if (OPT_IO(PBUF0) == 0) { - printk("WARNING: option kernel didn't startup\n"); - return(-1); - } else { - printk("option kernel IU running\n"); - DiskInfo = (struct DiskInfo *)(OPT_IO(PBUF0) + OPIBUS_BASE); - RTable = (struct RequestTable *)(DiskInfo->ptrs[0]+OPIBUS_BASE); - PrintBufs = (struct OPrintBufArray *)(DiskInfo->ptrs[1]+OPIBUS_BASE); - AlignBufs = (struct OAlignBufArray *)(DiskInfo->ptrs[2]+OPIBUS_BASE); - - printk("Disk capacity: %d blocks of size %d\n", - (int)DiskInfo->blocks,(int)DiskInfo->blk_size); - - OPT_IO(PBUF0) = 0; - } - return(0); -} - - - diff --git a/drivers/ap1000/mac.c b/drivers/ap1000/mac.c deleted file mode 100644 index 8e85ff555..000000000 --- a/drivers/ap1000/mac.c +++ /dev/null @@ -1,1177 +0,0 @@ - /* - * Copyright 1996 The Australian National University. - * Copyright 1996 Fujitsu Laboratories Limited - * - * This software may be distributed under the terms of the Gnu - * Public License version 2 or later - */ -/* - * Routines for controlling the FORMAC+ - */ -#include <linux/types.h> -#include <linux/string.h> -#include <linux/kernel.h> -#include <linux/skbuff.h> -#include <linux/interrupt.h> -#include <linux/if_ether.h> /* For the statistics structure. */ -#include <linux/netdevice.h> -#include <linux/if_arp.h> -#include <linux/inet.h> -#include <net/sock.h> - -#include <asm/ap1000/apreg.h> -#include <asm/ap1000/apservice.h> -#include <asm/pgtable.h> - -#include "apfddi.h" -#include "smt-types.h" -#include "am79c830.h" -#include "mac.h" -#include "plc.h" -#include "apfddi-reg.h" - -#define MAC_DEBUG 0 - -/* Values for dma_state */ -#define IDLE 0 -#define XMITTING 1 -#define RECVING 2 - -/* - * Messages greater than this value are transferred to the FDDI send buffer - * using DMA. - */ -#define DMA_XMIT_THRESHOLD 64 -#define DMA_RECV_THRESHOLD 64 - -/* - * If the FDDI receive buffer is occupied by less than this value, then - * sending has priority. - */ -#define RECV_THRESHOLD (20*1024) - -#define DMA_RESET_MASKS ((AP_CLR_INTR_MASK<<DMA_INTR_NORMAL_SH) | \ - (AP_CLR_INTR_MASK<<DMA_INTR_ERROR_SH)) - -#define DMA_INTR_REQS ((AP_INTR_REQ<<DMA_INTR_NORMAL_SH) | \ - (AP_INTR_REQ<<DMA_INTR_ERROR_SH)) - -static void mac_print_state(void); - -typedef unsigned int mac_status_t; - -static volatile struct mac_queue *mac_queue_top = NULL; -static volatile struct mac_queue *mac_queue_bottom = NULL; - -struct formac_state { - LoopbackType loopback; - int ring_op; - int recv_ptr; - int recv_empty; - int recv_ovf; - int xmit_ptr; - int xmit_free; - int xmit_start; - int xmit_chains; - int xmit_more_ptr; - int frames_xmitted; - int xmit_chain_start[3]; - int frames_recvd; - int recv_aborted; - int xmit_aborted; - int wrong_bb; - int recv_error; - volatile struct mac_queue *cur_macq; /* Current queue el for send DMA */ - volatile struct mac_buf cur_mbuf; /* Current mac_buf for send DMA */ - struct sk_buff *cur_skb; /* skb for received packets by DMA */ - int dma_state; -}; - -#define SPFRAMES_SIZE 64 /* # words for special frames area */ -#define RECV_BUF_START SPFRAMES_SIZE -#define RECV_BUF_END (BUFFER_SIZE / 2 + 2048) -#define RECV_BUF_SIZE (RECV_BUF_END - RECV_BUF_START) -#define XMIT_BUF_START RECV_BUF_END -#define XMIT_BUF_END BUFFER_SIZE - -#define S2_RMT_EVENTS (S2_CLAIM_STATE | S2_MY_CLAIM | S2_HIGHER_CLAIM | \ - S2_LOWER_CLAIM | S2_BEACON_STATE | S2_MY_BEACON | \ - S2_OTHER_BEACON | S2_RING_OP | S2_MULTIPLE_DA | \ - S2_TOKEN_ERR | S2_DUPL_CLAIM | S2_TRT_EXP_RECOV) - -struct mac_info *this_mac_info; -struct formac_state this_mac_state; - -int -mac_init(struct mac_info *mip) -{ - struct formac_state *msp = &this_mac_state; - - bif_add_debug_key('f',mac_print_state,"show FDDI mac state"); - - this_mac_info = mip; - - mac->cmdreg1 = C1_SOFTWARE_RESET; - mac->said = (mip->s_address[0] << 8) + mip->s_address[1]; - mac->laim = (mip->l_address[0] << 8) + mip->l_address[1]; - mac->laic = (mip->l_address[2] << 8) + mip->l_address[3]; - mac->lail = (mip->l_address[4] << 8) + mip->l_address[5]; - mac->sagp = (mip->s_group_adrs[0] << 8) + mip->s_group_adrs[1]; - mac->lagm = (mip->l_group_adrs[0] << 8) + mip->l_group_adrs[1]; - mac->lagc = (mip->l_group_adrs[2] << 8) + mip->l_group_adrs[3]; - mac->lagl = (mip->l_group_adrs[4] << 8) + mip->l_group_adrs[5]; - mac->tmax = mip->tmax >> 5; - mac->tvx = (mip->tvx - 254) / 255; /* it's -ve, round downwards */ - mac->treq0 = mip->treq; - mac->treq1 = mip->treq >> 16; - mac->pri0 = ~0; - mac->pri1 = ~0; - mac->pri2 = ~0; - mac->mdreg2 = /*M2_STRIP_FCS +*/ M2_CHECK_PARITY + M2_EVEN_PARITY - + 3 * M2_RCV_BYTE_BDRY + M2_ENABLE_HSREQ - + M2_ENABLE_NPDMA + M2_SYNC_NPDMA + M2_RECV_BAD_FRAMES; - mac->eacb = RECV_BUF_START - 1; - mac->earv = XMIT_BUF_START - 1; - mac->eas = mac->earv; - mac->eaa0 = BUFFER_SIZE - 1; - mac->eaa1 = mac->eaa0; - mac->eaa2 = mac->eaa1; - mac->wpxsf = 0; - mac->rpr = RECV_BUF_START; - mac->wpr = RECV_BUF_START + 1; - mac->swpr = RECV_BUF_START; - mac->wpxs = mac->eas; - mac->swpxs = mac->eas; - mac->rpxs = mac->eas; - mac->wpxa0 = XMIT_BUF_START; - mac->rpxa0 = XMIT_BUF_START; - - memset(msp, 0, sizeof(*msp)); - msp->recv_ptr = RECV_BUF_START; - msp->recv_empty = 1; - msp->xmit_ptr = XMIT_BUF_START; - msp->xmit_free = XMIT_BUF_START + 1; - msp->xmit_start = XMIT_BUF_START; - msp->xmit_chains = 0; - msp->frames_xmitted = 0; - msp->frames_recvd = 0; - msp->recv_aborted = 0; - - mac->mdreg1 = M1_MODE_MEMORY; - - mac_make_spframes(); - - return 0; -} - -int -mac_inited(struct mac_info *mip) -{ - struct formac_state *msp = &this_mac_state; - mac_status_t st1, st2; - - if (mac->said != (mip->s_address[0] << 8) + mip->s_address[1] - || mac->laim != (mip->l_address[0] << 8) + mip->l_address[1] - || mac->laic != (mip->l_address[2] << 8) + mip->l_address[3] - || mac->lail != (mip->l_address[4] << 8) + mip->l_address[5] - || mac->sagp != (mip->s_group_adrs[0] << 8) + mip->s_group_adrs[1] - || mac->lagm != (mip->l_group_adrs[0] << 8) + mip->l_group_adrs[1] - || mac->lagc != (mip->l_group_adrs[2] << 8) + mip->l_group_adrs[3] - || mac->lagl != (mip->l_group_adrs[4] << 8) + mip->l_group_adrs[5]) - return 1; - if ((mac->mdreg1 & ~M1_ADDET) != (M1_MODE_ONLINE | M1_SELECT_RA - | M1_FULL_DUPLEX)) - return 3; - if (mac->treq0 != (mip->treq & 0xffff) - || mac->treq1 != ((unsigned)mip->treq >> 16)) - return 4; - - st1 = (mac->st1u << 16) + mac->st1l; - st2 = (mac->st2u << 16) + mac->st2l; - if ((st2 & S2_RING_OP) == 0) - return 5; - - /* It's probably OK, reset some things to be safe. */ - this_mac_info = mip; - *csr0 &= ~CS0_HREQ; - mac->tmax = mip->tmax >> 5; - mac->tvx = (mip->tvx - 254) / 255; /* it's -ve, round downwards */ - mac->pri0 = ~0; - mac->pri1 = ~0; - mac->pri2 = ~0; - mac->mdreg2 = /*M2_STRIP_FCS +*/ M2_CHECK_PARITY + M2_EVEN_PARITY - + 3 * M2_RCV_BYTE_BDRY + M2_ENABLE_HSREQ - + M2_ENABLE_NPDMA + M2_SYNC_NPDMA + M2_RECV_BAD_FRAMES; - - /* clear out the receive queue */ - mac->mdreg1 = (mac->mdreg1 & ~M1_ADDET) | M1_ADDET_DISABLE_RECV; - mac->rpr = RECV_BUF_START; - mac->wpr = RECV_BUF_START + 1; - mac->swpr = RECV_BUF_START; - - memset(msp, 0, sizeof(*msp)); - msp->recv_ptr = RECV_BUF_START; - msp->recv_empty = 1; - - /* XXX reset transmit pointers */ - mac->cmdreg2 = C2_ABORT_XMIT; - mac->cmdreg2 = C2_RESET_XMITQS; - mac->wpxa0 = XMIT_BUF_START; - mac->rpxa0 = XMIT_BUF_START; - msp->xmit_ptr = XMIT_BUF_START; - msp->xmit_free = XMIT_BUF_START + 1; - msp->xmit_start = XMIT_BUF_START; - msp->xmit_chains = 0; - - mac_make_spframes(); - mac->cmdreg1 = C1_CLR_ALL_LOCKS; - - msp->frames_xmitted = 0; - msp->frames_recvd = 0; - msp->recv_aborted = 0; - msp->ring_op = 1; - - mac->mdreg1 = (mac->mdreg1 & ~M1_ADDET) | M1_ADDET_NSA; - mac->imsk1u = ~(S1_XMIT_ABORT | S1_END_FRAME_ASYNC0) >> 16; - mac->imsk1l = ~(S1_PAR_ERROR_ASYNC0 | S1_QUEUE_LOCK_ASYNC0); - mac->imsk2u = ~(S2_RECV_COMPLETE | S2_RECV_BUF_FULL | S2_RECV_FIFO_OVF - | S2_ERR_SPECIAL_FR | S2_RMT_EVENTS - | S2_NP_SIMULT_LOAD) >> 16; - mac->imsk2l = ~(S2_RMT_EVENTS | S2_MISSED_FRAME); - - return 0; -} - -void mac_make_spframes(void) -{ - volatile int *bp; - struct mac_info *mip = this_mac_info; - int sa; - struct formac_state *msp = &this_mac_state; - - /* initialize memory to avoid parity errors */ - *csr0 &= ~CS0_HREQ; - *csr1 &= ~CS1_BUF_WR_TAG; - for (bp = &buffer_mem[BUFFER_SIZE]; bp > &buffer_mem[XMIT_BUF_START];) - *--bp = 0xdeadbeef; - for (; bp > buffer_mem;) - *--bp = 0xfeedf00d; - buffer_mem[msp->recv_ptr] = 0; - - bp = buffer_mem; - *bp++ = 0; /* auto-void frame pointer (not used) */ - - /* make claim frame */ - sa = bp - buffer_mem; - *bp++ = 0xd8000011; /* claim frame descr. + length */ - *bp++ = 0xc3; /* FC value for claim frame, long addr */ - *bp++ = (mip->l_address[0] << 24) + (mip->l_address[1] << 16) - + (mip->l_address[2] << 8) + mip->l_address[3]; - *bp++ = (mip->l_address[4] << 24) + (mip->l_address[5] << 16) - + (mip->l_address[0] << 8) + mip->l_address[1]; - *bp++ = (mip->l_address[2] << 24) + (mip->l_address[3] << 16) - + (mip->l_address[4] << 8) + mip->l_address[5]; - *bp++ = mip->treq; - mac->sacl = bp - buffer_mem; /* points to pointer to claim frame */ - *bp++ = 0xa0000000 + sa; /* pointer to start of claim frame */ - - /* make beacon frame */ - sa = bp - buffer_mem; - *bp++ = 0xd8000011; /* beacon frame descr. + length */ - *bp++ = 0xc2; /* FC value for beacon frame, long addr */ - *bp++ = 0; /* DA = 0 */ - *bp++ = (mip->l_address[0] << 8) + mip->l_address[1]; - *bp++ = (mip->l_address[2] << 24) + (mip->l_address[3] << 16) - + (mip->l_address[4] << 8) + mip->l_address[5]; - *bp++ = 0; /* beacon reason = failed claim */ - mac->sabc = bp - buffer_mem; - *bp++ = 0xa0000000 + sa; /* pointer to start of beacon frame */ -} - -void mac_reset(LoopbackType loopback) -{ - int mode; - struct formac_state *msp = &this_mac_state; - - msp->loopback = loopback; - switch (loopback) { - case loop_none: - mode = M1_MODE_ONLINE; - break; - case loop_formac: - mode = M1_MODE_INT_LOOP; - break; - default: - mode = M1_MODE_EXT_LOOP; - break; - } - mac->mdreg1 = mode | M1_ADDET_NSA | M1_SELECT_RA | M1_FULL_DUPLEX; - mac->cmdreg1 = C1_IDLE_LISTEN; - mac->cmdreg1 = C1_CLR_ALL_LOCKS; - mac->imsk1u = ~(S1_XMIT_ABORT | S1_END_FRAME_ASYNC0) >> 16; - mac->imsk1l = ~(S1_PAR_ERROR_ASYNC0 | S1_QUEUE_LOCK_ASYNC0); - mac->imsk2u = ~(S2_RECV_COMPLETE | S2_RECV_BUF_FULL | S2_RECV_FIFO_OVF - | S2_ERR_SPECIAL_FR | S2_RMT_EVENTS - | S2_NP_SIMULT_LOAD) >> 16; - mac->imsk2l = ~(S2_RMT_EVENTS | S2_MISSED_FRAME); -} - -void mac_claim(void) -{ - mac->cmdreg1 = C1_CLAIM_LISTEN; -} - -void mac_disable(void) -{ - mac->mdreg1 = M1_MODE_MEMORY; - mac->imsk1u = ~0; - mac->imsk1l = ~0; - mac->imsk2u = ~0; - mac->imsk2l = ~0; - mac->wpr = mac->swpr + 1; - if (mac->wpr > mac->earv) - mac->wpr = mac->eacb + 1; - buffer_mem[mac->swpr] = 0; -} - -void mac_stats(void) -{ - struct formac_state *msp = &this_mac_state; - - if (msp->recv_ovf) - printk("%d receive buffer overflows\n", msp->recv_ovf); - if (msp->wrong_bb) - printk("%d frames on wrong byte bdry\n", msp->wrong_bb); - printk("%d frames transmitted, %d aborted\n", msp->frames_xmitted, - msp->xmit_aborted); - printk("%d frames received, %d aborted\n", msp->frames_recvd, - msp->recv_aborted); - printk("%d frames received with errors\n", msp->recv_error); -} - -void mac_sleep(void) -{ - /* disable the receiver */ - mac->mdreg1 = (mac->mdreg1 & ~M1_ADDET) | M1_ADDET_DISABLE_RECV; -} - -void mac_poll(void) -{ - mac_status_t st1, st2; - struct formac_state *msp = &this_mac_state; - int up, f, d, l, r, e, i; - - st1 = (mac->st1u << 16) + mac->st1l; - st2 = (mac->st2u << 16) + mac->st2l; - - if (st2 & S2_NP_SIMULT_LOAD) - panic("NP/formac simultaneous load!!!"); - - up = (st2 & S2_RING_OP) != 0; - if (up != msp->ring_op) { - /* ring has come up or down */ - msp->ring_op = up; - printk("mac: ring %s\n", up? "up": "down"); - set_ring_op(up); - } - - if (up) { - if (st1 & S1_XMIT_ABORT) { - ++msp->xmit_aborted; - if (st1 & S1_QUEUE_LOCK_ASYNC0) { - printk("mac: xmit queue locked, resetting xmit buffer\n"); - mac->cmdreg2 = C2_RESET_XMITQS; /* XXX bit gross */ - mac->rpxa0 = XMIT_BUF_START; - buffer_mem[XMIT_BUF_START] = 0; - msp->xmit_ptr = XMIT_BUF_START; - msp->xmit_start = XMIT_BUF_START; - msp->xmit_chains = 0; - mac->cmdreg1 = C1_CLR_ASYNCQ0_LOCK; - st1 &= ~(S1_END_CHAIN_ASYNC0 | S1_END_FRAME_ASYNC0 - | S1_XINSTR_FULL_ASYNC0); - } else - st1 |= S1_END_FRAME_ASYNC0; - } else if (st1 & S1_QUEUE_LOCK_ASYNC0) { - printk("mac: xmit queue locked, why?\n"); - mac->cmdreg1 = C1_CLR_ASYNCQ0_LOCK; - } - - if (st1 & S1_END_FRAME_ASYNC0) { - /* advance xmit_start */ - e = msp->xmit_start; - while (e != msp->xmit_ptr) { - /* find the end of the current frame */ - f = buffer_mem[e]; /* read pointer */ - if (f == 0) - break; /* huh?? */ - f &= 0xffff; - d = buffer_mem[f]; /* read descriptor */ - l = ((d & 0xffff) + ((d >> TD_BYTE_BDRY_LG) & 3) + 3) >> 2; - e = f + 1 + l; /* index of ptr at end of frame */ - r = mac->rpxa0; - if ((r <= msp->xmit_ptr && r < e && e <= msp->xmit_ptr) - || (r > msp->xmit_ptr && (r < e || e <= msp->xmit_ptr))) - break; /* up to current frame */ - /* printk("frame @ %x done\n", msp->xmit_start); */ - msp->xmit_start = e; - if ((st1 & S1_XMIT_ABORT) == 0) - ++msp->frames_xmitted; - if ((msp->xmit_chains == 1 && e == msp->xmit_ptr) || - (msp->xmit_chains > 1 && e == msp->xmit_chain_start[1])) { - /* we've finished chain 0 */ - --msp->xmit_chains; - for (i = 0; i < msp->xmit_chains; ++i) - msp->xmit_chain_start[i] = msp->xmit_chain_start[i+1]; - if (msp->xmit_chains >= 2) { - mac->cmdreg2 = C2_XMIT_ASYNCQ0; - /* printk("mac_poll: xmit chain\n"); */ - } - if (msp->xmit_chains == 0) - *csr0 &= ~CS0_LED1; - } - } - /* - * Now that we have a bit more space in the transmit buffer, - * see if we want to put another frame in. - */ -#if MAC_DEBUG - printk("Removed space in transmit buffer.\n"); -#endif - mac_process(); - } - } - - if (st2 & S2_RMT_EVENTS) { - rmt_event(st2); - } - - if (st2 & S2_RECV_COMPLETE) { - /* - * A frame has just finished arriving in the receive buffer. - */ - *csr0 |= CS0_LED2; - msp->recv_empty = 0; -#if MAC_DEBUG - printk("Frame has just trickled in...\n"); -#endif - mac_process(); - } - - if (st2 & S2_RECV_BUF_FULL) { - /* - * receive buffer overflow: reset and unlock the receive buffer. - */ -/* printk("mac: receive buffer full\n"); */ - mac->rpr = RECV_BUF_START; - mac->wpr = RECV_BUF_START + 1; - mac->swpr = RECV_BUF_START; - msp->recv_ptr = RECV_BUF_START; - msp->recv_empty = 1; - buffer_mem[RECV_BUF_START] = 0; - mac->cmdreg1 = C1_CLR_RECVQ_LOCK; - ++msp->recv_ovf; - -#if 0 - } else if (st2 & S2_RECV_FIFO_OVF) { - printk("mac: receive FIFO overflow\n"); - /* any further action required here? */ - - } else if (st2 & S2_MISSED_FRAME) { - printk("mac: missed frame\n"); -#endif - } - - if (st2 & S2_ERR_SPECIAL_FR) { - printk("mac: bug: error in special frame\n"); - mac_disable(); - } -} - -void -mac_xmit_alloc(sp, bb) - struct mac_buf *sp; - int bb; -{ - int nwords; - - nwords = (sp->length + bb + 3) >> 2; - sp->fr_start = mac_xalloc(nwords + 2); - sp->fr_end = sp->fr_start + nwords + 1; - sp->ptr = (char *) &buffer_mem[sp->fr_start + 1] + bb; - buffer_mem[sp->fr_start] = TD_MAGIC + (bb << TD_BYTE_BDRY_LG) + sp->length; -} - -void -mac_queue_frame(sp) - struct mac_buf *sp; -{ - struct formac_state *msp = &this_mac_state; - - buffer_mem[sp->fr_end] = 0; /* null pointer at end of frame */ - buffer_mem[msp->xmit_ptr] = PT_MAGIC + sp->fr_start; - if (msp->xmit_chains <= 2) { - msp->xmit_chain_start[msp->xmit_chains] = msp->xmit_ptr; - if (msp->xmit_chains < 2) - mac->cmdreg2 = C2_XMIT_ASYNCQ0; - ++msp->xmit_chains; - } else { - buffer_mem[msp->xmit_more_ptr] |= TD_MORE; - } - msp->xmit_ptr = sp->fr_end; - msp->xmit_more_ptr = sp->fr_start; - *csr0 |= CS0_LED1; -} - -int -mac_xalloc(int nwords) -{ - int fr_start; - struct formac_state *msp = &this_mac_state; - - /* - * Find some room in the transmit buffer. - */ - fr_start = msp->xmit_free; - if (fr_start > msp->xmit_start) { - if (fr_start + nwords > XMIT_BUF_END) { - /* no space at end - see if we can start again from the front */ - fr_start = XMIT_BUF_START; - if (fr_start + nwords > msp->xmit_start) - panic("no space in xmit buffer (1)"); - } - } else { - if (fr_start + nwords > msp->xmit_start) - panic("no space in xmit buffer (2)"); - } - - msp->xmit_free = fr_start + nwords; - - return fr_start; -} - -int -mac_recv_frame(sp) - struct mac_buf *sp; -{ - struct formac_state *msp = &this_mac_state; - int status, bb, orig_recv_ptr; - - orig_recv_ptr = msp->recv_ptr; - for (;;) { - status = buffer_mem[msp->recv_ptr]; - if ((status & RS_VALID) == 0) { - if (status != 0) { - printk("recv buf out of sync: recv_ptr=%x status=%x\n", - msp->recv_ptr, status); - printk(" rpr=%x swpr=%x, buf[rpr]=%x\n", mac->rpr, mac->swpr, - buffer_mem[mac->rpr]); - msp->recv_ptr = mac->swpr; - } - *csr0 &= ~CS0_LED2; - msp->recv_empty = 1; - if (mac->rpr == orig_recv_ptr) - mac->rpr = msp->recv_ptr; - return 0; - } - if (status & RS_ABORTED) - ++msp->recv_aborted; - else { - bb = (status >> RS_BYTE_BDRY_LG) & 3; - if (bb != 3) { - ++msp->wrong_bb; - bb = 3; - } - if ((status & RS_ERROR) == 0) - break; - ++msp->recv_error; - msp->recv_ptr += NWORDS((status & RS_LENGTH) + bb); - } - if (++msp->recv_ptr >= RECV_BUF_END) - msp->recv_ptr -= RECV_BUF_SIZE; - } - ++msp->frames_recvd; - if (mac->rpr == orig_recv_ptr) - mac->rpr = msp->recv_ptr; - - sp->fr_start = msp->recv_ptr; - sp->length = (status & RS_LENGTH) + bb; /* + 4 (status) - 4 (FCS) */ - sp->ptr = (void *) &buffer_mem[sp->fr_start]; - if ((msp->recv_ptr += NWORDS(sp->length) + 1) >= RECV_BUF_END) - msp->recv_ptr -= RECV_BUF_SIZE; - sp->fr_end = msp->recv_ptr; - sp->wraplen = (RECV_BUF_END - sp->fr_start) * 4; - sp->wrapptr = (void *) &buffer_mem[RECV_BUF_START]; - - return 1; -} - -void -mac_discard_frame(sp) - struct mac_buf *sp; -{ - mac->rpr = sp->fr_end; -} - -/* - * Return the number of bytes free in the async 0 transmit queue. - */ -int -mac_xmit_space(void) -{ - struct formac_state *msp = &this_mac_state; - int nw; - - if (msp->xmit_free > msp->xmit_start) { - nw = XMIT_BUF_END - msp->xmit_free; - if (nw < msp->xmit_start - XMIT_BUF_START) - nw = msp->xmit_start - XMIT_BUF_START; - } else - nw = msp->xmit_start - msp->xmit_free; - return nw <= 2? 0: (nw - 2) << 2; -} - -/* - * Return the number of bytes of frames available in the receive queue. - */ -int -mac_recv_level(void) -{ - int nw; - - nw = mac->swpr - mac->rpr; - if (nw < 0) - nw += mac->earv - mac->eacb; - return nw << 2; -} - -/* - * Return 1 iff all transmission has been completed, 0 otherwise. - */ -int mac_xmit_done(void) -{ - struct formac_state *msp = &this_mac_state; - - return msp->xmit_chains == 0; -} - -/* - * Append skbuff packet to queue. - */ -int mac_queue_append (struct sk_buff *skb) -{ - struct mac_queue *el; - unsigned flags; - save_flags(flags); cli(); - -#if MAC_DEBUG - printk("Appending queue element skb 0x%x\n", skb); -#endif - - if ((el = (struct mac_queue *)kmalloc(sizeof(*el), GFP_ATOMIC)) == NULL) { - restore_flags(flags); - return 1; - } - el->next = NULL; - el->skb = skb; - - if (mac_queue_top == NULL) { - mac_queue_top = mac_queue_bottom = el; - } - else { - mac_queue_bottom->next = el; - mac_queue_bottom = el; - } - restore_flags(flags); - return 0; -} - -/* - * If the packet originated from the same FDDI subnet as we are on, - * there is no need to perform checksumming as FDDI will does this - * us. - */ -#define CHECK_IF_CHECKSUM_REQUIRED(skb) \ - if ((skb)->protocol == ETH_P_IP) { \ - extern struct cap_init cap_init; \ - int *from_ip = (int *)((skb)->data+12); \ - int *to_ip = (int *)((skb)->data+16); \ - if ((*from_ip & cap_init.netmask) == (*to_ip & cap_init.netmask)) \ - (skb)->ip_summed = CHECKSUM_UNNECESSARY; \ - } - -/* - * Try to send and/or recv frames. - */ -void mac_process(void) -{ - volatile struct dma_chan *dma = (volatile struct dma_chan *) DMA3; - struct formac_state *msp = &this_mac_state; - struct mac_queue *el; - int nw=0, mrl = 0, fstart, send_buffer_full = 0; - unsigned flags; - - save_flags(flags); cli(); - -#if MAC_DEBUG - printk("In mac_process()\n"); -#endif - - /* - * Check if the DMA is being used. - */ - if (msp->dma_state != IDLE) { - restore_flags(flags); - return; - } - - while (mac_queue_top != NULL || /* Something to transmit */ - (mrl = mac_recv_level()) > 0) { /* Frames in receive buffer */ - send_buffer_full = 0; -#if MAC_DEBUG - printk("mac_process(): something to do... mqt %x mrl is %d\n", - mac_queue_top, mrl); -#endif - if (mac_queue_top != NULL && mrl < RECV_THRESHOLD) { - el = (struct mac_queue *)mac_queue_top; - - /* - * Check there is enough space in the FDDI send buffer. - */ - if (mac_xmit_space() < el->skb->len) { -#if MAC_DEBUG - printk("process_queue(): FDDI send buffer is full\n"); -#endif - send_buffer_full = 1; - } - else { -#if MAC_DEBUG - printk("mac_process(): sending a frame\n"); -#endif - /* - * Update mac_queue_top. - */ - mac_queue_top = mac_queue_top->next; - - /* - * Allocate space in the FDDI send buffer. - */ - msp->cur_mbuf.length = el->skb->len-3; - mac_xmit_alloc((struct mac_buf *)&msp->cur_mbuf, 3); - - /* - * If message size is greater than DMA_XMIT_THRESHOLD, send - * using DMA, otherwise use memcpy(). - */ - if (el->skb->len > DMA_XMIT_THRESHOLD) { - /* - * Start the DMA. - */ -#if MAC_DEBUG - printk("mac_process(): Starting send DMA...\n"); -#endif - nw = msp->cur_mbuf.fr_end - msp->cur_mbuf.fr_start + 1; - mac->wpxa0 = msp->cur_mbuf.fr_start + 1; - - *csr0 |= CS0_HREQ_WA0; - - msp->cur_macq = el; - msp->dma_state = XMITTING; - dma->st = DMA_DMST_RST; - dma->st = DMA_RESET_MASKS; - dma->hskip = 1; /* skip = 0, count = 1 */ - dma->vskip = 1; /* skip = 0, count = 1 */ - dma->maddr = (u_char *) - mmu_v2p((unsigned long)el->skb->data); - dma->cmd = DMA_DCMD_ST + DMA_DCMD_TYP_AUTO + - DMA_DCMD_TD_MD + nw; - *csr0 &= ~CS0_DMA_RECV; - *csr0 |= CS0_DMA_ENABLE; - - /* - * Don't process any more packets since the DMA is - * being used. - */ - break; - } - else { /* el->skb->len <= DMA_XMIT_THRESHOLD */ - /* - * Copy the data directly into the FDDI buffer. - */ -#if MAC_DEBUG - printk("mac_proces(): Copying send data...\n"); -#endif - memcpy(msp->cur_mbuf.ptr - 3, el->skb->data, - ROUND4(el->skb->len)); - mac_queue_frame((struct mac_buf *)&msp->cur_mbuf); - dev_kfree_skb(el->skb); - kfree_s(el, sizeof(*el)); - continue; - } - } - - /* - * We have reached here if there is not enough space in the - * send buffer. Try to receive some packets instead. - */ - } - - if (mac_recv_frame((struct mac_buf *)&msp->cur_mbuf)) { - volatile int fc, llc_header_word2; - int pkt_len = 0; - -#if MAC_DEBUG - printk("mac_process(): Receiving frames...\n"); -#endif - /* - * Get the fc, note only word accesses are allowed from the - * FDDI buffers. - */ - if (msp->cur_mbuf.wraplen > 4) { - fc = *(int *)(msp->cur_mbuf.ptr+4); - } - else { - /* - * fc_word must be at the start of the FDDI buffer. - */ -#if MAC_DEBUG - printk("Grabbed fc_word from wrapptr, wraplen %d\n", - msp->cur_mbuf.wraplen); -#endif - fc = *(int *)msp->cur_mbuf.wrapptr; - } - fc &= 0xff; - -#if MAC_DEBUG - printk("fc is 0x%x\n", fc); -#endif - if (fc < 0x50 || fc > 0x57) { - mac_discard_frame((struct mac_buf *)&msp->cur_mbuf); - continue; - } - - /* - * Determine the size of the packet data and allocate a socket - * buffer. - */ - pkt_len = msp->cur_mbuf.length - FDDI_HARDHDR_LEN; -#if MAC_DEBUG - printk("Packet of length %d\n", pkt_len); -#endif - msp->cur_skb = dev_alloc_skb(ROUND4(pkt_len)); - - if (msp->cur_skb == NULL) { - printk("mac_process(): Memory squeeze, dropping packet.\n"); - apfddi_stats->rx_dropped++; - restore_flags(flags); - return; - } - msp->cur_skb->dev = apfddi_device; - - /* - * Hardware header isn't copied to skbuff. - */ - msp->cur_skb->mac.raw = msp->cur_skb->data; - apfddi_stats->rx_packets++; - - /* - * Determine protocol from llc header. - */ - if (msp->cur_mbuf.wraplen < FDDI_HARDHDR_LEN) { - llc_header_word2 = *(int *)(msp->cur_mbuf.wrapptr + - (FDDI_HARDHDR_LEN - - msp->cur_mbuf.wraplen - 4)); - } - else { - llc_header_word2 = *(int *)(msp->cur_mbuf.ptr + - FDDI_HARDHDR_LEN - 4); - } - msp->cur_skb->protocol = llc_header_word2 & 0xFFFF; -#if MAC_DEBUG - printk("Got protocol 0x%x\n", msp->cur_skb->protocol); -#endif - - /* - * Copy data into socket buffer, which may be wrapped around the - * FDDI buffer. Use memcpy if the size of the data is less - * than DMA_RECV_THRESHOLD. Note if DMA is used, then wrap- - * arounds are handled automatically. - */ - if (pkt_len < DMA_RECV_THRESHOLD) { - if (msp->cur_mbuf.length < msp->cur_mbuf.wraplen) { - memcpy(skb_put(msp->cur_skb, ROUND4(pkt_len)), - msp->cur_mbuf.ptr + FDDI_HARDHDR_LEN, - ROUND4(pkt_len)); - } - else if (msp->cur_mbuf.wraplen < FDDI_HARDHDR_LEN) { -#if MAC_DEBUG - printk("Wrap case 2\n"); -#endif - memcpy(skb_put(msp->cur_skb, ROUND4(pkt_len)), - msp->cur_mbuf.wrapptr + - (FDDI_HARDHDR_LEN - msp->cur_mbuf.wraplen), - ROUND4(pkt_len)); - } - else { -#if MAC_DEBUG - printk("wrap case 3\n"); -#endif - memcpy(skb_put(msp->cur_skb, - ROUND4(msp->cur_mbuf.wraplen- - FDDI_HARDHDR_LEN)), - msp->cur_mbuf.ptr + FDDI_HARDHDR_LEN, - ROUND4(msp->cur_mbuf.wraplen - FDDI_HARDHDR_LEN)); - memcpy(skb_put(msp->cur_skb, - ROUND4(msp->cur_mbuf.length - - msp->cur_mbuf.wraplen)), - msp->cur_mbuf.wrapptr, - ROUND4(msp->cur_mbuf.length - - msp->cur_mbuf.wraplen)); - } - -#if MAC_DEBUG - if (msp->cur_skb->protocol == ETH_P_IP) { - dump_packet("apfddi_rx:", msp->cur_skb->data, pkt_len, 0); - } - else if (msp->cur_skb->protocol == ETH_P_ARP) { - struct arphdr *arp = (struct arphdr *)msp->cur_skb->data; - printk("arp->ar_op is 0x%x ar_hrd %d ar_pro 0x%x ar_hln %d ar_ln %d\n", - arp->ar_op, arp->ar_hrd, arp->ar_pro, arp->ar_hln, - arp->ar_pln); - printk("sender hardware address: %x:%x:%x:%x:%x:%x\n", - *((u_char *)msp->cur_skb->data+8), - *((u_char *)msp->cur_skb->data+9), - *((u_char *)msp->cur_skb->data+10), - *((u_char *)msp->cur_skb->data+11), - *((u_char *)msp->cur_skb->data+12), - *((u_char *)msp->cur_skb->data+13)); - printk("sender IP number %d.%d.%d.%d\n", - *((u_char *)msp->cur_skb->data+14), - *((u_char *)msp->cur_skb->data+15), - *((u_char *)msp->cur_skb->data+16), - *((u_char *)msp->cur_skb->data+17)); - printk("receiver hardware address: %x:%x:%x:%x:%x:%x\n", - *((u_char *)msp->cur_skb->data+18), - *((u_char *)msp->cur_skb->data+19), - *((u_char *)msp->cur_skb->data+20), - *((u_char *)msp->cur_skb->data+21), - *((u_char *)msp->cur_skb->data+22), - *((u_char *)msp->cur_skb->data+23)); - printk("receiver IP number %d.%d.%d.%d\n", - *((u_char *)msp->cur_skb->data+24), - *((u_char *)msp->cur_skb->data+25), - *((u_char *)msp->cur_skb->data+26), - *((u_char *)msp->cur_skb->data+27)); - } -#endif - CHECK_IF_CHECKSUM_REQUIRED(msp->cur_skb); - - /* - * Inform the network layer of the new packet. - */ -#if MAC_DEBUG - printk("Calling netif_rx()\n"); -#endif - netif_rx(msp->cur_skb); - - /* - * Remove frame from FDDI buffer. - */ - mac_discard_frame((struct mac_buf *)&msp->cur_mbuf); - continue; - } - else { - /* - * Set up dma and break. - */ -#if MAC_DEBUG - printk("mac_process(): Starting receive DMA...\n"); -#endif - nw = NWORDS(pkt_len); - msp->dma_state = RECVING; - *csr0 &= ~(CS0_HREQ | CS0_DMA_ENABLE); -/* *csr1 |= CS1_RESET_FIFO; - *csr1 &= ~CS1_RESET_FIFO; */ - if ((*csr1 & CS1_FIFO_LEVEL) != 0) { - int x; - printk("fifo not empty! (csr1 = 0x%x) emptying...", *csr1); - do { - x = *fifo; - } while ((*csr1 & CS1_FIFO_LEVEL) != 0); - printk("done\n"); - } - fstart = msp->cur_mbuf.fr_start + NWORDS(FDDI_HARDHDR_LEN); - if (fstart >= RECV_BUF_END) - fstart -= RECV_BUF_SIZE; - mac->rpr = fstart; -#if MAC_DEBUG - printk("rpr=0x%x, nw=0x%x, stat=0x%x\n", - mac->rpr, nw, buffer_mem[msp->cur_mbuf.fr_start]); -#endif - dma->st = DMA_DMST_RST; - dma->st = DMA_RESET_MASKS; - dma->hskip = 1; /* skip = 0, count = 1 */ - dma->vskip = 1; /* skip = 0, count = 1 */ - dma->maddr = (u_char *) - mmu_v2p((unsigned long) - skb_put(msp->cur_skb, ROUND4(pkt_len))); - dma->cmd = DMA_DCMD_ST + DMA_DCMD_TYP_AUTO + DMA_DCMD_TD_DM - + nw - 4; - *csr0 |= CS0_HREQ_RECV | CS0_DMA_RECV; - *csr0 |= CS0_DMA_ENABLE; -#if MAC_DEBUG - printk("mac_process(): DMA is away!\n"); -#endif - break; - } - } - else { -#if MAC_DEBUG - printk("mac_recv_frame failed\n"); -#endif - if (msp->recv_empty && send_buffer_full) - break; - } - } - /* - * Update mac_queue_bottom. - */ - if (mac_queue_top == NULL) - mac_queue_bottom = NULL; - -#if MAC_DEBUG - printk("End of mac_process()\n"); -#endif - restore_flags(flags); -} - - -#define DMA_IN(reg) (*(volatile unsigned *)(reg)) -#define DMA_OUT(reg,v) (*(volatile unsigned *)(reg) = (v)) - -/* - * DMA completion handler. - */ -void mac_dma_complete(void) -{ - volatile struct dma_chan *dma; - struct formac_state *msp = &this_mac_state; - unsigned a; - - a = DMA_IN(DMA3_DMST); - if (!(a & DMA_INTR_REQS)) { - if (msp->dma_state != IDLE && (a & DMA_DMST_AC) == 0) { - printk("dma completed but no interrupt!\n"); - msp->dma_state = IDLE; - } - return; - } - - DMA_OUT(DMA3_DMST,AP_CLR_INTR_REQ<<DMA_INTR_NORMAL_SH); - DMA_OUT(DMA3_DMST,AP_CLR_INTR_REQ<<DMA_INTR_ERROR_SH); - - dma = (volatile struct dma_chan *) DMA3; - -#if MAC_DEBUG - printk("In mac_dma_complete\n"); -#endif - - if (msp->dma_state == XMITTING && ((dma->st & DMA_DMST_AC) == 0)) { - /* - * Transmit DMA finished. - */ - int i = 20; -#if MAC_DEBUG - printk("In mac_dma_complete for transmit complete\n"); -#endif - while (*csr1 & CS1_FIFO_LEVEL) { - if (--i <= 0) { - printk("csr0=0x%x csr1=0x%x: fifo not emptying\n", *csr0, - *csr1); - return; - } - } - *csr0 &= ~(CS0_HREQ | CS0_DMA_ENABLE); - msp->dma_state = IDLE; -#if MAC_DEBUG - printk("mac_dma_complete(): Calling mac_queue_frame\n"); -#endif - mac_queue_frame((struct mac_buf *)&msp->cur_mbuf); - dev_kfree_skb(msp->cur_macq->skb); - kfree_s((struct mac_buf *)msp->cur_macq, sizeof(*(msp->cur_macq))); - msp->cur_macq = NULL; -#if MAC_DEBUG - printk("mac_dma_complete(): Calling mac_process()\n"); -#endif - mac_process(); -#if MAC_DEBUG - printk("End of mac_dma_complete transmitting\n"); -#endif - } - else if (msp->dma_state == RECVING && ((dma->st & DMA_DMST_AC) == 0)) { - /* - * Receive DMA finished. Copy the last four words from the - * fifo into the buffer, after turning off the host requests. - * We do this to avoid reading past the end of frame. - */ - int *ip, i; - -#if MAC_DEBUG - printk("In mac_dma_complete for receive complete\n"); -#endif - msp->dma_state = IDLE; - ip = (int *)mmu_p2v((unsigned long)dma->cmaddr); - -#if MAC_DEBUG - printk("ip is 0x%x, skb->data is 0x%x\n", ip, msp->cur_skb->data); -#endif - - *csr0 &= ~(CS0_DMA_ENABLE | CS0_HREQ); - - for (i = 0; (*csr1 & CS1_FIFO_LEVEL); ++i) - ip[i] = *fifo; - if (i != 4) - printk("mac_dma_complete(): not four words remaining in fifo?\n"); -#if MAC_DEBUG - printk("Copied last four words out of fifo\n"); -#endif - - /* - * Remove the frame from the FDDI receive buffer. - */ - mac_discard_frame((struct mac_buf *)&msp->cur_mbuf); - - CHECK_IF_CHECKSUM_REQUIRED(msp->cur_skb); - - /* - * Now inject the packet into the network system. - */ - netif_rx(msp->cur_skb); - -#if MAC_DEBUG - dump_packet("mac_dma_complete:", msp->cur_skb->data, 0, 0); -#endif - - /* - * Check if any more frames can be processed. - */ - mac_process(); - -#if MAC_DEBUG - printk("End of mac_dma_complete receiving\n"); -#endif - } -#if MAC_DEBUG - printk("End of mac_dma_complete()\n"); -#endif -} - -static void mac_print_state(void) -{ - struct formac_state *msp = &this_mac_state; - - printk("DMA3_DMST is 0x%x dma_state is %d\n", DMA_IN(DMA3_DMST), - msp->dma_state); - printk("csr0 = 0x%x, csr1 = 0x%x\n", *csr0, *csr1); -} - - diff --git a/drivers/ap1000/mac.h b/drivers/ap1000/mac.h deleted file mode 100644 index 85f02b4a3..000000000 --- a/drivers/ap1000/mac.h +++ /dev/null @@ -1,82 +0,0 @@ - /* - * Copyright 1996 The Australian National University. - * Copyright 1996 Fujitsu Laboratories Limited - * - * This software may be distributed under the terms of the Gnu - * Public License version 2 or later - */ -/* - * Definitions of MAC state structures etc. - */ - -struct mac_info { - TimerTwosComplement tmax; - TimerTwosComplement tvx; - TimerTwosComplement treq; - ShortAddressType s_address; - LongAddressType l_address; - ShortAddressType s_group_adrs; - LongAddressType l_group_adrs; - int rcv_own_frames; - int only_good_frames; -}; - - -struct mac_buf { - struct mac_buf *next; - int ack; - int length; - void *ptr; - int wraplen; - void *wrapptr; - int fr_start; - int fr_end; -}; - -int mac_xmit_space(void); -void mac_xmit_alloc(struct mac_buf *, int); -void mac_queue_frame(struct mac_buf *); -int mac_recv_frame(struct mac_buf *); -void mac_discard_frame(struct mac_buf *); -int mac_init(struct mac_info *mip); -int mac_inited(struct mac_info *mip); -void mac_reset(LoopbackType loopback); -void mac_claim(void); -void mac_sleep(void); -void mac_poll(void); -void mac_disable(void); -void mac_make_spframes(void); -int mac_xalloc(int nwords); -int mac_xmit_dma(struct sk_buff *skb); -void mac_dma_complete(void); -void mac_process(void); -int mac_queue_append(struct sk_buff *skb); - -struct dma_chan { - int cmd; /* cmd << 16 + size */ - int st; /* status << 16 + current size */ - int hskip; /* hskip << 16 + hcnt */ - int vskip; /* vskip << 16 + vcnt */ - unsigned char *maddr; /* memory address */ - unsigned char *cmaddr; /* current memory address */ - int ccount; /* h_count << 16 + v_count */ - int *tblp; /* table pointer */ - int *ctblp; /* current table pointer */ - unsigned char *hdptr; /* header pointer */ -}; - -#define ROUND4(x) (((x) + 3) & -4) -#define ROUND8(x) (((x) + 7) & -8) -#define ROUND16(x) (((x) + 15) & -16) -#define ROUNDLINE(x) ROUND16(x) - -#define NWORDS(x) (((x) + 3) >> 2) -#define NLINES(x) (((x) + 15) >> 4) - -/* - * Queue element used to queue transmit requests on the FDDI. - */ -struct mac_queue { - volatile struct mac_queue *next; - struct sk_buff *skb; -}; diff --git a/drivers/ap1000/plc.c b/drivers/ap1000/plc.c deleted file mode 100644 index b29b1a4c2..000000000 --- a/drivers/ap1000/plc.c +++ /dev/null @@ -1,393 +0,0 @@ - /* - * Copyright 1996 The Australian National University. - * Copyright 1996 Fujitsu Laboratories Limited - * - * This software may be distributed under the terms of the Gnu - * Public License version 2 or later - */ -/* - * Routines for controlling the Am79c864 physical layer controller. - * - * This chip implements some parts of the FDDI SMT standard - * (PCM: physical connection management, LEM: link error monitor, etc.) - * as well as the FDDI PHY standard. - */ -#include <linux/types.h> -#include <linux/string.h> -#include <linux/kernel.h> -#include "apfddi.h" -#include "smt-types.h" -#include "am79c864.h" -#include "plc.h" -#include "apfddi-reg.h" - -typedef enum { - off, - signalling, - doing_lct, - joining, - active -} PlcPhase; - -struct plc_state { - LoopbackType loopback; - char t_val[16]; - char r_val[16]; - int n; - PortType peer_type; - PlcPhase phase; -}; - -struct plc_info *this_plc_info; -struct plc_state this_plc_state; - -void plc_init(struct plc_info *pip) -{ - int class, x; - struct plc_state *psp = &this_plc_state; - - this_plc_info = pip; - - /* first turn it off, clear registers */ - class = pip->port_type == pt_s? CB_CLASS_S: 0; - plc->ctrl_b = CB_PC_STOP + class; - plc->intr_mask = IE_NP_ERROR; - x = plc->intr_event; /* these register clear when read */ - x = plc->viol_sym_ct; - x = plc->min_idle_ct; - x = plc->link_err_ct; - - /* initialize registers */ - plc->ctrl_a = 0; - plc->ctrl_b = class; - plc->c_min = pip->c_min >> 8; - plc->tl_min = pip->tl_min >> 8; - plc->tb_min = pip->tb_min >> 8; - plc->t_out = pip->t_out >> 8; - plc->t_scrub = pip->t_scrub >> 8; - plc->ns_max = pip->ns_max >> 2; - - psp->phase = off; -} - -int -plc_inited(struct plc_info *pip) -{ - int class, x; - struct plc_state *psp = &this_plc_state; - - class = pip->port_type == pt_s? CB_CLASS_S: 0; - if ((plc->ctrl_a & (CA_LOOPBACK|CA_FOT_OFF|CA_EB_LOOP|CA_LM_LOOP)) != 0) - return 1; - if ((plc->ctrl_b & (CB_CONFIG_CTRL|CB_CLASS_S|CB_PC_MAINT)) != class) - return 2; - if (plc->status_a & SA_SIG_DETECT) - return 3; - if ((plc->status_b & (SB_PCI_STATE|SB_PCM_STATE)) - != (SB_PCI_STATE_INSERTED|SB_PCM_STATE_ACTIVE)) - return 4; - - /* all seems OK, reset the timers and counters just to be sure */ - plc->intr_mask = IE_NP_ERROR; - x = plc->intr_event; /* these register clear when read */ - x = plc->viol_sym_ct; - x = plc->min_idle_ct; - x = plc->link_err_ct; - - plc->c_min = pip->c_min >> 8; - plc->tl_min = pip->tl_min >> 8; - plc->tb_min = pip->tb_min >> 8; - plc->t_out = pip->t_out >> 8; - plc->t_scrub = pip->t_scrub >> 8; - plc->ns_max = pip->ns_max >> 2; - - psp->phase = active; - /* XXX should initialize other fields of this_plc_state */ - - return 0; -} - -void plc_sleep(void) -{ -} - -void pc_start(LoopbackType loopback) -{ - int x; - struct plc_info *pip = this_plc_info; - struct plc_state *psp = &this_plc_state; - - /* make sure it's off */ - plc->ctrl_b &= ~CB_PCM_CTRL; - plc->ctrl_b |= CB_PC_STOP; - - /* set up loopback required */ - psp->loopback = loopback; - x = 0; - switch (loopback) { - case loop_plc_lm: - x = CA_LM_LOOP; - break; - case loop_plc_eb: - x = CA_EB_LOOP; - break; - case loop_pdx: - x = CA_LOOPBACK; - break; - default: - x = 0; - } - plc->ctrl_a = x; - - /* set up bits to be exchanged */ - psp->t_val[0] = 0; - psp->t_val[1] = ((int) pip->port_type >> 1) & 1; - psp->t_val[2] = (int) pip->port_type & 1; - psp->t_val[4] = 0; /* XXX assume we want short LCT */ - psp->t_val[5] = 0; - psp->t_val[6] = 0; /* XXX too lazy to fire up my MAC for LCT */ - psp->t_val[8] = 0; /* XXX don't wanna local loop */ - psp->t_val[9] = 1; /* gotta MAC on port output */ - - pc_restart(); -} - -void pc_restart(void) -{ - struct plc_state *psp = &this_plc_state; - - if (psp->phase != off) - printk("restarting pcm\n"); - if (psp->phase == active) - set_cf_join(0); /* we're down :-( */ - - psp->n = 0; - plc->vec_length = 3 - 1; - plc->xmit_vector = psp->t_val[0] + (psp->t_val[1] << 1) - + (psp->t_val[2] << 2); - - plc->intr_mask = IE_NP_ERROR | IE_PCM_BREAK | IE_PCM_CODE; - plc->ctrl_b &= ~CB_PCM_CTRL; - plc->ctrl_b |= CB_PC_START; /* light blue paper and stand clear */ - - psp->phase = signalling; -} - -void pc_stop(void) -{ - struct plc_state *psp = &this_plc_state; - - if (psp->phase == active) - set_cf_join(0); - plc->ctrl_b &= ~CB_PCM_CTRL; - plc->ctrl_b |= CB_PC_STOP; - plc->intr_mask = IE_NP_ERROR; - psp->phase = off; -} - -void plc_poll(void) -{ - struct plc_state *psp = &this_plc_state; - int events, i; - - if ((*csr0 & CS0_PHY_IRQ) == 0) - return; - events = plc->intr_event & plc->intr_mask; - if (events & IE_NP_ERROR) { - printk("plc: NP error!\n"); - } - if (events & IE_PCM_BREAK) { - i = plc->status_b & SB_BREAK_REASON; - if (i > SB_BREAK_REASON_START) { - if (psp->phase == signalling || psp->phase == doing_lct) - pcm_dump_rtcodes(); - printk("pcm: break reason %d\n", i); - if (psp->phase != off) - pc_restart(); - /* XXX need to check for trace? */ - } - } - if (events & IE_PCM_CODE) { - if (psp->phase == signalling) - pcm_pseudo_code(); - else if (psp->phase == doing_lct) - pcm_lct_done(); - else - printk("XXX pcm_code interrupt in phase %d?\n", psp->phase); - } - if (events & IE_PCM_ENABLED) { - if (psp->phase == joining) - pcm_enabled(); - else - printk("XXX pcm_enabled interrupt in phase %d?\n", psp->phase); - } - if (events & IE_TRACE_PROP) { - if (psp->phase == active) - pcm_trace_prop(); - else - printk("XXX trace_prop interrupt in phase %d\n", psp->phase); - } -} - -void pcm_pseudo_code(void) -{ - struct plc_info *pip = this_plc_info; - struct plc_state *psp = &this_plc_state; - int i, nb, lct, hislct; - - /* unpack the bits from the peer */ - nb = plc->vec_length + 1; - i = plc->rcv_vector; - do { - psp->r_val[psp->n++] = i & 1; - i >>= 1; - } while (--nb > 0); - - /* send some more, do LCT, whatever */ - switch (psp->n) { - case 3: - /* - * Got escape flag, port type; send compatibility, - * LCT duration, MAC for LCT flag. - */ - if (psp->r_val[0]) { - /* help! what do I do now? */ - pcm_dump_rtcodes(); - pc_restart(); - break; - } - psp->peer_type = (PortType) ((psp->r_val[1] << 1) + psp->r_val[2]); - /* XXX we're type S, we talk to anybody */ - psp->t_val[3] = 1; - - plc->vec_length = 4 - 1; - plc->xmit_vector = psp->t_val[3] + (psp->t_val[4] << 1) - + (psp->t_val[5] << 2) + (psp->t_val[6] << 3); - break; - - case 7: - /* - * Got compatibility, LCT duration, MAC for LCT flag; - * time to do the LCT. - */ - lct = (psp->t_val[4] << 1) + psp->t_val[5]; - hislct = (psp->r_val[4] << 1) + psp->r_val[5]; - if (hislct > lct) - lct = hislct; - - /* set LCT duration */ - switch (lct) { - case 0: - plc->lc_length = pip->lc_short >> 8; - plc->ctrl_b &= ~CB_LONG_LCT; - break; - case 1: - plc->lc_length = pip->lc_medium >> 8; - plc->ctrl_b &= ~CB_LONG_LCT; - break; - case 2: - plc->ctrl_b |= CB_LONG_LCT; - /* XXX set up a timeout for pip->lc_long */ - break; - case 3: - plc->ctrl_b |= CB_LONG_LCT; - /* XXX set up a timeout for pip->lc_extended */ - break; - } - - /* start the LCT */ - i = plc->link_err_ct; /* clear the register */ - plc->ctrl_b &= ~CB_PC_LCT; - /* XXX assume we're not using the MAC for LCT; - if he's got a MAC, loop his stuff back, otherwise send idle. */ - if (psp->r_val[6]) - plc->ctrl_b |= CB_PC_LCT_LOOP; - else - plc->ctrl_b |= CB_PC_LCT_IDLE; - psp->phase = doing_lct; - break; - - case 8: - /* - * Got LCT result, send MAC for local loop and MAC on port - * output flags. - */ - if (psp->t_val[7] || psp->r_val[7]) { - printk("LCT failed, restarting.\n"); - /* LCT failed - do at least a medium length test next time. */ - if (psp->t_val[4] == 0 && psp->t_val[5] == 0) - psp->t_val[5] = 1; - pcm_dump_rtcodes(); - pc_restart(); - break; - } - plc->vec_length = 2 - 1; - plc->xmit_vector = psp->t_val[8] + (psp->t_val[9] << 1); - break; - - case 10: - /* - * Got MAC for local loop and MAC on port output flags. - * Let's join. - */ - plc->intr_mask = IE_NP_ERROR | IE_PCM_BREAK | IE_PCM_ENABLED; - plc->ctrl_b |= CB_PC_JOIN; - psp->phase = joining; - /* printk("pcm: joining\n"); */ - break; - - default: - printk("pcm_pseudo_code bug: n = %d\n", psp->n); - } -} - -void pcm_lct_done(void) -{ - struct plc_state *psp = &this_plc_state; - int i; - - i = plc->link_err_ct; - psp->t_val[7] = i > 0; - printk("pcm: lct %s (%d errors)\n", psp->t_val[7]? "failed": "passed", i); - plc->ctrl_b &= ~(CB_PC_LCT | CB_LONG_LCT); - plc->vec_length = 1 - 1; - plc->xmit_vector = psp->t_val[7]; - psp->phase = signalling; -} - -void pcm_dump_rtcodes(void) -{ - struct plc_state *psp = &this_plc_state; - int i; - - if (psp->n > 0) { - printk("pcm signalling interrupted after %d bits:\nt_val:", psp->n); - for (i = 0; i < psp->n; ++i) - printk(" %d", psp->t_val[i]); - printk("\nr_val:"); - for (i = 0; i < psp->n; ++i) - printk(" %d", psp->r_val[i]); - printk("\n"); - } -} - -void pcm_enabled(void) -{ - struct plc_state *psp = &this_plc_state; - int i; - - printk("pcm: enabled\n"); - psp->phase = active; - i = plc->link_err_ct; /* clear the register */ - /* XXX should set up LEM here */ - /* XXX do we want to count violation symbols, minimum idle gaps, - or elasticity buffer errors? */ - plc->intr_mask = IE_NP_ERROR | IE_PCM_BREAK | IE_TRACE_PROP; - set_cf_join(1); /* we're up :-) */ -} - -void pcm_trace_prop(void) -{ - /* XXX help! what do I do now? */ - pc_stop(); -} diff --git a/drivers/ap1000/plc.h b/drivers/ap1000/plc.h deleted file mode 100644 index f87783f57..000000000 --- a/drivers/ap1000/plc.h +++ /dev/null @@ -1,53 +0,0 @@ - /* - * Copyright 1996 The Australian National University. - * Copyright 1996 Fujitsu Laboratories Limited - * - * This software may be distributed under the terms of the Gnu - * Public License version 2 or later - */ -/* - * Definitions for PLC state structures etc. - */ - -struct plc_info { - PortType port_type; - TimerTwosComplement c_min; - TimerTwosComplement tl_min; - TimerTwosComplement tb_min; - TimerTwosComplement t_out; - TimerTwosComplement lc_short; - TimerTwosComplement lc_medium; - TimerTwosComplement lc_long; - TimerTwosComplement lc_extended; - TimerTwosComplement t_scrub; - TimerTwosComplement ns_max; - Counter link_errors; - Counter viol_syms; - Counter mini_occur; - int min_idle_gap; - double link_error_rate; -}; - -void plc_init(struct plc_info *pip); -int plc_inited(struct plc_info *pip); -void pc_start(LoopbackType loopback); -void plc_sleep(void); -void plc_poll(void); -void pc_stop(void); -void pc_restart(void); -void pcm_dump_rtcodes(void); -void pcm_pseudo_code(void); -void pcm_lct_done(void); -void pcm_enabled(void); -void pcm_trace_prop(void); - - - - - - - - - - - diff --git a/drivers/ap1000/ringbuf.c b/drivers/ap1000/ringbuf.c deleted file mode 100644 index 8acb617b6..000000000 --- a/drivers/ap1000/ringbuf.c +++ /dev/null @@ -1,311 +0,0 @@ - /* - * Copyright 1996 The Australian National University. - * Copyright 1996 Fujitsu Laboratories Limited - * - * This software may be distributed under the terms of the Gnu - * Public License version 2 or later - */ -/* - * linux/drivers/ap1000/ringbuf.c - * - * This provides the /proc/XX/ringbuf interface to the Tnet ring buffer - */ -#define _APLIB_ -#include <linux/types.h> -#include <linux/errno.h> -#include <linux/sched.h> -#include <linux/kernel.h> -#include <linux/malloc.h> -#include <linux/mm.h> - -#include <asm/page.h> -#include <asm/segment.h> -#include <asm/io.h> -#include <asm/pgtable.h> -#include <asm/uaccess.h> - -#include <asm/ap1000/pgtapmmu.h> -#include <asm/ap1000/apreg.h> -#include <asm/ap1000/apservice.h> - - -/* we have a small number of reserved ring buffers to ensure that at - least one parallel program can always run */ -#define RBUF_RESERVED 4 -#define RBUF_RESERVED_ORDER 5 -static struct { - char *rb_ptr; - char *shared_ptr; - int used; -} reserved_ringbuf[RBUF_RESERVED]; - - -void ap_ringbuf_init(void) -{ - int i,j; - char *rb_ptr, *shared_ptr; - int rb_size = PAGE_SIZE * (1<<RBUF_RESERVED_ORDER); - - /* preallocate some ringbuffers */ - for (i=0;i<RBUF_RESERVED;i++) { - if (!(rb_ptr = (char *)__get_free_pages(GFP_ATOMIC,RBUF_RESERVED_ORDER))) { - printk("failed to preallocate ringbuf %d\n",i); - return; - } - for (j = MAP_NR(rb_ptr); j <= MAP_NR(rb_ptr+rb_size-1); j++) { - set_bit(PG_reserved,&mem_map[j].flags); - } - - if (!(shared_ptr = (char *)__get_free_page(GFP_ATOMIC))) { - printk("failed to preallocate shared ptr %d\n",i); - return; - } - set_bit(PG_reserved,&mem_map[MAP_NR(shared_ptr)].flags); - - reserved_ringbuf[i].used = 0; - reserved_ringbuf[i].rb_ptr = rb_ptr; - reserved_ringbuf[i].shared_ptr = shared_ptr; - } -} - - - -void exit_ringbuf(struct task_struct *tsk) -{ - int i; - - if (!tsk->ringbuf) return; - - if (tsk->ringbuf->ringbuf) { - char *rb_ptr = tsk->ringbuf->ringbuf; - char *shared_ptr = tsk->ringbuf->shared; - int order = tsk->ringbuf->order; - int rb_size = PAGE_SIZE * (1<<order); - - for (i=0;i<RBUF_RESERVED;i++) - if (rb_ptr == reserved_ringbuf[i].rb_ptr) break; - - if (i < RBUF_RESERVED) { - reserved_ringbuf[i].used = 0; - } else { - for (i = MAP_NR(rb_ptr); i <= MAP_NR(rb_ptr+rb_size-1); i++) { - clear_bit(PG_reserved, &mem_map[i].flags); - } - free_pages((unsigned)rb_ptr,order); - - i = MAP_NR(shared_ptr); - clear_bit(PG_reserved,&mem_map[i]); - free_page((unsigned)shared_ptr); - } - } - - kfree_s(tsk->ringbuf,sizeof(*(tsk->ringbuf))); - tsk->ringbuf = NULL; -} - - -/* - * map the ring buffer into users memory - */ -static int cap_map(int rb_size) -{ - struct task_struct *tsk=current; - int i; - char *rb_ptr=NULL; - char *shared_ptr=NULL; - int order = 0; - int error,old_uid; - - error = verify_area(VERIFY_WRITE,(char *)RBUF_VBASE,rb_size); - if (error) return error; - - if (!MPP_IS_PAR_TASK(tsk->taskid)) { - printk("ringbuf_mmap called from non-parallel task\n"); - return -EINVAL; - } - - - if (tsk->ringbuf) return -EINVAL; - - rb_size -= RBUF_RING_BUFFER_OFFSET; - rb_size >>= 1; - - switch (rb_size/1024) { - case 128: - order = 5; - break; - case 512: - order = 7; - break; - case 2048: - order = 9; - break; - case 8192: - order = 11; - break; - default: - printk("ringbuf_mmap with invalid size %d\n",rb_size); - return -EINVAL; - } - - if (order == RBUF_RESERVED_ORDER) { - for (i=0;i<RBUF_RESERVED;i++) - if (!reserved_ringbuf[i].used) { - rb_ptr = reserved_ringbuf[i].rb_ptr; - shared_ptr = reserved_ringbuf[i].shared_ptr; - reserved_ringbuf[i].used = 1; - break; - } - } - - if (!rb_ptr) { - rb_ptr = (char *)__get_free_pages(GFP_USER,order); - if (!rb_ptr) return -ENOMEM; - - for (i = MAP_NR(rb_ptr); i <= MAP_NR(rb_ptr+rb_size-1); i++) { - set_bit(PG_reserved,&mem_map[i].flags); - } - - shared_ptr = (char *)__get_free_page(GFP_USER); - if (!shared_ptr) - return -ENOMEM; - set_bit(PG_reserved,&mem_map[MAP_NR(shared_ptr)].flags); - } - - if (!rb_ptr) - return -ENOMEM; - - memset(rb_ptr,0,rb_size); - memset(shared_ptr,0,PAGE_SIZE); - - if (remap_page_range(RBUF_VBASE + RBUF_RING_BUFFER_OFFSET, - mmu_v2p((unsigned)rb_ptr), - rb_size,APMMU_PAGE_SHARED)) - return -EAGAIN; - - if (remap_page_range(RBUF_VBASE + RBUF_RING_BUFFER_OFFSET + rb_size, - mmu_v2p((unsigned)rb_ptr), - rb_size,APMMU_PAGE_SHARED)) - return -EAGAIN; - - /* the shared area */ - if (remap_page_range(RBUF_VBASE + RBUF_SHARED_PAGE_OFF, - mmu_v2p((unsigned)shared_ptr), - PAGE_SIZE,APMMU_PAGE_SHARED)) - return -EAGAIN; - -#if 0 - /* lock the ringbuffer in memory */ - old_uid = current->euid; - current->euid = 0; - error = sys_mlock(RBUF_VBASE,2*rb_size+RBUF_RING_BUFFER_OFFSET); - current->euid = old_uid; - if (error) { - printk("ringbuffer mlock failed\n"); - return error; - } -#endif - - /* the queue pages */ -#define MAP_QUEUE(offset,phys) \ - io_remap_page_range(RBUF_VBASE + offset, \ - phys<<PAGE_SHIFT,PAGE_SIZE,APMMU_PAGE_SHARED,0xa) - - MAP_QUEUE(RBUF_PUT_QUEUE, 0x00000); - MAP_QUEUE(RBUF_GET_QUEUE, 0x00001); - MAP_QUEUE(RBUF_SEND_QUEUE, 0x00040); - - MAP_QUEUE(RBUF_XY_QUEUE, 0x00640); - MAP_QUEUE(RBUF_X_QUEUE, 0x00240); - MAP_QUEUE(RBUF_Y_QUEUE, 0x00440); - MAP_QUEUE(RBUF_XYG_QUEUE, 0x00600); - MAP_QUEUE(RBUF_XG_QUEUE, 0x00200); - MAP_QUEUE(RBUF_YG_QUEUE, 0x00400); - MAP_QUEUE(RBUF_CSI_QUEUE, 0x02004); - MAP_QUEUE(RBUF_FOP_QUEUE, 0x02005); - -#undef MAP_QUEUE - - if (!tsk->ringbuf) { - tsk->ringbuf = (void *)kmalloc(sizeof(*(tsk->ringbuf)),GFP_ATOMIC); - if (!tsk->ringbuf) - return -ENOMEM; - } - - memset(tsk->ringbuf,0,sizeof(*tsk->ringbuf)); - tsk->ringbuf->ringbuf = rb_ptr; - tsk->ringbuf->shared = shared_ptr; - tsk->ringbuf->order = order; - tsk->ringbuf->write_ptr = mmu_v2p((unsigned)rb_ptr)<<1; - tsk->ringbuf->vaddr = RBUF_VBASE; - - memset(tsk->ringbuf->vaddr+RBUF_SHARED_PAGE_OFF,0,PAGE_SIZE); - { - struct _kernel_cap_shared *_kernel = - (struct _kernel_cap_shared *)tsk->ringbuf->vaddr; - _kernel->rbuf_read_ptr = (rb_size>>5) - 1; - } - - return 0; -} - - -static int -ringbuf_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) -{ - int numcells, *phys_cells; - extern struct cap_init cap_init; - - switch (cmd) { - case CAP_GETINIT: - if (copy_to_user((char *)arg,(char *)&cap_init,sizeof(cap_init))) - return -EFAULT; - break; - - case CAP_SYNC: - if (verify_area(VERIFY_READ, (void *) arg, sizeof(int)*2)) - return -EFAULT; - if (get_user(numcells,(int *)arg)) return -EFAULT; - if (get_user((unsigned)phys_cells, - ((int *)arg)+1)) return -EFAULT; - if (verify_area(VERIFY_READ,phys_cells,sizeof(int)*numcells)) - return -EFAULT; - return ap_sync(numcells,phys_cells); - break; - - case CAP_SETGANG: - { - int v; - if (get_user(v,(int *)arg)) return -EFAULT; - mpp_set_gang_factor(v); - break; - } - - case CAP_MAP: - return cap_map(arg); - - default: - printk("unknown ringbuf ioctl %d\n",cmd); - return -EINVAL; - } - return 0; -} - - -static struct file_operations proc_ringbuf_operations = { - NULL, - NULL, - NULL, - NULL, /* readdir */ - NULL, /* poll */ - ringbuf_ioctl, /* ioctl */ - NULL, /* mmap */ - NULL, /* no special open code */ - NULL, /* flush */ - NULL, /* no special release code */ - NULL /* can't fsync */ -}; - -struct inode_operations proc_ringbuf_inode_operations = { - &proc_ringbuf_operations, /* default base directory file-ops */ -}; diff --git a/drivers/ap1000/smt-types.h b/drivers/ap1000/smt-types.h deleted file mode 100644 index b17c83176..000000000 --- a/drivers/ap1000/smt-types.h +++ /dev/null @@ -1,167 +0,0 @@ - /* - * Copyright 1996 The Australian National University. - * Copyright 1996 Fujitsu Laboratories Limited - * - * This software may be distributed under the terms of the Gnu - * Public License version 2 or later - */ -/* - * Definitions for FDDI Station Management. - */ - -/* - * FDDI-COMMON types. - */ - -typedef unsigned int Counter; /* 32-bit event counter */ - -typedef enum { - cp_isolated, - cp_local, - cp_secondary, - cp_primary, - cp_concatenated, - cp_thru -} CurrentPath; - -typedef char Flag; - -typedef unsigned char LongAddressType[6]; - -typedef enum { - pt_a, - pt_b, - pt_s, - pt_m, - pt_none -} PortType; - -typedef unsigned short ResourceId; - -typedef int Time; /* time in 80ns units */ -#define FDDI_TIME_UNIT 80e-9 /* 80 nanoseconds */ -#define SECS_TO_FDDI_TIME(s) ((int)((s)/FDDI_TIME_UNIT+0.99)) - -typedef int TimerTwosComplement; - -/* - * FDDI-SMT types. - */ -typedef enum { - ec_Out, - ec_In, - ec_Trace, - ec_Leave, - ec_Path_Test, - ec_Insert, - ec_Check, - ec_Deinsert -} ECMState; - -/* - * FDDI-MAC types. - */ -typedef enum { - dat_none, - dat_pass, - dat_fail -} DupAddressTest; - -typedef unsigned short DupCondition; -#define DC_MYDUP 1 -#define DC_UNADUP 2 - -typedef unsigned short FS_Functions; -#define FSF_FS_REPEATING 1 -#define FSF_FS_SETTING 2 -#define FSF_FS_CLEARING 4 - -typedef unsigned char NACondition; -#define NAC_UNACHANGE 1 -#define NAC_DNACHANGE 2 - -typedef enum { - rmt_Isolated, - rmt_Non_Op, - rmt_Ring_Op, - rmt_Detect, - rmt_Non_Op_Dup, - rmt_Ring_Op_Dup, - rmt_Directed, - rmt_Trace -} RMTState; - -typedef unsigned char ShortAddressType[2]; - -/* - * FDDI-PATH types. - */ -typedef unsigned short TraceStatus; -#define TS_TRACEINITIATED 1 -#define TS_TRACEPROPAGATED 2 -#define TS_TRACETERMINATED 4 -#define TS_TRACETIMEOUT 8 - -/* - * FDDI-PORT types. - */ -typedef enum { - PC_Maint, - PC_Enable, - PC_Disable, - PC_Start, - PC_Stop -} ActionType; - -typedef unsigned char ConnectionPolicies; -#define PC_MAC_LCT 1 -#define PC_MAC_LOOP 2 - -typedef enum { - cs_disabled, - cs_connecting, - cs_standby, - cs_active -} ConnectState; - -typedef enum { - ls_qls, - ls_ils, - ls_mls, - ls_hls, - ls_pdr, - ls_lsu, - ls_nls -} LineState; - -typedef enum { - pc_Off, - pc_Break, - pc_Trace, - pc_Connect, - pc_Next, - pc_Signal, - pc_Join, - pc_Verify, - pc_Active, - pc_Maint -} PCMState; - -typedef enum { - pcw_none, - pcw_mm, - pcw_otherincompatible, - pcw_pathnotavailable -} PC_Withhold; - -typedef enum { - pmd_multimode, - pmd_single_mode1, - pmd_single_mode2, - pmd_sonet, - pmd_low_cost_fiber, - pmd_twisted_pair, - pmd_unknown, - pmd_unspecified -} PMDClass; - diff --git a/drivers/block/cmd64x.c b/drivers/block/cmd64x.c index 6346f216e..8f5f04aea 100644 --- a/drivers/block/cmd64x.c +++ b/drivers/block/cmd64x.c @@ -7,7 +7,7 @@ * on the 646U2 and not on the 646U. * * Copyright (C) 1998 Eddie C. Dost (ecd@skynet.be) - * Copyright (C) 1998 David S. Miller (davem@dm.cobaltmicro.com) + * Copyright (C) 1998 David S. Miller (davem@redhat.com) * Copyright (C) 1999 Andre Hedrick (andre@suse.com) */ diff --git a/drivers/block/swim3.c b/drivers/block/swim3.c index 3190c10b9..911bafe23 100644 --- a/drivers/block/swim3.c +++ b/drivers/block/swim3.c @@ -241,10 +241,6 @@ static int floppy_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long param); static int floppy_open(struct inode *inode, struct file *filp); static int floppy_release(struct inode *inode, struct file *filp); -static ssize_t floppy_read(struct file *filp, char *buf, - size_t count, loff_t *ppos); -static ssize_t floppy_write(struct file *filp, const char *buf, - size_t count, loff_t *ppos); static int floppy_check_change(kdev_t dev); static int floppy_revalidate(kdev_t dev); static int swim3_add_device(struct device_node *swims); @@ -988,42 +984,6 @@ static int floppy_revalidate(kdev_t dev) return ret; } -static ssize_t floppy_read(struct file *filp, char *buf, - size_t count, loff_t *ppos) -{ - struct inode *inode = filp->f_dentry->d_inode; - struct floppy_state *fs; - int devnum = MINOR(inode->i_rdev); - - if (devnum >= floppy_count) - return -ENODEV; - - fs = &floppy_states[devnum]; - if (fs->ejected) - return -ENXIO; - return block_read(filp, buf, count, ppos); -} - -static ssize_t floppy_write(struct file * filp, const char * buf, - size_t count, loff_t *ppos) -{ - struct inode * inode = filp->f_dentry->d_inode; - struct floppy_state *fs; - int devnum = MINOR(inode->i_rdev); - - if (devnum >= floppy_count) - return -ENODEV; - check_disk_change(inode->i_rdev); - fs = &floppy_states[devnum]; - if (fs->ejected) - return -ENXIO; - if (fs->write_prot < 0) - fs->write_prot = swim3_readbit(fs, WRITE_PROT); - if (fs->write_prot) - return -EROFS; - return block_write(filp, buf, count, ppos); -} - static void floppy_off(unsigned int nr) { } diff --git a/drivers/block/swim_iop.c b/drivers/block/swim_iop.c index 6a74f9a0b..467cda26d 100644 --- a/drivers/block/swim_iop.c +++ b/drivers/block/swim_iop.c @@ -102,10 +102,6 @@ static void swimiop_receive(struct iop_msg *, struct pt_regs *); static void swimiop_status_update(int, struct swim_drvstatus *); static int swimiop_eject(struct floppy_state *fs); -static ssize_t floppy_read(struct file *filp, char *buf, - size_t count, loff_t *ppos); -static ssize_t floppy_write(struct file *filp, const char *buf, - size_t count, loff_t *ppos); static int floppy_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long param); static int floppy_open(struct inode *inode, struct file *filp); @@ -338,40 +334,6 @@ static int swimiop_eject(struct floppy_state *fs) return cmd->error; } -static ssize_t floppy_read(struct file *filp, char *buf, - size_t count, loff_t *ppos) -{ - struct inode *inode = filp->f_dentry->d_inode; - struct floppy_state *fs; - int devnum = MINOR(inode->i_rdev); - - if (devnum >= floppy_count) - return -ENODEV; - - fs = &floppy_states[devnum]; - if (fs->ejected) - return -ENXIO; - return block_read(filp, buf, count, ppos); -} - -static ssize_t floppy_write(struct file * filp, const char * buf, - size_t count, loff_t *ppos) -{ - struct inode * inode = filp->f_dentry->d_inode; - struct floppy_state *fs; - int devnum = MINOR(inode->i_rdev); - - if (devnum >= floppy_count) - return -ENODEV; - check_disk_change(inode->i_rdev); - fs = &floppy_states[devnum]; - if (fs->ejected) - return -ENXIO; - if (fs->write_prot) - return -EROFS; - return block_write(filp, buf, count, ppos); -} - static struct floppy_struct floppy_type = { 2880,18,2,80,0,0x1B,0x00,0xCF,0x6C,NULL }; /* 7 1.44MB 3.5" */ diff --git a/drivers/char/agp/agpgart_be.c b/drivers/char/agp/agpgart_be.c index 27413489a..a83928a02 100644 --- a/drivers/char/agp/agpgart_be.c +++ b/drivers/char/agp/agpgart_be.c @@ -232,7 +232,6 @@ void agp_free_memory(agp_memory * curr) } if (curr->type != 0) { agp_bridge.free_by_type(curr); - MOD_DEC_USE_COUNT; return; } if (curr->page_count != 0) { @@ -260,15 +259,23 @@ agp_memory *agp_allocate_memory(size_t page_count, u32 type) agp_bridge.max_memory_agp) { return NULL; } + if (type != 0) { new = agp_bridge.alloc_by_type(page_count, type); return new; } + /* We always increase the module count, since free auto-decrements + * it + */ + + MOD_INC_USE_COUNT; + scratch_pages = (page_count + ENTRIES_PER_PAGE - 1) / ENTRIES_PER_PAGE; new = agp_create_memory(scratch_pages); if (new == NULL) { + MOD_DEC_USE_COUNT; return NULL; } for (i = 0; i < page_count; i++) { @@ -286,7 +293,6 @@ agp_memory *agp_allocate_memory(size_t page_count, u32 type) new->page_count++; } - MOD_INC_USE_COUNT; return new; } @@ -781,11 +787,13 @@ static aper_size_info_fixed intel_i810_sizes[] = }; #define AGP_DCACHE_MEMORY 1 +#define AGP_PHYS_MEMORY 2 static gatt_mask intel_i810_masks[] = { {I810_PTE_VALID, 0}, - {(I810_PTE_VALID | I810_PTE_LOCAL), AGP_DCACHE_MEMORY} + {(I810_PTE_VALID | I810_PTE_LOCAL), AGP_DCACHE_MEMORY}, + {I810_PTE_VALID, 0} }; static struct _intel_i810_private { @@ -896,7 +904,7 @@ static int intel_i810_insert_entries(agp_memory * mem, off_t pg_start, if ((type == AGP_DCACHE_MEMORY) && (mem->type == AGP_DCACHE_MEMORY)) { /* special insert */ - + CACHE_FLUSH(); for (i = pg_start; i < (pg_start + mem->page_count); i++) { OUTREG32(intel_i810_private.registers, @@ -904,20 +912,24 @@ static int intel_i810_insert_entries(agp_memory * mem, off_t pg_start, (i * 4096) | I810_PTE_LOCAL | I810_PTE_VALID); } - + CACHE_FLUSH(); agp_bridge.tlb_flush(mem); return 0; } + if((type == AGP_PHYS_MEMORY) && + (mem->type == AGP_PHYS_MEMORY)) { + goto insert; + } return -EINVAL; } - if (mem->is_flushed == FALSE) { - CACHE_FLUSH(); - mem->is_flushed = TRUE; - } + +insert: + CACHE_FLUSH(); for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { OUTREG32(intel_i810_private.registers, I810_PTE_BASE + (j * 4), mem->memory[i]); } + CACHE_FLUSH(); agp_bridge.tlb_flush(mem); return 0; @@ -955,15 +967,55 @@ static agp_memory *intel_i810_alloc_by_type(size_t pg_count, int type) new->page_count = pg_count; new->num_scratch_pages = 0; vfree(new->memory); + MOD_INC_USE_COUNT; return new; } + if(type == AGP_PHYS_MEMORY) { + /* The I810 requires a physical address to program + * it's mouse pointer into hardware. However the + * Xserver still writes to it through the agp + * aperture + */ + if (pg_count != 1) { + return NULL; + } + new = agp_create_memory(1); + + if (new == NULL) { + return NULL; + } + MOD_INC_USE_COUNT; + new->memory[0] = agp_alloc_page(); + + if (new->memory[0] == 0) { + /* Free this structure */ + agp_free_memory(new); + return NULL; + } + new->memory[0] = + agp_bridge.mask_memory( + virt_to_phys((void *) new->memory[0]), + type); + new->page_count = 1; + new->num_scratch_pages = 1; + new->type = AGP_PHYS_MEMORY; + new->physical = virt_to_phys((void *) new->memory[0]); + return new; + } + return NULL; } static void intel_i810_free_by_type(agp_memory * curr) { agp_free_key(curr->key); + if(curr->type == AGP_PHYS_MEMORY) { + agp_destroy_page((unsigned long) + phys_to_virt(curr->memory[0])); + vfree(curr->memory); + } kfree(curr); + MOD_DEC_USE_COUNT; } static unsigned long intel_i810_mask_memory(unsigned long addr, int type) @@ -1916,7 +1968,7 @@ static struct agp_max_table maxes_table[9] = static int agp_find_max(void) { - long memory, t, index, result; + long memory, index, result; memory = virt_to_phys(high_memory) >> 20; index = 1; @@ -1926,16 +1978,15 @@ static int agp_find_max(void) index++; } - t = (memory - maxes_table[index - 1].mem) / - (maxes_table[index].mem - maxes_table[index - 1].mem); - result = maxes_table[index - 1].agp + - (t * (maxes_table[index].agp - maxes_table[index - 1].agp)); + ( (memory - maxes_table[index - 1].mem) * + (maxes_table[index].agp - maxes_table[index - 1].agp)) / + (maxes_table[index].mem - maxes_table[index - 1].mem); printk(KERN_INFO "agpgart: Maximum main memory to use " "for agp memory: %ldM\n", result); result = result << (20 - PAGE_SHIFT); - return result; + return result; } #define AGPGART_VERSION_MAJOR 0 diff --git a/drivers/char/agp/agpgart_fe.c b/drivers/char/agp/agpgart_fe.c index e098b4bab..ef915259e 100644 --- a/drivers/char/agp/agpgart_fe.c +++ b/drivers/char/agp/agpgart_fe.c @@ -298,7 +298,7 @@ static agp_memory *agp_allocate_memory_wrap(size_t pg_count, u32 type) agp_memory *memory; memory = agp_allocate_memory(pg_count, type); - + printk("memory : %p\n", memory); if (memory == NULL) { return NULL; } @@ -911,6 +911,7 @@ static int agpioc_allocate_wrap(agp_file_private * priv, unsigned long arg) return -ENOMEM; } alloc.key = memory->key; + alloc.physical = memory->physical; if (copy_to_user((void *) arg, &alloc, sizeof(agp_allocate))) { agp_free_memory_wrap(memory); diff --git a/drivers/char/generic_serial.c b/drivers/char/generic_serial.c index f4ecc75a5..95a6c4370 100644 --- a/drivers/char/generic_serial.c +++ b/drivers/char/generic_serial.c @@ -103,10 +103,6 @@ static inline int copy_from_user(void *to,const void *from, int c) #include "generic_serial.h" -#ifndef MODULE -extern void my_hd (unsigned char *ptr, int n); -#endif - static char * tmp_buf; static DECLARE_MUTEX(tmp_buf_sem); @@ -119,8 +115,8 @@ int gs_debug = 0; #define gs_dprintk(f, str...) /* nothing */ #endif -#define func_enter() gs_dprintk (SX_DEBUG_FLOW, "gs: enter " __FUNCTION__ "\n") -#define func_exit() gs_dprintk (SX_DEBUG_FLOW, "gs: exit " __FUNCTION__ "\n") +#define func_enter() gs_dprintk (GS_DEBUG_FLOW, "gs: enter " __FUNCTION__ "\n") +#define func_exit() gs_dprintk (GS_DEBUG_FLOW, "gs: exit " __FUNCTION__ "\n") @@ -856,7 +852,6 @@ void gs_set_termios (struct tty_struct * tty, if (gs_debug & GS_DEBUG_TERMIOS) { gs_dprintk (GS_DEBUG_TERMIOS, "termios structure (%p):\n", tiosp); - my_hd ((unsigned char *)tiosp, sizeof (struct termios)); } #if 0 diff --git a/drivers/char/generic_serial.h b/drivers/char/generic_serial.h index ca321af9b..2e44bee95 100644 --- a/drivers/char/generic_serial.h +++ b/drivers/char/generic_serial.h @@ -12,6 +12,8 @@ #ifndef GENERIC_SERIAL_H #define GENERIC_SERIAL_H +#define RS_EVENT_WRITE_WAKEUP 0 + struct real_driver { void (*disable_tx_interrupts) (void *); void (*enable_tx_interrupts) (void *); @@ -75,6 +77,7 @@ struct gs_port { #define GS_DEBUG_TERMIOS 0x00000004 #define GS_DEBUG_STUFF 0x00000008 #define GS_DEBUG_CLOSE 0x00000010 +#define GS_DEBUG_FLOW 0x00000020 void gs_put_char(struct tty_struct *tty, unsigned char ch); diff --git a/drivers/char/joystick/joy-creative.c b/drivers/char/joystick/joy-creative.c index 7e97a677d..8faec5f82 100644 --- a/drivers/char/joystick/joy-creative.c +++ b/drivers/char/joystick/joy-creative.c @@ -66,7 +66,7 @@ static int js_cr_read_packet(int io, unsigned int *data) int r[2], t[2], p[2]; int i, j, ret; - for (i = 0; i < 2; i++); { + for (i = 0; i < 2; i++) { r[i] = buf[i] = 0; p[i] = t[i] = JS_CR_MAX_STROBE; p[i] += JS_CR_MAX_STROBE; diff --git a/drivers/char/scc.h b/drivers/char/scc.h new file mode 100644 index 000000000..53a4cd2ae --- /dev/null +++ b/drivers/char/scc.h @@ -0,0 +1,613 @@ +/* + * atari_SCC.h: Definitions for the Am8530 Serial Communications Controller + * + * Copyright 1994 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive + * for more details. + * + */ + + +#ifndef _SCC_H +#define _SCC_H + +#include <linux/delay.h> + +/* Special configuration ioctls for the Atari SCC5380 Serial + * Communications Controller + */ + +/* ioctl command codes */ + +#define TIOCGATSCC 0x54c0 /* get SCC configuration */ +#define TIOCSATSCC 0x54c1 /* set SCC configuration */ +#define TIOCDATSCC 0x54c2 /* reset configuration to defaults */ + +/* Clock sources */ + +#define CLK_RTxC 0 +#define CLK_TRxC 1 +#define CLK_PCLK 2 + +/* baud_bases for the common clocks in the Atari. These are the real + * frequencies divided by 16. + */ + +#define SCC_BAUD_BASE_TIMC 19200 /* 0.3072 MHz from TT-MFP, Timer C */ +#define SCC_BAUD_BASE_BCLK 153600 /* 2.4576 MHz */ +#define SCC_BAUD_BASE_PCLK4 229500 /* 3.6720 MHz */ +#define SCC_BAUD_BASE_PCLK 503374 /* 8.0539763 MHz */ +#define SCC_BAUD_BASE_NONE 0 /* for not connected or unused + * clock sources */ + +/* The SCC clock configuration structure */ + +struct scc_clock_config { + unsigned RTxC_base; /* base_baud of RTxC */ + unsigned TRxC_base; /* base_baud of TRxC */ + unsigned PCLK_base; /* base_baud of PCLK, both channels! */ + struct { + unsigned clksrc; /* CLK_RTxC, CLK_TRxC or CLK_PCLK */ + unsigned divisor; /* divisor for base baud, valid values: + * see below */ + } baud_table[17]; /* For 50, 75, 110, 135, 150, 200, 300, + * 600, 1200, 1800, 2400, 4800, 9600, + * 19200, 38400, 57600 and 115200 bps. + * The last two could be replaced by + * other rates > 38400 if they're not + * possible. + */ +}; + +/* The following divisors are valid: + * + * - CLK_RTxC: 1 or even (1, 2 and 4 are the direct modes, > 4 use + * the BRG) + * + * - CLK_TRxC: 1, 2 or 4 (no BRG, only direct modes possible) + * + * - CLK_PCLK: >= 4 and even (no direct modes, only BRG) + * + */ + +struct scc_port { + struct gs_port gs; + volatile unsigned char *ctrlp; + volatile unsigned char *datap; + int x_char; /* xon/xoff character */ + int c_dcd; + int channel; + struct scc_port *port_a; /* Reference to port A and B */ + struct scc_port *port_b; /* structs for reg access */ +}; + +#define SCC_MAGIC 0x52696368 + +/***********************************************************************/ +/* */ +/* Register Names */ +/* */ +/***********************************************************************/ + +/* The SCC documentation gives no explicit names to the registers, + * they're just called WR0..15 and RR0..15. To make the source code + * better readable and make the transparent write reg read access (see + * below) possible, I christen them here with self-invented names. + * Note that (real) read registers are assigned numbers 16..31. WR7' + * has number 33. + */ + +#define COMMAND_REG 0 /* wo */ +#define INT_AND_DMA_REG 1 /* wo */ +#define INT_VECTOR_REG 2 /* rw, common to both channels */ +#define RX_CTRL_REG 3 /* rw */ +#define AUX1_CTRL_REG 4 /* rw */ +#define TX_CTRL_REG 5 /* rw */ +#define SYNC_ADR_REG 6 /* wo */ +#define SYNC_CHAR_REG 7 /* wo */ +#define SDLC_OPTION_REG 33 /* wo */ +#define TX_DATA_REG 8 /* wo */ +#define MASTER_INT_CTRL 9 /* wo, common to both channels */ +#define AUX2_CTRL_REG 10 /* rw */ +#define CLK_CTRL_REG 11 /* wo */ +#define TIMER_LOW_REG 12 /* rw */ +#define TIMER_HIGH_REG 13 /* rw */ +#define DPLL_CTRL_REG 14 /* wo */ +#define INT_CTRL_REG 15 /* rw */ + +#define STATUS_REG 16 /* ro */ +#define SPCOND_STATUS_REG 17 /* wo */ +/* RR2 is WR2 for Channel A, Channel B gives vector + current status: */ +#define CURR_VECTOR_REG 18 /* Ch. B only, Ch. A for rw */ +#define INT_PENDING_REG 19 /* Channel A only! */ +/* RR4 is WR4, if b6(MR7') == 1 */ +/* RR5 is WR5, if b6(MR7') == 1 */ +#define FS_FIFO_LOW_REG 22 /* ro */ +#define FS_FIFO_HIGH_REG 23 /* ro */ +#define RX_DATA_REG 24 /* ro */ +/* RR9 is WR3, if b6(MR7') == 1 */ +#define DPLL_STATUS_REG 26 /* ro */ +/* RR11 is WR10, if b6(MR7') == 1 */ +/* RR12 is WR12 */ +/* RR13 is WR13 */ +/* RR14 not present */ +/* RR15 is WR15 */ + + +/***********************************************************************/ +/* */ +/* Register Values */ +/* */ +/***********************************************************************/ + + +/* WR0: COMMAND_REG "CR" */ + +#define CR_RX_CRC_RESET 0x40 +#define CR_TX_CRC_RESET 0x80 +#define CR_TX_UNDERRUN_RESET 0xc0 + +#define CR_EXTSTAT_RESET 0x10 +#define CR_SEND_ABORT 0x18 +#define CR_ENAB_INT_NEXT_RX 0x20 +#define CR_TX_PENDING_RESET 0x28 +#define CR_ERROR_RESET 0x30 +#define CR_HIGHEST_IUS_RESET 0x38 + + +/* WR1: INT_AND_DMA_REG "IDR" */ + +#define IDR_EXTSTAT_INT_ENAB 0x01 +#define IDR_TX_INT_ENAB 0x02 +#define IDR_PARERR_AS_SPCOND 0x04 + +#define IDR_RX_INT_DISAB 0x00 +#define IDR_RX_INT_FIRST 0x08 +#define IDR_RX_INT_ALL 0x10 +#define IDR_RX_INT_SPCOND 0x18 +#define IDR_RX_INT_MASK 0x18 + +#define IDR_WAITREQ_RX 0x20 +#define IDR_WAITREQ_IS_REQ 0x40 +#define IDR_WAITREQ_ENAB 0x80 + + +/* WR3: RX_CTRL_REG "RCR" */ + +#define RCR_RX_ENAB 0x01 +#define RCR_DISCARD_SYNC_CHARS 0x02 +#define RCR_ADDR_SEARCH 0x04 +#define RCR_CRC_ENAB 0x08 +#define RCR_SEARCH_MODE 0x10 +#define RCR_AUTO_ENAB_MODE 0x20 + +#define RCR_CHSIZE_MASK 0xc0 +#define RCR_CHSIZE_5 0x00 +#define RCR_CHSIZE_6 0x40 +#define RCR_CHSIZE_7 0x80 +#define RCR_CHSIZE_8 0xc0 + + +/* WR4: AUX1_CTRL_REG "A1CR" */ + +#define A1CR_PARITY_MASK 0x03 +#define A1CR_PARITY_NONE 0x00 +#define A1CR_PARITY_ODD 0x01 +#define A1CR_PARITY_EVEN 0x03 + +#define A1CR_MODE_MASK 0x0c +#define A1CR_MODE_SYNCR 0x00 +#define A1CR_MODE_ASYNC_1 0x04 +#define A1CR_MODE_ASYNC_15 0x08 +#define A1CR_MODE_ASYNC_2 0x0c + +#define A1CR_SYNCR_MODE_MASK 0x30 +#define A1CR_SYNCR_MONOSYNC 0x00 +#define A1CR_SYNCR_BISYNC 0x10 +#define A1CR_SYNCR_SDLC 0x20 +#define A1CR_SYNCR_EXTCSYNC 0x30 + +#define A1CR_CLKMODE_MASK 0xc0 +#define A1CR_CLKMODE_x1 0x00 +#define A1CR_CLKMODE_x16 0x40 +#define A1CR_CLKMODE_x32 0x80 +#define A1CR_CLKMODE_x64 0xc0 + + +/* WR5: TX_CTRL_REG "TCR" */ + +#define TCR_TX_CRC_ENAB 0x01 +#define TCR_RTS 0x02 +#define TCR_USE_CRC_CCITT 0x00 +#define TCR_USE_CRC_16 0x04 +#define TCR_TX_ENAB 0x08 +#define TCR_SEND_BREAK 0x10 + +#define TCR_CHSIZE_MASK 0x60 +#define TCR_CHSIZE_5 0x00 +#define TCR_CHSIZE_6 0x20 +#define TCR_CHSIZE_7 0x40 +#define TCR_CHSIZE_8 0x60 + +#define TCR_DTR 0x80 + + +/* WR7': SLDC_OPTION_REG "SOR" */ + +#define SOR_AUTO_TX_ENAB 0x01 +#define SOR_AUTO_EOM_RESET 0x02 +#define SOR_AUTO_RTS_MODE 0x04 +#define SOR_NRZI_DISAB_HIGH 0x08 +#define SOR_ALT_DTRREQ_TIMING 0x10 +#define SOR_READ_CRC_CHARS 0x20 +#define SOR_EXTENDED_REG_ACCESS 0x40 + + +/* WR9: MASTER_INT_CTRL "MIC" */ + +#define MIC_VEC_INCL_STAT 0x01 +#define MIC_NO_VECTOR 0x02 +#define MIC_DISAB_LOWER_CHAIN 0x04 +#define MIC_MASTER_INT_ENAB 0x08 +#define MIC_STATUS_HIGH 0x10 +#define MIC_IGN_INTACK 0x20 + +#define MIC_NO_RESET 0x00 +#define MIC_CH_A_RESET 0x40 +#define MIC_CH_B_RESET 0x80 +#define MIC_HARD_RESET 0xc0 + + +/* WR10: AUX2_CTRL_REG "A2CR" */ + +#define A2CR_SYNC_6 0x01 +#define A2CR_LOOP_MODE 0x02 +#define A2CR_ABORT_ON_UNDERRUN 0x04 +#define A2CR_MARK_IDLE 0x08 +#define A2CR_GO_ACTIVE_ON_POLL 0x10 + +#define A2CR_CODING_MASK 0x60 +#define A2CR_CODING_NRZ 0x00 +#define A2CR_CODING_NRZI 0x20 +#define A2CR_CODING_FM1 0x40 +#define A2CR_CODING_FM0 0x60 + +#define A2CR_PRESET_CRC_1 0x80 + + +/* WR11: CLK_CTRL_REG "CCR" */ + +#define CCR_TRxCOUT_MASK 0x03 +#define CCR_TRxCOUT_XTAL 0x00 +#define CCR_TRxCOUT_TXCLK 0x01 +#define CCR_TRxCOUT_BRG 0x02 +#define CCR_TRxCOUT_DPLL 0x03 + +#define CCR_TRxC_OUTPUT 0x04 + +#define CCR_TXCLK_MASK 0x18 +#define CCR_TXCLK_RTxC 0x00 +#define CCR_TXCLK_TRxC 0x08 +#define CCR_TXCLK_BRG 0x10 +#define CCR_TXCLK_DPLL 0x18 + +#define CCR_RXCLK_MASK 0x60 +#define CCR_RXCLK_RTxC 0x00 +#define CCR_RXCLK_TRxC 0x20 +#define CCR_RXCLK_BRG 0x40 +#define CCR_RXCLK_DPLL 0x60 + +#define CCR_RTxC_XTAL 0x80 + + +/* WR14: DPLL_CTRL_REG "DCR" */ + +#define DCR_BRG_ENAB 0x01 +#define DCR_BRG_USE_PCLK 0x02 +#define DCR_DTRREQ_IS_REQ 0x04 +#define DCR_AUTO_ECHO 0x08 +#define DCR_LOCAL_LOOPBACK 0x10 + +#define DCR_DPLL_EDGE_SEARCH 0x20 +#define DCR_DPLL_ERR_RESET 0x40 +#define DCR_DPLL_DISAB 0x60 +#define DCR_DPLL_CLK_BRG 0x80 +#define DCR_DPLL_CLK_RTxC 0xa0 +#define DCR_DPLL_FM 0xc0 +#define DCR_DPLL_NRZI 0xe0 + + +/* WR15: INT_CTRL_REG "ICR" */ + +#define ICR_OPTIONREG_SELECT 0x01 +#define ICR_ENAB_BRG_ZERO_INT 0x02 +#define ICR_USE_FS_FIFO 0x04 +#define ICR_ENAB_DCD_INT 0x08 +#define ICR_ENAB_SYNC_INT 0x10 +#define ICR_ENAB_CTS_INT 0x20 +#define ICR_ENAB_UNDERRUN_INT 0x40 +#define ICR_ENAB_BREAK_INT 0x80 + + +/* RR0: STATUS_REG "SR" */ + +#define SR_CHAR_AVAIL 0x01 +#define SR_BRG_ZERO 0x02 +#define SR_TX_BUF_EMPTY 0x04 +#define SR_DCD 0x08 +#define SR_SYNC_ABORT 0x10 +#define SR_CTS 0x20 +#define SR_TX_UNDERRUN 0x40 +#define SR_BREAK 0x80 + + +/* RR1: SPCOND_STATUS_REG "SCSR" */ + +#define SCSR_ALL_SENT 0x01 +#define SCSR_RESIDUAL_MASK 0x0e +#define SCSR_PARITY_ERR 0x10 +#define SCSR_RX_OVERRUN 0x20 +#define SCSR_CRC_FRAME_ERR 0x40 +#define SCSR_END_OF_FRAME 0x80 + + +/* RR3: INT_PENDING_REG "IPR" */ + +#define IPR_B_EXTSTAT 0x01 +#define IPR_B_TX 0x02 +#define IPR_B_RX 0x04 +#define IPR_A_EXTSTAT 0x08 +#define IPR_A_TX 0x10 +#define IPR_A_RX 0x20 + + +/* RR7: FS_FIFO_HIGH_REG "FFHR" */ + +#define FFHR_CNT_MASK 0x3f +#define FFHR_IS_FROM_FIFO 0x40 +#define FFHR_FIFO_OVERRUN 0x80 + + +/* RR10: DPLL_STATUS_REG "DSR" */ + +#define DSR_ON_LOOP 0x02 +#define DSR_ON_LOOP_SENDING 0x10 +#define DSR_TWO_CLK_MISSING 0x40 +#define DSR_ONE_CLK_MISSING 0x80 + +/***********************************************************************/ +/* */ +/* Register Access */ +/* */ +/***********************************************************************/ + + +/* The SCC needs 3.5 PCLK cycles recovery time between to register + * accesses. PCLK runs with 8 MHz on an Atari, so this delay is 3.5 * + * 125 ns = 437.5 ns. This is too short for udelay(). + * 10/16/95: A tstb mfp.par_dt_reg takes 600ns (sure?) and thus should be + * quite right + */ + +#define scc_reg_delay() \ + do { \ + if (MACH_IS_MVME16x || MACH_IS_BVME6000 || MACH_IS_MVME147) \ + __asm__ __volatile__ ( " nop; nop"); \ + else if (MACH_IS_ATARI) \ + __asm__ __volatile__ ( "tstb %0" : : "g" (*_scc_del) : "cc" );\ + } while (0) + +extern unsigned char scc_shadow[2][16]; + +/* The following functions should relax the somehow complicated + * register access of the SCC. _SCCwrite() stores all written values + * (except for WR0 and WR8) in shadow registers for later recall. This + * removes the burden of remembering written values as needed. The + * extra work of storing the value doesn't count, since a delay is + * needed after a SCC access anyway. Additionally, _SCCwrite() manages + * writes to WR0 and WR8 differently, because these can be accessed + * directly with less overhead. Another special case are WR7 and WR7'. + * _SCCwrite automatically checks what of this registers is selected + * and changes b0 of WR15 if needed. + * + * _SCCread() for standard read registers is straightforward, except + * for RR2 (split into two "virtual" registers: one for the value + * written to WR2 (from the shadow) and one for the vector including + * status from RR2, Ch. B) and RR3. The latter must be read from + * Channel A, because it reads as all zeros on Ch. B. RR0 and RR8 can + * be accessed directly as before. + * + * The two inline function contain complicated switch statements. But + * I rely on regno and final_delay being constants, so gcc can reduce + * the whole stuff to just some assembler statements. + * + * _SCCwrite and _SCCread aren't intended to be used directly under + * normal circumstances. The macros SCCread[_ND] and SCCwrite[_ND] are + * for that purpose. They assume that a local variable 'port' is + * declared and pointing to the port's scc_struct entry. The + * variants with "_NB" appended should be used if no other SCC + * accesses follow immediatly (within 0.5 usecs). They just skip the + * final delay nops. + * + * Please note that accesses to SCC registers should only take place + * when interrupts are turned off (at least if SCC interrupts are + * enabled). Otherwise, an interrupt could interfere with the + * two-stage accessing process. + * + */ + + +static __inline__ void _SCCwrite( + struct scc_port *port, + unsigned char *shadow, + volatile unsigned char *_scc_del, + int regno, + unsigned char val, int final_delay ) +{ + switch( regno ) { + + case COMMAND_REG: + /* WR0 can be written directly without pointing */ + *port->ctrlp = val; + break; + + case SYNC_CHAR_REG: + /* For WR7, first set b0 of WR15 to 0, if needed */ + if (shadow[INT_CTRL_REG] & ICR_OPTIONREG_SELECT) { + *port->ctrlp = 15; + shadow[INT_CTRL_REG] &= ~ICR_OPTIONREG_SELECT; + scc_reg_delay(); + *port->ctrlp = shadow[INT_CTRL_REG]; + scc_reg_delay(); + } + goto normal_case; + + case SDLC_OPTION_REG: + /* For WR7', first set b0 of WR15 to 1, if needed */ + if (!(shadow[INT_CTRL_REG] & ICR_OPTIONREG_SELECT)) { + *port->ctrlp = 15; + shadow[INT_CTRL_REG] |= ICR_OPTIONREG_SELECT; + scc_reg_delay(); + *port->ctrlp = shadow[INT_CTRL_REG]; + scc_reg_delay(); + } + *port->ctrlp = 7; + shadow[8] = val; /* WR7' shadowed at WR8 */ + scc_reg_delay(); + *port->ctrlp = val; + break; + + case TX_DATA_REG: /* WR8 */ + /* TX_DATA_REG can be accessed directly on some h/w */ + if (MACH_IS_MVME16x || MACH_IS_BVME6000 || MACH_IS_MVME147) + { + *port->ctrlp = regno; + scc_reg_delay(); + *port->ctrlp = val; + } + else + *port->datap = val; + break; + + case MASTER_INT_CTRL: + *port->ctrlp = regno; + val &= 0x3f; /* bits 6..7 are the reset commands */ + scc_shadow[0][regno] = val; + scc_reg_delay(); + *port->ctrlp = val; + break; + + case DPLL_CTRL_REG: + *port->ctrlp = regno; + val &= 0x1f; /* bits 5..7 are the DPLL commands */ + shadow[regno] = val; + scc_reg_delay(); + *port->ctrlp = val; + break; + + case 1 ... 6: + case 10 ... 13: + case 15: + normal_case: + *port->ctrlp = regno; + shadow[regno] = val; + scc_reg_delay(); + *port->ctrlp = val; + break; + + default: + printk( "Bad SCC write access to WR%d\n", regno ); + break; + + } + + if (final_delay) + scc_reg_delay(); +} + + +static __inline__ unsigned char _SCCread( + struct scc_port *port, + unsigned char *shadow, + volatile unsigned char *_scc_del, + int regno, int final_delay ) +{ + unsigned char rv; + + switch( regno ) { + + /* --- real read registers --- */ + case STATUS_REG: + rv = *port->ctrlp; + break; + + case INT_PENDING_REG: + /* RR3: read only from Channel A! */ + port = port->port_a; + goto normal_case; + + case RX_DATA_REG: + /* RR8 can be accessed directly on some h/w */ + if (MACH_IS_MVME16x || MACH_IS_BVME6000 || MACH_IS_MVME147) + { + *port->ctrlp = 8; + scc_reg_delay(); + rv = *port->ctrlp; + } + else + rv = *port->datap; + break; + + case CURR_VECTOR_REG: + /* RR2 (vector including status) from Ch. B */ + port = port->port_b; + goto normal_case; + + /* --- reading write registers: access the shadow --- */ + case 1 ... 7: + case 10 ... 15: + return shadow[regno]; /* no final delay! */ + + /* WR7' is special, because it is shadowed at the place of WR8 */ + case SDLC_OPTION_REG: + return shadow[8]; /* no final delay! */ + + /* WR9 is special too, because it is common for both channels */ + case MASTER_INT_CTRL: + return scc_shadow[0][9]; /* no final delay! */ + + default: + printk( "Bad SCC read access to %cR%d\n", (regno & 16) ? 'R' : 'W', + regno & ~16 ); + break; + + case SPCOND_STATUS_REG: + case FS_FIFO_LOW_REG: + case FS_FIFO_HIGH_REG: + case DPLL_STATUS_REG: + normal_case: + *port->ctrlp = regno & 0x0f; + scc_reg_delay(); + rv = *port->ctrlp; + break; + + } + + if (final_delay) + scc_reg_delay(); + return rv; +} + +#define SCC_ACCESS_INIT(port) \ + unsigned char *_scc_shadow = &scc_shadow[port->channel][0] + +#define SCCwrite(reg,val) _SCCwrite(port,_scc_shadow,scc_del,(reg),(val),1) +#define SCCwrite_NB(reg,val) _SCCwrite(port,_scc_shadow,scc_del,(reg),(val),0) +#define SCCread(reg) _SCCread(port,_scc_shadow,scc_del,(reg),1) +#define SCCread_NB(reg) _SCCread(port,_scc_shadow,scc_del,(reg),0) + +#define SCCmod(reg,and,or) SCCwrite((reg),(SCCread(reg)&(and))|(or)) + +#endif /* _SCC_H */ diff --git a/drivers/char/serial167.c b/drivers/char/serial167.c index 5142c8495..885b236fd 100644 --- a/drivers/char/serial167.c +++ b/drivers/char/serial167.c @@ -43,6 +43,7 @@ #include <linux/tty.h> #include <linux/interrupt.h> #include <linux/serial.h> +#include <linux/serialP.h> #include <linux/string.h> #include <linux/fcntl.h> #include <linux/ptrace.h> @@ -57,6 +58,8 @@ #include <asm/segment.h> #include <asm/bitops.h> #include <asm/mvme16xhw.h> +#include <asm/bootinfo.h> +#include <asm/setup.h> #include <linux/types.h> #include <linux/kernel.h> @@ -144,7 +147,7 @@ static struct termios *serial_termios_locked[NR_PORTS]; * memory if large numbers of serial ports are open. */ static unsigned char *tmp_buf = 0; -static struct semaphore tmp_buf_sem = MUTEX; +DECLARE_MUTEX(tmp_buf_sem); /* * This is used to look up the divisor speeds and the timeouts @@ -2501,8 +2504,8 @@ scrn[1] = '\0'; info->tqueue.data = info; info->callout_termios =cy_callout_driver.init_termios; info->normal_termios = cy_serial_driver.init_termios; - info->open_wait = 0; - info->close_wait = 0; + init_waitqueue_head(&info->open_wait); + init_waitqueue_head(&info->close_wait); /* info->session */ /* info->pgrp */ /*** !!!!!!!! this may expose new bugs !!!!!!!!! *********/ @@ -2728,7 +2731,7 @@ void console_setup(char *str, int *ints) * that serial167_init() doesn't leave the chip non-functional. */ -void serial167_write(struct console *co, const char *str, unsigned count) +void serial167_console_write(struct console *co, const char *str, unsigned count) { volatile unsigned char *base_addr = (u_char *)BASE_ADDR; unsigned long flags; @@ -2788,6 +2791,95 @@ void serial167_write(struct console *co, const char *str, unsigned count) restore_flags(flags); } +/* This is a hack; if there are multiple chars waiting in the chip we + * discard all but the last one, and return that. The cd2401 is not really + * designed to be driven in polled mode. + */ + +int serial167_console_wait_key(struct console *co) +{ + volatile unsigned char *base_addr = (u_char *)BASE_ADDR; + unsigned long flags; + volatile u_char sink; + u_char ier; + int port; + int keypress = 0; + + save_flags(flags); cli(); + + /* Ensure receiver is enabled! */ + + port = 0; + base_addr[CyCAR] = (u_char)port; + while (base_addr[CyCCR]) + ; + base_addr[CyCCR] = CyENB_RCVR; + ier = base_addr[CyIER]; + base_addr[CyIER] = CyRxData; + + while (!keypress) { + if (pcc2chip[PccSCCRICR] & 0x20) + { + /* We have an Rx int. Acknowledge it */ + sink = pcc2chip[PccRPIACKR]; + if ((base_addr[CyLICR] >> 2) == port) { + int cnt = base_addr[CyRFOC]; + while (cnt-- > 0) + { + keypress = base_addr[CyRDR]; + } + base_addr[CyREOIR] = 0; + } + else + base_addr[CyREOIR] = CyNOTRANS; + } + } + + base_addr[CyIER] = ier; + + restore_flags(flags); + + return keypress; +} + + +static kdev_t serial167_console_device(struct console *c) +{ + return MKDEV(TTY_MAJOR, 64 + c->index); +} + + +static int __init serial167_console_setup(struct console *co, char *options) +{ + return 0; +} + + +static struct console sercons = { + "ttyS", + serial167_console_write, + NULL, + serial167_console_device, + serial167_console_wait_key, + NULL, + serial167_console_setup, + CON_PRINTBUFFER, + -1, + 0, + NULL +}; + + +void __init serial167_console_init(void) +{ + if (vme_brdtype == VME_TYPE_MVME166 || + vme_brdtype == VME_TYPE_MVME167 || + vme_brdtype == VME_TYPE_MVME177) { + mvme167_serial_console_setup(0); + register_console(&sercons); + } +} + #ifdef CONFIG_REMOTE_DEBUG void putDebugChar (int c) { diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index c726f745c..b4194bb9b 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -128,6 +128,14 @@ static int tty_fasync(int fd, struct file * filp, int on); #ifdef CONFIG_SX extern int sx_init (void); #endif +#if defined(CONFIG_MVME162_SCC) || defined(CONFIG_BVME6000_SCC) || defined(CONFIG_MVME147_SCC) +extern int vme_scc_init (void); +extern long vme_scc_console_init(void); +#endif +#ifdef CONFIG_SERIAL167 +extern int serial167_init(void); +extern long serial167_console_init(void); +#endif #ifdef CONFIG_8xx extern console_8xx_init(void); extern int rs_8xx_init(void); @@ -2090,9 +2098,15 @@ void __init console_init(void) #ifdef CONFIG_SERIAL_CONSOLE #ifdef CONFIG_8xx console_8xx_init(); -#else +#elif defined(CONFIG_SERIAL) serial_console_init(); #endif /* CONFIG_8xx */ +#if defined(CONFIG_MVME162_SCC) || defined(CONFIG_BVME6000_SCC) || defined(CONFIG_MVME147_SCC) + vme_scc_console_init(); +#endif +#if defined(CONFIG_SERIAL167) + serial167_console_init(); +#endif #endif } @@ -2176,6 +2190,9 @@ void __init tty_init(void) #ifdef CONFIG_SERIAL rs_init(); #endif +#if defined(CONFIG_MVME162_SCC) || defined(CONFIG_BVME6000_SCC) || defined(CONFIG_MVME147_SCC) + vme_scc_init(); +#endif #ifdef CONFIG_COMPUTONE ip2_init(); #endif @@ -2185,7 +2202,7 @@ void __init tty_init(void) #ifdef CONFIG_ROCKETPORT rp_init(); #endif -#ifdef CONFIG_MVME16x +#ifdef CONFIG_SERIAL167 serial167_init(); #endif #ifdef CONFIG_CYCLADES diff --git a/drivers/char/vme_scc.c b/drivers/char/vme_scc.c new file mode 100644 index 000000000..15232f832 --- /dev/null +++ b/drivers/char/vme_scc.c @@ -0,0 +1,1136 @@ +/* + * drivers/char/vme_scc.c: MVME147, MVME162, BVME6000 SCC serial ports + * implementation. + * Copyright 1999 Richard Hirst <richard@sleepie.demon.co.uk> + * + * Based on atari_SCC.c which was + * Copyright 1994-95 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> + * Partially based on PC-Linux serial.c by Linus Torvalds and Theodore Ts'o + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive + * for more details. + * + */ + +#include <linux/module.h> +#include <linux/config.h> +#include <linux/kdev_t.h> +#include <asm/io.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/ioport.h> +#include <linux/interrupt.h> +#include <linux/errno.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> +#include <linux/mm.h> +#include <linux/serial.h> +#include <linux/fcntl.h> +#include <linux/major.h> +#include <linux/delay.h> +#include <linux/tqueue.h> +#include <linux/version.h> +#include <linux/malloc.h> +#include <linux/miscdevice.h> +#include <linux/console.h> +#include <linux/init.h> +#include <asm/setup.h> +#include <asm/bootinfo.h> + +#ifdef CONFIG_MVME147_SCC +#include <asm/mvme147hw.h> +#endif +#ifdef CONFIG_MVME162_SCC +#include <asm/mvme16xhw.h> +#endif +#ifdef CONFIG_BVME6000_SCC +#include <asm/bvme6000hw.h> +#endif + +#include "generic_serial.h" +#include "scc.h" + + +#define CHANNEL_A 0 +#define CHANNEL_B 1 + +#define SCC_MINOR_BASE 64 + +/* Shadows for all SCC write registers */ +static unsigned char scc_shadow[2][16]; + +/* Location to access for SCC register access delay */ +static volatile unsigned char *scc_del = NULL; + +/* To keep track of STATUS_REG state for detection of Ext/Status int source */ +static unsigned char scc_last_status_reg[2]; + +/***************************** Prototypes *****************************/ + +/* Function prototypes */ +static void scc_disable_tx_interrupts(void * ptr); +static void scc_enable_tx_interrupts(void * ptr); +static void scc_disable_rx_interrupts(void * ptr); +static void scc_enable_rx_interrupts(void * ptr); +static int scc_get_CD(void * ptr); +static void scc_shutdown_port(void * ptr); +static void scc_set_real_termios(void *ptr); +static void scc_hungup(void *ptr); +static void scc_close(void *ptr); +static int scc_chars_in_buffer(void * ptr); +static int scc_open(struct tty_struct * tty, struct file * filp); +static int scc_ioctl(struct tty_struct * tty, struct file * filp, + unsigned int cmd, unsigned long arg); +static void scc_throttle(struct tty_struct *tty); +static void scc_unthrottle(struct tty_struct *tty); +static void scc_tx_int(int irq, void *data, struct pt_regs *fp); +static void scc_rx_int(int irq, void *data, struct pt_regs *fp); +static void scc_stat_int(int irq, void *data, struct pt_regs *fp); +static void scc_spcond_int(int irq, void *data, struct pt_regs *fp); +static void scc_setsignals(struct scc_port *port, int dtr, int rts); +static void scc_break_ctl(struct tty_struct *tty, int break_state); + +static struct tty_driver scc_driver, scc_callout_driver; + +static struct tty_struct *scc_table[2] = { NULL, }; +static struct termios * scc_termios[2]; +static struct termios * scc_termios_locked[2]; +struct scc_port scc_ports[2]; + +int scc_refcount; +int scc_initialized = 0; + +/*--------------------------------------------------------------------------- + * Interface from generic_serial.c back here + *--------------------------------------------------------------------------*/ + +static struct real_driver scc_real_driver = { + scc_disable_tx_interrupts, + scc_enable_tx_interrupts, + scc_disable_rx_interrupts, + scc_enable_rx_interrupts, + scc_get_CD, + scc_shutdown_port, + scc_set_real_termios, + scc_chars_in_buffer, + scc_close, + scc_hungup, + NULL +}; + + +/*---------------------------------------------------------------------------- + * vme_scc_init() and support functions + *---------------------------------------------------------------------------*/ + +static int scc_init_drivers(void) +{ + int error; + + memset(&scc_driver, 0, sizeof(scc_driver)); + scc_driver.magic = TTY_DRIVER_MAGIC; + scc_driver.driver_name = "scc"; + scc_driver.name = "ttyS"; + scc_driver.major = TTY_MAJOR; + scc_driver.minor_start = SCC_MINOR_BASE; + scc_driver.num = 2; + scc_driver.type = TTY_DRIVER_TYPE_SERIAL; + scc_driver.subtype = SERIAL_TYPE_NORMAL; + scc_driver.init_termios = tty_std_termios; + scc_driver.init_termios.c_cflag = + B9600 | CS8 | CREAD | HUPCL | CLOCAL; + scc_driver.flags = TTY_DRIVER_REAL_RAW; + scc_driver.refcount = &scc_refcount; + scc_driver.table = scc_table; + scc_driver.termios = scc_termios; + scc_driver.termios_locked = scc_termios_locked; + + scc_driver.open = scc_open; + scc_driver.close = gs_close; + scc_driver.write = gs_write; + scc_driver.put_char = gs_put_char; + scc_driver.flush_chars = gs_flush_chars; + scc_driver.write_room = gs_write_room; + scc_driver.chars_in_buffer = gs_chars_in_buffer; + scc_driver.flush_buffer = gs_flush_buffer; + scc_driver.ioctl = scc_ioctl; + scc_driver.throttle = scc_throttle; + scc_driver.unthrottle = scc_unthrottle; + scc_driver.set_termios = gs_set_termios; + scc_driver.stop = gs_stop; + scc_driver.start = gs_start; + scc_driver.hangup = gs_hangup; + scc_driver.break_ctl = scc_break_ctl; + + scc_callout_driver = scc_driver; + scc_callout_driver.name = "cua"; + scc_callout_driver.major = TTYAUX_MAJOR; + scc_callout_driver.subtype = SERIAL_TYPE_CALLOUT; + + if ((error = tty_register_driver(&scc_driver))) { + printk(KERN_ERR "scc: Couldn't register scc driver, error = %d\n", + error); + return 1; + } + if ((error = tty_register_driver(&scc_callout_driver))) { + tty_unregister_driver(&scc_driver); + printk(KERN_ERR "scc: Couldn't register scc callout driver, error = %d\n", + error); + return 1; + } + + return 0; +} + + +/* ports[] array is indexed by line no (i.e. [0] for ttyS0, [1] for ttyS1). + */ + +static void scc_init_portstructs(void) +{ + struct scc_port *port; + int i; + + for (i = 0; i < 2; i++) { + port = scc_ports + i; + port->gs.callout_termios = tty_std_termios; + port->gs.normal_termios = tty_std_termios; + port->gs.magic = SCC_MAGIC; + port->gs.close_delay = HZ/2; + port->gs.closing_wait = 30 * HZ; + port->gs.rd = &scc_real_driver; +#ifdef NEW_WRITE_LOCKING + port->gs.port_write_sem = MUTEX; +#endif + init_waitqueue_head(&port->gs.open_wait); + init_waitqueue_head(&port->gs.close_wait); + } +} + + +#ifdef CONFIG_MVME147_SCC +static int mvme147_scc_init(void) +{ + struct scc_port *port; + + printk("SCC: MVME147 Serial Driver\n"); + /* Init channel A */ + port = &scc_ports[0]; + port->channel = CHANNEL_A; + port->ctrlp = (volatile unsigned char *)M147_SCC_A_ADDR; + port->datap = port->ctrlp + 1; + port->port_a = &scc_ports[0]; + port->port_b = &scc_ports[1]; + request_irq(MVME147_IRQ_SCCA_TX, scc_tx_int, SA_INTERRUPT, + "SCC-A TX", port); + request_irq(MVME147_IRQ_SCCA_STAT, scc_stat_int, SA_INTERRUPT, + "SCC-A status", port); + request_irq(MVME147_IRQ_SCCA_RX, scc_rx_int, SA_INTERRUPT, + "SCC-A RX", port); + request_irq(MVME147_IRQ_SCCA_SPCOND, scc_spcond_int, SA_INTERRUPT, + "SCC-A special cond", port); + { + SCC_ACCESS_INIT(port); + + /* disable interrupts for this channel */ + SCCwrite(INT_AND_DMA_REG, 0); + /* Set the interrupt vector */ + SCCwrite(INT_VECTOR_REG, MVME147_IRQ_SCC_BASE); + /* Interrupt parameters: vector includes status, status low */ + SCCwrite(MASTER_INT_CTRL, MIC_VEC_INCL_STAT); + SCCmod(MASTER_INT_CTRL, 0xff, MIC_MASTER_INT_ENAB); + } + + /* Init channel B */ + port = &scc_ports[1]; + port->channel = CHANNEL_B; + port->ctrlp = (volatile unsigned char *)M147_SCC_B_ADDR; + port->datap = port->ctrlp + 1; + port->port_a = &scc_ports[0]; + port->port_b = &scc_ports[1]; + request_irq(MVME147_IRQ_SCCB_TX, scc_tx_int, SA_INTERRUPT, + "SCC-B TX", port); + request_irq(MVME147_IRQ_SCCB_STAT, scc_stat_int, SA_INTERRUPT, + "SCC-B status", port); + request_irq(MVME147_IRQ_SCCB_RX, scc_rx_int, SA_INTERRUPT, + "SCC-B RX", port); + request_irq(MVME147_IRQ_SCCB_SPCOND, scc_spcond_int, SA_INTERRUPT, + "SCC-B special cond", port); + { + SCC_ACCESS_INIT(port); + + /* disable interrupts for this channel */ + SCCwrite(INT_AND_DMA_REG, 0); + } + + /* Ensure interrupts are enabled in the PCC chip */ + m147_pcc->serial_cntrl=PCC_LEVEL_SERIAL|PCC_INT_ENAB; + + /* Initialise the tty driver structures and register */ + scc_init_portstructs(); + scc_init_drivers(); + + return 0; +} +#endif + + +#ifdef CONFIG_MVME162_SCC +static int mvme162_scc_init(void) +{ + struct scc_port *port; + + if (!(mvme16x_config & MVME16x_CONFIG_GOT_SCCA)) + return (-ENODEV); + + printk("SCC: MVME162 Serial Driver\n"); + /* Init channel A */ + port = &scc_ports[0]; + port->channel = CHANNEL_A; + port->ctrlp = (volatile unsigned char *)MVME_SCC_A_ADDR; + port->datap = port->ctrlp + 2; + port->port_a = &scc_ports[0]; + port->port_b = &scc_ports[1]; + request_irq(MVME162_IRQ_SCCA_TX, scc_tx_int, SA_INTERRUPT, + "SCC-A TX", port); + request_irq(MVME162_IRQ_SCCA_STAT, scc_stat_int, SA_INTERRUPT, + "SCC-A status", port); + request_irq(MVME162_IRQ_SCCA_RX, scc_rx_int, SA_INTERRUPT, + "SCC-A RX", port); + request_irq(MVME162_IRQ_SCCA_SPCOND, scc_spcond_int, SA_INTERRUPT, + "SCC-A special cond", port); + { + SCC_ACCESS_INIT(port); + + /* disable interrupts for this channel */ + SCCwrite(INT_AND_DMA_REG, 0); + /* Set the interrupt vector */ + SCCwrite(INT_VECTOR_REG, MVME162_IRQ_SCC_BASE); + /* Interrupt parameters: vector includes status, status low */ + SCCwrite(MASTER_INT_CTRL, MIC_VEC_INCL_STAT); + SCCmod(MASTER_INT_CTRL, 0xff, MIC_MASTER_INT_ENAB); + } + + /* Init channel B */ + port = &scc_ports[1]; + port->channel = CHANNEL_B; + port->ctrlp = (volatile unsigned char *)MVME_SCC_B_ADDR; + port->datap = port->ctrlp + 2; + port->port_a = &scc_ports[0]; + port->port_b = &scc_ports[1]; + request_irq(MVME162_IRQ_SCCB_TX, scc_tx_int, SA_INTERRUPT, + "SCC-B TX", port); + request_irq(MVME162_IRQ_SCCB_STAT, scc_stat_int, SA_INTERRUPT, + "SCC-B status", port); + request_irq(MVME162_IRQ_SCCB_RX, scc_rx_int, SA_INTERRUPT, + "SCC-B RX", port); + request_irq(MVME162_IRQ_SCCB_SPCOND, scc_spcond_int, SA_INTERRUPT, + "SCC-B special cond", port); + + { + SCC_ACCESS_INIT(port); /* Either channel will do */ + + /* disable interrupts for this channel */ + SCCwrite(INT_AND_DMA_REG, 0); + } + + /* Ensure interrupts are enabled in the MC2 chip */ + *(volatile char *)0xfff4201d = 0x14; + + /* Initialise the tty driver structures and register */ + scc_init_portstructs(); + scc_init_drivers(); + + return 0; +} +#endif + + +#ifdef CONFIG_BVME6000_SCC +static int bvme6000_scc_init(void) +{ + struct scc_port *port; + + printk("SCC: BVME6000 Serial Driver\n"); + /* Init channel A */ + port = &scc_ports[0]; + port->channel = CHANNEL_A; + port->ctrlp = (volatile unsigned char *)BVME_SCC_A_ADDR; + port->datap = port->ctrlp + 4; + port->port_a = &scc_ports[0]; + port->port_b = &scc_ports[1]; + request_irq(BVME_IRQ_SCCA_TX, scc_tx_int, SA_INTERRUPT, + "SCC-A TX", port); + request_irq(BVME_IRQ_SCCA_STAT, scc_stat_int, SA_INTERRUPT, + "SCC-A status", port); + request_irq(BVME_IRQ_SCCA_RX, scc_rx_int, SA_INTERRUPT, + "SCC-A RX", port); + request_irq(BVME_IRQ_SCCA_SPCOND, scc_spcond_int, SA_INTERRUPT, + "SCC-A special cond", port); + { + SCC_ACCESS_INIT(port); + + /* disable interrupts for this channel */ + SCCwrite(INT_AND_DMA_REG, 0); + /* Set the interrupt vector */ + SCCwrite(INT_VECTOR_REG, BVME_IRQ_SCC_BASE); + /* Interrupt parameters: vector includes status, status low */ + SCCwrite(MASTER_INT_CTRL, MIC_VEC_INCL_STAT); + SCCmod(MASTER_INT_CTRL, 0xff, MIC_MASTER_INT_ENAB); + } + + /* Init channel B */ + port = &scc_ports[1]; + port->channel = CHANNEL_B; + port->ctrlp = (volatile unsigned char *)BVME_SCC_B_ADDR; + port->datap = port->ctrlp + 4; + port->port_a = &scc_ports[0]; + port->port_b = &scc_ports[1]; + request_irq(BVME_IRQ_SCCB_TX, scc_tx_int, SA_INTERRUPT, + "SCC-B TX", port); + request_irq(BVME_IRQ_SCCB_STAT, scc_stat_int, SA_INTERRUPT, + "SCC-B status", port); + request_irq(BVME_IRQ_SCCB_RX, scc_rx_int, SA_INTERRUPT, + "SCC-B RX", port); + request_irq(BVME_IRQ_SCCB_SPCOND, scc_spcond_int, SA_INTERRUPT, + "SCC-B special cond", port); + + { + SCC_ACCESS_INIT(port); /* Either channel will do */ + + /* disable interrupts for this channel */ + SCCwrite(INT_AND_DMA_REG, 0); + } + + /* Initialise the tty driver structures and register */ + scc_init_portstructs(); + scc_init_drivers(); + + return 0; +} +#endif + + +int vme_scc_init(void) +{ + int res = -ENODEV; + static int called = 0; + + if (called) + return res; + called = 1; +#ifdef CONFIG_MVME147_SCC + if (MACH_IS_MVME147) + res = mvme147_scc_init(); +#endif +#ifdef CONFIG_MVME162_SCC + if (MACH_IS_MVME16x) + res = mvme162_scc_init(); +#endif +#ifdef CONFIG_BVME6000_SCC + if (MACH_IS_BVME6000) + res = bvme6000_scc_init(); +#endif + return res; +} + + +/*--------------------------------------------------------------------------- + * Interrupt handlers + *--------------------------------------------------------------------------*/ + +static void scc_rx_int(int irq, void *data, struct pt_regs *fp) +{ + unsigned char ch; + struct scc_port *port = data; + struct tty_struct *tty = port->gs.tty; + SCC_ACCESS_INIT(port); + + ch = SCCread_NB(RX_DATA_REG); + if (!tty) { + printk ("scc_rx_int with NULL tty!\n"); + SCCwrite_NB(COMMAND_REG, CR_HIGHEST_IUS_RESET); + return; + } + if (tty->flip.count < TTY_FLIPBUF_SIZE) { + *tty->flip.char_buf_ptr = ch; + *tty->flip.flag_buf_ptr = 0; + tty->flip.flag_buf_ptr++; + tty->flip.char_buf_ptr++; + tty->flip.count++; + } + + /* Check if another character is already ready; in that case, the + * spcond_int() function must be used, because this character may have an + * error condition that isn't signalled by the interrupt vector used! + */ + if (SCCread(INT_PENDING_REG) & + (port->channel == CHANNEL_A ? IPR_A_RX : IPR_B_RX)) { + scc_spcond_int (irq, data, fp); + return; + } + + SCCwrite_NB(COMMAND_REG, CR_HIGHEST_IUS_RESET); + + tty_flip_buffer_push(tty); +} + + +static void scc_spcond_int(int irq, void *data, struct pt_regs *fp) +{ + struct scc_port *port = data; + struct tty_struct *tty = port->gs.tty; + unsigned char stat, ch, err; + int int_pending_mask = port->channel == CHANNEL_A ? + IPR_A_RX : IPR_B_RX; + SCC_ACCESS_INIT(port); + + if (!tty) { + printk ("scc_spcond_int with NULL tty!\n"); + SCCwrite(COMMAND_REG, CR_ERROR_RESET); + SCCwrite_NB(COMMAND_REG, CR_HIGHEST_IUS_RESET); + return; + } + do { + stat = SCCread(SPCOND_STATUS_REG); + ch = SCCread_NB(RX_DATA_REG); + + if (stat & SCSR_RX_OVERRUN) + err = TTY_OVERRUN; + else if (stat & SCSR_PARITY_ERR) + err = TTY_PARITY; + else if (stat & SCSR_CRC_FRAME_ERR) + err = TTY_FRAME; + else + err = 0; + + if (tty->flip.count < TTY_FLIPBUF_SIZE) { + *tty->flip.char_buf_ptr = ch; + *tty->flip.flag_buf_ptr = err; + tty->flip.flag_buf_ptr++; + tty->flip.char_buf_ptr++; + tty->flip.count++; + } + + /* ++TeSche: *All* errors have to be cleared manually, + * else the condition persists for the next chars + */ + if (err) + SCCwrite(COMMAND_REG, CR_ERROR_RESET); + + } while(SCCread(INT_PENDING_REG) & int_pending_mask); + + SCCwrite_NB(COMMAND_REG, CR_HIGHEST_IUS_RESET); + + tty_flip_buffer_push(tty); +} + + +static void scc_tx_int(int irq, void *data, struct pt_regs *fp) +{ + struct scc_port *port = data; + SCC_ACCESS_INIT(port); + + if (!port->gs.tty) { + printk ("scc_tx_int with NULL tty!\n"); + SCCmod (INT_AND_DMA_REG, ~IDR_TX_INT_ENAB, 0); + SCCwrite(COMMAND_REG, CR_TX_PENDING_RESET); + SCCwrite_NB(COMMAND_REG, CR_HIGHEST_IUS_RESET); + return; + } + while ((SCCread_NB(STATUS_REG) & SR_TX_BUF_EMPTY)) { + if (port->x_char) { + SCCwrite(TX_DATA_REG, port->x_char); + port->x_char = 0; + } + else if ((port->gs.xmit_cnt <= 0) || port->gs.tty->stopped || + port->gs.tty->hw_stopped) + break; + else { + SCCwrite(TX_DATA_REG, port->gs.xmit_buf[port->gs.xmit_tail++]); + port->gs.xmit_tail = port->gs.xmit_tail & (SERIAL_XMIT_SIZE-1); + if (--port->gs.xmit_cnt <= 0) + break; + } + } + if ((port->gs.xmit_cnt <= 0) || port->gs.tty->stopped || + port->gs.tty->hw_stopped) { + /* disable tx interrupts */ + SCCmod (INT_AND_DMA_REG, ~IDR_TX_INT_ENAB, 0); + SCCwrite(COMMAND_REG, CR_TX_PENDING_RESET); /* disable tx_int on next tx underrun? */ + port->gs.flags &= ~GS_TX_INTEN; + } + if (port->gs.tty && port->gs.xmit_cnt <= port->gs.wakeup_chars) { + if ((port->gs.tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + port->gs.tty->ldisc.write_wakeup) + (port->gs.tty->ldisc.write_wakeup)(port->gs.tty); + wake_up_interruptible(&port->gs.tty->write_wait); + } + + SCCwrite_NB(COMMAND_REG, CR_HIGHEST_IUS_RESET); +} + + +static void scc_stat_int(int irq, void *data, struct pt_regs *fp) +{ + struct scc_port *port = data; + unsigned channel = port->channel; + unsigned char last_sr, sr, changed; + SCC_ACCESS_INIT(port); + + last_sr = scc_last_status_reg[channel]; + sr = scc_last_status_reg[channel] = SCCread_NB(STATUS_REG); + changed = last_sr ^ sr; + + if (changed & SR_DCD) { + port->c_dcd = !!(sr & SR_DCD); + if (!(port->gs.flags & ASYNC_CHECK_CD)) + ; /* Don't report DCD changes */ + else if (port->c_dcd) { + if (~(port->gs.flags & ASYNC_NORMAL_ACTIVE) || + ~(port->gs.flags & ASYNC_CALLOUT_ACTIVE)) { + /* Are we blocking in open?*/ + wake_up_interruptible(&port->gs.open_wait); + } + } + else { + if (!((port->gs.flags & ASYNC_CALLOUT_ACTIVE) && + (port->gs.flags & ASYNC_CALLOUT_NOHUP))) { + if (port->gs.tty) + tty_hangup (port->gs.tty); + } + } + } + SCCwrite(COMMAND_REG, CR_EXTSTAT_RESET); + SCCwrite_NB(COMMAND_REG, CR_HIGHEST_IUS_RESET); +} + + +/*--------------------------------------------------------------------------- + * generic_serial.c callback funtions + *--------------------------------------------------------------------------*/ + +static void scc_disable_tx_interrupts(void *ptr) +{ + struct scc_port *port = ptr; + unsigned long flags; + SCC_ACCESS_INIT(port); + + save_flags(flags); + cli(); + SCCmod(INT_AND_DMA_REG, ~IDR_TX_INT_ENAB, 0); + port->gs.flags &= ~GS_TX_INTEN; + restore_flags(flags); +} + + +static void scc_enable_tx_interrupts(void *ptr) +{ + struct scc_port *port = ptr; + unsigned long flags; + SCC_ACCESS_INIT(port); + + save_flags(flags); + cli(); + SCCmod(INT_AND_DMA_REG, 0xff, IDR_TX_INT_ENAB); + /* restart the transmitter */ + scc_tx_int (0, port, 0); + restore_flags(flags); +} + + +static void scc_disable_rx_interrupts(void *ptr) +{ + struct scc_port *port = ptr; + unsigned long flags; + SCC_ACCESS_INIT(port); + + save_flags(flags); + cli(); + SCCmod(INT_AND_DMA_REG, + ~(IDR_RX_INT_MASK|IDR_PARERR_AS_SPCOND|IDR_EXTSTAT_INT_ENAB), 0); + restore_flags(flags); +} + + +static void scc_enable_rx_interrupts(void *ptr) +{ + struct scc_port *port = ptr; + unsigned long flags; + SCC_ACCESS_INIT(port); + + save_flags(flags); + cli(); + SCCmod(INT_AND_DMA_REG, 0xff, + IDR_EXTSTAT_INT_ENAB|IDR_PARERR_AS_SPCOND|IDR_RX_INT_ALL); + restore_flags(flags); +} + + +static int scc_get_CD(void *ptr) +{ + struct scc_port *port = ptr; + unsigned channel = port->channel; + + return !!(scc_last_status_reg[channel] & SR_DCD); +} + + +static void scc_shutdown_port(void *ptr) +{ + struct scc_port *port = ptr; + + port->gs.flags &= ~ GS_ACTIVE; + if (port->gs.tty && port->gs.tty->termios->c_cflag & HUPCL) { + scc_setsignals (port, 0, 0); + } +} + + +static void scc_set_real_termios (void *ptr) +{ + /* the SCC has char sizes 5,7,6,8 in that order! */ + static int chsize_map[4] = { 0, 2, 1, 3 }; + unsigned cflag, baud, chsize, channel, brgval = 0; + unsigned long flags; + struct scc_port *port = ptr; + SCC_ACCESS_INIT(port); + + if (!port->gs.tty || !port->gs.tty->termios) return; + + channel = port->channel; + + if (channel == CHANNEL_A) + return; /* Settings controlled by boot PROM */ + + cflag = port->gs.tty->termios->c_cflag; + baud = port->gs.baud; + chsize = (cflag & CSIZE) >> 4; + + if (baud == 0) { + /* speed == 0 -> drop DTR */ + save_flags(flags); + cli(); + SCCmod(TX_CTRL_REG, ~TCR_DTR, 0); + restore_flags(flags); + return; + } + else if ((MACH_IS_MVME16x && (baud < 50 || baud > 38400)) || + (MACH_IS_MVME147 && (baud < 50 || baud > 19200)) || + (MACH_IS_BVME6000 &&(baud < 50 || baud > 76800))) { + printk("SCC: Bad speed requested, %d\n", baud); + return; + } + + if (cflag & CLOCAL) + port->gs.flags &= ~ASYNC_CHECK_CD; + else + port->gs.flags |= ASYNC_CHECK_CD; + +#ifdef CONFIG_MVME147_SCC + if (MACH_IS_MVME147) + brgval = (M147_SCC_PCLK + baud/2) / (16 * 2 * baud) - 2; + else +#endif +#ifdef CONFIG_MVME162_SCC + if (MACH_IS_MVME16x) + brgval = (MVME_SCC_PCLK + baud/2) / (16 * 2 * baud) - 2; + else +#endif +#ifdef CONFIG_BVME6000_SCC + if (MACH_IS_BVME6000) + brgval = (BVME_SCC_RTxC + baud/2) / (16 * 2 * baud) - 2; +#endif + /* Now we have all parameters and can go to set them: */ + save_flags(flags); + cli(); + + /* receiver's character size and auto-enables */ + SCCmod(RX_CTRL_REG, ~(RCR_CHSIZE_MASK|RCR_AUTO_ENAB_MODE), + (chsize_map[chsize] << 6) | + ((cflag & CRTSCTS) ? RCR_AUTO_ENAB_MODE : 0)); + /* parity and stop bits (both, Tx and Rx), clock mode never changes */ + SCCmod (AUX1_CTRL_REG, + ~(A1CR_PARITY_MASK | A1CR_MODE_MASK), + ((cflag & PARENB + ? (cflag & PARODD ? A1CR_PARITY_ODD : A1CR_PARITY_EVEN) + : A1CR_PARITY_NONE) + | (cflag & CSTOPB ? A1CR_MODE_ASYNC_2 : A1CR_MODE_ASYNC_1))); + /* sender's character size, set DTR for valid baud rate */ + SCCmod(TX_CTRL_REG, ~TCR_CHSIZE_MASK, chsize_map[chsize] << 5 | TCR_DTR); + /* clock sources never change */ + /* disable BRG before changing the value */ + SCCmod(DPLL_CTRL_REG, ~DCR_BRG_ENAB, 0); + /* BRG value */ + SCCwrite(TIMER_LOW_REG, brgval & 0xff); + SCCwrite(TIMER_HIGH_REG, (brgval >> 8) & 0xff); + /* BRG enable, and clock source never changes */ + SCCmod(DPLL_CTRL_REG, 0xff, DCR_BRG_ENAB); + + restore_flags(flags); +} + + +static int scc_chars_in_buffer (void *ptr) +{ + struct scc_port *port = ptr; + SCC_ACCESS_INIT(port); + + return (SCCread (SPCOND_STATUS_REG) & SCSR_ALL_SENT) ? 0 : 1; +} + + +static void scc_hungup(void *ptr) +{ + scc_disable_tx_interrupts(ptr); + scc_disable_rx_interrupts(ptr); + MOD_DEC_USE_COUNT; +} + + +static void scc_close(void *ptr) +{ + scc_disable_tx_interrupts(ptr); + scc_disable_rx_interrupts(ptr); +} + + +/*--------------------------------------------------------------------------- + * Internal support functions + *--------------------------------------------------------------------------*/ + +static void scc_setsignals(struct scc_port *port, int dtr, int rts) +{ + unsigned long flags; + unsigned char t; + SCC_ACCESS_INIT(port); + + save_flags(flags); + t = SCCread(TX_CTRL_REG); + if (dtr >= 0) t = dtr? (t | TCR_DTR): (t & ~TCR_DTR); + if (rts >= 0) t = rts? (t | TCR_RTS): (t & ~TCR_RTS); + SCCwrite(TX_CTRL_REG, t); + restore_flags(flags); +} + + +static void scc_send_xchar(struct tty_struct *tty, char ch) +{ + struct scc_port *port = (struct scc_port *)tty->driver_data; + + port->x_char = ch; + if (ch) + scc_enable_tx_interrupts(port); +} + + +/*--------------------------------------------------------------------------- + * Driver entrypoints referenced from above + *--------------------------------------------------------------------------*/ + +static int scc_open (struct tty_struct * tty, struct file * filp) +{ + int line = MINOR(tty->device) - SCC_MINOR_BASE; + int retval; + struct scc_port *port = &scc_ports[line]; + int i, channel = port->channel; + unsigned long flags; + SCC_ACCESS_INIT(port); +#if defined(CONFIG_MVME162_SCC) || defined(CONFIG_MVME147_SCC) + static const struct { + unsigned reg, val; + } mvme_init_tab[] = { + /* Values for MVME162 and MVME147 */ + /* no parity, 1 stop bit, async, 1:16 */ + { AUX1_CTRL_REG, A1CR_PARITY_NONE|A1CR_MODE_ASYNC_1|A1CR_CLKMODE_x16 }, + /* parity error is special cond, ints disabled, no DMA */ + { INT_AND_DMA_REG, IDR_PARERR_AS_SPCOND | IDR_RX_INT_DISAB }, + /* Rx 8 bits/char, no auto enable, Rx off */ + { RX_CTRL_REG, RCR_CHSIZE_8 }, + /* DTR off, Tx 8 bits/char, RTS off, Tx off */ + { TX_CTRL_REG, TCR_CHSIZE_8 }, + /* special features off */ + { AUX2_CTRL_REG, 0 }, + { CLK_CTRL_REG, CCR_RXCLK_BRG | CCR_TXCLK_BRG }, + { DPLL_CTRL_REG, DCR_BRG_ENAB | DCR_BRG_USE_PCLK }, + /* Start Rx */ + { RX_CTRL_REG, RCR_RX_ENAB | RCR_CHSIZE_8 }, + /* Start Tx */ + { TX_CTRL_REG, TCR_TX_ENAB | TCR_RTS | TCR_DTR | TCR_CHSIZE_8 }, + /* Ext/Stat ints: DCD only */ + { INT_CTRL_REG, ICR_ENAB_DCD_INT }, + /* Reset Ext/Stat ints */ + { COMMAND_REG, CR_EXTSTAT_RESET }, + /* ...again */ + { COMMAND_REG, CR_EXTSTAT_RESET }, + }; +#endif +#if defined(CONFIG_BVME6000_SCC) + static const struct { + unsigned reg, val; + } bvme_init_tab[] = { + /* Values for BVME6000 */ + /* no parity, 1 stop bit, async, 1:16 */ + { AUX1_CTRL_REG, A1CR_PARITY_NONE|A1CR_MODE_ASYNC_1|A1CR_CLKMODE_x16 }, + /* parity error is special cond, ints disabled, no DMA */ + { INT_AND_DMA_REG, IDR_PARERR_AS_SPCOND | IDR_RX_INT_DISAB }, + /* Rx 8 bits/char, no auto enable, Rx off */ + { RX_CTRL_REG, RCR_CHSIZE_8 }, + /* DTR off, Tx 8 bits/char, RTS off, Tx off */ + { TX_CTRL_REG, TCR_CHSIZE_8 }, + /* special features off */ + { AUX2_CTRL_REG, 0 }, + { CLK_CTRL_REG, CCR_RTxC_XTAL | CCR_RXCLK_BRG | CCR_TXCLK_BRG }, + { DPLL_CTRL_REG, DCR_BRG_ENAB }, + /* Start Rx */ + { RX_CTRL_REG, RCR_RX_ENAB | RCR_CHSIZE_8 }, + /* Start Tx */ + { TX_CTRL_REG, TCR_TX_ENAB | TCR_RTS | TCR_DTR | TCR_CHSIZE_8 }, + /* Ext/Stat ints: DCD only */ + { INT_CTRL_REG, ICR_ENAB_DCD_INT }, + /* Reset Ext/Stat ints */ + { COMMAND_REG, CR_EXTSTAT_RESET }, + /* ...again */ + { COMMAND_REG, CR_EXTSTAT_RESET }, + }; +#endif + if (!(port->gs.flags & ASYNC_INITIALIZED)) { + save_flags(flags); + cli(); +#if defined(CONFIG_MVME147_SCC) || defined(CONFIG_MVME162_SCC) + if (MACH_IS_MVME147 || MACH_IS_MVME16x) { + for (i=0; i<sizeof(mvme_init_tab)/sizeof(*mvme_init_tab); ++i) + SCCwrite(mvme_init_tab[i].reg, mvme_init_tab[i].val); + } +#endif +#if defined(CONFIG_BVME6000_SCC) + if (MACH_IS_BVME6000) { + for (i=0; i<sizeof(bvme_init_tab)/sizeof(*bvme_init_tab); ++i) + SCCwrite(bvme_init_tab[i].reg, bvme_init_tab[i].val); + } +#endif + + /* remember status register for detection of DCD and CTS changes */ + scc_last_status_reg[channel] = SCCread(STATUS_REG); + + port->c_dcd = 0; /* Prevent initial 1->0 interrupt */ + scc_setsignals (port, 1,1); + restore_flags(flags); + } + + tty->driver_data = port; + port->gs.tty = tty; + port->gs.count++; + retval = gs_init_port(&port->gs); + if (retval) { + port->gs.count--; + return retval; + } + port->gs.flags |= GS_ACTIVE; + if (port->gs.count == 1) { + MOD_INC_USE_COUNT; + } + retval = block_til_ready(port, filp); + + if (retval) { + MOD_DEC_USE_COUNT; + port->gs.count--; + return retval; + } + + if ((port->gs.count == 1) && (port->gs.flags & ASYNC_SPLIT_TERMIOS)) { + if (tty->driver.subtype == SERIAL_TYPE_NORMAL) + *tty->termios = port->gs.normal_termios; + else + *tty->termios = port->gs.callout_termios; + scc_set_real_termios (port); + } + + port->gs.session = current->session; + port->gs.pgrp = current->pgrp; + port->c_dcd = scc_get_CD (port); + + scc_enable_rx_interrupts(port); + + return 0; +} + + +static void scc_throttle (struct tty_struct * tty) +{ + struct scc_port *port = (struct scc_port *)tty->driver_data; + unsigned long flags; + SCC_ACCESS_INIT(port); + + if (tty->termios->c_cflag & CRTSCTS) { + save_flags(flags); + cli(); + SCCmod(TX_CTRL_REG, ~TCR_RTS, 0); + restore_flags(flags); + } + if (I_IXOFF(tty)) + scc_send_xchar(tty, STOP_CHAR(tty)); +} + + +static void scc_unthrottle (struct tty_struct * tty) +{ + struct scc_port *port = (struct scc_port *)tty->driver_data; + unsigned long flags; + SCC_ACCESS_INIT(port); + + if (tty->termios->c_cflag & CRTSCTS) { + save_flags(flags); + cli(); + SCCmod(TX_CTRL_REG, 0xff, TCR_RTS); + restore_flags(flags); + } + if (I_IXOFF(tty)) + scc_send_xchar(tty, START_CHAR(tty)); +} + + +static int scc_ioctl(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg) +{ + return -ENOIOCTLCMD; +} + + +static void scc_break_ctl(struct tty_struct *tty, int break_state) +{ + struct scc_port *port = (struct scc_port *)tty->driver_data; + unsigned long flags; + SCC_ACCESS_INIT(port); + + save_flags(flags); + cli(); + SCCmod(TX_CTRL_REG, ~TCR_SEND_BREAK, + break_state ? TCR_SEND_BREAK : 0); + restore_flags(flags); +} + + +/*--------------------------------------------------------------------------- + * Serial console stuff... + *--------------------------------------------------------------------------*/ + +#define scc_delay() do { __asm__ __volatile__ (" nop; nop"); } while (0) + +static void scc_ch_write (char ch) +{ + volatile char *p = NULL; + +#ifdef CONFIG_MVME147_SCC + if (MACH_IS_MVME147) + p = (volatile char *)M147_SCC_A_ADDR; +#endif +#ifdef CONFIG_MVME162_SCC + if (MACH_IS_MVME16x) + p = (volatile char *)MVME_SCC_A_ADDR; +#endif +#ifdef CONFIG_BVME6000_SCC + if (MACH_IS_BVME6000) + p = (volatile char *)BVME_SCC_A_ADDR; +#endif + + do { + scc_delay(); + } + while (!(*p & 4)); + scc_delay(); + *p = 8; + scc_delay(); + *p = ch; +} + + +static void scc_console_write (struct console *co, const char *str, unsigned count) +{ + unsigned long flags; + + save_flags(flags); + cli(); + + while (count--) + { + if (*str == '\n') + scc_ch_write ('\r'); + scc_ch_write (*str++); + } + restore_flags(flags); +} + + +static int scc_console_wait_key(struct console *co) +{ + unsigned long flags; + volatile char *p = NULL; + int c; + +#ifdef CONFIG_MVME147_SCC + if (MACH_IS_MVME147) + p = (volatile char *)M147_SCC_A_ADDR; +#endif +#ifdef CONFIG_MVME162_SCC + if (MACH_IS_MVME16x) + p = (volatile char *)MVME_SCC_A_ADDR; +#endif +#ifdef CONFIG_BVME6000_SCC + if (MACH_IS_BVME6000) + p = (volatile char *)BVME_SCC_A_ADDR; +#endif + + save_flags(flags); + cli(); + + /* wait for rx buf filled */ + while ((*p & 0x01) == 0) + ; + + *p = 8; + scc_delay(); + c = *p; + restore_flags(flags); + return c; +} + + +static kdev_t scc_console_device(struct console *c) +{ + return MKDEV(TTY_MAJOR, SCC_MINOR_BASE + c->index); +} + + +static int __init scc_console_setup(struct console *co, char *options) +{ + return 0; +} + + +static struct console sercons = { + "ttyS", + scc_console_write, + NULL, + scc_console_device, + scc_console_wait_key, + NULL, + scc_console_setup, + CON_PRINTBUFFER, + -1, + 0, + NULL +}; + + +void __init vme_scc_console_init(void) +{ + if (vme_brdtype == VME_TYPE_MVME147 || + vme_brdtype == VME_TYPE_MVME162 || + vme_brdtype == VME_TYPE_MVME172 || + vme_brdtype == VME_TYPE_BVME4000 || + vme_brdtype == VME_TYPE_BVME6000) + register_console(&sercons); +} + diff --git a/drivers/i2c/Config.in b/drivers/i2c/Config.in index ba533aac0..9050f3528 100644 --- a/drivers/i2c/Config.in +++ b/drivers/i2c/Config.in @@ -8,22 +8,22 @@ tristate 'I2C support' CONFIG_I2C if [ "$CONFIG_I2C" != "n" ]; then - dep_tristate 'I2C bit-banging interfaces' CONFIG_I2C_ALGOBIT $CONFIG_I2C - if [ "$CONFIG_I2C_ALGOBIT" != "n" ]; then - dep_tristate ' Philips style parallel port adapter' CONFIG_I2C_PHILIPSPAR $CONFIG_I2C_ALGOBIT - dep_tristate ' ELV adapter' CONFIG_I2C_ELV $CONFIG_I2C_ALGOBIT - dep_tristate ' Velleman K9000 adapter' CONFIG_I2C_VELLEMAN $CONFIG_I2C_ALGOBIT - fi + dep_tristate 'I2C bit-banging interfaces' CONFIG_I2C_ALGOBIT $CONFIG_I2C + if [ "$CONFIG_I2C_ALGOBIT" != "n" ]; then + dep_tristate ' Philips style parallel port adapter' CONFIG_I2C_PHILIPSPAR $CONFIG_I2C_ALGOBIT + dep_tristate ' ELV adapter' CONFIG_I2C_ELV $CONFIG_I2C_ALGOBIT + dep_tristate ' Velleman K9000 adapter' CONFIG_I2C_VELLEMAN $CONFIG_I2C_ALGOBIT + fi - dep_tristate 'I2C PCF 8584 interfaces' CONFIG_I2C_ALGOPCF $CONFIG_I2C - if [ "$CONFIG_I2C_ALGOPCF" != "n" ]; then - dep_tristate ' Elektor ISA card' CONFIG_I2C_ELEKTOR $CONFIG_I2C_ALGOPCF - fi + dep_tristate 'I2C PCF 8584 interfaces' CONFIG_I2C_ALGOPCF $CONFIG_I2C + if [ "$CONFIG_I2C_ALGOPCF" != "n" ]; then + dep_tristate ' Elektor ISA card' CONFIG_I2C_ELEKTOR $CONFIG_I2C_ALGOPCF + fi # This is needed for automatic patch generation: sensors code starts here # This is needed for automatic patch generation: sensors code ends here - dep_tristate 'I2C device interface' CONFIG_I2C_CHARDEV $CONFIG_I2C + dep_tristate 'I2C device interface' CONFIG_I2C_CHARDEV $CONFIG_I2C fi endmenu diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index 3d88b6c39..e74337f5b 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -20,7 +20,7 @@ /* With some changes from Kyösti Mälkki <kmalkki@cc.hut.fi>. All SMBus-related things are written by Frodo Looijaard <frodol@dds.nl> */ -/* $Id: i2c-core.c,v 1.44 1999/12/21 23:45:58 frodo Exp $ */ +/* $Id: i2c-core.c,v 1.48 2000/01/24 21:41:19 frodo Exp $ */ #include <linux/module.h> #include <linux/kernel.h> @@ -33,37 +33,14 @@ /* ----- compatibility stuff ----------------------------------------------- */ -/* 2.0.0 kernel compatibility */ -#if LINUX_VERSION_CODE < 0x020100 -#define MODULE_AUTHOR(noone) -#define MODULE_DESCRIPTION(none) -#define MODULE_PARM(no,param) -#define MODULE_PARM_DESC(no,description) -#define EXPORT_SYMBOL(noexport) -#define EXPORT_NO_SYMBOLS -#endif - #include <linux/version.h> -#ifndef KERNEL_VERSION -#define KERNEL_VERSION(a,b,c) (((a) << 16) | ((b) << 8) | (c)) -#endif - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,53) #include <linux/init.h> -#else -#define __init -#endif #if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,1) #define init_MUTEX(s) do { *(s) = MUTEX; } while(0) #endif -#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,4)) -#define copy_from_user memcpy_fromfs -#define copy_to_user memcpy_tofs -#else #include <asm/uaccess.h> -#endif /* ----- global defines ---------------------------------------------------- */ @@ -83,8 +60,8 @@ /* ----- global variables -------------------------------------------------- */ /**** lock for writing to global variables: the adapter & driver list */ -struct semaphore adap_lock; -struct semaphore driver_lock; +struct semaphore adap_lock; +struct semaphore driver_lock; /**** adapter list */ static struct i2c_adapter *adapters[I2C_ADAP_MAX]; @@ -104,73 +81,29 @@ static void i2c_dummy_client(struct i2c_client *client); *---------------------------------------------------- */ -/* Note that quite some things changed within the 2.1 kernel series. - Some things below are somewhat difficult to read because of this. */ - #ifdef CONFIG_PROC_FS static int i2cproc_init(void); static int i2cproc_cleanup(void); -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,58)) && \ - (LINUX_VERSION_CODE <= KERNEL_VERSION(2,3,27)) +#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,3,27)) static void monitor_bus_i2c(struct inode *inode, int fill); #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,58)) */ -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) - static ssize_t i2cproc_bus_read(struct file * file, char * buf,size_t count, loff_t *ppos); static int read_bus_i2c(char *buf, char **start, off_t offset, int len, int *eof , void *private); -#else /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,29)) */ - -static int i2cproc_bus_read(struct inode * inode, struct file * file, - char * buf, int count); -static int read_bus_i2c(char *buf, char **start, off_t offset, int len, - int unused); - -static struct proc_dir_entry proc_bus_dir = - { - /* low_ino */ 0, /* Set by proc_register_dynamic */ - /* namelen */ 3, - /* name */ "bus", - /* mode */ S_IRUGO | S_IXUGO | S_IFDIR, - /* nlink */ 2, /* Corrected by proc_register[_dynamic] */ - /* uid */ 0, - /* gid */ 0, - /* size */ 0, -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,0,36)) - /* ops */ &proc_dir_inode_operations, -#endif - }; - -static struct proc_dir_entry proc_bus_i2c_dir = - { - /* low_ino */ 0, /* Set by proc_register_dynamic */ - /* namelen */ 3, - /* name */ "i2c", - /* mode */ S_IRUGO | S_IFREG, - /* nlink */ 1, - /* uid */ 0, - /* gid */ 0, - /* size */ 0, - /* ops */ NULL, - /* get_info */ &read_bus_i2c - }; - -#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) */ - /* To implement the dynamic /proc/bus/i2c-? files, we need our own implementation of the read hook */ static struct file_operations i2cproc_operations = { - NULL, - i2cproc_bus_read, + NULL, + i2cproc_bus_read, }; static struct inode_operations i2cproc_inode_operations = { - &i2cproc_operations + &i2cproc_operations }; static int i2cproc_initialized = 0; @@ -183,7 +116,7 @@ static int i2cproc_initialized = 0; #endif /* CONFIG_PROC_FS */ -/* --------------------------------------------------- +/* --------------------------------------------------- * registering functions * --------------------------------------------------- */ @@ -222,55 +155,21 @@ int i2c_add_adapter(struct i2c_adapter *adap) if (i2cproc_initialized) { char name[8]; struct proc_dir_entry *proc_entry; -#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,29)) - int res; -#endif sprintf(name,"i2c-%d", i); -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) proc_entry = create_proc_entry(name,0,proc_bus); if (! proc_entry) { printk("i2c-core.o: Could not create /proc/bus/%s\n", - name); + name); return -ENOENT; - } + } proc_entry->ops = &i2cproc_inode_operations; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,27)) proc_entry->owner = THIS_MODULE; -#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,58)) +#else proc_entry->fill_inode = &monitor_bus_i2c; #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,58)) */ -#else /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,29)) */ - adap->proc_entry = NULL; - if (!(proc_entry = kmalloc(sizeof(struct proc_dir_entry)+ - strlen(name)+1, GFP_KERNEL))) { - printk("i2c-core.o: Out of memory!\n"); - return -ENOMEM; - } - memset(proc_entry,0,sizeof(struct proc_dir_entry)); - proc_entry->namelen = strlen(name); - proc_entry->name = (char *) (proc_entry + 1); - proc_entry->mode = S_IRUGO | S_IFREG; - proc_entry->nlink = 1; - proc_entry->ops = &i2cproc_inode_operations; - - /* Nasty stuff to keep GCC satisfied */ - { - char *procname; - (const char *) procname = proc_entry->name; - strcpy (procname,name); - } - - if ((res = proc_register_dynamic(&proc_bus_dir, proc_entry))) { - printk("i2c-core.o: Could not create %s.\n",name); - kfree(proc_entry); - return res; - } - - adap->proc_entry = proc_entry; -#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) */ - adap->inode = proc_entry->low_ino; } @@ -283,7 +182,8 @@ int i2c_add_adapter(struct i2c_adapter *adap) drivers[j]->attach_adapter(adap); DRV_UNLOCK(); - DEB(printk("i2c-core.o: adapter %s registered as adapter %d.\n",adap->name,i)); + DEB(printk("i2c-core.o: adapter %s registered as adapter %d.\n", + adap->name,i)); return 0; } @@ -306,23 +206,9 @@ int i2c_del_adapter(struct i2c_adapter *adap) i2c_dummy_adapter(adap); /* actually i2c_dummy->del_adapter */ #ifdef CONFIG_PROC_FS if (i2cproc_initialized) { -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) char name[8]; sprintf(name,"i2c-%d", i); remove_proc_entry(name,proc_bus); -#else /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,29)) */ - int res; - if (adapters[i]->proc_entry) { - if ((res = proc_unregister(&proc_bus_dir, - adapters[i]->proc_entry->low_ino))) { - printk("i2c-core.o: Deregistration of /proc " - "entry failed\n"); - ADAP_UNLOCK(); - return res; - } - kfree(adapters[i]->proc_entry); - } -#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) */ } #endif /* def CONFIG_PROC_FS */ @@ -344,7 +230,7 @@ int i2c_del_adapter(struct i2c_adapter *adap) ADAP_UNLOCK(); DEB(printk("i2c-core.o: adapter unregistered: %s\n",adap->name)); - return 0; + return 0; } @@ -384,7 +270,8 @@ int i2c_add_driver(struct i2c_driver *driver) driver->attach_adapter(adapters[i]); for (j=0; j<I2C_CLIENT_MAX; j++) if (adapters[i]->clients[j]) - driver->detach_client(adapters[i]->clients[j]); + driver->detach_client( + adapters[i]->clients[j]); } } ADAP_UNLOCK(); @@ -418,7 +305,7 @@ int i2c_del_driver(struct i2c_driver *driver) } /* Have a look at each adapter, if clients of this driver are still * attached. If so, detach them to be able to kill the driver - * afterwards. + * afterwards. */ DEB2(printk("i2c-core.o: unregister_driver - looking for clients.\n")); /* removing clients does not depend on the notify flag, else @@ -453,11 +340,11 @@ int i2c_del_driver(struct i2c_driver *driver) int i2c_check_addr (struct i2c_adapter *adapter, int addr) { - int i; - for (i = 0; i < I2C_CLIENT_MAX ; i++) - if (adapter->clients[i] && (adapter->clients[i]->addr == addr)) - return -EBUSY; - return 0; + int i; + for (i = 0; i < I2C_CLIENT_MAX ; i++) + if (adapter->clients[i] && (adapter->clients[i]->addr == addr)) + return -EBUSY; + return 0; } int i2c_attach_client(struct i2c_client *client) @@ -465,8 +352,8 @@ int i2c_attach_client(struct i2c_client *client) struct i2c_adapter *adapter = client->adapter; int i; - if (i2c_check_addr(client->adapter,client->addr)) - return -EBUSY; + if (i2c_check_addr(client->adapter,client->addr)) + return -EBUSY; for (i = 0; i < I2C_CLIENT_MAX; i++) if (NULL == adapter->clients[i]) @@ -513,7 +400,7 @@ int i2c_detach_client(struct i2c_client *client) i2c_dummy_client(client); DEB(printk("i2c-core.o: client [%s] unregistered.\n",client->name)); - return 0; + return 0; } void i2c_inc_use_client(struct i2c_client *client) @@ -543,26 +430,21 @@ void i2c_dec_use_client(struct i2c_client *client) #ifdef CONFIG_PROC_FS -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,58)) && \ - (LINUX_VERSION_CODE <= KERNEL_VERSION(2,3,27)) +#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,3,27)) /* Monitor access to /proc/bus/i2c*; make unloading i2c-proc impossible if some process still uses it or some file in it */ void monitor_bus_i2c(struct inode *inode, int fill) { - if (fill) - MOD_INC_USE_COUNT; - else - MOD_DEC_USE_COUNT; + if (fill) + MOD_INC_USE_COUNT; + else + MOD_DEC_USE_COUNT; } -#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,58)) */ +#endif /* (LINUX_VERSION_CODE <= KERNEL_VERSION(2,3,37)) */ /* This function generates the output for /proc/bus/i2c */ -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) int read_bus_i2c(char *buf, char **start, off_t offset, int len, int *eof, void *private) -#else /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,29)) */ -int read_bus_i2c(char *buf, char **start, off_t offset, int len, int unused) -#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) */ { int i; int nr = 0; @@ -571,7 +453,7 @@ int read_bus_i2c(char *buf, char **start, off_t offset, int len, int unused) if (adapters[i]) { nr += sprintf(buf+nr, "i2c-%d\t", i); if (adapters[i]->algo->smbus_xfer) { - if (adapters[i]->algo->master_xfer) + if (adapters[i]->algo->master_xfer) nr += sprintf(buf+nr,"smbus/i2c"); else nr += sprintf(buf+nr,"smbus "); @@ -587,62 +469,55 @@ int read_bus_i2c(char *buf, char **start, off_t offset, int len, int unused) } /* This function generates the output for /proc/bus/i2c-? */ -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) ssize_t i2cproc_bus_read(struct file * file, char * buf,size_t count, loff_t *ppos) { - struct inode * inode = file->f_dentry->d_inode; -#else (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,29)) -int i2cproc_bus_read(struct inode * inode, struct file * file,char * buf, - int count) -{ -#endif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) - char *kbuf; - struct i2c_client *client; - int i,j,len=0; - - if (count < 0) - return -EINVAL; - if (count > 4000) - count = 4000; - for (i = 0; i < I2C_ADAP_MAX; i++) - if (adapters[i]->inode == inode->i_ino) { - /* We need a bit of slack in the kernel buffer; this makes the - sprintf safe. */ - if (! (kbuf = kmalloc(count + 80,GFP_KERNEL))) - return -ENOMEM; - for (j = 0; j < I2C_CLIENT_MAX; j++) - if ((client = adapters[i]->clients[j])) - /* Filter out dummy clients */ - if (client->driver->id != I2C_DRIVERID_I2CDEV) - len += sprintf(kbuf+len,"%02x\t%-32s\t%-32s\n", - client->addr, - client->name,client->driver->name); - if (file->f_pos+len > count) - len = count - file->f_pos; - len = len - file->f_pos; - if (len < 0) - len = 0; - copy_to_user (buf,kbuf+file->f_pos,len); - file->f_pos += len; - kfree(kbuf); - return len; - } - return -ENOENT; + struct inode * inode = file->f_dentry->d_inode; + char *kbuf; + struct i2c_client *client; + int i,j,len=0; + + if (count < 0) + return -EINVAL; + if (count > 4000) + count = 4000; + for (i = 0; i < I2C_ADAP_MAX; i++) + if (adapters[i]->inode == inode->i_ino) { + /* We need a bit of slack in the kernel buffer; this makes the + sprintf safe. */ + if (! (kbuf = kmalloc(count + 80,GFP_KERNEL))) + return -ENOMEM; + for (j = 0; j < I2C_CLIENT_MAX; j++) + if ((client = adapters[i]->clients[j])) + /* Filter out dummy clients */ + if (client->driver->id != I2C_DRIVERID_I2CDEV) + len += sprintf(kbuf+len,"%02x\t%-32s\t%-32s\n", + client->addr, + client->name,client->driver->name); + if (file->f_pos+len > count) + len = count - file->f_pos; + len = len - file->f_pos; + if (len < 0) + len = 0; + if (copy_to_user (buf,kbuf+file->f_pos, + len)) { + kfree(kbuf); + return -EFAULT; + } + file->f_pos += len; + kfree(kbuf); + return len; + } + return -ENOENT; } int i2cproc_init(void) { -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) struct proc_dir_entry *proc_bus_i2c; -#else - int res; -#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) */ i2cproc_initialized = 0; -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) if (! proc_bus) { printk("i2c-core.o: /proc/bus/ does not exist"); i2cproc_cleanup(); @@ -657,29 +532,10 @@ int i2cproc_init(void) proc_bus_i2c->read_proc = &read_bus_i2c; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,27)) proc_bus_i2c->owner = THIS_MODULE; -#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,58)) +#else proc_bus_i2c->fill_inode = &monitor_bus_i2c; -#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) */ +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,27)) */ i2cproc_initialized += 2; -#else /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,29)) */ - /* In Linux 2.0.x, there is no /proc/bus! But I hope no other module - introduced it, or we are fucked. And 2.0.35 and earlier does not - export proc_dir_inode_operations, so we grab it from proc_net, - which also uses it. Not nice. */ - proc_bus_dir.ops = proc_net.ops; - if ((res = proc_register_dynamic(&proc_root, &proc_bus_dir))) { - printk("i2c-core.o: Could not create /proc/bus/"); - i2cproc_cleanup(); - return res; - } - i2cproc_initialized ++; - if ((res = proc_register_dynamic(&proc_bus_dir, &proc_bus_i2c_dir))) { - printk("i2c-core.o: Could not create /proc/bus/i2c\n"); - i2cproc_cleanup(); - return res; - } - i2cproc_initialized ++; -#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) */ return 0; } @@ -687,27 +543,8 @@ int i2cproc_cleanup(void) { if (i2cproc_initialized >= 1) { -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) remove_proc_entry("i2c",proc_bus); i2cproc_initialized -= 2; -#else /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,29)) */ - int res; - if (i2cproc_initialized >= 2) { - if ((res = proc_unregister(&proc_bus_dir, - proc_bus_i2c_dir.low_ino))) { - printk("i2c-core.o: could not delete " - "/proc/bus/i2c, module not removed."); - return res; - } - i2cproc_initialized --; - } - if ((res = proc_unregister(&proc_root,proc_bus_dir.low_ino))) { - printk("i2c-core.o: could not delete /proc/bus/, " - "module not removed."); - return res; - } - i2cproc_initialized --; -#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) */ } return 0; } @@ -715,7 +552,7 @@ int i2cproc_cleanup(void) #endif /* def CONFIG_PROC_FS */ -/* --------------------------------------------------- +/* --------------------------------------------------- * dummy driver notification * --------------------------------------------------- */ @@ -724,16 +561,16 @@ static void i2c_dummy_adapter(struct i2c_adapter *adap) { int i; for (i=0; i<I2C_DRIVER_MAX; i++) - if (drivers[i] && (drivers[i]->flags & I2C_DF_DUMMY)) - drivers[i]->attach_adapter(adap); + if (drivers[i] && (drivers[i]->flags & I2C_DF_DUMMY)) + drivers[i]->attach_adapter(adap); } static void i2c_dummy_client(struct i2c_client *client) { int i; for (i=0; i<I2C_DRIVER_MAX; i++) - if (drivers[i] && (drivers[i]->flags & I2C_DF_DUMMY)) - drivers[i]->detach_client(client); + if (drivers[i] && (drivers[i]->flags & I2C_DF_DUMMY)) + drivers[i]->detach_client(client); } @@ -747,7 +584,8 @@ int i2c_transfer(struct i2c_adapter * adap, struct i2c_msg msgs[],int num) int ret; if (adap->algo->master_xfer) { - DEB2(printk("i2c-core.o: master_xfer: %s with %d msgs.\n",adap->name,num)); + DEB2(printk("i2c-core.o: master_xfer: %s with %d msgs.\n", + adap->name,num)); I2C_LOCK(adap); ret = adap->algo->master_xfer(adap,msgs,num); @@ -756,7 +594,7 @@ int i2c_transfer(struct i2c_adapter * adap, struct i2c_msg msgs[],int num) return ret; } else { printk("i2c-core.o: I2C adapter %04x: I2C level transfers not supported\n", - adap->id); + adap->id); return -ENOSYS; } } @@ -786,7 +624,7 @@ int i2c_master_send(struct i2c_client *client,const char *buf ,int count) return (ret == 1 )? count : ret; } else { printk("i2c-core.o: I2C adapter %04x: I2C level transfers not supported\n", - client->adapter->id); + client->adapter->id); return -ENOSYS; } } @@ -819,7 +657,7 @@ int i2c_master_recv(struct i2c_client *client, char *buf ,int count) return (ret == 1 )? count : ret; } else { printk("i2c-core.o: I2C adapter %04x: I2C level transfers not supported\n", - client->adapter->id); + client->adapter->id); return -ENOSYS; } } @@ -855,128 +693,124 @@ int i2c_probe(struct i2c_adapter *adapter, struct i2c_client_address_data *address_data, i2c_client_found_addr_proc *found_proc) { - int addr,i,found,err; - int adap_id = i2c_adapter_id(adapter); - - /* Forget it if we can't probe using SMBUS_QUICK */ - if (! i2c_check_functionality(adapter,I2C_FUNC_SMBUS_QUICK)) - return -1; - - for (addr = 0x00; - addr <= 0x7f; - addr++) { - - /* Skip if already in use */ - if (i2c_check_addr(adapter,addr)) - continue; - - /* If it is in one of the force entries, we don't do any detection - at all */ - found = 0; - - for (i = 0; - !found && (address_data->force[i] != I2C_CLIENT_END); - i += 3) { - if (((adap_id == address_data->force[i]) || - (address_data->force[i] == ANY_I2C_BUS)) && - (addr == address_data->force[i+1])) { - DEB2(printk("i2c-core.o: found force parameter for adapter %d, addr %04x\n", - adap_id,addr)); - if ((err = found_proc(adapter,addr,0,0))) - return err; - found = 1; - } - } - if (found) - continue; - - /* If this address is in one of the ignores, we can forget about it - right now */ - for (i = 0; - !found && (address_data->ignore[i] != I2C_CLIENT_END); - i += 2) { - if (((adap_id == address_data->ignore[i]) || - ((address_data->ignore[i] == ANY_I2C_BUS))) && - (addr == address_data->ignore[i+1])) { - DEB2(printk("i2c-core.o: found ignore parameter for adapter %d, " - "addr %04x\n", adap_id ,addr)); - found = 1; - } - } - for (i = 0; - !found && (address_data->ignore_range[i] != I2C_CLIENT_END); - i += 3) { - if (((adap_id == address_data->ignore_range[i]) || - ((address_data->ignore_range[i]==ANY_I2C_BUS))) && - (addr >= address_data->ignore_range[i+1]) && - (addr <= address_data->ignore_range[i+2])) { - DEB2(printk("i2c-core.o: found ignore_range parameter for adapter %d, " - "addr %04x\n", adap_id,addr)); - found = 1; - } - } - if (found) - continue; - - /* Now, we will do a detection, but only if it is in the normal or - probe entries */ - for (i = 0; - !found && (address_data->normal_i2c[i] != I2C_CLIENT_END); - i += 1) { - if (addr == address_data->normal_i2c[i]) { - found = 1; - DEB2(printk("i2c-core.o: found normal i2c entry for adapter %d, " - "addr %02x", adap_id,addr)); - } - } - - for (i = 0; - !found && (address_data->normal_i2c_range[i] != I2C_CLIENT_END); - i += 2) { - if ((addr >= address_data->normal_i2c_range[i]) && - (addr <= address_data->normal_i2c_range[i+1])) { - found = 1; - DEB2(printk("i2c-core.o: found normal i2c_range entry for adapter %d, " - "addr %04x\n", adap_id,addr)); - } - } - - for (i = 0; - !found && (address_data->probe[i] != I2C_CLIENT_END); - i += 2) { - if (((adap_id == address_data->probe[i]) || - ((address_data->probe[i] == ANY_I2C_BUS))) && - (addr == address_data->probe[i+1])) { - found = 1; - DEB2(printk("i2c-core.o: found probe parameter for adapter %d, " - "addr %04x\n", adap_id,addr)); - } - } - for (i = 0; - !found && (address_data->probe_range[i] != I2C_CLIENT_END); - i += 3) { - if (((adap_id == address_data->probe_range[i]) || - (address_data->probe_range[i] == ANY_I2C_BUS)) && - (addr >= address_data->probe_range[i+1]) && - (addr <= address_data->probe_range[i+2])) { - found = 1; - DEB2(printk("i2c-core.o: found probe_range parameter for adapter %d, " - "addr %04x\n", adap_id,addr)); - } - } - if (!found) - continue; - - /* OK, so we really should examine this address. First check - whether there is some client here at all! */ - if (i2c_smbus_xfer(adapter,addr,0,0,0,I2C_SMBUS_QUICK,NULL) >= 0) - if ((err = found_proc(adapter,addr,0,-1))) - return err; - } - return 0; + int addr,i,found,err; + int adap_id = i2c_adapter_id(adapter); + + /* Forget it if we can't probe using SMBUS_QUICK */ + if (! i2c_check_functionality(adapter,I2C_FUNC_SMBUS_QUICK)) + return -1; + + for (addr = 0x00; addr <= 0x7f; addr++) { + + /* Skip if already in use */ + if (i2c_check_addr(adapter,addr)) + continue; + + /* If it is in one of the force entries, we don't do any detection + at all */ + found = 0; + + for (i = 0; !found && (address_data->force[i] != I2C_CLIENT_END); i += 3) { + if (((adap_id == address_data->force[i]) || + (address_data->force[i] == ANY_I2C_BUS)) && + (addr == address_data->force[i+1])) { + DEB2(printk("i2c-core.o: found force parameter for adapter %d, addr %04x\n", + adap_id,addr)); + if ((err = found_proc(adapter,addr,0,0))) + return err; + found = 1; + } + } + if (found) + continue; + + /* If this address is in one of the ignores, we can forget about + it right now */ + for (i = 0; + !found && (address_data->ignore[i] != I2C_CLIENT_END); + i += 2) { + if (((adap_id == address_data->ignore[i]) || + ((address_data->ignore[i] == ANY_I2C_BUS))) && + (addr == address_data->ignore[i+1])) { + DEB2(printk("i2c-core.o: found ignore parameter for adapter %d, " + "addr %04x\n", adap_id ,addr)); + found = 1; + } + } + for (i = 0; + !found && (address_data->ignore_range[i] != I2C_CLIENT_END); + i += 3) { + if (((adap_id == address_data->ignore_range[i]) || + ((address_data->ignore_range[i]==ANY_I2C_BUS))) && + (addr >= address_data->ignore_range[i+1]) && + (addr <= address_data->ignore_range[i+2])) { + DEB2(printk("i2c-core.o: found ignore_range parameter for adapter %d, " + "addr %04x\n", adap_id,addr)); + found = 1; + } + } + if (found) + continue; + + /* Now, we will do a detection, but only if it is in the normal or + probe entries */ + for (i = 0; + !found && (address_data->normal_i2c[i] != I2C_CLIENT_END); + i += 1) { + if (addr == address_data->normal_i2c[i]) { + found = 1; + DEB2(printk("i2c-core.o: found normal i2c entry for adapter %d, " + "addr %02x", adap_id,addr)); + } + } + + for (i = 0; + !found && (address_data->normal_i2c_range[i] != I2C_CLIENT_END); + i += 2) { + if ((addr >= address_data->normal_i2c_range[i]) && + (addr <= address_data->normal_i2c_range[i+1])) { + found = 1; + DEB2(printk("i2c-core.o: found normal i2c_range entry for adapter %d, " + "addr %04x\n", adap_id,addr)); + } + } + + for (i = 0; + !found && (address_data->probe[i] != I2C_CLIENT_END); + i += 2) { + if (((adap_id == address_data->probe[i]) || + ((address_data->probe[i] == ANY_I2C_BUS))) && + (addr == address_data->probe[i+1])) { + found = 1; + DEB2(printk("i2c-core.o: found probe parameter for adapter %d, " + "addr %04x\n", adap_id,addr)); + } + } + for (i = 0; + !found && (address_data->probe_range[i] != I2C_CLIENT_END); + i += 3) { + if (((adap_id == address_data->probe_range[i]) || + (address_data->probe_range[i] == ANY_I2C_BUS)) && + (addr >= address_data->probe_range[i+1]) && + (addr <= address_data->probe_range[i+2])) { + found = 1; + DEB2(printk("i2c-core.o: found probe_range parameter for adapter %d, " + "addr %04x\n", adap_id,addr)); + } + } + if (!found) + continue; + + /* OK, so we really should examine this address. First check + whether there is some client here at all! */ + if (i2c_smbus_xfer(adapter,addr,0,0,0,I2C_SMBUS_QUICK,NULL) >= 0) + if ((err = found_proc(adapter,addr,0,-1))) + return err; + } + return 0; } -/* +++ frodo +/* * return id number for a specific adapter */ int i2c_adapter_id(struct i2c_adapter *adap) @@ -993,14 +827,14 @@ int i2c_adapter_id(struct i2c_adapter *adap) extern s32 i2c_smbus_write_quick(struct i2c_client * client, u8 value) { return i2c_smbus_xfer(client->adapter,client->addr,client->flags, - value,0,I2C_SMBUS_QUICK,NULL); + value,0,I2C_SMBUS_QUICK,NULL); } extern s32 i2c_smbus_read_byte(struct i2c_client * client) { union i2c_smbus_data data; if (i2c_smbus_xfer(client->adapter,client->addr,client->flags, - I2C_SMBUS_READ,0,I2C_SMBUS_BYTE, &data)) + I2C_SMBUS_READ,0,I2C_SMBUS_BYTE, &data)) return -1; else return 0x0FF & data.byte; @@ -1009,26 +843,26 @@ extern s32 i2c_smbus_read_byte(struct i2c_client * client) extern s32 i2c_smbus_write_byte(struct i2c_client * client, u8 value) { return i2c_smbus_xfer(client->adapter,client->addr,client->flags, - I2C_SMBUS_WRITE,value, I2C_SMBUS_BYTE,NULL); + I2C_SMBUS_WRITE,value, I2C_SMBUS_BYTE,NULL); } extern s32 i2c_smbus_read_byte_data(struct i2c_client * client, u8 command) { union i2c_smbus_data data; if (i2c_smbus_xfer(client->adapter,client->addr,client->flags, - I2C_SMBUS_READ,command, I2C_SMBUS_BYTE_DATA,&data)) + I2C_SMBUS_READ,command, I2C_SMBUS_BYTE_DATA,&data)) return -1; else return 0x0FF & data.byte; } -extern s32 i2c_smbus_write_byte_data(struct i2c_client * client, - u8 command, u8 value) +extern s32 i2c_smbus_write_byte_data(struct i2c_client * client, u8 command, + u8 value) { union i2c_smbus_data data; data.byte = value; return i2c_smbus_xfer(client->adapter,client->addr,client->flags, - I2C_SMBUS_WRITE,command, + I2C_SMBUS_WRITE,command, I2C_SMBUS_BYTE_DATA,&data); } @@ -1036,7 +870,7 @@ extern s32 i2c_smbus_read_word_data(struct i2c_client * client, u8 command) { union i2c_smbus_data data; if (i2c_smbus_xfer(client->adapter,client->addr,client->flags, - I2C_SMBUS_READ,command, I2C_SMBUS_WORD_DATA, &data)) + I2C_SMBUS_READ,command, I2C_SMBUS_WORD_DATA, &data)) return -1; else return 0x0FFFF & data.word; @@ -1048,8 +882,8 @@ extern s32 i2c_smbus_write_word_data(struct i2c_client * client, union i2c_smbus_data data; data.word = value; return i2c_smbus_xfer(client->adapter,client->addr,client->flags, - I2C_SMBUS_WRITE,command, - I2C_SMBUS_WORD_DATA,&data); + I2C_SMBUS_WRITE,command, + I2C_SMBUS_WORD_DATA,&data); } extern s32 i2c_smbus_process_call(struct i2c_client * client, @@ -1058,8 +892,8 @@ extern s32 i2c_smbus_process_call(struct i2c_client * client, union i2c_smbus_data data; data.word = value; if (i2c_smbus_xfer(client->adapter,client->addr,client->flags, - I2C_SMBUS_WRITE,command, - I2C_SMBUS_PROC_CALL, &data)) + I2C_SMBUS_WRITE,command, + I2C_SMBUS_PROC_CALL, &data)) return -1; else return 0x0FFFF & data.word; @@ -1072,7 +906,7 @@ extern s32 i2c_smbus_read_block_data(struct i2c_client * client, union i2c_smbus_data data; int i; if (i2c_smbus_xfer(client->adapter,client->addr,client->flags, - I2C_SMBUS_READ,command, + I2C_SMBUS_READ,command, I2C_SMBUS_BLOCK_DATA,&data)) return -1; else { @@ -1093,7 +927,7 @@ extern s32 i2c_smbus_write_block_data(struct i2c_client * client, data.block[i] = values[i-1]; data.block[0] = length; return i2c_smbus_xfer(client->adapter,client->addr,client->flags, - I2C_SMBUS_WRITE,command, + I2C_SMBUS_WRITE,command, I2C_SMBUS_BLOCK_DATA,&data); } @@ -1177,7 +1011,7 @@ static s32 i2c_smbus_xfer_emulated(struct i2c_adapter * adapter, u16 addr, size); return -1; } - + if (i2c_transfer(adapter, msg, num) < 0) return -1; @@ -1193,8 +1027,8 @@ static s32 i2c_smbus_xfer_emulated(struct i2c_adapter * adapter, u16 addr, case I2C_SMBUS_PROC_CALL: data->word = msgbuf1[0] | (msgbuf1[1] << 8); break; - } - return 0; + } + return 0; } @@ -1203,7 +1037,7 @@ s32 i2c_smbus_xfer(struct i2c_adapter * adapter, u16 addr, unsigned short flags, union i2c_smbus_data * data) { s32 res; - flags = flags & I2C_M_TEN; + flags = flags & I2C_M_TEN; if (adapter->algo->smbus_xfer) { I2C_LOCK(adapter); res = adapter->algo->smbus_xfer(adapter,addr,flags,read_write, @@ -1211,7 +1045,7 @@ s32 i2c_smbus_xfer(struct i2c_adapter * adapter, u16 addr, unsigned short flags, I2C_UNLOCK(adapter); } else res = i2c_smbus_xfer_emulated(adapter,addr,flags,read_write, - command,size,data); + command,size,data); return res; } @@ -1220,22 +1054,22 @@ s32 i2c_smbus_xfer(struct i2c_adapter * adapter, u16 addr, unsigned short flags, backward compatibility. */ u32 i2c_get_functionality (struct i2c_adapter *adap) { - if (adap->algo->functionality) - return adap->algo->functionality(adap); - else - return 0xffffffff; + if (adap->algo->functionality) + return adap->algo->functionality(adap); + else + return 0xffffffff; } int i2c_check_functionality (struct i2c_adapter *adap, u32 func) { - u32 adap_func = i2c_get_functionality (adap); - return (func & adap_func) == func; + u32 adap_func = i2c_get_functionality (adap); + return (func & adap_func) == func; } static int __init i2c_init(void) { - printk("i2c-core.o: i2c core module\n"); + printk("i2c-core.o: i2c core module\n"); memset(adapters,0,sizeof(adapters)); memset(drivers,0,sizeof(drivers)); adap_count=0; diff --git a/drivers/i2c/i2c-dev.c b/drivers/i2c/i2c-dev.c index 088d730fc..c2c51bef4 100644 --- a/drivers/i2c/i2c-dev.c +++ b/drivers/i2c/i2c-dev.c @@ -23,7 +23,7 @@ But I have used so much of his original code and ideas that it seems only fair to recognize him as co-author -- Frodo */ -/* $Id: i2c-dev.c,v 1.18 1999/12/21 23:45:58 frodo Exp $ */ +/* $Id: i2c-dev.c,v 1.25 2000/01/26 14:14:20 frodo Exp $ */ #include <linux/kernel.h> #include <linux/module.h> @@ -34,34 +34,8 @@ /* If you want debugging uncomment: */ /* #define DEBUG */ -#ifndef KERNEL_VERSION -#define KERNEL_VERSION(a,b,c) (((a) << 16) | ((b) << 8) | (c)) -#endif - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,51) #include <linux/init.h> -#else -#define __init -#endif - -#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,4)) -#define copy_from_user memcpy_fromfs -#define copy_to_user memcpy_tofs -#define get_user_data(to,from) ((to) = get_user(from),0) -#else #include <asm/uaccess.h> -#define get_user_data(to,from) get_user(to,from) -#endif - -/* 2.0.0 kernel compatibility */ -#if LINUX_VERSION_CODE < 0x020100 -#define MODULE_AUTHOR(noone) -#define MODULE_DESCRIPTION(none) -#define MODULE_PARM(no,param) -#define MODULE_PARM_DESC(no,description) -#define EXPORT_SYMBOL(noexport) -#define EXPORT_NO_SYMBOLS -#endif #include <linux/i2c.h> #include <linux/i2c-dev.h> @@ -73,45 +47,18 @@ extern int cleanup_module(void); /* struct file_operations changed too often in the 2.1 series for nice code */ -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,70)) static loff_t i2cdev_lseek (struct file *file, loff_t offset, int origin); -#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,56)) -static long long i2cdev_lseek (struct file *file, long long offset, int origin); -#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)) -static long long i2cdev_llseek (struct inode *inode, struct file *file, - long long offset, int origin); -#else -static int i2cdev_lseek (struct inode *inode, struct file *file, off_t offset, - int origin); -#endif -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,70)) static ssize_t i2cdev_read (struct file *file, char *buf, size_t count, loff_t *offset); static ssize_t i2cdev_write (struct file *file, const char *buf, size_t count, loff_t *offset); -#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)) -static long i2cdev_read (struct inode *inode, struct file *file, char *buf, - unsigned long count); -static long i2cdev_write (struct inode *inode, struct file *file, - const char *buf, unsigned long offset); -#else -static int i2cdev_read(struct inode *inode, struct file *file, char *buf, - int count); -static int i2cdev_write(struct inode *inode, struct file *file, - const char *buf, int count); -#endif static int i2cdev_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg); static int i2cdev_open (struct inode *inode, struct file *file); -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,31)) static int i2cdev_release (struct inode *inode, struct file *file); -#else -static void i2cdev_release (struct inode *inode, struct file *file); -#endif - static int i2cdev_attach_adapter(struct i2c_adapter *adap); static int i2cdev_detach_client(struct i2c_client *client); @@ -127,413 +74,349 @@ extern static int i2cdev_cleanup(void); static struct file_operations i2cdev_fops = { - i2cdev_lseek, - i2cdev_read, - i2cdev_write, - NULL, /* i2cdev_readdir */ - NULL, /* i2cdev_select */ - i2cdev_ioctl, - NULL, /* i2cdev_mmap */ - i2cdev_open, -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,118) - NULL, /* i2cdev_flush */ -#endif - i2cdev_release, + i2cdev_lseek, + i2cdev_read, + i2cdev_write, + NULL, /* i2cdev_readdir */ + NULL, /* i2cdev_select */ + i2cdev_ioctl, + NULL, /* i2cdev_mmap */ + i2cdev_open, + NULL, /* i2cdev_flush */ + i2cdev_release, }; #define I2CDEV_ADAPS_MAX I2C_ADAP_MAX static struct i2c_adapter *i2cdev_adaps[I2CDEV_ADAPS_MAX]; static struct i2c_driver i2cdev_driver = { - /* name */ "i2c-dev dummy driver", - /* id */ I2C_DRIVERID_I2CDEV, - /* flags */ I2C_DF_DUMMY, - /* attach_adapter */ i2cdev_attach_adapter, - /* detach_client */ i2cdev_detach_client, - /* command */ i2cdev_command, - /* inc_use */ NULL, - /* dec_use */ NULL, + /* name */ "i2c-dev dummy driver", + /* id */ I2C_DRIVERID_I2CDEV, + /* flags */ I2C_DF_DUMMY, + /* attach_adapter */ i2cdev_attach_adapter, + /* detach_client */ i2cdev_detach_client, + /* command */ i2cdev_command, + /* inc_use */ NULL, + /* dec_use */ NULL, }; static struct i2c_client i2cdev_client_template = { - /* name */ "I2C /dev entry", - /* id */ 1, - /* flags */ 0, - /* addr */ -1, - /* adapter */ NULL, - /* driver */ &i2cdev_driver, - /* data */ NULL + /* name */ "I2C /dev entry", + /* id */ 1, + /* flags */ 0, + /* addr */ -1, + /* adapter */ NULL, + /* driver */ &i2cdev_driver, + /* data */ NULL }; static int i2cdev_initialized; /* Note that the lseek function is called llseek in 2.1 kernels. But things are complicated enough as is. */ -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,70)) loff_t i2cdev_lseek (struct file *file, loff_t offset, int origin) -#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,56)) -long long i2cdev_lseek (struct file *file, long long offset, int origin) -#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)) -long long i2cdev_llseek (struct inode *inode, struct file *file, - long long offset, int origin) -#else -int i2cdev_lseek (struct inode *inode, struct file *file, off_t offset, - int origin) -#endif { #ifdef DEBUG -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,56)) - struct inode *inode = file->f_dentry->d_inode; -#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,70)) */ - printk("i2c-dev,o: i2c-%d lseek to %ld bytes relative to %d.\n", - MINOR(inode->i_rdev),(long) offset,origin); + struct inode *inode = file->f_dentry->d_inode; + printk("i2c-dev,o: i2c-%d lseek to %ld bytes relative to %d.\n", + MINOR(inode->i_rdev),(long) offset,origin); #endif /* DEBUG */ - return -ESPIPE; + return -ESPIPE; } -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,70)) static ssize_t i2cdev_read (struct file *file, char *buf, size_t count, loff_t *offset) -#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)) -static long i2cdev_read (struct inode *inode, struct file *file, char *buf, - unsigned long count) -#else -static int i2cdev_read(struct inode *inode, struct file *file, char *buf, - int count) -#endif { - char *tmp; - int ret; + char *tmp; + int ret; #ifdef DEBUG -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,70)) - struct inode *inode = file->f_dentry->d_inode; -#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,70)) */ + struct inode *inode = file->f_dentry->d_inode; #endif /* DEBUG */ - struct i2c_client *client = (struct i2c_client *)file->private_data; + struct i2c_client *client = (struct i2c_client *)file->private_data; - /* copy user space data to kernel space. */ - tmp = kmalloc(count,GFP_KERNEL); - if (tmp==NULL) - return -ENOMEM; + /* copy user space data to kernel space. */ + tmp = kmalloc(count,GFP_KERNEL); + if (tmp==NULL) + return -ENOMEM; #ifdef DEBUG - printk("i2c-dev,o: i2c-%d reading %d bytes.\n",MINOR(inode->i_rdev),count); + printk("i2c-dev,o: i2c-%d reading %d bytes.\n",MINOR(inode->i_rdev), + count); #endif - ret = i2c_master_recv(client,tmp,count); - copy_to_user(buf,tmp,count); - kfree(tmp); - return ret; + ret = i2c_master_recv(client,tmp,count); + if (! ret) + ret = copy_to_user(buf,tmp,count)?-EFAULT:0; + kfree(tmp); + return ret; } -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,70)) static ssize_t i2cdev_write (struct file *file, const char *buf, size_t count, loff_t *offset) -#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)) -static long i2cdev_write (struct inode *inode, struct file *file, - const char *buf, unsigned long offset) -#else -static int i2cdev_write(struct inode *inode, struct file *file, - const char *buf, int count) -#endif { - int ret; - char *tmp; - struct i2c_client *client = (struct i2c_client *)file->private_data; + int ret; + char *tmp; + struct i2c_client *client = (struct i2c_client *)file->private_data; #ifdef DEBUG -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,70)) - struct inode *inode = file->f_dentry->d_inode; -#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,70)) */ + struct inode *inode = file->f_dentry->d_inode; #endif /* DEBUG */ - /* copy user space data to kernel space. */ - tmp = kmalloc(count,GFP_KERNEL); - if (tmp==NULL) - return -ENOMEM; - copy_from_user(tmp,buf,count); + /* copy user space data to kernel space. */ + tmp = kmalloc(count,GFP_KERNEL); + if (tmp==NULL) + return -ENOMEM; + if (copy_from_user(tmp,buf,count)) { + kfree(tmp); + return -EFAULT; + } #ifdef DEBUG - printk("i2c-dev,o: i2c-%d writing %d bytes.\n",MINOR(inode->i_rdev),count); + printk("i2c-dev,o: i2c-%d writing %d bytes.\n",MINOR(inode->i_rdev), + count); #endif - ret = i2c_master_send(client,tmp,count); - kfree(tmp); - return ret; + ret = i2c_master_send(client,tmp,count); + kfree(tmp); + return ret; } int i2cdev_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { - struct i2c_client *client = (struct i2c_client *)file->private_data; - struct i2c_smbus_ioctl_data data_arg; - union i2c_smbus_data temp; - int ver,datasize,res; - unsigned long funcs; + struct i2c_client *client = (struct i2c_client *)file->private_data; + struct i2c_smbus_ioctl_data data_arg; + union i2c_smbus_data temp; + int datasize,res; + unsigned long funcs; #ifdef DEBUG - printk("i2c-dev.o: i2c-%d ioctl, cmd: 0x%x, arg: %lx.\n", - MINOR(inode->i_rdev),cmd, arg); + printk("i2c-dev.o: i2c-%d ioctl, cmd: 0x%x, arg: %lx.\n", + MINOR(inode->i_rdev),cmd, arg); #endif /* DEBUG */ - switch ( cmd ) { - case I2C_SLAVE: - case I2C_SLAVE_FORCE: - if ((arg > 0x3ff) || (((client->flags & I2C_M_TEN) == 0) && arg > 0x7f)) - return -EINVAL; - if ((cmd == I2C_SLAVE) && i2c_check_addr(client->adapter,arg)) - return -EBUSY; - client->addr = arg; - return 0; - case I2C_TENBIT: - if (arg) - client->flags |= I2C_M_TEN; - else - client->flags &= ~I2C_M_TEN; - return 0; - case I2C_FUNCS: - if (! arg) { -#ifdef DEBUG - printk("i2c-dev.o: NULL argument pointer in ioctl I2C_SMBUS.\n"); -#endif - return -EINVAL; - } - if (verify_area(VERIFY_WRITE,(unsigned long *) arg, - sizeof(unsigned long))) { + switch ( cmd ) { + case I2C_SLAVE: + case I2C_SLAVE_FORCE: + if ((arg > 0x3ff) || + (((client->flags & I2C_M_TEN) == 0) && arg > 0x7f)) + return -EINVAL; + if ((cmd == I2C_SLAVE) && i2c_check_addr(client->adapter,arg)) + return -EBUSY; + client->addr = arg; + return 0; + case I2C_TENBIT: + if (arg) + client->flags |= I2C_M_TEN; + else + client->flags &= ~I2C_M_TEN; + return 0; + case I2C_FUNCS: + funcs = i2c_get_functionality(client->adapter); + return (copy_to_user((unsigned long *)arg,&funcs, + sizeof(unsigned long)))?-EFAULT:0; + case I2C_SMBUS: + copy_from_user_ret(&data_arg, + (struct i2c_smbus_ioctl_data *) arg, + sizeof(struct i2c_smbus_ioctl_data), + -EFAULT); + if ((data_arg.size != I2C_SMBUS_BYTE) && + (data_arg.size != I2C_SMBUS_QUICK) && + (data_arg.size != I2C_SMBUS_BYTE_DATA) && + (data_arg.size != I2C_SMBUS_WORD_DATA) && + (data_arg.size != I2C_SMBUS_PROC_CALL) && + (data_arg.size != I2C_SMBUS_BLOCK_DATA)) { #ifdef DEBUG - printk("i2c-dev.o: invalid argument pointer (%ld) " - "in IOCTL I2C_SMBUS.\n", arg); + printk("i2c-dev.o: size out of range (%x) in ioctl I2C_SMBUS.\n", + data_arg.size); #endif - return -EINVAL; - } - - funcs = i2c_get_functionality(client->adapter); - copy_to_user((unsigned long *)arg,&funcs,sizeof(unsigned long)); - return 0; - case I2C_SMBUS: - if (! arg) { + return -EINVAL; + } + /* Note that I2C_SMBUS_READ and I2C_SMBUS_WRITE are 0 and 1, + so the check is valid if size==I2C_SMBUS_QUICK too. */ + if ((data_arg.read_write != I2C_SMBUS_READ) && + (data_arg.read_write != I2C_SMBUS_WRITE)) { #ifdef DEBUG - printk("i2c-dev.o: NULL argument pointer in ioctl I2C_SMBUS.\n"); + printk("i2c-dev.o: read_write out of range (%x) in ioctl I2C_SMBUS.\n", + data_arg.read_write); #endif - return -EINVAL; - } - if (verify_area(VERIFY_READ,(struct i2c_smbus_ioctl_data *) arg, - sizeof(struct i2c_smbus_ioctl_data))) { + return -EINVAL; + } + + /* Note that command values are always valid! */ + + if ((data_arg.size == I2C_SMBUS_QUICK) || + ((data_arg.size == I2C_SMBUS_BYTE) && + (data_arg.read_write == I2C_SMBUS_WRITE))) + /* These are special: we do not use data */ + return i2c_smbus_xfer(client->adapter, client->addr, + client->flags, + data_arg.read_write, + data_arg.command, + data_arg.size, NULL); + + if (data_arg.data == NULL) { #ifdef DEBUG - printk("i2c-dev.o: invalid argument pointer (%ld) " - "in IOCTL I2C_SMBUS.\n", arg); + printk("i2c-dev.o: data is NULL pointer in ioctl I2C_SMBUS.\n"); #endif - return -EINVAL; - } - copy_from_user(&data_arg,(struct i2c_smbus_ioctl_data *) arg, - sizeof(struct i2c_smbus_ioctl_data)); - if ((data_arg.size != I2C_SMBUS_BYTE) && - (data_arg.size != I2C_SMBUS_QUICK) && - (data_arg.size != I2C_SMBUS_BYTE_DATA) && - (data_arg.size != I2C_SMBUS_WORD_DATA) && - (data_arg.size != I2C_SMBUS_PROC_CALL) && - (data_arg.size != I2C_SMBUS_BLOCK_DATA)) { -#ifdef DEBUG - printk("i2c-dev.o: size out of range (%x) in ioctl I2C_SMBUS.\n", - data_arg.size); -#endif - return -EINVAL; - } - /* Note that I2C_SMBUS_READ and I2C_SMBUS_WRITE are 0 and 1, - so the check is valid if size==I2C_SMBUS_QUICK too. */ - if ((data_arg.read_write != I2C_SMBUS_READ) && - (data_arg.read_write != I2C_SMBUS_WRITE)) { -#ifdef DEBUG - printk("i2c-dev.o: read_write out of range (%x) in ioctl I2C_SMBUS.\n", - data_arg.read_write); -#endif - return -EINVAL; - } - - /* Note that command values are always valid! */ - - if ((data_arg.size == I2C_SMBUS_QUICK) || - ((data_arg.size == I2C_SMBUS_BYTE) && - (data_arg.read_write == I2C_SMBUS_WRITE))) - /* These are special: we do not use data */ - return i2c_smbus_xfer(client->adapter, client->addr, client->flags, - data_arg.read_write, data_arg.command, - data_arg.size, NULL); - - if (data_arg.data == NULL) { -#ifdef DEBUG - printk("i2c-dev.o: data is NULL pointer in ioctl I2C_SMBUS.\n"); -#endif - return -EINVAL; - } - - /* This seems unlogical but it is not: if the user wants to read a - value, we must write that value to user memory! */ - ver = ((data_arg.read_write == I2C_SMBUS_WRITE) && - (data_arg.size != I2C_SMBUS_PROC_CALL))?VERIFY_READ:VERIFY_WRITE; - - if ((data_arg.size == I2C_SMBUS_BYTE_DATA) || (data_arg.size == I2C_SMBUS_BYTE)) - datasize = sizeof(data_arg.data->byte); - else if ((data_arg.size == I2C_SMBUS_WORD_DATA) || - (data_arg.size == I2C_SMBUS_PROC_CALL)) - datasize = sizeof(data_arg.data->word); - else /* size == I2C_SMBUS_BLOCK_DATA */ - datasize = sizeof(data_arg.data->block); - - if (verify_area(ver,data_arg.data,datasize)) { -#ifdef DEBUG - printk("i2c-dev.o: invalid pointer data (%p) in ioctl I2C_SMBUS.\n", - data_arg.data); -#endif - return -EINVAL; - } - - if ((data_arg.size == I2C_SMBUS_PROC_CALL) || - (data_arg.read_write == I2C_SMBUS_WRITE)) - copy_from_user(&temp,data_arg.data,datasize); - res = i2c_smbus_xfer(client->adapter,client->addr,client->flags, - data_arg.read_write, - data_arg.command,data_arg.size,&temp); - if (! res && ((data_arg.size == I2C_SMBUS_PROC_CALL) || - (data_arg.read_write == I2C_SMBUS_READ))) - copy_to_user(data_arg.data,&temp,datasize); - return res; - - default: - return i2c_control(client,cmd,arg); - } - return 0; + return -EINVAL; + } + + if ((data_arg.size == I2C_SMBUS_BYTE_DATA) || + (data_arg.size == I2C_SMBUS_BYTE)) + datasize = sizeof(data_arg.data->byte); + else if ((data_arg.size == I2C_SMBUS_WORD_DATA) || + (data_arg.size == I2C_SMBUS_PROC_CALL)) + datasize = sizeof(data_arg.data->word); + else /* size == I2C_SMBUS_BLOCK_DATA */ + datasize = sizeof(data_arg.data->block); + + if ((data_arg.size == I2C_SMBUS_PROC_CALL) || + (data_arg.read_write == I2C_SMBUS_WRITE)) + copy_from_user_ret(&temp,data_arg.data,datasize, + -EFAULT); + res = i2c_smbus_xfer(client->adapter,client->addr,client->flags, + data_arg.read_write, + data_arg.command,data_arg.size,&temp); + if (! res && ((data_arg.size == I2C_SMBUS_PROC_CALL) || + (data_arg.read_write == I2C_SMBUS_READ))) + copy_to_user_ret(data_arg.data,&temp,datasize,-EFAULT); + return res; + + default: + return i2c_control(client,cmd,arg); + } + return 0; } int i2cdev_open (struct inode *inode, struct file *file) { - unsigned int minor = MINOR(inode->i_rdev); - struct i2c_client *client; + unsigned int minor = MINOR(inode->i_rdev); + struct i2c_client *client; - if ((minor >= I2CDEV_ADAPS_MAX) || ! (i2cdev_adaps[minor])) { + if ((minor >= I2CDEV_ADAPS_MAX) || ! (i2cdev_adaps[minor])) { #ifdef DEBUG - printk("i2c-dev.o: Trying to open unattached adapter i2c-%d\n",minor); + printk("i2c-dev.o: Trying to open unattached adapter i2c-%d\n", + minor); #endif - return -ENODEV; - } + return -ENODEV; + } - /* Note that we here allocate a client for later use, but we will *not* - register this client! Yes, this is safe. No, it is not very clean. */ - if(! (client = kmalloc(sizeof(struct i2c_client),GFP_KERNEL))) - return -ENOMEM; - memcpy(client,&i2cdev_client_template,sizeof(struct i2c_client)); - client->adapter = i2cdev_adaps[minor]; - file->private_data = client; + /* Note that we here allocate a client for later use, but we will *not* + register this client! Yes, this is safe. No, it is not very clean. */ + if(! (client = kmalloc(sizeof(struct i2c_client),GFP_KERNEL))) + return -ENOMEM; + memcpy(client,&i2cdev_client_template,sizeof(struct i2c_client)); + client->adapter = i2cdev_adaps[minor]; + file->private_data = client; - i2cdev_adaps[minor]->inc_use(i2cdev_adaps[minor]); - MOD_INC_USE_COUNT; + if (i2cdev_adaps[minor]->inc_use) + i2cdev_adaps[minor]->inc_use(i2cdev_adaps[minor]); + MOD_INC_USE_COUNT; #ifdef DEBUG - printk("i2c-dev.o: opened i2c-%d\n",minor); + printk("i2c-dev.o: opened i2c-%d\n",minor); #endif - return 0; + return 0; } -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,31)) static int i2cdev_release (struct inode *inode, struct file *file) -#else -static void i2cdev_release (struct inode *inode, struct file *file) -#endif { - unsigned int minor = MINOR(inode->i_rdev); - kfree(file->private_data); - file->private_data=NULL; + unsigned int minor = MINOR(inode->i_rdev); + kfree(file->private_data); + file->private_data=NULL; #ifdef DEBUG - printk("i2c-dev.o: Closed: i2c-%d\n", minor); -#endif - MOD_DEC_USE_COUNT; - i2cdev_adaps[minor]->dec_use(i2cdev_adaps[minor]); -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,31)) - return 0; + printk("i2c-dev.o: Closed: i2c-%d\n", minor); #endif + MOD_DEC_USE_COUNT; + if (i2cdev_adaps[minor]->dec_use) + i2cdev_adaps[minor]->dec_use(i2cdev_adaps[minor]); + return 0; } int i2cdev_attach_adapter(struct i2c_adapter *adap) { - int i; - - if ((i = i2c_adapter_id(adap)) < 0) { - printk("i2c-dev.o: Unknown adapter ?!?\n"); - return -ENODEV; - } - if (i >= I2CDEV_ADAPS_MAX) { - printk("i2c-dev.o: Adapter number too large?!? (%d)\n",i); - return -ENODEV; - } - - if (! i2cdev_adaps[i]) { - i2cdev_adaps[i] = adap; - printk("i2c-dev.o: Registered '%s' as minor %d\n",adap->name,i); - } else { - i2cdev_adaps[i] = NULL; + int i; + + if ((i = i2c_adapter_id(adap)) < 0) { + printk("i2c-dev.o: Unknown adapter ?!?\n"); + return -ENODEV; + } + if (i >= I2CDEV_ADAPS_MAX) { + printk("i2c-dev.o: Adapter number too large?!? (%d)\n",i); + return -ENODEV; + } + + if (! i2cdev_adaps[i]) { + i2cdev_adaps[i] = adap; + printk("i2c-dev.o: Registered '%s' as minor %d\n",adap->name,i); + } else { + i2cdev_adaps[i] = NULL; #ifdef DEBUG - printk("i2c-dev.o: Adapter unregistered: %s\n",adap->name); + printk("i2c-dev.o: Adapter unregistered: %s\n",adap->name); #endif - } + } - return 0; + return 0; } int i2cdev_detach_client(struct i2c_client *client) { - return 0; + return 0; } static int i2cdev_command(struct i2c_client *client, unsigned int cmd, void *arg) { - return -1; + return -1; } int __init i2c_dev_init(void) { - int res; - - printk("i2c-dev.o: i2c /dev entries driver module\n"); - - i2cdev_initialized = 0; - if (register_chrdev(I2C_MAJOR,"i2c",&i2cdev_fops)) { - printk("i2c-dev.o: unable to get major %d for i2c bus\n",I2C_MAJOR); - return -EIO; - } - i2cdev_initialized ++; - - if ((res = i2c_add_driver(&i2cdev_driver))) { - printk("i2c-dev.o: Driver registration failed, module not inserted.\n"); - i2cdev_cleanup(); - return res; - } - i2cdev_initialized ++; - return 0; + int res; + + printk("i2c-dev.o: i2c /dev entries driver module\n"); + + i2cdev_initialized = 0; + if (register_chrdev(I2C_MAJOR,"i2c",&i2cdev_fops)) { + printk("i2c-dev.o: unable to get major %d for i2c bus\n", + I2C_MAJOR); + return -EIO; + } + i2cdev_initialized ++; + + if ((res = i2c_add_driver(&i2cdev_driver))) { + printk("i2c-dev.o: Driver registration failed, module not inserted.\n"); + i2cdev_cleanup(); + return res; + } + i2cdev_initialized ++; + return 0; } int i2cdev_cleanup(void) { - int res; - - if (i2cdev_initialized >= 2) { - if ((res = i2c_del_driver(&i2cdev_driver))) { - printk("i2c-dev.o: Driver deregistration failed, " - "module not removed.\n"); - return res; - } - i2cdev_initialized ++; - } - - if (i2cdev_initialized >= 1) { - if ((res = unregister_chrdev(I2C_MAJOR,"i2c"))) { - printk("i2c-dev.o: unable to release major %d for i2c bus\n",I2C_MAJOR); - return res; - } - i2cdev_initialized --; - } - return 0; + int res; + + if (i2cdev_initialized >= 2) { + if ((res = i2c_del_driver(&i2cdev_driver))) { + printk("i2c-dev.o: Driver deregistration failed, " + "module not removed.\n"); + return res; + } + i2cdev_initialized ++; + } + + if (i2cdev_initialized >= 1) { + if ((res = unregister_chrdev(I2C_MAJOR,"i2c"))) { + printk("i2c-dev.o: unable to release major %d for i2c bus\n", + I2C_MAJOR); + return res; + } + i2cdev_initialized --; + } + return 0; } EXPORT_NO_SYMBOLS; @@ -545,12 +428,12 @@ MODULE_DESCRIPTION("I2C /dev entries driver"); int init_module(void) { - return i2c_dev_init(); + return i2c_dev_init(); } int cleanup_module(void) { - return i2cdev_cleanup(); + return i2cdev_cleanup(); } #endif /* def MODULE */ diff --git a/drivers/i2c/i2c-elektor.c b/drivers/i2c/i2c-elektor.c index fb965df0f..ad7b9d41c 100644 --- a/drivers/i2c/i2c-elektor.c +++ b/drivers/i2c/i2c-elektor.c @@ -22,7 +22,7 @@ /* With some changes from Kyösti Mälkki <kmalkki@cc.hut.fi> and even Frodo Looijaard <frodol@dds.nl> */ -/* $Id: i2c-elektor.c,v 1.13 1999/12/21 23:45:58 frodo Exp $ */ +/* $Id: i2c-elektor.c,v 1.16 2000/01/24 02:06:33 mds Exp $ */ #include <linux/kernel.h> #include <linux/ioport.h> @@ -30,24 +30,10 @@ #include <linux/delay.h> #include <linux/malloc.h> #include <linux/version.h> -#if LINUX_VERSION_CODE >= 0x020135 #include <linux/init.h> -#else -#define __init -#endif #include <asm/irq.h> #include <asm/io.h> -/* 2.0.0 kernel compatibility */ -#if LINUX_VERSION_CODE < 0x020100 -#define MODULE_AUTHOR(noone) -#define MODULE_DESCRIPTION(none) -#define MODULE_PARM(no,param) -#define MODULE_PARM_DESC(no,description) -#define EXPORT_SYMBOL(noexport) -#define EXPORT_NO_SYMBOLS -#endif - #include <linux/i2c.h> #include <linux/i2c-algo-pcf.h> #include <linux/i2c-elektor.h> @@ -87,56 +73,48 @@ static int pcf_pending; static void pcf_isa_setbyte(void *data, int ctl, int val) { - if (ctl) { - if (gpi.pi_irq > 0) { - DEB3(printk("i2c-elektor.o: Write control 0x%x\n", val|I2C_PCF_ENI)); - outb(val | I2C_PCF_ENI, CTRL); - } else { - DEB3(printk("i2c-elektor.o: Write control 0x%x\n", val)); - outb(val, CTRL); - } - } else { - DEB3(printk("i2c-elektor.o: Write data 0x%x\n", val)); - outb(val, DATA); - } + if (ctl) { + if (gpi.pi_irq > 0) { + DEB3(printk("i2c-elektor.o: Write control 0x%x\n", + val|I2C_PCF_ENI)); + outb(val | I2C_PCF_ENI, CTRL); + } else { + DEB3(printk("i2c-elektor.o: Write control 0x%x\n", val)); + outb(val, CTRL); + } + } else { + DEB3(printk("i2c-elektor.o: Write data 0x%x\n", val)); + outb(val, DATA); + } } static int pcf_isa_getbyte(void *data, int ctl) { - int val; - - if (ctl) { - val = inb(CTRL); - DEB3(printk("i2c-elektor.o: Read control 0x%x\n", val)); - } else { - val = inb(DATA); - DEB3(printk("i2c-elektor.o: Read data 0x%x\n", val)); - } - return (val); + int val; + + if (ctl) { + val = inb(CTRL); + DEB3(printk("i2c-elektor.o: Read control 0x%x\n", val)); + } else { + val = inb(DATA); + DEB3(printk("i2c-elektor.o: Read data 0x%x\n", val)); + } + return (val); } static int pcf_isa_getown(void *data) { - return (gpi.pi_own); + return (gpi.pi_own); } static int pcf_isa_getclock(void *data) { - return (gpi.pi_clock); + return (gpi.pi_clock); } -#if LINUX_VERSION_CODE < 0x02017f -static void schedule_timeout(int j) -{ - current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + j; - schedule(); -} -#endif - #if 0 static void pcf_isa_sleep(unsigned long timeout) { @@ -147,64 +125,53 @@ static void pcf_isa_sleep(unsigned long timeout) static void pcf_isa_waitforpin(void) { - int timeout = 2; - - if (gpi.pi_irq > 0) { - cli(); - if (pcf_pending == 0) { -#if LINUX_VERSION_CODE < 0x02017f - current->timeout = jiffies + timeout * HZ; - interruptible_sleep_on(&pcf_wait); -#else - interruptible_sleep_on_timeout(&pcf_wait, timeout*HZ ); -#endif - } - else - pcf_pending = 0; - sti(); -#if LINUX_VERSION_CODE < 0x02017f - current->timeout = 0; -#endif - } - else { - udelay(100); - } + int timeout = 2; + + if (gpi.pi_irq > 0) { + cli(); + if (pcf_pending == 0) { + interruptible_sleep_on_timeout(&pcf_wait, timeout*HZ ); + } else + pcf_pending = 0; + sti(); + } else { + udelay(100); + } } static void pcf_isa_handler(int this_irq, void *dev_id, struct pt_regs *regs) { - - pcf_pending = 1; - wake_up_interruptible(&pcf_wait); + pcf_pending = 1; + wake_up_interruptible(&pcf_wait); } static int pcf_isa_init(void) { - if (check_region(gpi.pi_base, 2) < 0 ) { - return -ENODEV; - } else { - request_region(gpi.pi_base, 2, "i2c (isa bus adapter)"); - } - if (gpi.pi_irq > 0) { - if (request_irq(gpi.pi_irq, pcf_isa_handler, 0, "PCF8584", 0) < 0) { - printk("i2c-elektor.o: Request irq%d failed\n", gpi.pi_irq); - gpi.pi_irq = 0; - } - else - enable_irq(gpi.pi_irq); - } - return 0; + if (check_region(gpi.pi_base, 2) < 0 ) { + return -ENODEV; + } else { + request_region(gpi.pi_base, 2, "i2c (isa bus adapter)"); + } + if (gpi.pi_irq > 0) { + if (request_irq(gpi.pi_irq, pcf_isa_handler, 0, "PCF8584", 0) + < 0) { + printk("i2c-elektor.o: Request irq%d failed\n", gpi.pi_irq); + gpi.pi_irq = 0; + } else + enable_irq(gpi.pi_irq); + } + return 0; } static void pcf_isa_exit(void) { - if (gpi.pi_irq > 0) { - disable_irq(gpi.pi_irq); - free_irq(gpi.pi_irq, 0); - } - release_region(gpi.pi_base , 2); + if (gpi.pi_irq > 0) { + disable_irq(gpi.pi_irq); + free_irq(gpi.pi_irq, 0); + } + release_region(gpi.pi_base , 2); } @@ -222,14 +189,14 @@ static int pcf_isa_unreg(struct i2c_client *client) static void pcf_isa_inc_use(struct i2c_adapter *adap) { #ifdef MODULE - MOD_INC_USE_COUNT; + MOD_INC_USE_COUNT; #endif } static void pcf_isa_dec_use(struct i2c_adapter *adap) { #ifdef MODULE - MOD_DEC_USE_COUNT; + MOD_DEC_USE_COUNT; #endif } @@ -262,41 +229,41 @@ static struct i2c_adapter pcf_isa_ops = { int __init i2c_pcfisa_init(void) { - struct i2c_pcf_isa *pisa = &gpi; + struct i2c_pcf_isa *pisa = &gpi; - printk("i2c-elektor.o: i2c pcf8584-isa adapter module\n"); - if (base == 0) - pisa->pi_base = DEFAULT_BASE; - else - pisa->pi_base = base; + printk("i2c-elektor.o: i2c pcf8584-isa adapter module\n"); + if (base == 0) + pisa->pi_base = DEFAULT_BASE; + else + pisa->pi_base = base; - if (irq == 0) - pisa->pi_irq = DEFAULT_IRQ; - else - pisa->pi_irq = irq; + if (irq == 0) + pisa->pi_irq = DEFAULT_IRQ; + else + pisa->pi_irq = irq; - if (clock == 0) - pisa->pi_clock = DEFAULT_CLOCK; - else - pisa->pi_clock = clock; + if (clock == 0) + pisa->pi_clock = DEFAULT_CLOCK; + else + pisa->pi_clock = clock; - if (own == 0) - pisa->pi_own = DEFAULT_OWN; - else - pisa->pi_own = own; + if (own == 0) + pisa->pi_own = DEFAULT_OWN; + else + pisa->pi_own = own; - pcf_isa_data.data = (void *)pisa; + pcf_isa_data.data = (void *)pisa; #if (LINUX_VERSION_CODE >= 0x020301) - init_waitqueue_head(&pcf_wait); + init_waitqueue_head(&pcf_wait); #endif - if (pcf_isa_init() == 0) { - if (i2c_pcf_add_bus(&pcf_isa_ops) < 0) - return -ENODEV; - } else { - return -ENODEV; - } - printk("i2c-elektor.o: found device at %#x.\n", pisa->pi_base); - return 0; + if (pcf_isa_init() == 0) { + if (i2c_pcf_add_bus(&pcf_isa_ops) < 0) + return -ENODEV; + } else { + return -ENODEV; + } + printk("i2c-elektor.o: found device at %#x.\n", pisa->pi_base); + return 0; } @@ -313,7 +280,7 @@ MODULE_PARM(own, "i"); int init_module(void) { - return i2c_pcfisa_init(); + return i2c_pcfisa_init(); } void cleanup_module(void) diff --git a/drivers/i2c/i2c-pcf8584.h b/drivers/i2c/i2c-pcf8584.h index 7dabbd9a9..9f0874012 100644 --- a/drivers/i2c/i2c-pcf8584.h +++ b/drivers/i2c/i2c-pcf8584.h @@ -21,20 +21,20 @@ /* With some changes from Frodo Looijaard <frodol@dds.nl> */ -/* $Id: i2c-pcf8584.h,v 1.2 1999/12/21 23:45:58 frodo Exp $ */ +/* $Id: i2c-pcf8584.h,v 1.3 2000/01/18 23:54:07 frodo Exp $ */ #ifndef I2C_PCF8584_H #define I2C_PCF8584_H 1 /* ----- Control register bits ---------------------------------------- */ -#define I2C_PCF_PIN 0x80 -#define I2C_PCF_ESO 0x40 -#define I2C_PCF_ES1 0x20 -#define I2C_PCF_ES2 0x10 -#define I2C_PCF_ENI 0x08 -#define I2C_PCF_STA 0x04 -#define I2C_PCF_STO 0x02 -#define I2C_PCF_ACK 0x01 +#define I2C_PCF_PIN 0x80 +#define I2C_PCF_ESO 0x40 +#define I2C_PCF_ES1 0x20 +#define I2C_PCF_ES2 0x10 +#define I2C_PCF_ENI 0x08 +#define I2C_PCF_STA 0x04 +#define I2C_PCF_STO 0x02 +#define I2C_PCF_ACK 0x01 #define I2C_PCF_START (I2C_PCF_PIN | I2C_PCF_ESO | I2C_PCF_STA | I2C_PCF_ACK) #define I2C_PCF_STOP (I2C_PCF_PIN | I2C_PCF_ESO | I2C_PCF_STO | I2C_PCF_ACK) @@ -45,7 +45,7 @@ /*#define I2C_PCF_PIN 0x80 as above*/ #define I2C_PCF_INI 0x40 /* 1 if not initialized */ -#define I2C_PCF_STS 0x20 +#define I2C_PCF_STS 0x20 #define I2C_PCF_BER 0x10 #define I2C_PCF_AD0 0x08 #define I2C_PCF_LRB 0x08 @@ -54,17 +54,17 @@ #define I2C_PCF_BB 0x01 /* ----- Chip clock frequencies --------------------------------------- */ -#define I2C_PCF_CLK3 0x00 -#define I2C_PCF_CLK443 0x10 -#define I2C_PCF_CLK6 0x14 -#define I2C_PCF_CLK8 0x18 -#define I2C_PCF_CLK12 0x1c +#define I2C_PCF_CLK3 0x00 +#define I2C_PCF_CLK443 0x10 +#define I2C_PCF_CLK6 0x14 +#define I2C_PCF_CLK 0x18 +#define I2C_PCF_CLK12 0x1c /* ----- transmission frequencies ------------------------------------- */ -#define I2C_PCF_TRNS90 0x00 /* 90 kHz */ -#define I2C_PCF_TRNS45 0x01 /* 45 kHz */ -#define I2C_PCF_TRNS11 0x02 /* 11 kHz */ -#define I2C_PCF_TRNS15 0x03 /* 1.5 kHz */ +#define I2C_PCF_TRNS90 0x00 /* 90 kHz */ +#define I2C_PCF_TRNS45 0x01 /* 45 kHz */ +#define I2C_PCF_TRNS11 0x02 /* 11 kHz */ +#define I2C_PCF_TRNS15 0x03 /* 1.5 kHz */ /* ----- Access to internal registers according to ES1,ES2 ------------ */ diff --git a/drivers/ieee1394/ieee1394_syms.c b/drivers/ieee1394/ieee1394_syms.c index e4d92fa0f..ee4bde182 100644 --- a/drivers/ieee1394/ieee1394_syms.c +++ b/drivers/ieee1394/ieee1394_syms.c @@ -39,6 +39,7 @@ EXPORT_SYMBOL(hpsb_make_readqpacket); EXPORT_SYMBOL(hpsb_make_readbpacket); EXPORT_SYMBOL(hpsb_make_writeqpacket); EXPORT_SYMBOL(hpsb_make_writebpacket); +EXPORT_SYMBOL(hpsb_make_lockpacket); EXPORT_SYMBOL(hpsb_read); EXPORT_SYMBOL(hpsb_write); EXPORT_SYMBOL(hpsb_lock); diff --git a/drivers/ieee1394/ieee1394_transactions.c b/drivers/ieee1394/ieee1394_transactions.c index c50bce62a..e2eeffc68 100644 --- a/drivers/ieee1394/ieee1394_transactions.c +++ b/drivers/ieee1394/ieee1394_transactions.c @@ -329,6 +329,30 @@ struct hpsb_packet *hpsb_make_writebpacket(struct hpsb_host *host, return p; } +struct hpsb_packet *hpsb_make_lockpacket(struct hpsb_host *host, nodeid_t node, + u64 addr, int extcode) +{ + struct hpsb_packet *p; + + p = alloc_hpsb_packet(8); + if (!p) return NULL; + + p->host = host; + p->tlabel = get_tlabel(host, node, 1); + p->node_id = node; + + switch (extcode) { + case EXTCODE_FETCH_ADD: + case EXTCODE_LITTLE_ADD: + fill_async_lock(p, addr, extcode, 4); + break; + default: + fill_async_lock(p, addr, extcode, 8); + break; + } + + return p; +} /* * FIXME - these functions should probably read from / write to user space to diff --git a/drivers/ieee1394/ieee1394_transactions.h b/drivers/ieee1394/ieee1394_transactions.h index 136d4f7d9..ebec602a9 100644 --- a/drivers/ieee1394/ieee1394_transactions.h +++ b/drivers/ieee1394/ieee1394_transactions.h @@ -39,6 +39,9 @@ struct hpsb_packet *hpsb_make_writeqpacket(struct hpsb_host *host, struct hpsb_packet *hpsb_make_writebpacket(struct hpsb_host *host, nodeid_t node, u64 addr, size_t length); +struct hpsb_packet *hpsb_make_lockpacket(struct hpsb_host *host, nodeid_t node, + u64 addr, int extcode); + /* * hpsb_packet_success - Make sense of the ack and reply codes and diff --git a/drivers/ieee1394/ohci1394.c b/drivers/ieee1394/ohci1394.c index f39a8de86..4da40f6c4 100644 --- a/drivers/ieee1394/ohci1394.c +++ b/drivers/ieee1394/ohci1394.c @@ -1,5 +1,5 @@ /* - * ti_ohci1394.c - Texas Instruments Ohci1394 driver + * ohci1394.c - driver for OHCI 1394 boards * Copyright (C)1999,2000 Sebastien Rougeaux <sebastien.rougeaux@anu.edu.au> * Gord Peters <GordPeters@smarttech.com> * @@ -18,6 +18,7 @@ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +#include <linux/config.h> #include <linux/kernel.h> #include <linux/slab.h> #include <linux/interrupt.h> @@ -40,8 +41,6 @@ #include "ieee1394_core.h" #include "ohci1394.h" -#undef CONFIG_PROC_FS - /* print general (card independent) information */ #define PRINT_G(level, fmt, args...) \ printk(level "ohci1394: " fmt "\n" , ## args) @@ -68,7 +67,6 @@ static int init_driver(void); * IEEE-1394 functionality section * ***********************************/ - #if 0 /* not needed at this time */ static int get_phy_reg(struct ti_ohci *ohci, int addr) { @@ -324,31 +322,53 @@ static int ohci_initialize(struct hpsb_host *host) } /* Initialize AR dma */ - ohci->AR_resp_prg->control=0x283C << 16 | AR_RESP_BUF_SIZE; - ohci->AR_resp_prg->address=virt_to_bus(ohci->AR_resp_buf); - ohci->AR_resp_prg->status=AR_RESP_BUF_SIZE; - PRINT(KERN_INFO, ohci->id, "AR control: %x", - ohci->AR_resp_prg->control); - PRINT(KERN_INFO, ohci->id, "AR status: %x %d", - ohci->AR_resp_prg->status & 0xffff, - ohci->AR_resp_prg->status & 0xffff); - - /* Tell the controller where the AR program is */ - reg_write(ohci, OHCI1394_AsRspRcvCommandPtr, - virt_to_bus(ohci->AR_resp_prg)|0x00000001); + /* make sure the context isn't running, dead, or active */ + if (!(reg_read(ohci, OHCI1394_AsRspRcvContextControlSet) & 0x00008F00)) { -#if 1 - /* Accept phy packets into AR request context */ - reg_write(ohci, OHCI1394_LinkControlSet, 0x00000400); -#endif + /* initialize AR program */ + for (i= 0; i < AR_RESP_NUM_DESC; i++) { - /* Run AR context */ - reg_write(ohci, OHCI1394_AsRspRcvContextControlSet, 0x00008000); + /* end of descriptor list? */ + if ((i + 1) < AR_RESP_NUM_DESC) { + ohci->AR_resp_prg[i]->control= + (0x283C << 16) | AR_RESP_BUF_SIZE; + ohci->AR_resp_prg[i]->branchAddress= + (virt_to_bus(ohci->AR_resp_prg[i + 1]) + & 0xfffffff0) | 0x1; + } else { + ohci->AR_resp_prg[i]->control= + (0x283C << 16) | AR_RESP_BUF_SIZE; + ohci->AR_resp_prg[i]->branchAddress= + (virt_to_bus(ohci->AR_resp_prg[0]) + & 0xfffffff0) | 0x1; + } + + ohci->AR_resp_prg[i]->address= + virt_to_bus(ohci->AR_resp_buf[i]); + ohci->AR_resp_prg[i]->status= AR_RESP_BUF_SIZE; + } + + /* Tell the controller where the first AR program is */ + reg_write(ohci, OHCI1394_AsRspRcvCommandPtr, + virt_to_bus(ohci->AR_resp_prg[0]) | 0x1 ); + + /* Accept phy packets into AR request context */ + reg_write(ohci, OHCI1394_LinkControlSet, 0x00000400); + + /* Run AR context */ + reg_write(ohci, OHCI1394_AsRspRcvContextControlSet, 0x00008000); + } + + /* Specify AT retries */ + reg_write(ohci, OHCI1394_ATRetries, + OHCI1394_MAX_AT_REQ_RETRIES | + (OHCI1394_MAX_AT_RESP_RETRIES<<4) | + (OHCI1394_MAX_PHYS_RESP_RETRIES<<8)); #ifndef __BIG_ENDIAN - reg_write(ohci, OHCI1394_HCControlSet, 0x40000000); -#else reg_write(ohci, OHCI1394_HCControlClear, 0x40000000); +#else + reg_write(ohci, OHCI1394_HCControlSet, 0x40000000); #endif /* Enable interrupts */ @@ -386,8 +406,9 @@ static void send_next_async(struct ti_ohci *ohci) int i=0; struct hpsb_packet *packet = ohci->async_queue; struct dma_cmd prg; +#if 0 quadlet_t *ptr = (quadlet_t *)ohci->AT_req_prg; - +#endif //HPSB_TRACE(); /* stop the channel program if it's still running */ @@ -435,14 +456,14 @@ static void send_next_async(struct ti_ohci *ohci) prg.status = 0; memcpy(ohci->AT_req_prg, &prg, 16); memcpy(ohci->AT_req_prg + 1, packet->header, 16); - +#if 0 PRINT(KERN_INFO, ohci->id, "dma_cmd: %08x %08x %08x %08x", *ptr, *(ptr+1), *(ptr+2), *(ptr+3)); PRINT(KERN_INFO, ohci->id, "header: %08x %08x %08x %08x", *(ptr+4), *(ptr+5), *(ptr+6), *(ptr+7)); - +#endif reg_write(ohci, OHCI1394_AsReqTrCommandPtr, virt_to_bus(ohci->AT_req_prg)|0x2); } @@ -455,11 +476,11 @@ static void send_next_async(struct ti_ohci *ohci) prg.branchAddress = 0; prg.status = 0; memcpy(ohci->AT_req_prg, &prg, 16); - +#if 0 PRINT(KERN_INFO, ohci->id, "dma_cmd: %08x %08x %08x %08x", *ptr, *(ptr+1), *(ptr+2), *(ptr+3)); - +#endif reg_write(ohci, OHCI1394_AsReqTrCommandPtr, virt_to_bus(ohci->AT_req_prg)|0x2); } @@ -638,6 +659,26 @@ static int ohci_devctl(struct hpsb_host *host, enum devctl_cmd cmd, int arg) * Global stuff (interrupt handler, init/shutdown code) * ********************************************************/ +static void stop_ar_resp_context(struct ti_ohci *ohci, char *msg) +{ + int i=0; + + /* stop the channel program if it's still running */ + reg_write(ohci, OHCI1394_AsRspRcvContextControlClear, 0x8000); + + /* Wait until it effectively stops */ + while (reg_read(ohci, OHCI1394_AsRspRcvContextControlSet) + & 0x400) { + i++; + if (i>5000) { + PRINT(KERN_ERR, ohci->id, + "runaway loop in Dma Ar Resp. bailing out..."); + break; + } + } + PRINT(KERN_ERR, ohci->id, "%s\n async response receive dma stopped\n", msg); +} + static void ohci_irq_handler(int irq, void *dev_id, struct pt_regs *regs_are_unused) { @@ -656,15 +697,14 @@ static void ohci_irq_handler(int irq, void *dev_id, event,reg_read(ohci, OHCI1394_IntMaskSet)); */ if (event & OHCI1394_busReset) { +#if 0 PRINT(KERN_INFO, ohci->id, "bus reset interrupt"); +#endif if (!host->in_bus_reset) { hpsb_bus_reset(host); } ohci->NumBusResets++; } - if (event & OHCI1394_reqTxComplete) { - PRINT(KERN_INFO, ohci->id, "reqTxComplete int received"); - } if (event & OHCI1394_RQPkt) { PRINT(KERN_INFO, ohci->id, "RQPkt int received"); } @@ -673,58 +713,55 @@ static void ohci_irq_handler(int irq, void *dev_id, reg_read(ohci, OHCI1394_AsReqRcvContextControlSet)); } if (event & OHCI1394_RSPkt) { - int rcv_bytes; - int i=0; - - /* we calculate the number of received bytes from the - residual count field */ - rcv_bytes = AR_RESP_BUF_SIZE - - (ohci->AR_resp_prg->status & 0xFFFF); - - PRINT(KERN_INFO, ohci->id, "AR_status 0x%x %d, %d bytes read", - ohci->AR_resp_prg->status, - ohci->AR_resp_prg->status & 0xffff, - rcv_bytes); - - ohci->AR_resp_active = 0; - - if ((ohci->AR_resp_prg->status & 0x84000000) - && (ohci->AR_resp_prg->status & 0xFFFF) >= 8 ) { - hpsb_packet_received(host, ohci->AR_resp_buf, - rcv_bytes); - } else { - //HPSB_TRACE(); - PRINT(KERN_ERR, ohci->id, - "AR resp DMA program status value 0x%x is incorrect!", - ohci->AR_resp_prg->status); - } - - - /* --------------- FIXME --------------------------------- - this is a complete hack... we stop the dma prg - and start it again so as to reset the dma buffer address - Very slow, very bad design... to change ASAP */ - - /* stop the channel program if it's still running */ - reg_write(ohci, OHCI1394_AsRspRcvContextControlClear, 0x8000); - - /* Wait until it effectively stops */ - while (reg_read(ohci, OHCI1394_AsRspRcvContextControlSet) - & 0x400) { - i++; - if (i>5000) { - PRINT(KERN_ERR, ohci->id, - "runaway loop in DmaAT. bailing out..."); - break; + unsigned int idx,offset,rescount; + + spin_lock(&ohci->AR_resp_lock); + + idx = ohci->AR_resp_buf_th_ind; + offset = ohci->AR_resp_buf_th_offset; + + rescount = ohci->AR_resp_prg[idx]->status&0xffff; + ohci->AR_resp_bytes_left += AR_RESP_BUF_SIZE - rescount - offset; + offset = AR_RESP_BUF_SIZE - rescount; + + if (!rescount) { /* We cross a buffer boundary */ + idx = (idx+1) % AR_RESP_NUM_DESC; + +#if 0 + /* This bit of code does not work */ + /* Let's see how many bytes were written in the async response + receive buf since last interrupt. This is done by finding + the next active context (See OHCI Spec p91) */ + while (ohci->AR_resp_bytes_left <= AR_RESP_TOTAL_BUF_SIZE) { + if (ohci->AR_resp_prg[idx]->status&0x04000000) break; + idx = (idx+1) % AR_RESP_NUM_DESC; + PRINT(KERN_INFO,ohci->id,"crossing more than one buffer boundary !!!"); + ohci->AR_resp_bytes_left += AR_RESP_BUF_SIZE; } +#endif + /* ASSUMPTION: only one buffer boundary is crossed */ + rescount = ohci->AR_resp_prg[idx]->status&0xffff; + offset = AR_RESP_BUF_SIZE - rescount; + ohci->AR_resp_bytes_left += offset; } + if (offset==AR_RESP_BUF_SIZE) { + offset=0; + idx = (idx+1) % AR_RESP_NUM_DESC; + } + ohci->AR_resp_buf_th_ind = idx; + ohci->AR_resp_buf_th_offset = offset; - reg_write(ohci, OHCI1394_AsRspRcvCommandPtr, - virt_to_bus(ohci->AR_resp_prg)|0x00000001); - ohci->AR_resp_prg->status=AR_RESP_BUF_SIZE; - reg_write(ohci, OHCI1394_AsRspRcvContextControlSet, 0x8000); + /* is buffer processing too slow? (all buffers used) */ + if (ohci->AR_resp_bytes_left > AR_RESP_TOTAL_BUF_SIZE) { + stop_ar_resp_context(ohci,"async response receive processing too slow"); + spin_unlock(&ohci->AR_resp_lock); + return; + } + spin_unlock(&ohci->AR_resp_lock); - /* ---------------- end of FIXME --------------------------*/ + /* queue bottom half in immediate queue */ + queue_task(&ohci->AR_resp_pdl_task, &tq_immediate); + mark_bh(IMMEDIATE_BH); } if (event & OHCI1394_isochRx) { quadlet_t isoRecvIntEvent; @@ -826,8 +863,10 @@ static void ohci_irq_handler(int irq, void *dev_id, send_next_async(ohci); } spin_unlock(&ohci->async_queue_lock); +#if 0 PRINT(KERN_INFO,ohci->id, "packet sent with ack code %d",ack); +#endif hpsb_packet_sent(host, packet, ack); } else PRINT(KERN_INFO,ohci->id, @@ -839,6 +878,129 @@ static void ohci_irq_handler(int irq, void *dev_id, ohci->NumInterrupts++; } + +/* This is the bottom half that processes async response receive descriptor buffers. */ +static void ohci_ar_resp_proc_desc(void *data) +{ + quadlet_t *buf_ptr; + char *split_ptr; + unsigned int split_left; + struct ti_ohci *ohci= (struct ti_ohci*)data; + unsigned int packet_length; + unsigned int idx,offset,tcode; + unsigned long flags; + char msg[256]; + + spin_lock_irqsave(&ohci->AR_resp_lock, flags); + + idx = ohci->AR_resp_buf_bh_ind; + offset = ohci->AR_resp_buf_bh_offset; + + buf_ptr = ohci->AR_resp_buf[idx]; + buf_ptr += offset/4; + + while(ohci->AR_resp_bytes_left > 0) { + + /* check to see if a fatal error occurred */ + if ((ohci->AR_resp_prg[idx]->status >> 16) & 0x800) { + sprintf(msg,"fatal async response receive error -- status is %d", + ohci->AR_resp_prg[idx]->status & 0x1F); + stop_ar_resp_context(ohci, msg); + spin_unlock_irqrestore(&ohci->AR_resp_lock, flags); + return; + } + + spin_unlock_irqrestore(&ohci->AR_resp_lock, flags); + + /* Let's see what kind of packet is in there */ + tcode = (buf_ptr[0]>>4)&0xf; + if (tcode==2) /* no-data receive */ + packet_length=16; + else if (tcode==6) /* quadlet receive */ + packet_length=20; + else if (tcode==7) { /* block receive */ + /* Where is the data length ? */ + if (offset+12>=AR_RESP_BUF_SIZE) + packet_length=(ohci->AR_resp_buf[(idx+1)%AR_RESP_NUM_DESC] + [3-(AR_RESP_BUF_SIZE-offset)/4]>>16)+20; + else + packet_length=(buf_ptr[3]>>16)+20; + if (packet_length % 4) + packet_length += 4 - (packet_length % 4); + } + else /* something is wrong */ { + sprintf(msg,"unexpected packet tcode %d in async response receive buffer",tcode); + stop_ar_resp_context(ohci,msg); + return; + } + if ((offset+packet_length)>AR_RESP_BUF_SIZE) { + /* we have a split packet */ + if (packet_length>AR_RESP_SPLIT_PACKET_BUF_SIZE) { + sprintf(msg,"packet size %d bytes exceed split packet buffer size %d bytes", + packet_length,AR_RESP_SPLIT_PACKET_BUF_SIZE); + stop_ar_resp_context(ohci, msg); + return; + } + split_left = packet_length; + split_ptr = (char *)ohci->AR_resp_spb; + while (split_left>0) { + memcpy(split_ptr,buf_ptr,AR_RESP_BUF_SIZE-offset); + split_left -= AR_RESP_BUF_SIZE-offset; + split_ptr += AR_RESP_BUF_SIZE-offset; + ohci->AR_resp_prg[idx]->status = AR_RESP_BUF_SIZE; + idx = (idx+1) % AR_RESP_NUM_DESC; + buf_ptr = ohci->AR_resp_buf[idx]; + offset=0; + while (split_left >= AR_RESP_BUF_SIZE) { + memcpy(split_ptr,buf_ptr,AR_RESP_BUF_SIZE); + split_ptr += AR_RESP_BUF_SIZE; + split_left -= AR_RESP_BUF_SIZE; + ohci->AR_resp_prg[idx]->status = AR_RESP_BUF_SIZE; + idx = (idx+1) % AR_RESP_NUM_DESC; + buf_ptr = ohci->AR_resp_buf[idx]; + } + if (split_left>0) { + memcpy(split_ptr,buf_ptr,split_left); + offset = split_left; + split_left=0; + buf_ptr += split_left/4; + } + } +#if 0 + PRINT(KERN_INFO,ohci->id,"AR resp: received split packet tcode=%d length=%d", + tcode,packet_length); +#endif + hpsb_packet_received(ohci->host, ohci->AR_resp_spb, packet_length); + ohci->AR_resp_bytes_left -= packet_length; + } + else { +#if 0 + PRINT(KERN_INFO,ohci->id,"AR resp: received packet tcode=%d length=%d", + tcode,packet_length); +#endif + hpsb_packet_received(ohci->host, buf_ptr, packet_length); + offset += packet_length; + buf_ptr += packet_length/4; + ohci->AR_resp_bytes_left -= packet_length; + if (offset==AR_RESP_BUF_SIZE) { + ohci->AR_resp_prg[idx]->status = AR_RESP_BUF_SIZE; + idx = (idx+1) % AR_RESP_NUM_DESC; + buf_ptr = ohci->AR_resp_buf[idx]; + offset=0; + } + } + + } + + if (ohci->AR_resp_bytes_left<0) + stop_ar_resp_context(ohci, "Sync problem in AR resp dma buffer"); + + ohci->AR_resp_buf_bh_ind = idx; + ohci->AR_resp_buf_bh_offset = offset; + + spin_unlock_irqrestore(&ohci->AR_resp_lock, flags); +} + /* This is the bottom half that processes iso receive descriptor buffers. */ static void ohci_ir_proc_desc(void *data) { @@ -1023,23 +1185,61 @@ static int add_card(struct pci_dev *dev) FAIL("failed to allocate DMA buffer for self-id packets"); } - /* AR dma buffer allocation */ - ohci->AR_resp_buf = kmalloc(AR_RESP_BUF_SIZE, GFP_KERNEL); - if (ohci->AR_resp_buf != NULL) { - memset(ohci->AR_resp_buf, 0, AR_RESP_BUF_SIZE); - } else { - FAIL("failed to allocate AR response DMA buffer"); + /* AR dma buffer and program allocation */ + ohci->AR_resp_buf= + kmalloc(AR_RESP_NUM_DESC * sizeof(quadlet_t*), + GFP_KERNEL); + + if (ohci->AR_resp_buf == NULL) { + FAIL("failed to allocate AR response receive DMA buffer"); } - /* AR dma program allocation */ - ohci->AR_resp_prg = (struct dma_cmd *) kmalloc(AR_RESP_PRG_SIZE, - GFP_KERNEL); - if (ohci->AR_resp_prg != NULL) { - memset(ohci->AR_resp_prg, 0, AR_RESP_PRG_SIZE); - } else { - FAIL("failed to allocate AR response DMA program"); + ohci->AR_resp_prg= + kmalloc(AR_RESP_NUM_DESC * sizeof(struct dma_cmd*), + GFP_KERNEL); + + if (ohci->AR_resp_prg == NULL) { + FAIL("failed to allocate AR response receive DMA program"); + } + + ohci->AR_resp_spb= kmalloc(AR_RESP_SPLIT_PACKET_BUF_SIZE, GFP_KERNEL); + + if (ohci->AR_resp_spb == NULL) { + FAIL("failed to allocate AR response split packet buffer"); + } + + for (i= 0; i < AR_RESP_NUM_DESC; i++) { + ohci->AR_resp_buf[i]= kmalloc(AR_RESP_BUF_SIZE, GFP_KERNEL); + + if (ohci->AR_resp_buf[i] != NULL) { + memset(ohci->AR_resp_buf[i], 0, AR_RESP_BUF_SIZE); + } else { + FAIL("failed to allocate AR response DMA buffer"); + } + + ohci->AR_resp_prg[i]= kmalloc(sizeof(struct dma_cmd), + GFP_KERNEL); + + if (ohci->AR_resp_prg[i] != NULL) { + memset(ohci->AR_resp_prg[i], 0, + sizeof(struct dma_cmd)); + } else { + FAIL("failed to allocate AR response DMA buffer"); + } + } + ohci->AR_resp_buf_th_ind = 0; + ohci->AR_resp_buf_th_offset = 0; + ohci->AR_resp_buf_bh_ind = 0; + ohci->AR_resp_buf_bh_offset = 0; + ohci->AR_resp_bytes_left = 0; + spin_lock_init(&ohci->AR_resp_lock); + + /* initialize AR response receive task */ + ohci->AR_resp_pdl_task.routine= ohci_ar_resp_proc_desc; + ohci->AR_resp_pdl_task.data= (void*)ohci; + /* AT dma program allocation */ ohci->AT_req_prg = (struct dma_cmd *) kmalloc(AT_REQ_PRG_SIZE, GFP_KERNEL); @@ -1129,8 +1329,12 @@ static int add_card(struct pci_dev *dev) p += sprintf(p,fmt,reg_read(ohci, reg0),\ reg_read(ohci, reg1),reg_read(ohci, reg2)); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0) +static int ohci_get_status(char *buf) +#else int ohci_get_info(char *buf, char **start, off_t fpos, int length, int dummy) +#endif { struct ti_ohci *ohci=&cards[0]; struct hpsb_host *host=ohci->host; @@ -1165,15 +1369,23 @@ int ohci_get_info(char *buf, char **start, off_t fpos, host->is_irm ? "iso_res_mgr" : "", host->is_busmgr ? "bus_mgr" : ""); - p += sprintf(p,"\n### ohci data ###\n"); - p += sprintf(p,"AR_resp_buf : %p AR_resp_prg: %p\n", - ohci->AR_resp_buf, ohci->AR_resp_prg); - + p += sprintf(p,"\n---Iso Receive DMA---\n"); for (i= 0; i < IR_NUM_DESC; i++) { p += sprintf(p, "IR_recv_buf[%d] : %p IR_recv_prg[%d]: %p\n", i, ohci->IR_recv_buf[i], i, ohci->IR_recv_prg[i]); } + + p += sprintf(p,"\n---Async Reponse Receive DMA---\n"); + for (i= 0; i < AR_RESP_NUM_DESC; i++) { + p += sprintf(p, "AR_resp_buf[%d] : %p AR_resp_prg[%d]: %p\n", + i, ohci->AR_resp_buf[i], i, ohci->AR_resp_prg[i]); + } + p += sprintf(p, "Current AR resp buf in irq handler: %d offset: %d\n", + ohci->AR_resp_buf_th_ind,ohci->AR_resp_buf_th_offset); + p += sprintf(p, "Current AR resp buf in bottom half: %d offset: %d\n", + ohci->AR_resp_buf_bh_ind,ohci->AR_resp_buf_bh_offset); + /* ----- Register Dump ----- */ p += sprintf(p,"\n### HC Register dump ###\n"); SR("Version : %08x GUID_ROM : %08x ATRetries : %08x\n", @@ -1242,12 +1454,27 @@ int ohci_get_info(char *buf, char **start, off_t fpos, phyreg&0x3f); #endif +#if 0 p += sprintf(p,"AR_resp_prg ctrl: %08x\n",ohci->AR_resp_prg->control); p += sprintf(p,"AR_resp_prg status: %08x\n",ohci->AR_resp_prg->status); +#endif return p - buf; } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0) +static int ohci1394_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len = ohci_get_status(page); + if (len <= off+count) *eof = 1; + *start = page + off; + len -= off; + if (len>count) len = count; + if (len<0) len = 0; + return len; +} +#else struct proc_dir_entry ohci_proc_entry = { 0, /* Inode number - dynamic */ @@ -1261,18 +1488,36 @@ struct proc_dir_entry ohci_proc_entry = ohci_get_info, /* The read function for this file */ NULL }; -#endif +#endif /* LINUX_VERSION_CODE */ +#endif /* CONFIG_PROC_FS */ static void remove_card(struct ti_ohci *ohci) { if (ohci->registers) iounmap(ohci->registers); - if (ohci->AR_resp_buf) - kfree(ohci->AR_resp_buf); - if (ohci->AR_resp_prg) - kfree(ohci->AR_resp_prg); + + /* Free AR response buffers and programs */ + if (ohci->AR_resp_buf) { + int i; + for (i= 0; i < AR_RESP_NUM_DESC; i++) { + kfree(ohci->AR_resp_buf[i]); + } + kfree(ohci->AR_resp_buf); + } + if (ohci->AR_resp_prg) { + int i; + for (i= 0; i < AR_RESP_NUM_DESC; i++) { + kfree(ohci->AR_resp_prg[i]); + } + kfree(ohci->AR_resp_prg); + } + kfree(ohci->AR_resp_spb); + + /* Free AT request buffer and program */ if (ohci->AT_req_prg) kfree(ohci->AT_req_prg); + + /* Free Iso receive buffers and programs */ if (ohci->IR_recv_buf) { int i; for (i= 0; i < IR_NUM_DESC; i++) { @@ -1288,11 +1533,16 @@ static void remove_card(struct ti_ohci *ohci) kfree(ohci->IR_recv_prg); } kfree(ohci->IR_spb); + + /* Free self-id buffer */ if (ohci->self_id_buffer) kfree(ohci->self_id_buffer); + + /* Free config rom */ if (ohci->csr_config_rom) kfree(ohci->csr_config_rom); + /* Free the IRQ */ free_irq(ohci->dev->irq, ohci); ohci->state = 0; @@ -1327,11 +1577,15 @@ static int init_driver() } #ifdef CONFIG_PROC_FS +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0) + create_proc_read_entry ("ohci1394", 0, NULL, ohci1394_read_proc, NULL); +#else if (proc_register(&proc_root, &ohci_proc_entry)) { PRINT_G(KERN_ERR, "unable to register proc file\n"); return -EIO; } #endif +#endif return 0; } @@ -1384,8 +1638,12 @@ void cleanup_module(void) { hpsb_unregister_lowlevel(get_ohci_template()); #ifdef CONFIG_PROC_FS +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0) + remove_proc_entry ("ohci1394", NULL); +#else proc_unregister(&proc_root, ohci_proc_entry.low_ino); #endif +#endif PRINT_G(KERN_INFO, "removed " OHCI1394_DRIVER_NAME " module\n"); } diff --git a/drivers/ieee1394/ohci1394.h b/drivers/ieee1394/ohci1394.h index c6d7f469a..35d8fa2af 100644 --- a/drivers/ieee1394/ohci1394.h +++ b/drivers/ieee1394/ohci1394.h @@ -20,9 +20,15 @@ #define MAX_OHCI1394_CARDS 4 -#define AR_RESP_BUF_SIZE 4096 -#define AR_RESP_PRG_SIZE 256 -#define AT_REQ_PRG_SIZE 256 +#define OHCI1394_MAX_AT_REQ_RETRIES 1 +#define OHCI1394_MAX_AT_RESP_RETRIES 1 +#define OHCI1394_MAX_PHYS_RESP_RETRIES 4 + +#define AR_RESP_NUM_DESC 4 /* number of AR resp descriptors */ +#define AR_RESP_BUF_SIZE 4096 /* size of AR resp buffers */ +#define AR_RESP_SPLIT_PACKET_BUF_SIZE 256 /* split packet buffer */ +#define AR_RESP_TOTAL_BUF_SIZE (AR_RESP_BUF_SIZE * AR_RESP_NUM_DESC) +#define AT_REQ_PRG_SIZE 256 #define IR_RECV_BUF_SIZE 4096 /* 4096 bytes/buffer */ #define IR_SPLIT_PACKET_BUF_SIZE 8192 /* size of buffer for split packets */ @@ -49,8 +55,18 @@ struct ti_ohci { quadlet_t *csr_config_rom; /* buffer for csr config rom */ /* asynchronous receive */ - struct dma_cmd *AR_resp_prg; - quadlet_t *AR_resp_buf; + struct dma_cmd **AR_resp_prg; + quadlet_t **AR_resp_buf; + unsigned int AR_resp_buf_bh_ind; + unsigned int AR_resp_buf_bh_offset; + unsigned int AR_resp_buf_th_ind; + unsigned int AR_resp_buf_th_offset; + int AR_resp_bytes_left; + quadlet_t *AR_resp_spb; + spinlock_t AR_resp_lock; + + /* async receive task */ + struct tq_struct AR_resp_pdl_task; /* asynchronous transmit */ struct dma_cmd *AT_req_prg; diff --git a/drivers/ieee1394/raw1394.c b/drivers/ieee1394/raw1394.c index 596f95219..adc45896d 100644 --- a/drivers/ieee1394/raw1394.c +++ b/drivers/ieee1394/raw1394.c @@ -495,8 +495,8 @@ static int handle_local_request(struct file_info *fi, if (req->req.length == 8) { req->req.error = highlevel_lock(fi->host, req->data, - addr, req->data[0], - req->data[1], + addr, req->data[1], + req->data[0], req->req.misc); req->req.length = 4; } else { @@ -567,6 +567,32 @@ static int handle_remote_request(struct file_info *fi, break; case RAW1394_REQ_LOCK: + if ((req->req.misc != EXTCODE_FETCH_ADD) + && (req->req.misc != EXTCODE_LITTLE_ADD)) { + if (req->req.length != 4) { + req->req.error = RAW1394_ERROR_INVALID_ARG; + break; + } + } else { + if (req->req.length != 8) { + req->req.error = RAW1394_ERROR_INVALID_ARG; + break; + } + } + + packet = hpsb_make_lockpacket(fi->host, node, addr, + req->req.misc); + if (!packet) return -ENOMEM; + + if (copy_from_user(packet->data, req->req.sendb, + req->req.length)) { + req->req.error = RAW1394_ERROR_MEMFAULT; + break; + } + + req->req.length = 4; + break; + case RAW1394_REQ_LOCK64: default: req->req.error = RAW1394_ERROR_STATE_ORDER; diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 03864c4ba..232075634 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -115,7 +115,7 @@ obj-$(CONFIG_NET) += Space.o setup.o net_init.o loopback.o obj-$(CONFIG_SEEQ8005) += seeq8005.o obj-$(CONFIG_ETHERTAP) += ethertap.o obj-$(CONFIG_NET_SB1000) += sb1000.o -obj-$(CONFIG_DAYNAPORT) += daynaport.o 8390.o +obj-$(CONFIG_MAC8390) += daynaport.o 8390.o obj-$(CONFIG_APNE) += apne.o 8390.o obj-$(CONFIG_PCMCIA_PCNET) += 8390.o obj-$(CONFIG_SHAPER) += shaper.o @@ -248,6 +248,8 @@ obj-$(CONFIG_IPDDP) += ipddp.o obj-$(CONFIG_RCPCI) += rcpci.o obj-$(CONFIG_MACE) += mace.o obj-$(CONFIG_MACSONIC) += macsonic.o +obj-$(CONFIG_MACMACE) += macmace.o +obj-$(CONFIG_MAC89x0) += mac89x0.o obj-$(CONFIG_BMAC) += bmac.o obj-$(CONFIG_NCR885E) += ncr885e.o obj-$(CONFIG_ADAPTEC_STARFIRE) += starfire.o diff --git a/drivers/net/Space.c b/drivers/net/Space.c index dc18256ce..52c3a23d0 100644 --- a/drivers/net/Space.c +++ b/drivers/net/Space.c @@ -106,7 +106,13 @@ extern int dec_lance_probe(struct net_device *); extern int mvme147lance_probe(struct net_device *dev); extern int tc515_probe(struct net_device *dev); extern int lance_probe(struct net_device *dev); -extern int mac_onboard_sonic_probe(struct net_device *dev); +extern int mace68k_probe(struct net_device *dev); +extern int macsonic_probe(struct net_device *dev); +extern int mac8390_probe(struct net_device *dev); +extern int mac89x0_probe(struct net_device *dev); + + /* Gigabit Ethernet adapters */ + extern int yellowfin_probe(struct net_device *dev); /* Detachable devices ("pocket adaptors") */ extern int atp_init(struct net_device *); @@ -356,8 +362,17 @@ struct devprobe m68k_probes[] __initdata = { #ifdef CONFIG_MVME147_NET /* MVME147 internal Ethernet */ {mvme147lance_probe, 0}, #endif -#ifdef CONFIG_MACSONIC /* Mac 68k Quadra builtin Ethernet */ - {mac_onboard_sonic_probe, 0}, +#ifdef CONFIG_MACMACE /* Mac 68k Quadra AV builtin Ethernet */ + {mace68k_probe, 0}, +#endif +#ifdef CONFIG_MACSONIC /* Mac SONIC-based Ethernet of all sorts */ + {macsonic_probe, 0}, +#endif +#ifdef CONFIG_MAC8390 /* NuBus NS8390-based cards */ + {mac8390_probe, 0}, +#endif +#ifdef CONFIG_MAC89x0 + {mac89x0_probe, 0}, #endif {NULL, 0}, }; diff --git a/drivers/net/cs89x0.h b/drivers/net/cs89x0.h index 42776088f..7e78805af 100644 --- a/drivers/net/cs89x0.h +++ b/drivers/net/cs89x0.h @@ -14,6 +14,8 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include <linux/config.h> + #define PP_ChipID 0x0000 /* offset 0h -> Corp -ID */ /* offset 2h -> Model/Product Number */ /* offset 3h -> Chip Revision Number */ @@ -77,6 +79,12 @@ #define ADD_MASK 0x3000 /* Mask it use of the ADD_PORT register */ #define ADD_SIG 0x3000 /* Expected ID signature */ +/* On Macs, we only need use the ISA I/O stuff until we do MEMORY_ON */ +#ifdef CONFIG_MAC +#define LCSLOTBASE 0xfee00000 +#define MMIOBASE 0x40000 +#endif + #define CHIP_EISA_ID_SIG 0x630E /* Product ID Code for Crystal Chip (CS8900 spec 4.3) */ #ifdef IBMEIPKT diff --git a/drivers/net/daynaport.c b/drivers/net/daynaport.c index 02ebff8b6..724369bb8 100644 --- a/drivers/net/daynaport.c +++ b/drivers/net/daynaport.c @@ -1,4 +1,4 @@ -/* mac_ns8390.c: A Macintosh 8390 based ethernet driver for linux. */ +/* daynaport.c: A Macintosh 8390 based ethernet driver for linux. */ /* Derived from code: @@ -15,14 +15,20 @@ The block output routines may be wrong for non Dayna cards - Reading MAC addresses -*/ + Fix this driver so that it will attempt to use the info + (i.e. iobase, iosize) given to it by the new and improved + NuBus code. + + Despite its misleading filename, this driver is not Dayna-specific + anymore. */ +/* Cabletron E6100 card support added by Tony Mantler (eek@escape.ca) April 1999 */ static const char *version = - "mac_ns8390.c:v0.01 7/5/97 Alan Cox (Alan.Cox@linux.org)\n"; + "daynaport.c: v0.02 1999-05-17 Alan Cox (Alan.Cox@linux.org) and others\n"; +static int version_printed = 0; #include <linux/module.h> - +#include <linux/init.h> #include <linux/kernel.h> #include <linux/sched.h> #include <linux/errno.h> @@ -31,20 +37,26 @@ static const char *version = #include <asm/io.h> #include <asm/system.h> #include <asm/hwtest.h> +#include <asm/macints.h> #include <linux/delay.h> #include <linux/netdevice.h> #include <linux/etherdevice.h> #include "8390.h" -int ns8390_probe1(struct net_device *dev, int word16, char *name, int id, int prom); +extern int console_loglevel; + +int ns8390_probe1(struct net_device *dev, int word16, char *name, int id, + int prom, struct nubus_dev *ndev); static int ns8390_open(struct net_device *dev); static void ns8390_no_reset(struct net_device *dev); static int ns8390_close_card(struct net_device *dev); +/* Interlan */ static void interlan_reset(struct net_device *dev); +/* Dayna */ static void dayna_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page); static void dayna_block_input(struct net_device *dev, int count, @@ -52,6 +64,7 @@ static void dayna_block_input(struct net_device *dev, int count, static void dayna_block_output(struct net_device *dev, int count, const unsigned char *buf, const int start_page); +/* Sane (32-bit chunk memory read/write) */ static void sane_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page); static void sane_block_input(struct net_device *dev, int count, @@ -59,6 +72,7 @@ static void sane_block_input(struct net_device *dev, int count, static void sane_block_output(struct net_device *dev, int count, const unsigned char *buf, const int start_page); +/* Slow Sane (16-bit chunk memory read/write) */ static void slow_sane_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page); static void slow_sane_block_input(struct net_device *dev, int count, @@ -71,6 +85,10 @@ static void slow_sane_block_output(struct net_device *dev, int count, #define WD03_STOP_PG 0x20 /* Last page +1 of RX ring */ #define WD13_STOP_PG 0x40 /* Last page +1 of RX ring */ +#define CABLETRON_RX_START_PG 0x00 /* First page of RX buffer */ +#define CABLETRON_RX_STOP_PG 0x30 /* Last page +1 of RX ring */ +#define CABLETRON_TX_START_PG CABLETRON_RX_STOP_PG /* First page of TX buffer */ + #define DAYNA_MAC_BASE 0xf0007 #define DAYNA_8390_BASE 0x80000 /* 3 */ @@ -81,9 +99,14 @@ static void slow_sane_block_output(struct net_device *dev, int count, #define APPLE_8390_MEM 0xD0000 #define APPLE_MEMSIZE 8192 /* FIXME: need to dynamically check */ -#define KINETICS_8390_BASE 0x80003 -#define KINETICS_8390_MEM 0x00000 +#define KINETICS_MAC_BASE 0xf0004 /* first byte of each long */ +#define KINETICS_8390_BASE 0x80000 +#define KINETICS_8390_MEM 0x00000 /* first word of each long */ #define KINETICS_MEMSIZE 8192 /* FIXME: need to dynamically check */ +/*#define KINETICS_MEMSIZE (0x10000/2) * CSA: on the board I have, at least */ + +#define CABLETRON_8390_BASE 0x90000 +#define CABLETRON_8390_MEM 0x00000 static int test_8390(volatile char *ptr, int scale) { @@ -113,34 +136,59 @@ static int test_8390(volatile char *ptr, int scale) * Identify the species of NS8390 card/driver we need */ -#define NS8390_DAYNA 1 -#define NS8390_INTERLAN 2 -#define NS8390_KINETICS 3 -#define NS8390_APPLE 4 -#define NS8390_FARALLON 5 -#define NS8390_ASANTE 6 +enum mac8390_type { + NS8390_DAYNA, + NS8390_INTERLAN, + NS8390_KINETICS, + NS8390_APPLE, + NS8390_FARALLON, + NS8390_ASANTE, + NS8390_CABLETRON +}; -int ns8390_ident(struct nubus_type *nb) +int __init ns8390_ident(struct nubus_dev* ndev) { - /* It appears anything with a software type of 0 is an apple - compatible - even if the hardware matches others */ - - if(nb->DrSW==0x0001 || nb->DrSW==0x0109 || nb->DrSW==0x0000 || nb->DrSW==0x0100) - return NS8390_APPLE; - + /* This really needs to be tested and tested hard. */ + + /* Summary of what we know so far -- + * SW: 0x0104 -- asante, 16 bit, back4_offsets + * SW: 0x010b -- daynaport, 16 bit, fwrd4_offsets + * SW: 0x010c -- farallon, 16 bit, back4_offsets, no long word access + * SW: 0x011a -- focus, [no details yet] + * SW: ?????? -- interlan, 16 bit, back4_offsets, funny reset + * SW: ?????? -- kinetics, 8 bit, back4_offsets + * -- so i've this hypothesis going that says DrSW&1 says whether the + * map is forward or backwards -- and maybe DrSW&256 says what the + * register spacing is -- for all cards that report a DrSW in some + * range. + * This would allow the "apple compatible" driver to drive many + * seemingly different types of cards. More DrSW info is needed + * to investigate this properly. [CSA, 21-May-1999] + */ /* Dayna ex Kinetics board */ - if(nb->DrHW==0x0103) + if(ndev->dr_sw == NUBUS_DRSW_DAYNA) return NS8390_DAYNA; - - /* Asante board */ - if(nb->DrHW==0x0104) + if(ndev->dr_sw == NUBUS_DRSW_ASANTE) return NS8390_ASANTE; - if(nb->DrHW==0x0100) - return NS8390_INTERLAN; - if(nb->DrHW==0x0106) - return NS8390_KINETICS; - if(nb->DrSW==0x010C) + if(ndev->dr_sw == NUBUS_DRSW_FARALLON) /* farallon or sonic systems */ return NS8390_FARALLON; + if(ndev->dr_sw == NUBUS_DRSW_KINETICS) + return NS8390_KINETICS; + /* My ATI Engineering card with this combination crashes the */ + /* driver trying to xmit packets. Best not touch it for now. */ + /* - 1999-05-20 (funaho@jurai.org) */ + if(ndev->dr_sw == NUBUS_DRSW_FOCUS) + return -1; + + /* Check the HW on this one, because it shares the same DrSW as + the on-board SONIC chips */ + if(ndev->dr_hw == NUBUS_DRHW_CABLETRON) + return NS8390_CABLETRON; + /* does anyone have one of these? */ + if(ndev->dr_hw == NUBUS_DRHW_INTERLAN) + return NS8390_INTERLAN; + + /* FIXME: what do genuine Apple boards look like? */ return -1; } @@ -148,7 +196,7 @@ int ns8390_ident(struct nubus_type *nb) * Memory probe for 8390 cards */ -int apple_8390_mem_probe(volatile unsigned short *p) +int __init apple_8390_mem_probe(volatile unsigned short *p) { int i, j; /* @@ -192,61 +240,79 @@ int apple_8390_mem_probe(volatile unsigned short *p) /* * Probe for 8390 cards. * The ns8390_probe1() routine initializes the card and fills the - * station address field. On entry base_addr is set, irq is set - * (These come from the nubus probe code). dev->mem_start points + * station address field. + * + * The NuBus interface has changed! We now scan for these somewhat + * like how the PCI and Zorro drivers do. It's not clear whether + * this is actually better, but it makes things more consistent. + * + * dev->mem_start points * at the memory ring, dev->mem_end gives the end of it. */ -int ns8390_probe(struct nubus_device_specifier *d, int slot, struct nubus_type *match) +int __init mac8390_probe(struct net_device *dev) { - struct net_device *dev; + static int slots = 0; volatile unsigned short *i; volatile unsigned char *p; int plen; int id; + static struct nubus_dev* ndev = NULL; + + /* Find the first card that hasn't already been seen */ + while ((ndev = nubus_find_type(NUBUS_CAT_NETWORK, + NUBUS_TYPE_ETHERNET, ndev)) != NULL) { + /* Have we seen it already? */ + if (slots & (1<<ndev->board->slot)) + continue; + slots |= 1<<ndev->board->slot; + + /* Is it one of ours? */ + if ((id = ns8390_ident(ndev)) != -1) + break; + } - if(match->category!=NUBUS_CAT_NETWORK || match->type!=1) - return -ENODEV; - /* Ok so it is an ethernet network device */ - if((id=ns8390_ident(match))==-1) - { - printk("Ethernet but type unknown %d\n",match->DrHW); + /* Hm. No more cards, then */ + if (ndev == NULL) return -ENODEV; + + dev = init_etherdev(dev, 0); + + if (!version_printed) { + printk(KERN_INFO "%s", version); + version_printed = 1; } - dev = init_etherdev(0, 0); - if(dev==NULL) - return -ENOMEM; /* * Dayna specific init */ if(id==NS8390_DAYNA) { - dev->base_addr=(int)(nubus_slot_addr(slot)+DAYNA_8390_BASE); - dev->mem_start=(int)(nubus_slot_addr(slot)+DAYNA_8390_MEM); - dev->mem_end=dev->mem_start+DAYNA_MEMSIZE; /* 8K it seems */ + dev->base_addr = (int)(ndev->board->slot_addr+DAYNA_8390_BASE); + dev->mem_start = (int)(ndev->board->slot_addr+DAYNA_8390_MEM); + dev->mem_end = dev->mem_start+DAYNA_MEMSIZE; /* 8K it seems */ - printk("daynaport: testing board: "); - + printk(KERN_INFO "%s: daynaport. testing board: ", dev->name); + printk("memory - "); - - i=(void *)dev->mem_start; + + i = (void *)dev->mem_start; memset((void *)i,0xAA, DAYNA_MEMSIZE); while(i<(volatile unsigned short *)dev->mem_end) { if(*i!=0xAAAA) goto membad; - *i=0x5555; - if(*i!=0x5555) + *i=0x5678; /* make sure we catch byte smearing */ + if(*i!=0x5678) goto membad; i+=2; /* Skip a word */ } - + printk("controller - "); - + p=(void *)dev->base_addr; plen=0; - + while(plen<0x3FF00) { if(test_8390(p,0)==0) @@ -263,26 +329,71 @@ int ns8390_probe(struct nubus_device_specifier *d, int slot, struct nubus_type * if(plen==0x3FF00) goto membad; printk("OK\n"); - dev->irq=slot; - if(ns8390_probe1(dev, 0, "dayna", id, -1)==0) - return 0; + dev->irq = SLOT2IRQ(ndev->board->slot); + if(ns8390_probe1(dev, 0, "dayna", id, -1, ndev)==0) + return 0; + } + /* Cabletron */ + if (id==NS8390_CABLETRON) { + int memsize = 16<<10; /* fix this */ + + dev->base_addr=(int)(ndev->board->slot_addr+CABLETRON_8390_BASE); + dev->mem_start=(int)(ndev->board->slot_addr+CABLETRON_8390_MEM); + dev->mem_end=dev->mem_start+memsize; + dev->irq = SLOT2IRQ(ndev->board->slot); + + /* The base address is unreadable if 0x00 has been written to the command register */ + /* Reset the chip by writing E8390_NODMA+E8390_PAGE0+E8390_STOP just to be sure */ + i = (void *)dev->base_addr; + *i = 0x21; + + printk(KERN_INFO "%s: cabletron: testing board: ", dev->name); + printk("%dK memory - ", memsize>>10); + i=(void *)dev->mem_start; + while(i<(volatile unsigned short *)(dev->mem_start+memsize)) + { + *i=0xAAAA; + if(*i!=0xAAAA) + goto membad; + *i=0x5555; + if(*i!=0x5555) + goto membad; + i+=2; /* Skip a word */ + } + printk("OK\n"); + + if(ns8390_probe1(dev, 1, "cabletron", id, -1, ndev)==0) + return 0; } /* Apple, Farallon, Asante */ - if(id==NS8390_APPLE|| id==NS8390_FARALLON || id==NS8390_ASANTE) + if(id==NS8390_APPLE || id==NS8390_FARALLON || id==NS8390_ASANTE) { int memsize; - - dev->base_addr=(int)(nubus_slot_addr(slot)+APPLE_8390_BASE); - dev->mem_start=(int)(nubus_slot_addr(slot)+APPLE_8390_MEM); - + + dev->base_addr=(int)(ndev->board->slot_addr+APPLE_8390_BASE); + dev->mem_start=(int)(ndev->board->slot_addr+APPLE_8390_MEM); + memsize = apple_8390_mem_probe((void *)dev->mem_start); - + dev->mem_end=dev->mem_start+memsize; - dev->irq=slot; - printk("apple/clone: testing board: "); - + dev->irq = SLOT2IRQ(ndev->board->slot); + + switch(id) + { + case NS8390_FARALLON: + printk(KERN_INFO "%s: farallon: testing board: ", dev->name); + break; + case NS8390_ASANTE: + printk(KERN_INFO "%s: asante: testing board: ", dev->name); + break; + case NS8390_APPLE: + default: + printk(KERN_INFO "%s: apple/clone: testing board: ", dev->name); + break; + } + printk("%dK memory - ", memsize>>10); - + i=(void *)dev->mem_start; memset((void *)i,0xAA, memsize); while(i<(volatile unsigned short *)dev->mem_end) @@ -295,51 +406,75 @@ int ns8390_probe(struct nubus_device_specifier *d, int slot, struct nubus_type * i+=2; /* Skip a word */ } printk("OK\n"); - - if(id==NS8390_FARALLON) + + switch (id) { - if(ns8390_probe1(dev, 1, "farallon", id, -1)==0) + case NS8390_FARALLON: + if(ns8390_probe1(dev, 1, "farallon", id, -1, ndev)==0) return 0; - } - else - { - if(ns8390_probe1(dev, 1, "apple/clone", id, -1)==0) - return 0; + break; + case NS8390_ASANTE: + if(ns8390_probe1(dev, 1, "asante", id, -1, ndev)==0) + return 0; + break; + case NS8390_APPLE: + default: + if(ns8390_probe1(dev, 1, "apple/clone", id, -1, ndev)==0) + return 0; + break; } } /* Interlan */ if(id==NS8390_INTERLAN) { /* As apple and asante */ - dev->base_addr=(int)(nubus_slot_addr(slot)+APPLE_8390_BASE); - dev->mem_start=(int)(nubus_slot_addr(slot)+APPLE_8390_MEM); + dev->base_addr=(int)(ndev->board->slot_addr+APPLE_8390_BASE); + dev->mem_start=(int)(ndev->board->slot_addr+APPLE_8390_MEM); dev->mem_end=dev->mem_start+APPLE_MEMSIZE; /* 8K it seems */ - dev->irq=slot; - if(ns8390_probe1(dev, 1, "interlan", id, -1)==0) + dev->irq = SLOT2IRQ(ndev->board->slot); + if(ns8390_probe1(dev, 1, "interlan", id, -1, ndev)==0) return 0; } - /* Kinetics */ + /* Kinetics (Shiva Etherport) */ if(id==NS8390_KINETICS) { - dev->base_addr=(int)(nubus_slot_addr(slot)+KINETICS_8390_BASE); - dev->mem_start=(int)(nubus_slot_addr(slot)+KINETICS_8390_MEM); + dev->base_addr=(int)(ndev->board->slot_addr+KINETICS_8390_BASE); + dev->mem_start=(int)(ndev->board->slot_addr+KINETICS_8390_MEM); dev->mem_end=dev->mem_start+KINETICS_MEMSIZE; /* 8K it seems */ - dev->irq=slot; - if(ns8390_probe1(dev, 0, "kinetics", id, -1)==0) + dev->irq = SLOT2IRQ(ndev->board->slot); + if(ns8390_probe1(dev, 0, "kinetics", id, -1, ndev)==0) return 0; } - kfree(dev); + + /* We should hopefully not get here */ + printk(KERN_ERR "Probe unsucessful.\n"); return -ENODEV; -membad: - printk("failed.\n"); - kfree(dev); + + membad: + printk(KERN_ERR "failed at %p in %p - %p.\n", i, + (void *)dev->mem_start, (void *)dev->mem_end); return -ENODEV; } -int ns8390_probe1(struct net_device *dev, int word16, char *model_name, int type, int promoff) +int __init mac8390_ethernet_addr(struct nubus_dev* ndev, + unsigned char addr[6]) { - static unsigned version_printed = 0; + struct nubus_dir dir; + struct nubus_dirent ent; + + /* Get the functional resource for this device */ + if (nubus_get_func_dir(ndev, &dir) == -1) + return -1; + if (nubus_find_rsrc(&dir, NUBUS_RESID_MAC_ADDRESS, &ent) == -1) + return -1; + + nubus_get_rsrc_mem(addr, &ent, 6); + return 0; +} +int __init ns8390_probe1(struct net_device *dev, int word16, char *model_name, + int type, int promoff, struct nubus_dev *ndev) +{ static u32 fwrd4_offsets[16]={ 0, 4, 8, 12, 16, 20, 24, 28, @@ -352,25 +487,19 @@ int ns8390_probe1(struct net_device *dev, int word16, char *model_name, int type 28, 24, 20, 16, 12, 8, 4, 0 }; + static u32 fwrd2_offsets[16]={ + 0, 2, 4, 6, + 8, 10, 12, 14, + 16, 18, 20, 22, + 24, 26, 28, 30 + }; - unsigned char *prom=((unsigned char *)nubus_slot_addr(dev->irq))+promoff; + unsigned char *prom = (unsigned char*) ndev->board->slot_addr + promoff; - if (ei_debug && version_printed++ == 0) - printk(version); - - /* Snarf the interrupt now. There's no point in waiting since we cannot - share a slot! and the board will usually be enabled. */ - if (nubus_request_irq(dev->irq, dev, ei_interrupt)) - { - printk (" unable to get nubus IRQ %d.\n", dev->irq); - return EAGAIN; - } - /* Allocate dev->priv and fill in 8390 specific dev fields. */ if (ethdev_init(dev)) { - printk (" unable to get memory for dev->priv.\n"); - nubus_free_irq(dev->irq); + printk ("%s: unable to get memory for dev->priv.\n", dev->name); return -ENOMEM; } @@ -378,16 +507,25 @@ int ns8390_probe1(struct net_device *dev, int word16, char *model_name, int type ei_status.name = model_name; ei_status.word16 = word16; - ei_status.tx_start_page = WD_START_PG; - ei_status.rx_start_page = WD_START_PG + TX_PAGES; - dev->rmem_start = dev->mem_start + TX_PAGES*256; - ei_status.stop_page = (dev->mem_end - dev->mem_start)/256; - dev->rmem_end = dev->mem_end; + if (type==NS8390_CABLETRON) { + /* Cabletron card puts the RX buffer before the TX buffer */ + ei_status.tx_start_page = CABLETRON_TX_START_PG; + ei_status.rx_start_page = CABLETRON_RX_START_PG; + ei_status.stop_page = CABLETRON_RX_STOP_PG; + dev->rmem_start = dev->mem_start; + dev->rmem_end = dev->mem_start + CABLETRON_RX_STOP_PG*256; + } else { + ei_status.tx_start_page = WD_START_PG; + ei_status.rx_start_page = WD_START_PG + TX_PAGES; + ei_status.stop_page = (dev->mem_end - dev->mem_start)/256; + dev->rmem_start = dev->mem_start + TX_PAGES*256; + dev->rmem_end = dev->mem_end; + } if(promoff==-1) /* Use nubus resources ? */ { - if(nubus_ethernet_addr(dev->irq /* slot */, dev->dev_addr)) + if(mac8390_ethernet_addr(ndev, dev->dev_addr)) { printk("mac_ns8390: MAC address not in resources!\n"); return -ENODEV; @@ -400,7 +538,7 @@ int ns8390_probe1(struct net_device *dev, int word16, char *model_name, int type /* These should go in the end I hope */ if(type==NS8390_DAYNA) x=2; - if(type==NS8390_INTERLAN) + if(type==NS8390_INTERLAN || type==NS8390_KINETICS) x=4; while(i<6) { @@ -412,12 +550,24 @@ int ns8390_probe1(struct net_device *dev, int word16, char *model_name, int type } } - printk(" %s, IRQ %d, shared memory at %#lx-%#lx.\n", - model_name, dev->irq, dev->mem_start, dev->mem_end-1); + printk(KERN_INFO "%s: %s in slot %X (type %s)\n", + dev->name, ndev->board->name, ndev->board->slot, model_name); + printk(KERN_INFO "MAC "); + { + int i; + for (i = 0; i < 6; i++) { + printk("%2.2x", dev->dev_addr[i]); + if (i < 5) + printk(":"); + } + } + printk(" IRQ %d, shared memory at %#lx-%#lx.\n", + dev->irq, dev->mem_start, dev->mem_end-1); switch(type) { case NS8390_DAYNA: /* Dayna card */ + case NS8390_KINETICS: /* Kinetics -- 8 bit config, but 16 bit mem */ /* 16 bit, 4 word offsets */ ei_status.reset_8390 = &ns8390_no_reset; ei_status.block_input = &dayna_block_input; @@ -425,6 +575,15 @@ int ns8390_probe1(struct net_device *dev, int word16, char *model_name, int type ei_status.get_8390_hdr = &dayna_get_8390_hdr; ei_status.reg_offset = fwrd4_offsets; break; + case NS8390_CABLETRON: /* Cabletron */ + /* 16 bit card, register map is short forward */ + ei_status.reset_8390 = &ns8390_no_reset; + /* Ctron card won't accept 32bit values read or written to it */ + ei_status.block_input = &slow_sane_block_input; + ei_status.block_output = &slow_sane_block_output; + ei_status.get_8390_hdr = &slow_sane_get_8390_hdr; + ei_status.reg_offset = fwrd2_offsets; + break; case NS8390_FARALLON: case NS8390_APPLE: /* Apple/Asante/Farallon */ /* 16 bit card, register map is reversed */ @@ -450,6 +609,8 @@ int ns8390_probe1(struct net_device *dev, int word16, char *model_name, int type ei_status.get_8390_hdr = &sane_get_8390_hdr; ei_status.reg_offset = back4_offsets; break; +#if 0 /* i think this suffered code rot. my kinetics card has much + * different settings. -- CSA [22-May-1999] */ case NS8390_KINETICS: /* Kinetics */ /* 8bit card, map is forward */ ei_status.reset_8390 = &ns8390_no_reset; @@ -458,6 +619,7 @@ int ns8390_probe1(struct net_device *dev, int word16, char *model_name, int type ei_status.get_8390_hdr = &sane_get_8390_hdr; ei_status.reg_offset = back4_offsets; break; +#endif default: panic("Detected a card I can't drive - whoops\n"); } @@ -472,6 +634,19 @@ int ns8390_probe1(struct net_device *dev, int word16, char *model_name, int type static int ns8390_open(struct net_device *dev) { ei_open(dev); + + /* At least on my card (a Focus Enhancements PDS card) I start */ + /* getting interrupts right away, so the driver needs to be */ + /* completely initialized before enabling the interrupt. */ + /* - funaho@jurai.org (1999-05-17) */ + + /* Non-slow interrupt, works around issues with the SONIC driver */ + if (request_irq(dev->irq, ei_interrupt, 0, "8390 Ethernet", dev)) + { + printk ("%s: unable to get IRQ %d.\n", dev->name, dev->irq); + return EAGAIN; + } + MOD_INC_USE_COUNT; return 0; } @@ -489,24 +664,19 @@ static int ns8390_close_card(struct net_device *dev) { if (ei_debug > 1) printk("%s: Shutting down ethercard.\n", dev->name); + free_irq(dev->irq, dev); ei_close(dev); MOD_DEC_USE_COUNT; return 0; } -struct nubus_device_specifier nubus_8390={ - ns8390_probe, - NULL -}; - - /* * Interlan Specific Code Starts Here */ static void interlan_reset(struct net_device *dev) { - unsigned char *target=nubus_slot_addr(dev->irq); + unsigned char *target=nubus_slot_addr(IRQ2SLOT(dev->irq)); if (ei_debug > 1) printk("Need to reset the NS8390 t=%lu...", jiffies); ei_status.txing = 0; @@ -531,16 +701,23 @@ static void interlan_reset(struct net_device *dev) The only complications are that the ring buffer wraps. */ -static void dayna_cpu_memcpy(struct net_device *dev, void *to, int from, int count) +static void dayna_memcpy_fromcard(struct net_device *dev, void *to, int from, int count) { volatile unsigned short *ptr; unsigned short *target=to; from<<=1; /* word, skip overhead */ ptr=(unsigned short *)(dev->mem_start+from); + /* + * Leading byte? + */ + if (from&2) { + *((char *)target)++ = *(((char *)ptr++)-1); + count--; + } while(count>=2) { *target++=*ptr++; /* Copy and */ - ptr++; /* Cruft and */ + ptr++; /* skip cruft */ count-=2; } /* @@ -554,16 +731,24 @@ static void dayna_cpu_memcpy(struct net_device *dev, void *to, int from, int cou } } -static void cpu_dayna_memcpy(struct net_device *dev, int to, const void *from, int count) +static void dayna_memcpy_tocard(struct net_device *dev, int to, const void *from, int count) { volatile unsigned short *ptr; const unsigned short *src=from; to<<=1; /* word, skip overhead */ ptr=(unsigned short *)(dev->mem_start+to); + /* + * Leading byte? + */ + if (to&2) { /* avoid a byte write (stomps on other data) */ + ptr[-1] = (ptr[-1]&0xFF00)|*((unsigned char *)src)++; + ptr++; + count--; + } while(count>=2) { *ptr++=*src++; /* Copy and */ - ptr++; /* Cruft and */ + ptr++; /* skip cruft */ count-=2; } /* @@ -573,14 +758,15 @@ static void cpu_dayna_memcpy(struct net_device *dev, int to, const void *from, i { /* Big endian */ unsigned short v=*src; - *((char *)ptr)=v>>8; + /* card doesn't like byte writes */ + *ptr=(*ptr&0x00FF)|(v&0xFF00); } } static void dayna_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page) { unsigned long hdr_start = (ring_page - WD_START_PG)<<8; - dayna_cpu_memcpy(dev, (void *)hdr, hdr_start, 4); + dayna_memcpy_fromcard(dev, (void *)hdr, hdr_start, 4); /* Register endianism - fix here rather than 8390.c */ hdr->count=(hdr->count&0xFF)<<8|(hdr->count>>8); } @@ -599,14 +785,14 @@ static void dayna_block_input(struct net_device *dev, int count, struct sk_buff { /* We must wrap the input move. */ int semi_count = dev->rmem_end - xfer_start; - dayna_cpu_memcpy(dev, skb->data, xfer_base, semi_count); + dayna_memcpy_fromcard(dev, skb->data, xfer_base, semi_count); count -= semi_count; - dayna_cpu_memcpy(dev, skb->data + semi_count, + dayna_memcpy_fromcard(dev, skb->data + semi_count, dev->rmem_start - dev->mem_start, count); } else { - dayna_cpu_memcpy(dev, skb->data, xfer_base, count); + dayna_memcpy_fromcard(dev, skb->data, xfer_base, count); } } @@ -615,7 +801,7 @@ static void dayna_block_output(struct net_device *dev, int count, const unsigned { long shmem = (start_page - WD_START_PG)<<8; - cpu_dayna_memcpy(dev, shmem, buf, count); + dayna_memcpy_tocard(dev, shmem, buf, count); } /* @@ -739,6 +925,7 @@ static void slow_sane_block_output(struct net_device *dev, int count, const unsi * Local variables: * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c daynaport.c" * version-control: t + * c-basic-offset: 4 * tab-width: 4 * kept-new-versions: 5 * End: diff --git a/drivers/net/irda/Config.in b/drivers/net/irda/Config.in index da1df253c..da0b5f4be 100644 --- a/drivers/net/irda/Config.in +++ b/drivers/net/irda/Config.in @@ -6,10 +6,12 @@ dep_tristate 'IrTTY (uses Linux serial driver)' CONFIG_IRTTY_SIR $CONFIG_IRDA dep_tristate 'IrPORT (IrDA serial driver)' CONFIG_IRPORT_SIR $CONFIG_IRDA comment 'FIR device drivers' -dep_tristate 'NSC PC87108/PC97338' CONFIG_NSC_FIR $CONFIG_IRDA +dep_tristate 'NSC PC87108/PC87338' CONFIG_NSC_FIR $CONFIG_IRDA dep_tristate 'Winbond W83977AF (IR)' CONFIG_WINBOND_FIR $CONFIG_IRDA dep_tristate 'Toshiba Type-O IR Port' CONFIG_TOSHIBA_FIR $CONFIG_IRDA -dep_tristate 'SMC IrCC' CONFIG_SMC_IRCC_FIR $CONFIG_IRDA +if [ "$CONFIG_EXPERIMENTAL" != "n" ]; then +dep_tristate 'SMC IrCC (Experimental)' CONFIG_SMC_IRCC_FIR $CONFIG_IRDA +fi comment 'Dongle support' bool 'Serial dongle support' CONFIG_DONGLE @@ -19,9 +21,7 @@ if [ "$CONFIG_DONGLE" != "n" ]; then dep_tristate ' Tekram IrMate 210B dongle' CONFIG_TEKRAM_DONGLE $CONFIG_IRDA dep_tristate ' Greenwich GIrBIL dongle' CONFIG_GIRBIL_DONGLE $CONFIG_IRDA dep_tristate ' Parallax LiteLink dongle' CONFIG_LITELINK_DONGLE $CONFIG_IRDA - dep_tristate ' Adaptec Airport 1000/2000 dongle' CONFIG_AIRPORT_DONGLE $CONFIG_IRDA - dep_tristate ' Old Belkin dongle' CONFIG_OLD_BELKIN_DONGLE $CONFIG_IRDA - + dep_tristate ' Old Belkin dongle' CONFIG_OLD_BELKIN_DONGLE $CONFIG_IRDA fi endmenu diff --git a/drivers/net/irda/Makefile b/drivers/net/irda/Makefile index bf5628ebd..291a27f05 100644 --- a/drivers/net/irda/Makefile +++ b/drivers/net/irda/Makefile @@ -29,10 +29,10 @@ else endif ifeq ($(CONFIG_NSC_FIR),y) -L_OBJS += nsc_fir.o +L_OBJS += nsc-ircc.o else ifeq ($(CONFIG_NSC_FIR),m) - M_OBJS += nsc_fir.o + M_OBJS += nsc-ircc.o endif endif @@ -118,14 +118,6 @@ else endif endif -ifeq ($(CONFIG_AIRPORT_DONGLE),y) -L_OBJS += airport.o -else - ifeq ($(CONFIG_AIRPORT_DONGLE),m) - M_OBJS += airport.o - endif -endif - ifeq ($(CONFIG_OLD_BELKIN_DONGLE),y) L_OBJS += old_belkin.o else diff --git a/drivers/net/irda/airport.c b/drivers/net/irda/airport.c deleted file mode 100644 index b68378189..000000000 --- a/drivers/net/irda/airport.c +++ /dev/null @@ -1,358 +0,0 @@ -/********************************************************************* - * - * Filename: airport.c - * Version: 0.2 - * Description: Implementation for the Adaptec Airport 1000 and 2000 - * dongles - * Status: Experimental. - * Author: Fons Botman <budely@tref.nl> - * Created at: Wed May 19 23:14:34 CEST 1999 - * Based on: actisys.c by Dag Brattli <dagb@cs.uit.no> - * - * Copyright (c) 1998-1999 Fons Botman, All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Fons Botman nor anyone else admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - ********************************************************************/ - -#include <linux/module.h> -#include <linux/delay.h> -#include <linux/tty.h> -#include <linux/sched.h> -#include <linux/init.h> - -#include <net/irda/irda.h> -#include <net/irda/irmod.h> -#include <net/irda/irda_device.h> - -static int airport_reset_wrapper(struct irda_task *task); -static void airport_open(dongle_t *self, struct qos_info *qos); -static void airport_close(dongle_t *self); -static int airport_change_speed_wrapper(struct irda_task *task); - -static struct dongle_reg dongle = { - Q_NULL, - IRDA_AIRPORT_DONGLE, - airport_open, - airport_close, - airport_reset_wrapper, - airport_change_speed_wrapper, -}; - -int __init airport_init(void) -{ - int ret; - - IRDA_DEBUG(2, __FUNCTION__ "()\n"); - ret = irda_device_register_dongle(&dongle); - if (ret < 0) - return ret; - return 0; -} - -void airport_cleanup(void) -{ - IRDA_DEBUG(2, __FUNCTION__ "()\n"); - irda_device_unregister_dongle(&dongle); -} - -static void airport_open(dongle_t *self, struct qos_info *qos) -{ - qos->baud_rate.bits &= - IR_2400|IR_9600|IR_19200|IR_38400|IR_57600|IR_115200; - /* May need 1ms */ - qos->min_turn_time.bits = 0x07; - - MOD_INC_USE_COUNT; -} - -static void airport_close(dongle_t *self) -{ - IRDA_DEBUG(2, __FUNCTION__ "()\n"); - /* Power off dongle */ - self->set_dtr_rts(self->dev, FALSE, FALSE); - - MOD_DEC_USE_COUNT; -} - -static void airport_set_command_mode(dongle_t *self) -{ - IRDA_DEBUG(2, __FUNCTION__ "()\n"); - self->set_dtr_rts(self->dev, FALSE, TRUE); -} - -static void airport_set_normal_mode(dongle_t *self) -{ - IRDA_DEBUG(2, __FUNCTION__ "()\n"); - self->set_dtr_rts(self->dev, TRUE, TRUE); -} - -void airport_write_char(dongle_t *self, unsigned char c) -{ - int actual; - IRDA_DEBUG(2, __FUNCTION__ "(,0x%x)\n", c & 0xff); - actual = self->write(self->dev, &c, 1); - ASSERT(actual == 1, return;); -} - -#define JIFFIES_TO_MSECS(j) ((j)*1000/HZ) - -static int airport_waitfor_char(dongle_t *self, unsigned char c) -{ - __u8 buf[100]; - int i, found = FALSE; - int before; - int len; - - IRDA_DEBUG(2, __FUNCTION__ "(,0x%x)\n", c); - - /* Sleep approx. 10 ms */ - before = jiffies; - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(MSECS_TO_JIFFIES(20)); - IRDA_DEBUG(4, __FUNCTION__ " waited %ldms\n", - JIFFIES_TO_MSECS(jiffies - before)); - - len = self->read(self->dev, buf, 100); - - for (i = 0; !found && i < len; i++ ) { - /* IRDA_DEBUG(6, __FUNCTION__ " 0x02x\n", idev->rx_buff.data[i]); */ - found = c == buf[i]; - } - - IRDA_DEBUG(2, __FUNCTION__ " returns %s\n", (found ? "true" : "false")); - return found; -} - -static int airport_check_command_mode(dongle_t *self) -{ - int i; - int found = FALSE; - - IRDA_DEBUG(2, __FUNCTION__ "()\n"); - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(MSECS_TO_JIFFIES(20)); - airport_set_command_mode(self); - - /* Loop until the time expires (200ms) or we get the magic char. */ - - for ( i = 0 ; i < 25 ; i++ ) { - airport_write_char(self, 0xff); - if (airport_waitfor_char(self, 0xc3)) { - found = TRUE; - break; - } - } - - if (found) { - IRDA_DEBUG(2, __FUNCTION__ " OK. (%d)\n", i); - } else { - IRDA_DEBUG(0, __FUNCTION__ " FAILED!\n"); - } - return found; -} - -static int airport_write_register(dongle_t *self, unsigned char reg) -{ - int ok = FALSE; - int i; - - IRDA_DEBUG(4, __FUNCTION__ "(,0x%x)\n", reg); - airport_check_command_mode(self); - - for ( i = 0 ; i < 6 ; i++ ) { - airport_write_char(self, reg); - if (!airport_waitfor_char(self, reg)) - continue; - - /* Now read it back */ - airport_write_char(self, (reg << 4) | 0x0f); - if (airport_waitfor_char(self, reg)) { - ok = TRUE; - break; - } - } - - airport_set_normal_mode(self); - if (ok) { - IRDA_DEBUG(4, __FUNCTION__ "(,0x%x) returns OK\n", reg); - } else { - IRDA_DEBUG(0, __FUNCTION__ "(,0x%x) returns False!\n", reg); - } - return ok; -} - - -/* - * Function airport_change_speed (self, speed) - * - * Change speed of the Airport type IrDA dongles. - */ -static void airport_change_speed(dongle_t *self, __u32 speed) -{ - __u32 current_baudrate; - int baudcode; - - IRDA_DEBUG(4, __FUNCTION__ "(,%d)\n", speed); - - ASSERT(self != NULL, return;); - - /* Find the correct baudrate code for the required baudrate */ - switch (speed) { - case 2400: baudcode = 0x10; break; - case 4800: baudcode = 0x20; break; - case 9600: baudcode = 0x30; break; - case 19200: baudcode = 0x40; break; - case 38400: baudcode = 0x50; break; - case 57600: baudcode = 0x60; break; - case 115200: baudcode = 0x70; break; - default: - IRDA_DEBUG(0, __FUNCTION__ " bad baud rate: %d\n", speed); - return; - } - - current_baudrate = self->speed; - IRDA_DEBUG(4, __FUNCTION__ " current baudrate: %d\n", current_baudrate); - - self->set_mode(self->dev, IRDA_RAW); - - /* Set the new speed in both registers */ - if (airport_write_register(self, baudcode)) { - if (airport_write_register(self, baudcode|0x01)) { - /* ok */ - } else { - IRDA_DEBUG(0, __FUNCTION__ - " Cannot set new speed in second register\n"); - } - } else { - IRDA_DEBUG(0, __FUNCTION__ - " Cannot set new speed in first register\n"); - } - - self->set_mode(self->dev, IRDA_IRLAP); - - /* How do I signal an error in these functions? */ - - IRDA_DEBUG(4, __FUNCTION__ " returning\n"); -} - -int airport_change_speed_wrapper(struct irda_task *task) -{ - dongle_t *self = (dongle_t *) task->instance; - __u32 speed = (__u32) task->param; - - irda_execute_as_process(self, (TODO_CALLBACK) airport_change_speed, - speed); - - irda_task_next_state(task, IRDA_TASK_DONE); - - return 0; -} - -/* - * Function airport_reset (self) - * - * Reset the Airport type dongle. Warning, this function must only be - * called with a process context! - * - */ -static void airport_reset(dongle_t *self) -{ - int ok; - - IRDA_DEBUG(2, __FUNCTION__ "()\n"); - ASSERT(self != NULL, return;); - - self->set_mode(self->dev, IRDA_RAW); - - airport_set_normal_mode(self); - - /* Sleep 2000 ms */ - IRDA_DEBUG(2, __FUNCTION__ " waiting for powerup\n"); - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(MSECS_TO_JIFFIES(2000)); - IRDA_DEBUG(2, __FUNCTION__ " finished waiting for powerup\n"); - - /* set dongle speed to 9600 */ - ok = TRUE; - - if (ok) - ok = airport_write_register(self, 0x30); - if (!ok) - MESSAGE(__FUNCTION__ "() dongle not connected?\n"); - if (ok) - ok = airport_write_register(self, 0x31); - - if (ok) - ok = airport_write_register(self, 0x02); - if (ok) - ok = airport_write_register(self, 0x03); - - if (ok) { - ok = airport_check_command_mode(self); - - if (ok) { - airport_write_char(self, 0x04); - ok = airport_waitfor_char(self, 0x04); - } - airport_set_normal_mode(self); - } - - self->set_mode(self->dev, IRDA_IRLAP); - - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(MSECS_TO_JIFFIES(20)); - IRDA_DEBUG(4, __FUNCTION__ " waited 20ms\n"); - - self->speed = 9600; - if (!ok) - MESSAGE(__FUNCTION__ "() failed.\n"); - IRDA_DEBUG(2, __FUNCTION__ " returning.\n"); -} - -int airport_reset_wrapper(struct irda_task *task) -{ - dongle_t *self = (dongle_t *) task->instance; - - irda_execute_as_process(self, (TODO_CALLBACK) airport_reset, 0); - - irda_task_next_state(task, IRDA_TASK_DONE); - - return 0; -} - -#ifdef MODULE - -MODULE_AUTHOR("Fons Botman <budely@tref.nl>"); -MODULE_DESCRIPTION("Adaptec Airport 1000 and 2000 dongle driver"); - -/* - * Function init_module (void) - * - * Initialize Airport module - * - */ -int init_module(void) -{ - return airport_init(); -} - -/* - * Function cleanup_module (void) - * - * Cleanup Airport module - * - */ -void cleanup_module(void) -{ - airport_cleanup(); -} - -#endif diff --git a/drivers/net/irda/irport.c b/drivers/net/irda/irport.c index 352dddecc..d8832292a 100644 --- a/drivers/net/irda/irport.c +++ b/drivers/net/irda/irport.c @@ -6,7 +6,7 @@ * Status: Experimental. * Author: Dag Brattli <dagb@cs.uit.no> * Created at: Sun Aug 3 13:49:59 1997 - * Modified at: Wed Jan 5 13:59:38 2000 + * Modified at: Fri Jan 28 20:22:38 2000 * Modified by: Dag Brattli <dagb@cs.uit.no> * Sources: serial.c by Linus Torvalds * @@ -161,20 +161,19 @@ irport_open(int i, unsigned int iobase, unsigned int irq) self->index = i; /* Initialize IO */ - self->io.iobase = iobase; + self->io.sir_base = iobase; + self->io.sir_ext = IO_EXTENT; self->io.irq = irq; - self->io.io_ext = IO_EXTENT; self->io.fifo_size = 16; /* Lock the port that we need */ - ret = check_region(self->io.iobase, self->io.io_ext); + ret = check_region(self->io.sir_base, self->io.sir_ext); if (ret < 0) { IRDA_DEBUG(0, __FUNCTION__ "(), can't get iobase of 0x%03x\n", - self->io.iobase); - /* irport_cleanup(self->self); */ + self->io.sir_base); return NULL; } - request_region(self->io.iobase, self->io.io_ext, driver_name); + request_region(self->io.sir_base, self->io.sir_ext, driver_name); /* Initialize QoS for this device */ irda_init_max_qos_capabilies(&self->qos); @@ -218,7 +217,6 @@ irport_open(int i, unsigned int iobase, unsigned int irq) ERROR(__FUNCTION__ "(), dev_alloc() failed!\n"); return NULL; } - self->netdev = dev; /* May be overridden by piggyback drivers */ @@ -268,19 +266,19 @@ int irport_close(struct irport_cb *self) /* Release the IO-port that this driver is using */ IRDA_DEBUG(0 , __FUNCTION__ "(), Releasing Region %03x\n", - self->io.iobase); - release_region(self->io.iobase, self->io.io_ext); + self->io.sir_base); + release_region(self->io.sir_base, self->io.sir_ext); if (self->tx_buff.head) kfree(self->tx_buff.head); if (self->rx_buff.head) kfree(self->rx_buff.head); - + /* Remove ourselves */ dev_self[self->index] = NULL; kfree(self); - + return 0; } @@ -289,7 +287,7 @@ void irport_start(struct irport_cb *self) unsigned long flags; int iobase; - iobase = self->io.iobase; + iobase = self->io.sir_base; spin_lock_irqsave(&self->lock, flags); @@ -310,7 +308,7 @@ void irport_stop(struct irport_cb *self) unsigned long flags; int iobase; - iobase = self->io.iobase; + iobase = self->io.sir_base; spin_lock_irqsave(&self->lock, flags); @@ -355,7 +353,7 @@ void irport_change_speed(void *priv, __u32 speed) ASSERT(self != NULL, return;); - iobase = self->io.iobase; + iobase = self->io.sir_base; /* Update accounting for new speed */ self->io.speed = speed; @@ -431,7 +429,7 @@ int __irport_change_speed(struct irda_task *task) break; case IRDA_TASK_CHILD_INIT: /* Go to default speed */ - irport_change_speed(self, 9600); + self->change_speed(self->priv, 9600); /* Change speed of dongle */ if (irda_task_execute(self->dongle, @@ -454,7 +452,7 @@ int __irport_change_speed(struct irda_task *task) break; case IRDA_TASK_CHILD_DONE: /* Finally we are ready to change the speed */ - irport_change_speed(self, speed); + self->change_speed(self->priv, speed); irda_task_next_state(task, IRDA_TASK_DONE); break; @@ -484,7 +482,7 @@ static void irport_write_wakeup(struct irport_cb *self) IRDA_DEBUG(4, __FUNCTION__ "()\n"); - iobase = self->io.iobase; + iobase = self->io.sir_base; /* Finished with frame? */ if (self->tx_buff.len > 0) { @@ -598,7 +596,6 @@ int irport_hard_xmit(struct sk_buff *skb, struct net_device *dev) { struct irport_cb *self; unsigned long flags; - int actual = 0; int iobase; __u32 speed; @@ -607,7 +604,7 @@ int irport_hard_xmit(struct sk_buff *skb, struct net_device *dev) self = (struct irport_cb *) dev->priv; ASSERT(self != NULL, return 0;); - iobase = self->io.iobase; + iobase = self->io.sir_base; /* Lock transmit buffer */ if (irda_lock((void *) &dev->tbusy) == FALSE) { @@ -617,7 +614,7 @@ int irport_hard_xmit(struct sk_buff *skb, struct net_device *dev) WARNING("%s: transmit timed out\n", dev->name); irport_start(self); - irport_change_speed(self, self->io.speed ); + self->change_speed(self->priv, self->io.speed); dev->trans_start = jiffies; } @@ -635,8 +632,7 @@ int irport_hard_xmit(struct sk_buff *skb, struct net_device *dev) self->tx_buff.len = async_wrap_skb(skb, self->tx_buff.data, self->tx_buff.truesize); - self->tx_buff.data += actual; - self->tx_buff.len -= actual; + self->stats.tx_bytes += self->tx_buff.len; /* Turn on transmit finished interrupt. Will fire immediately! */ outb(UART_IER_THRI, iobase+UART_IER); @@ -661,7 +657,7 @@ static void irport_receive(struct irport_cb *self) ASSERT(self != NULL, return;); - iobase = self->io.iobase; + iobase = self->io.sir_base; /* * Receive all characters in Rx FIFO, unwrap and unstuff them. @@ -702,15 +698,16 @@ void irport_interrupt(int irq, void *dev_id, struct pt_regs *regs) dev->interrupt = 1; - iobase = self->io.iobase; + iobase = self->io.sir_base; iir = inb(iobase+UART_IIR) & UART_IIR_ID; while (iir) { /* Clear interrupt */ lsr = inb(iobase+UART_LSR); - IRDA_DEBUG(4, __FUNCTION__ "(), iir=%02x, lsr=%02x, iobase=%#x\n", - iir, lsr, iobase); + IRDA_DEBUG(4, __FUNCTION__ + "(), iir=%02x, lsr=%02x, iobase=%#x\n", + iir, lsr, iobase); switch (iir) { case UART_IIR_RLSI: @@ -765,7 +762,7 @@ int irport_net_open(struct net_device *dev) ASSERT(dev != NULL, return -1;); self = (struct irport_cb *) dev->priv; - iobase = self->io.iobase; + iobase = self->io.sir_base; if (request_irq(self->io.irq, self->interrupt, 0, dev->name, (void *) dev)) @@ -809,7 +806,7 @@ int irport_net_close(struct net_device *dev) ASSERT(self != NULL, return -1;); - iobase = self->io.iobase; + iobase = self->io.sir_base; /* Stop device */ dev->tbusy = 1; @@ -840,7 +837,7 @@ void irport_wait_until_sent(struct irport_cb *self) { int iobase; - iobase = self->io.iobase; + iobase = self->io.sir_base; /* Wait until Tx FIFO is empty */ while (!(inb(iobase+UART_LSR) & UART_LSR_THRE)) { @@ -875,7 +872,7 @@ static int irport_set_dtr_rts(struct net_device *dev, int dtr, int rts) ASSERT(self != NULL, return -1;); - iobase = self->io.iobase; + iobase = self->io.sir_base; if (dtr) dtr = UART_MCR_DTR; @@ -895,7 +892,7 @@ static int irport_raw_write(struct net_device *dev, __u8 *buf, int len) ASSERT(self != NULL, return -1;); - iobase = self->io.iobase; + iobase = self->io.sir_base; /* Tx FIFO should be empty! */ if (!(inb(iobase+UART_LSR) & UART_LSR_THRE)) { diff --git a/drivers/net/irda/irtty.c b/drivers/net/irda/irtty.c index 2ec4eba14..370a9c7fe 100644 --- a/drivers/net/irda/irtty.c +++ b/drivers/net/irda/irtty.c @@ -6,7 +6,7 @@ * Status: Experimental. * Author: Dag Brattli <dagb@cs.uit.no> * Created at: Tue Dec 9 21:18:38 1997 - * Modified at: Wed Jan 5 14:00:13 2000 + * Modified at: Fri Jan 14 21:02:27 2000 * Modified by: Dag Brattli <dagb@cs.uit.no> * Sources: slip.c by Laurence Culhane, <loz@holmes.demon.co.uk> * Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org> @@ -233,6 +233,8 @@ static int irtty_open(struct tty_struct *tty) ERROR(__FUNCTION__ "(), dev_alloc() failed!\n"); return -ENOMEM; } + /* dev_alloc doesn't clear the struct */ + memset(((__u8*)dev)+sizeof(char*),0,sizeof(struct net_device)-sizeof(char*)); dev->priv = (void *) self; self->netdev = dev; @@ -654,6 +656,7 @@ static int irtty_hard_xmit(struct sk_buff *skb, struct net_device *dev) self->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP); dev->trans_start = jiffies; + self->stats.tx_bytes += self->tx_buff.len; if (self->tty->driver.write) actual = self->tty->driver.write(self->tty, 0, @@ -663,9 +666,6 @@ static int irtty_hard_xmit(struct sk_buff *skb, struct net_device *dev) self->tx_buff.data += actual; self->tx_buff.len -= actual; - self->stats.tx_packets++; - self->stats.tx_bytes += self->tx_buff.len; - dev_kfree_skb(skb); return 0; @@ -709,6 +709,8 @@ static void irtty_write_wakeup(struct tty_struct *tty) self->tx_buff.data += actual; self->tx_buff.len -= actual; + + self->stats.tx_packets++; } else { /* * Now serial buffer is almost free & we can start diff --git a/drivers/net/irda/nsc_fir.c b/drivers/net/irda/nsc-ircc.c index affd1d8fa..e8aaa8459 100644 --- a/drivers/net/irda/nsc_fir.c +++ b/drivers/net/irda/nsc-ircc.c @@ -1,12 +1,12 @@ /********************************************************************* * - * Filename: nsc_fir.c + * Filename: nsc-ircc.c * Version: 1.0 * Description: Driver for the NSC PC'108 and PC'338 IrDA chipsets * Status: Stable. * Author: Dag Brattli <dagb@cs.uit.no> * Created at: Sat Nov 7 21:43:15 1998 - * Modified at: Wed Jan 5 13:59:21 2000 + * Modified at: Fri Jan 28 12:10:10 2000 * Modified by: Dag Brattli <dagb@cs.uit.no> * * Copyright (c) 1998-2000 Dag Brattli <dagb@cs.uit.no> @@ -41,8 +41,9 @@ * ********************************************************************/ -#include <linux/module.h> #include <linux/config.h> +#include <linux/module.h> + #include <linux/kernel.h> #include <linux/types.h> #include <linux/skbuff.h> @@ -67,29 +68,38 @@ #include <net/irda/irlap_frame.h> #include <net/irda/irda_device.h> -#include <net/irda/nsc_fir.h> +#include <net/irda/nsc-ircc.h> #define CHIP_IO_EXTENT 8 #define BROKEN_DONGLE_ID -/* - * Define if you have multiple NSC IrDA controllers in your machine. Not - * enabled by default since some single chips detects at multiple addresses - */ -#undef CONFIG_NSC_FIR_MULTIPLE - -static char *driver_name = "nsc_fir"; +static char *driver_name = "nsc-ircc"; /* Module parameters */ static int qos_mtt_bits = 0x07; /* 1 ms or more */ static int dongle_id = 0; -static unsigned int io[] = { 0x2f8, 0x2f8, 0x2f8, 0x2f8, 0x2f8 }; -static unsigned int io2[] = { 0x150, 0x398, 0xea, 0x15c, 0x2e }; -static unsigned int irq[] = { 3, 3, 3, 3, 3 }; -static unsigned int dma[] = { 0, 0, 0, 0, 3 }; +/* Use BIOS settions by default, but user may supply module parameters */ +static unsigned int io[] = { ~0, ~0, ~0, ~0 }; +static unsigned int irq[] = { 0, 0, 0, 0, 0 }; +static unsigned int dma[] = { 0, 0, 0, 0, 0 }; + +static int nsc_ircc_probe_108(nsc_chip_t *chip, chipio_t *info); +static int nsc_ircc_probe_338(nsc_chip_t *chip, chipio_t *info); +static int nsc_ircc_init_108(nsc_chip_t *chip, chipio_t *info); +static int nsc_ircc_init_338(nsc_chip_t *chip, chipio_t *info); + +/* These are the known NSC chips */ +static nsc_chip_t chips[] = { + { "PC87108", { 0x150, 0x398, 0xea }, 0x05, 0x10, 0xf0, + nsc_ircc_probe_108, nsc_ircc_init_108 }, + { "PC87338", { 0x398, 0x15c, 0x2e }, 0x08, 0xb0, 0xf0, + nsc_ircc_probe_338, nsc_ircc_init_338 }, + { NULL } +}; -static struct nsc_fir_cb *dev_self[] = { NULL, NULL, NULL, NULL, NULL }; +/* Max 4 instances for now */ +static struct nsc_ircc_cb *dev_self[] = { NULL, NULL, NULL, NULL }; static char *dongle_types[] = { "Differential serial interface", @@ -111,143 +121,182 @@ static char *dongle_types[] = { }; /* Some prototypes */ -static int nsc_fir_open(int i, unsigned int iobase, unsigned int board_addr, - unsigned int irq, unsigned int dma); +static int nsc_ircc_open(int i, chipio_t *info); #ifdef MODULE -static int nsc_fir_close(struct nsc_fir_cb *self); +static int nsc_ircc_close(struct nsc_ircc_cb *self); #endif /* MODULE */ -static int nsc_fir_probe(int iobase, int board_addr, int irq, int dma); -static void nsc_fir_pio_receive(struct nsc_fir_cb *self); -static int nsc_fir_dma_receive(struct nsc_fir_cb *self); -static int nsc_fir_dma_receive_complete(struct nsc_fir_cb *self, int iobase); -static int nsc_fir_hard_xmit_sir(struct sk_buff *skb, struct net_device *dev); -static int nsc_fir_hard_xmit_fir(struct sk_buff *skb, struct net_device *dev); -static int nsc_fir_pio_write(int iobase, __u8 *buf, int len, int fifo_size); -static void nsc_fir_dma_xmit(struct nsc_fir_cb *self, int iobase); -static void nsc_fir_change_speed(struct nsc_fir_cb *self, __u32 baud); -static void nsc_fir_interrupt(int irq, void *dev_id, struct pt_regs *regs); -static int nsc_fir_is_receiving(struct nsc_fir_cb *self); -static int nsc_fir_read_dongle_id (int iobase); -static void nsc_fir_init_dongle_interface (int iobase, int dongle_id); - -static int nsc_fir_net_init(struct net_device *dev); -static int nsc_fir_net_open(struct net_device *dev); -static int nsc_fir_net_close(struct net_device *dev); -static int nsc_fir_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); -static struct net_device_stats *nsc_fir_net_get_stats(struct net_device *dev); +static int nsc_ircc_setup(chipio_t *info); +static void nsc_ircc_pio_receive(struct nsc_ircc_cb *self); +static int nsc_ircc_dma_receive(struct nsc_ircc_cb *self); +static int nsc_ircc_dma_receive_complete(struct nsc_ircc_cb *self, int iobase); +static int nsc_ircc_hard_xmit_sir(struct sk_buff *skb, struct net_device *dev); +static int nsc_ircc_hard_xmit_fir(struct sk_buff *skb, struct net_device *dev); +static int nsc_ircc_pio_write(int iobase, __u8 *buf, int len, int fifo_size); +static void nsc_ircc_dma_xmit(struct nsc_ircc_cb *self, int iobase); +static void nsc_ircc_change_speed(struct nsc_ircc_cb *self, __u32 baud); +static void nsc_ircc_interrupt(int irq, void *dev_id, struct pt_regs *regs); +static int nsc_ircc_is_receiving(struct nsc_ircc_cb *self); +static int nsc_ircc_read_dongle_id (int iobase); +static void nsc_ircc_init_dongle_interface (int iobase, int dongle_id); + +static int nsc_ircc_net_init(struct net_device *dev); +static int nsc_ircc_net_open(struct net_device *dev); +static int nsc_ircc_net_close(struct net_device *dev); +static int nsc_ircc_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); +static struct net_device_stats *nsc_ircc_net_get_stats(struct net_device *dev); #ifdef CONFIG_APM -static int nsc_fir_apmproc(apm_event_t event); +static int nsc_ircc_apmproc(apm_event_t event); #endif /* CONFIG_APM */ + /* - * Function nsc_fir_init () + * Function nsc_ircc_init () * * Initialize chip. Just try to find out how many chips we are dealing with * and where they are */ -int __init nsc_fir_init(void) +int __init nsc_ircc_init(void) { + chipio_t info; + nsc_chip_t *chip; int ret = -ENODEV; - int ioaddr; - int i; + int cfg_base; + int cfg, id; + int reg; + int i = 0; + + /* Probe for all the NSC chipsets we know about */ + for (chip=chips; chip->name ; chip++,i++) { + IRDA_DEBUG(2, __FUNCTION__"(), Probing for %s ...\n", + chip->name); + + /* Try all config registers for this chip */ + for (cfg=0; cfg<3; cfg++) { + cfg_base = chip->cfg[cfg]; + if (!cfg_base) + continue; + + memset(&info, 0, sizeof(chipio_t)); + info.cfg_base = cfg_base; + info.fir_base = io[i]; + info.dma = dma[i]; + info.irq = irq[i]; + + /* Read index register */ + reg = inb(cfg_base); + if (reg == 0xff) { + IRDA_DEBUG(2, __FUNCTION__ + "() no chip at 0x%03x\n", cfg_base); + continue; + } + + /* Read chip identification register */ + outb(chip->cid_index, cfg_base); + id = inb(cfg_base+1); + if ((id & chip->cid_mask) == chip->cid_value) { + IRDA_DEBUG(2, __FUNCTION__ + "() Found %s chip, revision=%d\n", + chip->name, id & ~chip->cid_mask); + /* + * If the user supplies the base address, then + * we init the chip, if not we probe the values + * set by the BIOS + */ + if (io[i] < 2000) { + chip->init(chip, &info); + } else + chip->probe(chip, &info); + if (nsc_ircc_open(i, &info) == 0) + ret = 0; + i++; + } else { + IRDA_DEBUG(2, __FUNCTION__ + "(), Wrong chip id=0x%02x\n", id); + } + } + + } #ifdef CONFIG_APM - apm_register_callback(nsc_fir_apmproc); + /* Make sure at least one chip was found before enabling APM */ + if (ret == 0) + apm_register_callback(nsc_ircc_apmproc); #endif /* CONFIG_APM */ - for (i=0; (io[i] < 2000) && (i < 5); i++) { - ioaddr = io[i]; - if (check_region(ioaddr, CHIP_IO_EXTENT) < 0) - continue; - if (nsc_fir_open(i, io[i], io2[i], irq[i], dma[i]) == 0) - { -#ifdef CONFIG_NSC_FIR_MULTIPLE - ret = 0; -#else - return 0; -#endif - } - } - return ret; } /* - * Function nsc_fir_cleanup () + * Function nsc_ircc_cleanup () * * Close all configured chips * */ #ifdef MODULE -static void nsc_fir_cleanup(void) +static void nsc_ircc_cleanup(void) { int i; - IRDA_DEBUG(4, __FUNCTION__ "()\n"); - #ifdef CONFIG_APM - apm_unregister_callback(nsc_fir_apmproc); + apm_unregister_callback(nsc_ircc_apmproc); #endif /* CONFIG_APM */ - - for (i=0; i < 5; i++) { + for (i=0; i < 4; i++) { if (dev_self[i]) - nsc_fir_close(dev_self[i]); + nsc_ircc_close(dev_self[i]); } } #endif /* MODULE */ /* - * Function nsc_fir_open (iobase, irq) + * Function nsc_ircc_open (iobase, irq) * * Open driver instance * */ -static int -nsc_fir_open(int i, unsigned int iobase, unsigned int board_addr, - unsigned int irq, unsigned int dma) +static int nsc_ircc_open(int i, chipio_t *info) { struct net_device *dev; - struct nsc_fir_cb *self; - int dongle_id; + struct nsc_ircc_cb *self; int ret; int err; IRDA_DEBUG(2, __FUNCTION__ "()\n"); - if ((dongle_id = nsc_fir_probe(iobase, board_addr, irq, dma)) == -1) + if ((nsc_ircc_setup(info)) == -1) return -1; - /* - * Allocate new instance of the driver - */ - self = kmalloc(sizeof(struct nsc_fir_cb), GFP_KERNEL); + /* Allocate new instance of the driver */ + self = kmalloc(sizeof(struct nsc_ircc_cb), GFP_KERNEL); if (self == NULL) { ERROR(__FUNCTION__ "(), can't allocate memory for " "control block!\n"); return -ENOMEM; } - memset(self, 0, sizeof(struct nsc_fir_cb)); + memset(self, 0, sizeof(struct nsc_ircc_cb)); spin_lock_init(&self->lock); /* Need to store self somewhere */ dev_self[i] = self; + self->index = i; /* Initialize IO */ - self->io.iobase = iobase; - self->io.irq = irq; - self->io.io_ext = CHIP_IO_EXTENT; - self->io.dma = dma; + self->io.cfg_base = info->cfg_base; + self->io.fir_base = info->fir_base; + self->io.irq = info->irq; + self->io.fir_ext = CHIP_IO_EXTENT; + self->io.dma = info->dma; self->io.fifo_size = 32; - - /* Lock the port that we need */ - ret = check_region(self->io.iobase, self->io.io_ext); + + /* Reserve the ioports that we need */ + ret = check_region(self->io.fir_base, self->io.fir_ext); if (ret < 0) { - IRDA_DEBUG(0, __FUNCTION__ "(), can't get iobase of 0x%03x\n", - self->io.iobase); - /* nsc_fir_cleanup(self->self); */ + WARNING(__FUNCTION__ "(), can't get iobase of 0x%03x\n", + self->io.fir_base); + dev_self[i] = NULL; + kfree(self); return -ENODEV; } - request_region(self->io.iobase, self->io.io_ext, driver_name); + request_region(self->io.fir_base, self->io.fir_ext, driver_name); /* Initialize QoS for this device */ irda_init_max_qos_capabilies(&self->qos); @@ -268,13 +317,16 @@ nsc_fir_open(int i, unsigned int iobase, unsigned int board_addr, /* Allocate memory if needed */ self->rx_buff.head = (__u8 *) kmalloc(self->rx_buff.truesize, GFP_KERNEL|GFP_DMA); - if (self->rx_buff.head == NULL) + if (self->rx_buff.head == NULL) { + kfree(self); return -ENOMEM; + } memset(self->rx_buff.head, 0, self->rx_buff.truesize); self->tx_buff.head = (__u8 *) kmalloc(self->tx_buff.truesize, GFP_KERNEL|GFP_DMA); if (self->tx_buff.head == NULL) { + kfree(self); kfree(self->rx_buff.head); return -ENOMEM; } @@ -298,12 +350,12 @@ nsc_fir_open(int i, unsigned int iobase, unsigned int board_addr, self->netdev = dev; /* Override the network functions we need to use */ - dev->init = nsc_fir_net_init; - dev->hard_start_xmit = nsc_fir_hard_xmit_sir; - dev->open = nsc_fir_net_open; - dev->stop = nsc_fir_net_close; - dev->do_ioctl = nsc_fir_net_ioctl; - dev->get_stats = nsc_fir_net_get_stats; + dev->init = nsc_ircc_net_init; + dev->hard_start_xmit = nsc_ircc_hard_xmit_sir; + dev->open = nsc_ircc_net_open; + dev->stop = nsc_ircc_net_close; + dev->do_ioctl = nsc_ircc_net_ioctl; + dev->get_stats = nsc_ircc_net_get_stats; rtnl_lock(); err = register_netdevice(dev); @@ -312,23 +364,33 @@ nsc_fir_open(int i, unsigned int iobase, unsigned int board_addr, ERROR(__FUNCTION__ "(), register_netdev() failed!\n"); return -1; } - MESSAGE("IrDA: Registered device %s\n", dev->name); + + /* Check if user has supplied the dongle id or not */ + if (!dongle_id) { + dongle_id = nsc_ircc_read_dongle_id(self->io.fir_base); + + MESSAGE("%s, Found dongle: %s\n", driver_name, + dongle_types[dongle_id]); + } else { + MESSAGE("%s, Using dongle: %s\n", driver_name, + dongle_types[dongle_id]); + } self->io.dongle_id = dongle_id; - nsc_fir_init_dongle_interface(iobase, dongle_id); + nsc_ircc_init_dongle_interface(self->io.fir_base, dongle_id); return 0; } #ifdef MODULE /* - * Function nsc_fir_close (self) + * Function nsc_ircc_close (self) * * Close driver instance * */ -static int nsc_fir_close(struct nsc_fir_cb *self) +static int nsc_ircc_close(struct nsc_ircc_cb *self) { int iobase; @@ -336,7 +398,7 @@ static int nsc_fir_close(struct nsc_fir_cb *self) ASSERT(self != NULL, return -1;); - iobase = self->io.iobase; + iobase = self->io.fir_base; /* Remove netdevice */ if (self->netdev) { @@ -347,8 +409,8 @@ static int nsc_fir_close(struct nsc_fir_cb *self) /* Release the PORT that this driver is using */ IRDA_DEBUG(4, __FUNCTION__ "(), Releasing Region %03x\n", - self->io.iobase); - release_region(self->io.iobase, self->io.io_ext); + self->io.fir_base); + release_region(self->io.fir_base, self->io.fir_ext); if (self->tx_buff.head) kfree(self->tx_buff.head); @@ -356,37 +418,39 @@ static int nsc_fir_close(struct nsc_fir_cb *self) if (self->rx_buff.head) kfree(self->rx_buff.head); + dev_self[self->index] = NULL; kfree(self); - + return 0; } #endif /* MODULE */ /* - * Function nsc_fir_init_807 (iobase, board_addr, irq, dma) + * Function nsc_ircc_init_108 (iobase, cfg_base, irq, dma) * * Initialize the NSC '108 chip * */ -static void nsc_fir_init_807(int iobase, int board_addr, int irq, int dma) +static int nsc_ircc_init_108(nsc_chip_t *chip, chipio_t *info) { + int cfg_base = info->cfg_base; __u8 temp=0; - outb(2, board_addr); /* Mode Control Register (MCTL) */ - outb(0x00, board_addr+1); /* Disable device */ + outb(2, cfg_base); /* Mode Control Register (MCTL) */ + outb(0x00, cfg_base+1); /* Disable device */ /* Base Address and Interrupt Control Register (BAIC) */ - outb(0, board_addr); - switch (iobase) { - case 0x3e8: outb(0x14, board_addr+1); break; - case 0x2e8: outb(0x15, board_addr+1); break; - case 0x3f8: outb(0x16, board_addr+1); break; - case 0x2f8: outb(0x17, board_addr+1); break; + outb(0, cfg_base); + switch (info->fir_base) { + case 0x3e8: outb(0x14, cfg_base+1); break; + case 0x2e8: outb(0x15, cfg_base+1); break; + case 0x3f8: outb(0x16, cfg_base+1); break; + case 0x2f8: outb(0x17, cfg_base+1); break; default: ERROR(__FUNCTION__ "(), invalid base_address"); } /* Control Signal Routing Register (CSRT) */ - switch (irq) { + switch (info->irq) { case 3: temp = 0x01; break; case 4: temp = 0x02; break; case 5: temp = 0x03; break; @@ -396,90 +460,242 @@ static void nsc_fir_init_807(int iobase, int board_addr, int irq, int dma) case 15: temp = 0x07; break; default: ERROR(__FUNCTION__ "(), invalid irq"); } - outb(1, board_addr); + outb(1, cfg_base); - switch (dma) { - case 0: outb(0x08+temp, board_addr+1); break; - case 1: outb(0x10+temp, board_addr+1); break; - case 3: outb(0x18+temp, board_addr+1); break; + switch (info->dma) { + case 0: outb(0x08+temp, cfg_base+1); break; + case 1: outb(0x10+temp, cfg_base+1); break; + case 3: outb(0x18+temp, cfg_base+1); break; default: ERROR(__FUNCTION__ "(), invalid dma"); } - outb(2, board_addr); /* Mode Control Register (MCTL) */ - outb(0x03, board_addr+1); /* Enable device */ + outb(2, cfg_base); /* Mode Control Register (MCTL) */ + outb(0x03, cfg_base+1); /* Enable device */ + + return 0; +} + +/* + * Function nsc_ircc_probe_108 (chip, info) + * + * + * + */ +static int nsc_ircc_probe_108(nsc_chip_t *chip, chipio_t *info) +{ + int cfg_base = info->cfg_base; + int reg; + + /* Read address and interrupt control register (BAIC) */ + outb(CFG_BAIC, cfg_base); + reg = inb(cfg_base+1); + + switch (reg & 0x03) { + case 0: + info->fir_base = 0x3e8; + break; + case 1: + info->fir_base = 0x2e8; + break; + case 2: + info->fir_base = 0x3f8; + break; + case 3: + info->fir_base = 0x2f8; + break; + } + info->sir_base = info->fir_base; + IRDA_DEBUG(2, __FUNCTION__ "(), probing fir_base=0x%03x\n", + info->fir_base); + + /* Read control signals routing register (CSRT) */ + outb(CFG_CSRT, cfg_base); + reg = inb(cfg_base+1); + + switch (reg & 0x07) { + case 0: + info->irq = -1; + break; + case 1: + info->irq = 3; + break; + case 2: + info->irq = 4; + break; + case 3: + info->irq = 5; + break; + case 4: + info->irq = 7; + break; + case 5: + info->irq = 9; + break; + case 6: + info->irq = 11; + break; + case 7: + info->irq = 15; + break; + } + IRDA_DEBUG(2, __FUNCTION__ "(), probing irq=%d\n", info->irq); + + /* Currently we only read Rx DMA but it will also be used for Tx */ + switch ((reg >> 3) & 0x03) { + case 0: + info->dma = -1; + break; + case 1: + info->dma = 0; + break; + case 2: + info->dma = 1; + break; + case 3: + info->dma = 3; + break; + } + IRDA_DEBUG(2, __FUNCTION__ "(), probing dma=%d\n", info->dma); + + /* Read mode control register (MCTL) */ + outb(CFG_MCTL, cfg_base); + reg = inb(cfg_base+1); + + info->enabled = reg & 0x01; + info->suspended = !((reg >> 1) & 0x01); + + return 0; } /* - * Function nsc_fir_init_338 (iobase, board_addr, irq, dma) + * Function nsc_ircc_init_338 (chip, info) * * Initialize the NSC '338 chip. Remember that the 87338 needs two * consecutive writes to the data registers while CPU interrupts are * disabled. The 97338 does not require this, but shouldn't be any * harm if we do it anyway. */ -static void nsc_fir_init_338(int iobase, int board_addr, int irq, int dma) +static int nsc_ircc_init_338(nsc_chip_t *chip, chipio_t *info) { /* No init yet */ + + return 0; } -static int nsc_fir_find_chip(int board_addr) +/* + * Function nsc_ircc_probe_338 (chip, info) + * + * + * + */ +static int nsc_ircc_probe_338(nsc_chip_t *chip, chipio_t *info) { - __u8 index, id; + int cfg_base = info->cfg_base; + int reg, com = 0; + int pnp; - IRDA_DEBUG(4, __FUNCTION__ "()\n"); + /* Read funtion enable register (FER) */ + outb(CFG_FER, cfg_base); + reg = inb(cfg_base+1); - /* Read index register */ - index = inb(board_addr); - if (index == 0xff) { - IRDA_DEBUG(0, __FUNCTION__ "(), no chip at 0x%03x\n", - board_addr); - return -1; - } + info->enabled = (reg >> 2) & 0x01; + + /* Check if we are in Legacy or PnP mode */ + outb(CFG_PNP0, cfg_base); + reg = inb(cfg_base+1); + + pnp = (reg >> 4) & 0x01; + if (pnp) { + IRDA_DEBUG(2, "(), Chip is in PnP mode\n"); + outb(0x46, cfg_base); + reg = (inb(cfg_base+1) & 0xfe) << 2; + + outb(0x47, cfg_base); + reg |= ((inb(cfg_base+1) & 0xfc) << 8); - /* Read chip identification register (SID) for the PC97338 */ - outb(8, board_addr); - id = inb(board_addr+1); - if ((id & 0xf0) == PC97338) { - MESSAGE("%s, Found NSC PC97338 chip, revision=%d\n", - driver_name, id & 0x0f); - return PC97338; - } - - /* Read device identification (DID) for the PC87108 */ - outb(5, board_addr); - id = inb(board_addr+1); - if ((id & 0xf0) == PC87108) { - MESSAGE("%s, Found NSC PC87108 chip, revision=%d\n", - driver_name, id & 0x0f); - return PC87108; + info->fir_base = reg; + } else { + /* Read function address register (FAR) */ + outb(CFG_FAR, cfg_base); + reg = inb(cfg_base+1); + + switch ((reg >> 4) & 0x03) { + case 0: + info->fir_base = 0x3f8; + break; + case 1: + info->fir_base = 0x2f8; + break; + case 2: + com = 3; + break; + case 3: + com = 4; + break; + } + + if (com) { + switch ((reg >> 6) & 0x03) { + case 0: + if (com == 3) + info->fir_base = 0x3e8; + else + info->fir_base = 0x2e8; + break; + case 1: + if (com == 3) + info->fir_base = 0x338; + else + info->fir_base = 0x238; + break; + case 2: + if (com == 3) + info->fir_base = 0x2e8; + else + info->fir_base = 0x2e0; + break; + case 3: + if (com == 3) + info->fir_base = 0x220; + else + info->fir_base = 0x228; + break; + } + } } + info->sir_base = info->fir_base; + + /* Read PnP register 1 (PNP1) */ + outb(CFG_PNP1, cfg_base); + reg = inb(cfg_base+1); + + info->irq = reg >> 4; + + /* Read PnP register 3 (PNP3) */ + outb(CFG_PNP3, cfg_base); + reg = inb(cfg_base+1); + + info->dma = (reg & 0x07) - 1; - return -1; + /* Read power and test register (PTR) */ + outb(CFG_PTR, cfg_base); + reg = inb(cfg_base+1); + + info->suspended = reg & 0x01; + + return 0; } /* - * Function nsc_fir_probe (iobase, board_addr, irq, dma) + * Function nsc_ircc_setup (info) * * Returns non-negative on success. * */ -static int nsc_fir_probe(int iobase, int board_addr, int irq, int dma) +static int nsc_ircc_setup(chipio_t *info) { int version; - __u8 chip; - - chip = nsc_fir_find_chip(board_addr); - switch (chip) { - case PC87108: - nsc_fir_init_807(iobase, board_addr, irq, dma); - break; - case PC97338: - nsc_fir_init_338(iobase, board_addr, irq, dma); - break; - default: - /* Found no chip */ - return -1; - } + int iobase = info->fir_base; /* Read the Module ID */ switch_bank(iobase, BANK3); @@ -490,23 +706,13 @@ static int nsc_fir_probe(int iobase, int board_addr, int irq, int dma) ERROR("%s, Wrong chip version %02x\n", driver_name, version); return -1; } - MESSAGE("%s, Found chip at base=0x%04x\n", driver_name, board_addr); + MESSAGE("%s, Found chip at base=0x%03x\n", driver_name, + info->cfg_base); /* Switch to advanced mode */ switch_bank(iobase, BANK2); outb(ECR1_EXT_SL, iobase+ECR1); switch_bank(iobase, BANK0); - - /* Check if user has supplied the dongle id or not */ - if (!dongle_id) { - dongle_id = nsc_fir_read_dongle_id(iobase); - - MESSAGE("%s, Found dongle: %s\n", driver_name, - dongle_types[dongle_id]); - } else { - MESSAGE("%s, Using dongle: %s\n", driver_name, - dongle_types[dongle_id]); - } /* Set FIFO threshold to TX17, RX16, reset and enable FIFO's */ switch_bank(iobase, BANK0); @@ -533,24 +739,21 @@ static int nsc_fir_probe(int iobase, int board_addr, int irq, int dma) switch_bank(iobase, BANK0); outb(IER_RXHDL_IE, iobase+IER); - return dongle_id; + return 0; } /* - * Function nsc_fir_read_dongle_id (void) + * Function nsc_ircc_read_dongle_id (void) * * Try to read dongle indentification. This procedure needs to be executed * once after power-on/reset. It also needs to be used whenever you suspect * that the user may have plugged/unplugged the IrDA Dongle. - * */ -static int nsc_fir_read_dongle_id (int iobase) +static int nsc_ircc_read_dongle_id (int iobase) { int dongle_id; __u8 bank; - IRDA_DEBUG(4, __FUNCTION__ "()\n"); - bank = inb(iobase+BSR); /* Select Bank 7 */ @@ -568,8 +771,7 @@ static int nsc_fir_read_dongle_id (int iobase) #ifdef BROKEN_DONGLE_ID if (dongle_id == 0x0a) dongle_id = 0x09; -#endif - +#endif /* Go back to bank 0 before returning */ switch_bank(iobase, BANK0); @@ -579,14 +781,14 @@ static int nsc_fir_read_dongle_id (int iobase) } /* - * Function nsc_fir_init_dongle_interface (iobase, dongle_id) + * Function nsc_ircc_init_dongle_interface (iobase, dongle_id) * * This function initializes the dongle for the transceiver that is * used. This procedure needs to be executed once after * power-on/reset. It also needs to be used whenever you suspect that * the dongle is changed. */ -static void nsc_fir_init_dongle_interface (int iobase, int dongle_id) +static void nsc_ircc_init_dongle_interface (int iobase, int dongle_id) { int bank; @@ -625,11 +827,11 @@ static void nsc_fir_init_dongle_interface (int iobase, int dongle_id) dongle_types[dongle_id]); break; case 0x08: /* HP HSDL-2300, HP HSDL-3600/HSDL-3610 */ - IRDA_DEBUG(0, __FUNCTION__ "(), %s not supported yet\n", - dongle_types[dongle_id]); + IRDA_DEBUG(0, __FUNCTION__ "(), %s\n", + dongle_types[dongle_id]); break; case 0x09: /* IBM31T1100 or Temic TFDS6000/TFDS6500 */ - outb_p(0x28, iobase+7); /* Set irsl[0-2] as output */ + outb(0x28, iobase+7); /* Set irsl[0-2] as output */ break; case 0x0A: /* same as */ case 0x0B: /* Reserved */ @@ -668,18 +870,16 @@ static void nsc_fir_init_dongle_interface (int iobase, int dongle_id) } /* set_up_dongle_interface */ /* - * Function nsc_fir_change_dongle_speed (iobase, speed, dongle_id) + * Function nsc_ircc_change_dongle_speed (iobase, speed, dongle_id) * * Change speed of the attach dongle * */ -static void nsc_fir_change_dongle_speed(int iobase, int speed, int dongle_id) +static void nsc_ircc_change_dongle_speed(int iobase, int speed, int dongle_id) { unsigned long flags; __u8 bank; - IRDA_DEBUG(4, __FUNCTION__ "()\n"); - /* Save current bank */ bank = inb(iobase+BSR); @@ -714,11 +914,14 @@ static void nsc_fir_change_dongle_speed(int iobase, int speed, int dongle_id) dongle_types[dongle_id]); break; case 0x08: /* HP HSDL-2300, HP HSDL-3600/HSDL-3610 */ - IRDA_DEBUG(0, __FUNCTION__ "(), %s not supported yet\n", + IRDA_DEBUG(0, __FUNCTION__ "(), %s\n", dongle_types[dongle_id]); + outb(0x00, iobase+4); + if (speed > 115200) + outb(0x01, iobase+4); + break; case 0x09: /* IBM31T1100 or Temic TFDS6000/TFDS6500 */ - switch_bank(iobase, BANK7); - outb_p(0x01, iobase+4); + outb(0x01, iobase+4); if (speed == 4000000) { save_flags(flags); @@ -727,7 +930,7 @@ static void nsc_fir_change_dongle_speed(int iobase, int speed, int dongle_id) outb(0x80, iobase+4); restore_flags(flags); } else - outb_p(0x00, iobase+4); + outb(0x00, iobase+4); break; case 0x0A: /* same as */ case 0x0B: /* Reserved */ @@ -754,12 +957,12 @@ static void nsc_fir_change_dongle_speed(int iobase, int speed, int dongle_id) } /* - * Function nsc_fir_change_speed (self, baud) + * Function nsc_ircc_change_speed (self, baud) * * Change the speed of the device * */ -static void nsc_fir_change_speed(struct nsc_fir_cb *self, __u32 speed) +static void nsc_ircc_change_speed(struct nsc_ircc_cb *self, __u32 speed) { struct net_device *dev = self->netdev; __u8 mcr = MCR_SIR; @@ -770,7 +973,7 @@ static void nsc_fir_change_speed(struct nsc_fir_cb *self, __u32 speed) ASSERT(self != NULL, return;); - iobase = self->io.iobase; + iobase = self->io.fir_base; /* Update accounting for new speed */ self->io.speed = speed; @@ -821,7 +1024,7 @@ static void nsc_fir_change_speed(struct nsc_fir_cb *self, __u32 speed) outb(mcr | MCR_TX_DFR, iobase+MCR); /* Give some hits to the transceiver */ - nsc_fir_change_dongle_speed(iobase, speed, self->io.dongle_id); + nsc_ircc_change_dongle_speed(iobase, speed, self->io.dongle_id); /* Set FIFO threshold to TX17, RX16 */ switch_bank(iobase, BANK0); @@ -844,12 +1047,12 @@ static void nsc_fir_change_speed(struct nsc_fir_cb *self, __u32 speed) switch_bank(iobase, BANK0); if (speed > 115200) { /* Install FIR xmit handler */ - dev->hard_start_xmit = nsc_fir_hard_xmit_fir; + dev->hard_start_xmit = nsc_ircc_hard_xmit_fir; outb(IER_SFIF_IE, iobase+IER); - nsc_fir_dma_receive(self); + nsc_ircc_dma_receive(self); } else { /* Install SIR xmit handler */ - dev->hard_start_xmit = nsc_fir_hard_xmit_sir; + dev->hard_start_xmit = nsc_ircc_hard_xmit_sir; outb(IER_RXHDL_IE, iobase+IER); } @@ -858,24 +1061,24 @@ static void nsc_fir_change_speed(struct nsc_fir_cb *self, __u32 speed) } /* - * Function nsc_fir_hard_xmit (skb, dev) + * Function nsc_ircc_hard_xmit (skb, dev) * * Transmit the frame! * */ -static int nsc_fir_hard_xmit_sir(struct sk_buff *skb, struct net_device *dev) +static int nsc_ircc_hard_xmit_sir(struct sk_buff *skb, struct net_device *dev) { - struct nsc_fir_cb *self; + struct nsc_ircc_cb *self; unsigned long flags; int iobase; __u32 speed; __u8 bank; - self = (struct nsc_fir_cb *) dev->priv; + self = (struct nsc_ircc_cb *) dev->priv; ASSERT(self != NULL, return 0;); - iobase = self->io.iobase; + iobase = self->io.fir_base; /* Lock transmit buffer */ if (irda_lock((void *) &dev->tbusy) == FALSE) @@ -894,6 +1097,8 @@ static int nsc_fir_hard_xmit_sir(struct sk_buff *skb, struct net_device *dev) self->tx_buff.len = async_wrap_skb(skb, self->tx_buff.data, self->tx_buff.truesize); + + self->stats.tx_bytes += self->tx_buff.len; /* Add interrupt on tx low level (will fire immediately) */ switch_bank(iobase, BANK0); @@ -909,20 +1114,17 @@ static int nsc_fir_hard_xmit_sir(struct sk_buff *skb, struct net_device *dev) return 0; } -static int nsc_fir_hard_xmit_fir(struct sk_buff *skb, struct net_device *dev) +static int nsc_ircc_hard_xmit_fir(struct sk_buff *skb, struct net_device *dev) { - struct nsc_fir_cb *self; + struct nsc_ircc_cb *self; unsigned long flags; int iobase; __u32 speed; __u8 bank; int mtt, diff; - self = (struct nsc_fir_cb *) dev->priv; - - ASSERT(self != NULL, return 0;); - - iobase = self->io.iobase; + self = (struct nsc_ircc_cb *) dev->priv; + iobase = self->io.fir_base; /* Lock transmit buffer */ if (irda_lock((void *) &dev->tbusy) == FALSE) @@ -942,6 +1144,8 @@ static int nsc_fir_hard_xmit_fir(struct sk_buff *skb, struct net_device *dev) self->tx_fifo.queue[self->tx_fifo.free].len = skb->len; self->tx_fifo.tail += skb->len; + self->stats.tx_bytes += skb->len; + memcpy(self->tx_fifo.queue[self->tx_fifo.free].start, skb->data, skb->len); @@ -950,10 +1154,11 @@ static int nsc_fir_hard_xmit_fir(struct sk_buff *skb, struct net_device *dev) /* Start transmit only if there is currently no transmit going on */ if (self->tx_fifo.len == 1) { + /* Check if we must wait the min turn time or not */ mtt = irda_get_mtt(skb); if (mtt) { /* Check how much time we have used already */ - do_gettimeofday(&self->now); + get_fast_time(&self->now); diff = self->now.tv_usec - self->stamp.tv_usec; if (diff < 0) diff += 1000000; @@ -963,9 +1168,15 @@ static int nsc_fir_hard_xmit_fir(struct sk_buff *skb, struct net_device *dev) */ if (mtt > diff) { mtt -= diff; + + /* + * Use timer if delay larger than 125 us, and + * use udelay for smaller values which should + * be acceptable + */ if (mtt > 125) { /* Adjust for timer resolution */ - mtt = mtt / 125 + 1; + mtt = mtt / 125; /* Setup timer */ switch_bank(iobase, BANK4); @@ -985,34 +1196,35 @@ static int nsc_fir_hard_xmit_fir(struct sk_buff *skb, struct net_device *dev) } else udelay(mtt); } - } - + } /* Enable DMA interrupt */ switch_bank(iobase, BANK0); outb(IER_DMA_IE, iobase+IER); - nsc_fir_dma_xmit(self, iobase); + + /* Transmit frame */ + nsc_ircc_dma_xmit(self, iobase); } + out: /* Not busy transmitting anymore if window is not full */ - if (self->tx_fifo.len < MAX_WINDOW) + if (self->tx_fifo.free < MAX_TX_WINDOW) dev->tbusy = 0; - out: + /* Restore bank register */ outb(bank, iobase+BSR); spin_unlock_irqrestore(&self->lock, flags); - dev_kfree_skb(skb); return 0; } /* - * Function nsc_fir_dma_xmit (self, iobase) + * Function nsc_ircc_dma_xmit (self, iobase) * * Transmit data using DMA * */ -static void nsc_fir_dma_xmit(struct nsc_fir_cb *self, int iobase) +static void nsc_ircc_dma_xmit(struct nsc_ircc_cb *self, int iobase) { int bsr; @@ -1043,13 +1255,13 @@ static void nsc_fir_dma_xmit(struct nsc_fir_cb *self, int iobase) } /* - * Function nsc_fir_pio_xmit (self, iobase) + * Function nsc_ircc_pio_xmit (self, iobase) * * Transmit data using PIO. Returns the number of bytes that actually * got transfered * */ -static int nsc_fir_pio_write(int iobase, __u8 *buf, int len, int fifo_size) +static int nsc_ircc_pio_write(int iobase, __u8 *buf, int len, int fifo_size) { int actual = 0; __u8 bank; @@ -1064,6 +1276,7 @@ static int nsc_fir_pio_write(int iobase, __u8 *buf, int len, int fifo_size) IRDA_DEBUG(4, __FUNCTION__ "(), warning, FIFO not empty yet!\n"); + /* FIFO may still be filled to the Tx interrupt threshold */ fifo_size -= 17; } @@ -1083,13 +1296,13 @@ static int nsc_fir_pio_write(int iobase, __u8 *buf, int len, int fifo_size) } /* - * Function nsc_fir_dma_xmit_complete (self) + * Function nsc_ircc_dma_xmit_complete (self) * * The transfer of a frame in finished. This function will only be called * by the interrupt handler * */ -static int nsc_fir_dma_xmit_complete(struct nsc_fir_cb *self) +static int nsc_ircc_dma_xmit_complete(struct nsc_ircc_cb *self) { int iobase; __u8 bank; @@ -1097,7 +1310,7 @@ static int nsc_fir_dma_xmit_complete(struct nsc_fir_cb *self) IRDA_DEBUG(2, __FUNCTION__ "()\n"); - iobase = self->io.iobase; + iobase = self->io.fir_base; /* Save current bank */ bank = inb(iobase+BSR); @@ -1115,11 +1328,11 @@ static int nsc_fir_dma_xmit_complete(struct nsc_fir_cb *self) outb(ASCR_TXUR, iobase+ASCR); } else { self->stats.tx_packets++; - self->stats.tx_bytes += self->tx_buff.len; } - + + /* Check if we need to change the speed */ if (self->new_speed) { - nsc_fir_change_speed(self, self->new_speed); + nsc_ircc_change_speed(self, self->new_speed); self->new_speed = 0; } @@ -1129,17 +1342,24 @@ static int nsc_fir_dma_xmit_complete(struct nsc_fir_cb *self) /* Any frames to be sent back-to-back? */ if (self->tx_fifo.len) { - nsc_fir_dma_xmit(self, iobase); + nsc_ircc_dma_xmit(self, iobase); /* Not finished yet! */ ret = FALSE; + } else { + /* Reset Tx FIFO info */ + self->tx_fifo.len = self->tx_fifo.ptr = self->tx_fifo.free = 0; + self->tx_fifo.tail = self->tx_buff.head; } - /* Not busy transmitting anymore */ - self->netdev->tbusy = 0; + /* Make sure we have room for more frames */ + if (self->tx_fifo.free < MAX_TX_WINDOW) { + /* Not busy transmitting anymore */ + self->netdev->tbusy = 0; - /* Tell the network layer, that we can accept more frames */ - mark_bh(NET_BH); + /* Tell the network layer, that we can accept more frames */ + mark_bh(NET_BH); + } /* Restore bank */ outb(bank, iobase+BSR); @@ -1148,20 +1368,18 @@ static int nsc_fir_dma_xmit_complete(struct nsc_fir_cb *self) } /* - * Function nsc_fir_dma_receive (self) + * Function nsc_ircc_dma_receive (self) * * Get ready for receiving a frame. The device will initiate a DMA * if it starts to receive a frame. * */ -static int nsc_fir_dma_receive(struct nsc_fir_cb *self) +static int nsc_ircc_dma_receive(struct nsc_ircc_cb *self) { int iobase; __u8 bsr; - ASSERT(self != NULL, return -1;); - - iobase = self->io.iobase; + iobase = self->io.fir_base; /* Reset Tx FIFO info */ self->tx_fifo.len = self->tx_fifo.ptr = self->tx_fifo.free = 0; @@ -1184,7 +1402,9 @@ static int nsc_fir_dma_receive(struct nsc_fir_cb *self) /* Reset Rx FIFO. This will also flush the ST_FIFO */ switch_bank(iobase, BANK0); outb(FCR_RXSR|FCR_FIFO_EN, iobase+FCR); - self->st_fifo.len = self->st_fifo.tail = self->st_fifo.head = 0; + + self->st_fifo.len = self->st_fifo.pending_bytes = 0; + self->st_fifo.tail = self->st_fifo.head = 0; setup_dma(self->io.dma, self->rx_buff.data, self->rx_buff.truesize, DMA_RX_MODE); @@ -1200,18 +1420,18 @@ static int nsc_fir_dma_receive(struct nsc_fir_cb *self) } /* - * Function nsc_fir_dma_receive_complete (self) + * Function nsc_ircc_dma_receive_complete (self) * * Finished with receiving frames * * */ -static int nsc_fir_dma_receive_complete(struct nsc_fir_cb *self, int iobase) +static int nsc_ircc_dma_receive_complete(struct nsc_ircc_cb *self, int iobase) { - struct sk_buff *skb; struct st_fifo *st_fifo; - __u8 bank; + struct sk_buff *skb; __u8 status; + __u8 bank; int len; st_fifo = &self->st_fifo; @@ -1219,22 +1439,27 @@ static int nsc_fir_dma_receive_complete(struct nsc_fir_cb *self, int iobase) /* Save current bank */ bank = inb(iobase+BSR); - /* Read status FIFO */ + /* Read all entries in status FIFO */ switch_bank(iobase, BANK5); while ((status = inb(iobase+FRM_ST)) & FRM_ST_VLD) { - st_fifo->entries[st_fifo->tail].status = status; + /* We must empty the status FIFO no matter what */ + len = inb(iobase+RFLFL) | ((inb(iobase+RFLFH) & 0x1f) << 8); - st_fifo->entries[st_fifo->tail].len = inb(iobase+RFLFL); - st_fifo->entries[st_fifo->tail].len |= inb(iobase+RFLFH) << 8; - + if (st_fifo->tail >= MAX_RX_WINDOW) + continue; + + st_fifo->entries[st_fifo->tail].status = status; + st_fifo->entries[st_fifo->tail].len = len; + st_fifo->pending_bytes += len; st_fifo->tail++; st_fifo->len++; } /* Try to process all entries in status FIFO */ - while (st_fifo->len) { + while (st_fifo->len > 0) { /* Get first entry */ status = st_fifo->entries[st_fifo->head].status; len = st_fifo->entries[st_fifo->head].len; + st_fifo->pending_bytes -= len; st_fifo->head++; st_fifo->len--; @@ -1265,36 +1490,46 @@ static int nsc_fir_dma_receive_complete(struct nsc_fir_cb *self, int iobase) if (status & FRM_ST_OVR2) self->stats.rx_fifo_errors++; } else { - /* Check if we have transfered all data to memory */ - switch_bank(iobase, BANK0); - if (inb(iobase+LSR) & LSR_RXDA) { - /* Put this entry back in fifo */ - st_fifo->head--; - st_fifo->len++; - st_fifo->entries[st_fifo->head].status = status; - st_fifo->entries[st_fifo->head].len = len; - - /* Restore bank register */ - outb(bank, iobase+BSR); + /* + * First we must make sure that the frame we + * want to deliver is all in main memory. If we + * cannot tell, then we check if the Rx FIFO is + * empty. If not then we will have to take a nap + * and try again later. + */ + if (st_fifo->pending_bytes < self->io.fifo_size) { + switch_bank(iobase, BANK0); + if (inb(iobase+LSR) & LSR_RXDA) { + /* Put this entry back in fifo */ + st_fifo->head--; + st_fifo->len++; + st_fifo->pending_bytes += len; + st_fifo->entries[st_fifo->head].status = status; + st_fifo->entries[st_fifo->head].len = len; - return FALSE; /* I'll be back! */ + /* Restore bank register */ + outb(bank, iobase+BSR); + + return FALSE; /* I'll be back! */ + } } /* - * Remember when we received this frame, so we can + * Remember the time we received this frame, so we can * reduce the min turn time a bit since we will know * how much time we have used for protocol processing */ - do_gettimeofday(&self->stamp); + get_fast_time(&self->stamp); skb = dev_alloc_skb(len+1); if (skb == NULL) { WARNING(__FUNCTION__ "(), memory squeeze, " "dropping frame.\n"); - + self->stats.rx_dropped++; + /* Restore bank register */ outb(bank, iobase+BSR); - + return FALSE; } @@ -1312,6 +1547,7 @@ static int nsc_fir_dma_receive_complete(struct nsc_fir_cb *self, int iobase) /* Move to next frame */ self->rx_buff.data += len; + self->stats.rx_bytes += len; self->stats.rx_packets++; skb->dev = self->netdev; @@ -1327,21 +1563,17 @@ static int nsc_fir_dma_receive_complete(struct nsc_fir_cb *self, int iobase) } /* - * Function nsc_fir_pio_receive (self) + * Function nsc_ircc_pio_receive (self) * * Receive all data in receiver FIFO * */ -static void nsc_fir_pio_receive(struct nsc_fir_cb *self) +static void nsc_ircc_pio_receive(struct nsc_ircc_cb *self) { - __u8 byte = 0x00; + __u8 byte; int iobase; - IRDA_DEBUG(4, __FUNCTION__ "()\n"); - - ASSERT(self != NULL, return;); - - iobase = self->io.iobase; + iobase = self->io.fir_base; /* Receive all characters in Rx FIFO */ do { @@ -1352,20 +1584,19 @@ static void nsc_fir_pio_receive(struct nsc_fir_cb *self) } /* - * Function nsc_fir_sir_interrupt (self, eir) + * Function nsc_ircc_sir_interrupt (self, eir) * * Handle SIR interrupt * */ -static __u8 nsc_fir_sir_interrupt(struct nsc_fir_cb *self, int eir) +static void nsc_ircc_sir_interrupt(struct nsc_ircc_cb *self, int eir) { int actual; - __u8 new_ier = 0; /* Check if transmit FIFO is low on data */ if (eir & EIR_TXLDL_EV) { /* Write data left in transmit buffer */ - actual = nsc_fir_pio_write(self->io.iobase, + actual = nsc_ircc_pio_write(self->io.fir_base, self->tx_buff.data, self->tx_buff.len, self->io.fifo_size); @@ -1376,14 +1607,14 @@ static __u8 nsc_fir_sir_interrupt(struct nsc_fir_cb *self, int eir) /* Check if finished */ if (self->tx_buff.len > 0) - new_ier |= IER_TXLDL_IE; + self->ier = IER_TXLDL_IE; else { self->netdev->tbusy = 0; /* Unlock */ self->stats.tx_packets++; mark_bh(NET_BH); - new_ier |= IER_TXEMP_IE; + self->ier = IER_TXEMP_IE; } } @@ -1392,56 +1623,62 @@ static __u8 nsc_fir_sir_interrupt(struct nsc_fir_cb *self, int eir) /* Check if we need to change the speed? */ if (self->new_speed) { IRDA_DEBUG(2, __FUNCTION__ "(), Changing speed!\n"); - nsc_fir_change_speed(self, self->new_speed); + nsc_ircc_change_speed(self, self->new_speed); self->new_speed = 0; - } + /* Check if we are going to FIR */ + if (self->io.speed > 115200) { + /* Should wait for status FIFO interrupt */ + self->ier = IER_SFIF_IE; + + /* No need to do anymore SIR stuff */ + return; + } + } /* Turn around and get ready to receive some data */ self->io.direction = IO_RECV; - new_ier |= IER_RXHDL_IE; + self->ier = IER_RXHDL_IE; } /* Rx FIFO threshold or timeout */ if (eir & EIR_RXHDL_EV) { - nsc_fir_pio_receive(self); + nsc_ircc_pio_receive(self); /* Keep receiving */ - new_ier |= IER_RXHDL_IE; + self->ier = IER_RXHDL_IE; } - return new_ier; } /* - * Function nsc_fir_fir_interrupt (self, eir) + * Function nsc_ircc_fir_interrupt (self, eir) * * Handle MIR/FIR interrupt * */ -static __u8 nsc_fir_fir_interrupt(struct nsc_fir_cb *self, int iobase, int eir) +static void nsc_ircc_fir_interrupt(struct nsc_ircc_cb *self, int iobase, + int eir) { - __u8 new_ier = 0; __u8 bank; bank = inb(iobase+BSR); - /* Status event, or end of frame detected in FIFO */ - if (eir & (EIR_SFIF_EV|EIR_LS_EV)) { - if (nsc_fir_dma_receive_complete(self, iobase)) { - + /* Status FIFO event*/ + if (eir & EIR_SFIF_EV) { + if (nsc_ircc_dma_receive_complete(self, iobase)) { /* Wait for next status FIFO interrupt */ - new_ier |= IER_SFIF_IE; + self->ier = IER_SFIF_IE; } else { - /* DMA not finished yet */ - - /* Set timer value, resolution 125 us */ + /* + * DMA not finished yet, so try again later, set + * timer value, resolution 125 us + */ switch_bank(iobase, BANK4); - outb(0x0f, iobase+TMRL); /* 125 us * 15 */ + outb(0x02, iobase+TMRL); /* 2 * 125 us */ outb(0x00, iobase+TMRH); /* Start timer */ outb(IRCR1_TMR_EN, iobase+IRCR1); - - new_ier |= IER_TMR_IE; + self->ier = IER_TMR_IE | IER_SFIF_IE; } } else if (eir & EIR_TMR_EV) { /* Timer finished */ /* Disable timer */ @@ -1452,101 +1689,102 @@ static __u8 nsc_fir_fir_interrupt(struct nsc_fir_cb *self, int iobase, int eir) switch_bank(iobase, BANK0); outb(ASCR_CTE, iobase+ASCR); - /* Check if this is a TX timer interrupt */ + /* Check if this is a Tx timer interrupt */ if (self->io.direction == IO_XMIT) { - nsc_fir_dma_xmit(self, iobase); + nsc_ircc_dma_xmit(self, iobase); /* Interrupt on DMA */ - new_ier |= IER_DMA_IE; + self->ier = IER_DMA_IE; } else { /* Check if DMA has now finished */ - nsc_fir_dma_receive_complete(self, iobase); + nsc_ircc_dma_receive_complete(self, iobase); - new_ier |= IER_SFIF_IE; + self->ier = IER_SFIF_IE; } - } else if (eir & EIR_DMA_EV) { /* Finished with transmission */ - if (nsc_fir_dma_xmit_complete(self)) { + } else if (eir & EIR_DMA_EV) { + /* Finished with all transmissions? */ + if (nsc_ircc_dma_xmit_complete(self)) { /* Check if there are more frames to be transmitted */ if (irda_device_txqueue_empty(self->netdev)) { /* Prepare for receive */ - nsc_fir_dma_receive(self); + nsc_ircc_dma_receive(self); - new_ier = IER_LS_IE|IER_SFIF_IE; + self->ier = IER_SFIF_IE; } } else { /* Not finished yet, so interrupt on DMA again */ - new_ier |= IER_DMA_IE; + self->ier = IER_DMA_IE; } } outb(bank, iobase+BSR); - - return new_ier; } /* - * Function nsc_fir_interrupt (irq, dev_id, regs) + * Function nsc_ircc_interrupt (irq, dev_id, regs) * * An interrupt from the chip has arrived. Time to do some work * */ -static void nsc_fir_interrupt(int irq, void *dev_id, struct pt_regs *regs) +static void nsc_ircc_interrupt(int irq, void *dev_id, struct pt_regs *regs) { struct net_device *dev = (struct net_device *) dev_id; - struct nsc_fir_cb *self; - __u8 bsr, eir, ier; + struct nsc_ircc_cb *self; + __u8 bsr, eir; int iobase; if (!dev) { - printk(KERN_WARNING "%s: irq %d for unknown device.\n", - driver_name, irq); + WARNING("%s: irq %d for unknown device.\n", driver_name, irq); return; } - self = (struct nsc_fir_cb *) dev->priv; + self = (struct nsc_ircc_cb *) dev->priv; spin_lock(&self->lock); dev->interrupt = 1; - iobase = self->io.iobase; + iobase = self->io.fir_base; bsr = inb(iobase+BSR); /* Save current bank */ switch_bank(iobase, BANK0); - ier = inb(iobase+IER); - eir = inb(iobase+EIR) & ier; /* Mask out the interesting ones */ + self->ier = inb(iobase+IER); + eir = inb(iobase+EIR) & self->ier; /* Mask out the interesting ones */ outb(0, iobase+IER); /* Disable interrupts */ if (eir) { /* Dispatch interrupt handler for the current speed */ if (self->io.speed > 115200) - ier = nsc_fir_fir_interrupt(self, iobase, eir); + nsc_ircc_fir_interrupt(self, iobase, eir); else - ier = nsc_fir_sir_interrupt(self, eir); + nsc_ircc_sir_interrupt(self, eir); } - - outb(ier, iobase+IER); /* Restore interrupts */ - outb(bsr, iobase+BSR); /* Restore bank register */ + + outb(self->ier, iobase+IER); /* Restore interrupts */ + outb(bsr, iobase+BSR); /* Restore bank register */ dev->interrupt = 0; spin_unlock(&self->lock); } /* - * Function nsc_fir_is_receiving (self) + * Function nsc_ircc_is_receiving (self) * * Return TRUE is we are currently receiving a frame * */ -static int nsc_fir_is_receiving(struct nsc_fir_cb *self) +static int nsc_ircc_is_receiving(struct nsc_ircc_cb *self) { + unsigned long flags; int status = FALSE; int iobase; __u8 bank; ASSERT(self != NULL, return FALSE;); + spin_lock_irqsave(&self->lock, flags); + if (self->io.speed > 115200) { - iobase = self->io.iobase; + iobase = self->io.fir_base; /* Check if rx FIFO is not empty */ bank = inb(iobase+BSR); @@ -1559,16 +1797,18 @@ static int nsc_fir_is_receiving(struct nsc_fir_cb *self) } else status = (self->rx_buff.state != OUTSIDE_FRAME); + spin_unlock_irqrestore(&self->lock, flags); + return status; } /* - * Function nsc_fir_net_init (dev) + * Function nsc_ircc_net_init (dev) * * Initialize network device * */ -static int nsc_fir_net_init(struct net_device *dev) +static int nsc_ircc_net_init(struct net_device *dev) { IRDA_DEBUG(4, __FUNCTION__ "()\n"); @@ -1581,29 +1821,29 @@ static int nsc_fir_net_init(struct net_device *dev) } /* - * Function nsc_fir_net_open (dev) + * Function nsc_ircc_net_open (dev) * * Start the device * */ -static int nsc_fir_net_open(struct net_device *dev) +static int nsc_ircc_net_open(struct net_device *dev) { - struct nsc_fir_cb *self; + struct nsc_ircc_cb *self; int iobase; __u8 bank; IRDA_DEBUG(4, __FUNCTION__ "()\n"); ASSERT(dev != NULL, return -1;); - self = (struct nsc_fir_cb *) dev->priv; + self = (struct nsc_ircc_cb *) dev->priv; ASSERT(self != NULL, return 0;); - iobase = self->io.iobase; - - if (request_irq(self->io.irq, nsc_fir_interrupt, 0, dev->name, - (void *) dev)) - { + iobase = self->io.fir_base; + + if (request_irq(self->io.irq, nsc_ircc_interrupt, 0, dev->name, dev)) { + WARNING("%s, unable to allocate irq=%d\n", driver_name, + self->io.irq); return -EAGAIN; } /* @@ -1611,6 +1851,8 @@ static int nsc_fir_net_open(struct net_device *dev) * failure. */ if (request_dma(self->io.dma, dev->name)) { + WARNING("%s, unable to allocate dma=%d\n", driver_name, + self->io.dma); free_irq(self->io.irq, self); return -EAGAIN; } @@ -1642,22 +1884,22 @@ static int nsc_fir_net_open(struct net_device *dev) } /* - * Function nsc_fir_net_close (dev) + * Function nsc_ircc_net_close (dev) * * Stop the device * */ -static int nsc_fir_net_close(struct net_device *dev) +static int nsc_ircc_net_close(struct net_device *dev) { - struct nsc_fir_cb *self; + struct nsc_ircc_cb *self; int iobase; __u8 bank; IRDA_DEBUG(4, __FUNCTION__ "()\n"); ASSERT(dev != NULL, return -1;); - self = (struct nsc_fir_cb *) dev->priv; - + + self = (struct nsc_ircc_cb *) dev->priv; ASSERT(self != NULL, return 0;); /* Stop device */ @@ -1669,7 +1911,7 @@ static int nsc_fir_net_close(struct net_device *dev) irlap_close(self->irlap); self->irlap = NULL; - iobase = self->io.iobase; + iobase = self->io.fir_base; disable_dma(self->io.dma); @@ -1692,15 +1934,15 @@ static int nsc_fir_net_close(struct net_device *dev) } /* - * Function nsc_fir_net_ioctl (dev, rq, cmd) + * Function nsc_ircc_net_ioctl (dev, rq, cmd) * * Process IOCTL commands for this device * */ -static int nsc_fir_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +static int nsc_ircc_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) { struct if_irda_req *irq = (struct if_irda_req *) rq; - struct nsc_fir_cb *self; + struct nsc_ircc_cb *self; unsigned long flags; int ret = 0; @@ -1718,13 +1960,13 @@ static int nsc_fir_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) switch (cmd) { case SIOCSBANDWIDTH: /* Set bandwidth */ - nsc_fir_change_speed(self, irq->ifr_baudrate); + nsc_ircc_change_speed(self, irq->ifr_baudrate); break; case SIOCSMEDIABUSY: /* Set media busy */ irda_device_set_media_busy(self->netdev, TRUE); break; case SIOCGRECEIVING: /* Check if we are receiving right now */ - irq->ifr_receiving = nsc_fir_is_receiving(self); + irq->ifr_receiving = nsc_ircc_is_receiving(self); break; default: ret = -EOPNOTSUPP; @@ -1735,54 +1977,60 @@ static int nsc_fir_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) return ret; } -static struct net_device_stats *nsc_fir_net_get_stats(struct net_device *dev) +static struct net_device_stats *nsc_ircc_net_get_stats(struct net_device *dev) { - struct nsc_fir_cb *self = (struct nsc_fir_cb *) dev->priv; + struct nsc_ircc_cb *self = (struct nsc_ircc_cb *) dev->priv; return &self->stats; } #ifdef CONFIG_APM -static void nsc_fir_suspend(struct nsc_fir_cb *self) +static void nsc_ircc_suspend(struct nsc_ircc_cb *self) { - int i = 10; - MESSAGE("%s, Suspending\n", driver_name); - if (self->suspend) + if (self->io.suspended) return; - self->suspend = 1; -} + nsc_ircc_net_close(self->netdev); + self->io.suspended = 1; +} -static void nsc_fir_wakeup(struct nsc_fir_cb *self) +static void nsc_ircc_wakeup(struct nsc_ircc_cb *self) { struct net_device *dev = self->netdev; - unsigned long flags; + int iobase; - if (!self->suspend) + if (!self->io.suspended) return; - save_flags(flags); - cli(); + iobase = self->io.fir_base; - restore_flags(flags); + /* Switch to advanced mode */ + switch_bank(iobase, BANK2); + outb(ECR1_EXT_SL, iobase+ECR1); + switch_bank(iobase, BANK0); + + nsc_ircc_net_open(self->netdev); + MESSAGE("%s, Waking up\n", driver_name); + + self->io.suspended = 0; } -static int nsc_fir_apmproc(apm_event_t event) +static int nsc_ircc_apmproc(apm_event_t event) { - static int down = 0; /* Filter out double events */ + static int down = 0; /* Filter out double events */ int i; switch (event) { case APM_SYS_SUSPEND: case APM_USER_SUSPEND: if (!down) { - for (i = 0; i < 4; i++) { + for (i=0; i<4; i++) { if (dev_self[i]) - nsc_fir_suspend(dev_self[i]); + nsc_ircc_suspend(dev_self[i]); } } down = 1; @@ -1790,9 +2038,9 @@ static int nsc_fir_apmproc(apm_event_t event) case APM_NORMAL_RESUME: case APM_CRITICAL_RESUME: if (down) { - for (i = 0; i < 4; i++) { + for (i=0; i<4; i++) { if (dev_self[i]) - nsc_fir_wakeup(dev_self[i]); + nsc_ircc_wakeup(dev_self[i]); } } down = 0; @@ -1804,22 +2052,22 @@ static int nsc_fir_apmproc(apm_event_t event) #ifdef MODULE MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no>"); -MODULE_DESCRIPTION("NSC FIR IrDA Device Driver"); +MODULE_DESCRIPTION("NSC IrDA Device Driver"); MODULE_PARM(qos_mtt_bits, "i"); -MODULE_PARM(io, "1-4i"); -MODULE_PARM(io2, "1-4i"); +MODULE_PARM(io, "1-4i"); MODULE_PARM(irq, "1-4i"); +MODULE_PARM(dma, "1-4i"); MODULE_PARM(dongle_id, "i"); int init_module(void) { - return nsc_fir_init(); + return nsc_ircc_init(); } void cleanup_module(void) { - nsc_fir_cleanup(); + nsc_ircc_cleanup(); } #endif /* MODULE */ diff --git a/drivers/net/irda/smc-ircc.c b/drivers/net/irda/smc-ircc.c index 1d857c2b0..48b38227e 100644 --- a/drivers/net/irda/smc-ircc.c +++ b/drivers/net/irda/smc-ircc.c @@ -1,12 +1,12 @@ /********************************************************************* * * Filename: smc-ircc.c - * Version: 0.3 + * Version: 0.4 * Description: Driver for the SMC Infrared Communications Controller * Status: Experimental. * Author: Thomas Davis (tadavis@jps.net) * Created at: - * Modified at: Wed Jan 5 12:38:06 2000 + * Modified at: Fri Jan 21 09:41:08 2000 * Modified by: Dag Brattli <dagb@cs.uit.no> * * Copyright (c) 1999-2000 Dag Brattli @@ -28,11 +28,12 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA * - * SIO's: SMC FDC37N869, FDC37C669 + * SIO's: SMC FDC37N869, FDC37C669, FDC37N958 * Applicable Models : Fujitsu Lifebook 635t, Sony PCG-505TX * ********************************************************************/ +#include <linux/config.h> #include <linux/module.h> #include <linux/kernel.h> @@ -44,11 +45,16 @@ #include <linux/malloc.h> #include <linux/init.h> #include <linux/rtnetlink.h> +#include <linux/serial_reg.h> #include <asm/io.h> #include <asm/dma.h> #include <asm/byteorder.h> +#ifdef CONFIG_APM +#include <linux/apm_bios.h> +#endif + #include <net/irda/wrapper.h> #include <net/irda/irda.h> #include <net/irda/irmod.h> @@ -62,10 +68,10 @@ static char *driver_name = "smc-ircc"; #define CHIP_IO_EXTENT 8 -static unsigned int io[] = { 0x2e8, 0x140, 0x118, 0x240 }; -static unsigned int io2[] = { 0x2f8, 0x3e8, 0x2e8, 0x3e8 }; +static unsigned int io[] = { ~0, ~0 }; +static unsigned int io2[] = { 0, 0 }; -static struct ircc_cb *dev_self[] = { NULL, NULL, NULL, NULL}; +static struct ircc_cb *dev_self[] = { NULL, NULL}; /* Some prototypes */ static int ircc_open(int i, unsigned int iobase, unsigned int board_addr); @@ -73,17 +79,30 @@ static int ircc_open(int i, unsigned int iobase, unsigned int board_addr); static int ircc_close(struct ircc_cb *self); #endif /* MODULE */ static int ircc_probe(int iobase, int board_addr); -static int ircc_probe_smc(int *ioaddr, int *ioaddr2); -static int ircc_dma_receive(struct ircc_cb *self); -static int ircc_dma_receive_complete(struct ircc_cb *self, int iobase); +static int ircc_probe_58(smc_chip_t *chip, chipio_t *info); +static int ircc_probe_69(smc_chip_t *chip, chipio_t *info); +static int ircc_dma_receive(struct ircc_cb *self, int iobase); +static void ircc_dma_receive_complete(struct ircc_cb *self, int iobase); static int ircc_hard_xmit(struct sk_buff *skb, struct net_device *dev); -static void ircc_dma_xmit(struct ircc_cb *self, int iobase); +static void ircc_dma_xmit(struct ircc_cb *self, int iobase, int bofs); static void ircc_change_speed(void *priv, __u32 speed); static void ircc_interrupt(int irq, void *dev_id, struct pt_regs *regs); static int ircc_is_receiving(struct ircc_cb *self); static int ircc_net_open(struct net_device *dev); static int ircc_net_close(struct net_device *dev); +#ifdef CONFIG_APM +static int ircc_apmproc(apm_event_t event); +#endif /* CONFIG_APM */ + +/* These are the currently known SMC chipsets */ +static smc_chip_t chips[] = +{ + { "FDC37C669", 0x55, 0x55, 0x0d, 0x04, ircc_probe_69 }, + { "FDC37N869", 0x55, 0x00, 0x0d, 0x29, ircc_probe_69 }, + { "FDC37N958", 0x55, 0x55, 0x20, 0x09, ircc_probe_58 }, + { NULL } +}; static int ircc_irq=255; static int ircc_dma=255; @@ -102,26 +121,38 @@ static inline void register_bank(int iobase, int bank) */ int __init ircc_init(void) { - int ioaddr, ioaddr2; + static int smcreg[] = { 0x3f0, 0x370 }; + smc_chip_t *chip; + chipio_t info; + int ret = -ENODEV; int i; IRDA_DEBUG(0, __FUNCTION__ "\n"); - for (i=0; (io[i] < 2000) && (i < 4); i++) { - int ioaddr = io[i]; - if (check_region(ioaddr, CHIP_IO_EXTENT)) - continue; - if (ircc_open(i, io[i], io2[i]) == 0) - return 0; - } - /* last chance saloon, see what the controller says */ - if (ircc_probe_smc(&ioaddr, &ioaddr2) == 0) { - if (check_region(ioaddr, CHIP_IO_EXTENT) == 0) - if (ircc_open(0, ioaddr, ioaddr2) == 0) - return 0; + /* Probe for all the NSC chipsets we know about */ + for (chip=chips; chip->name ; chip++,i++) { + for (i=0; i<2; i++) { + info.cfg_base = smcreg[i]; + + /* + * First we check if the user has supplied any + * parameters which we should use instead of probed + * values + */ + if (io[i] < 2000) { + info.fir_base = io[i]; + info.sir_base = io2[i]; + } else if (chip->probe(chip, &info) < 0) + continue; + if (check_region(info.fir_base, CHIP_IO_EXTENT) < 0) + continue; + if (check_region(info.sir_base, CHIP_IO_EXTENT) < 0) + continue; + if (ircc_open(i, info.fir_base, info.sir_base) == 0) + ret = 0; + } } - - return -ENODEV; + return ret; } /* @@ -137,7 +168,7 @@ static void ircc_cleanup(void) IRDA_DEBUG(0, __FUNCTION__ "\n"); - for (i=0; i < 4; i++) { + for (i=0; i < 2; i++) { if (dev_self[i]) ircc_close(dev_self[i]); } @@ -150,7 +181,7 @@ static void ircc_cleanup(void) * Open driver instance * */ -static int ircc_open(int i, unsigned int iobase, unsigned int iobase2) +static int ircc_open(int i, unsigned int fir_base, unsigned int sir_base) { struct ircc_cb *self; struct irport_cb *irport; @@ -159,9 +190,9 @@ static int ircc_open(int i, unsigned int iobase, unsigned int iobase2) IRDA_DEBUG(0, __FUNCTION__ "\n"); - if ((config = ircc_probe(iobase, iobase2)) == -1) { + if ((config = ircc_probe(fir_base, sir_base)) == -1) { IRDA_DEBUG(0, __FUNCTION__ - "(), addr 0x%04x - no device found!\n", iobase); + "(), addr 0x%04x - no device found!\n", fir_base); return -1; } @@ -180,7 +211,7 @@ static int ircc_open(int i, unsigned int iobase, unsigned int iobase2) /* Need to store self somewhere */ dev_self[i] = self; - irport = irport_open(0, iobase2, config >> 4 & 0x0f); + irport = irport_open(i, sir_base, config >> 4 & 0x0f); if (!irport) return -ENODEV; @@ -190,34 +221,32 @@ static int ircc_open(int i, unsigned int iobase, unsigned int iobase2) irport->priv = self; /* Initialize IO */ - self->io.iobase = iobase; - self->io.iobase2 = iobase2; /* Used by irport */ + self->io.fir_base = fir_base; + self->io.sir_base = sir_base; /* Used by irport */ self->io.irq = config >> 4 & 0x0f; if (ircc_irq < 255) { MESSAGE("%s, Overriding IRQ - chip says %d, using %d\n", driver_name, self->io.irq, ircc_irq); self->io.irq = ircc_irq; } - self->io.io_ext = CHIP_IO_EXTENT; - self->io.io_ext2 = 8; /* Used by irport */ + self->io.fir_ext = CHIP_IO_EXTENT; + self->io.sir_ext = 8; /* Used by irport */ self->io.dma = config & 0x0f; if (ircc_dma < 255) { MESSAGE("%s, Overriding DMA - chip says %d, using %d\n", driver_name, self->io.dma, ircc_dma); self->io.dma = ircc_dma; } - self->io.fifo_size = 16; /* Lock the port that we need */ - ret = check_region(self->io.iobase, self->io.io_ext); + ret = check_region(self->io.fir_base, self->io.fir_ext); if (ret < 0) { - IRDA_DEBUG(0, __FUNCTION__ ": can't get iobase of 0x%03x\n", - self->io.iobase); - /* ircc_cleanup(self->self); */ + IRDA_DEBUG(0, __FUNCTION__ ": can't get fir_base of 0x%03x\n", + self->io.fir_base); + kfree(self); return -ENODEV; } - - request_region(self->io.iobase, self->io.io_ext, driver_name); + request_region(self->io.fir_base, self->io.fir_ext, driver_name); /* Initialize QoS for this device */ irda_init_max_qos_capabilies(&irport->qos); @@ -229,7 +258,7 @@ static int ircc_open(int i, unsigned int iobase, unsigned int iobase2) irport->qos.min_turn_time.bits = 0x07; irda_qos_bits_to_value(&irport->qos); - irport->flags = IFF_FIR|IFF_SIR|IFF_DMA|IFF_PIO; + irport->flags = IFF_FIR|IFF_MIR|IFF_SIR|IFF_DMA|IFF_PIO; /* Max DMA buffer size needed = (data_size + 6) * (window_size) + 6; */ self->rx_buff.truesize = 4000; @@ -256,6 +285,7 @@ static int ircc_open(int i, unsigned int iobase, unsigned int iobase2) /* Override the speed change function, since we must control it now */ irport->change_speed = &ircc_change_speed; + irport->interrupt = &ircc_interrupt; self->netdev->open = &ircc_net_open; self->netdev->stop = &ircc_net_close; @@ -279,23 +309,26 @@ static int ircc_close(struct ircc_cb *self) ASSERT(self != NULL, return -1;); - iobase = self->io.iobase; + iobase = self->io.fir_base; irport_close(self->irport); + /* Stop interrupts */ register_bank(iobase, 0); outb(0, iobase+IRCC_IER); outb(IRCC_MASTER_RESET, iobase+IRCC_MASTER); - + outb(0x00, iobase+IRCC_MASTER); +#if 0 + /* Reset to SIR mode */ register_bank(iobase, 1); - outb(IRCC_CFGA_IRDA_SIR_A|IRCC_CFGA_TX_POLARITY, iobase+IRCC_SCE_CFGA); outb(IRCC_CFGB_IR, iobase+IRCC_SCE_CFGB); - +#endif /* Release the PORT that this driver is using */ - IRDA_DEBUG(0, __FUNCTION__ "(), releasing 0x%03x\n", self->io.iobase); + IRDA_DEBUG(0, __FUNCTION__ "(), releasing 0x%03x\n", + self->io.fir_base); - release_region(self->io.iobase, self->io.io_ext); + release_region(self->io.fir_base, self->io.fir_ext); if (self->tx_buff.head) kfree(self->tx_buff.head); @@ -310,58 +343,106 @@ static int ircc_close(struct ircc_cb *self) #endif /* MODULE */ /* - * Function ircc_probe_smc (ioaddr, ioaddr2) + * Function ircc_probe_69 (chip, info) * - * Probe the SMC Chip for an IrDA port + * Probes for the SMC FDC37C669 and FDC37N869 * */ -static int ircc_probe_smc(int *ioaddr, int *ioaddr2) +static int ircc_probe_69(smc_chip_t *chip, chipio_t *info) { - static int smcreg[] = { 0x3f0, 0x370 }; + int cfg_base = info->cfg_base; __u8 devid, mode; - __u8 conf_reg; - int ret = -1; + int ret = -ENODEV; int fir_io; - int i; IRDA_DEBUG(0, __FUNCTION__ "()\n"); - for (i = 0; i < 2 && ret == -1; i++) { - conf_reg = smcreg[i]; - - /* Enter configuration */ - outb(0x55, conf_reg); - outb(0x55, conf_reg); - - outb(0x0d, conf_reg); - devid = inb(conf_reg+1); - IRDA_DEBUG(0, __FUNCTION__ "(), devid=0x%02x\n",devid); + /* Enter configuration */ + outb(chip->entr1, cfg_base); + outb(chip->entr2, cfg_base); + + outb(chip->cid_index, cfg_base); + devid = inb(cfg_base+1); + IRDA_DEBUG(0, __FUNCTION__ "(), devid=0x%02x\n",devid); + + /* Check for expected device ID; are there others? */ + if (devid == chip->cid_value) { + outb(0x0c, cfg_base); + mode = inb(cfg_base+1); + mode = (mode & 0x38) >> 3; - /* Check for expected device ID; are there others? */ - if (devid == 0x29) { - outb(0x0c, conf_reg); - mode = inb(conf_reg+1); - mode = (mode & 0x38) >> 3; - - /* Value for IR port */ - if (mode && mode < 4) { - /* SIR iobase */ - outb(0x25, conf_reg); - *ioaddr2 = inb(conf_reg+1) << 2; - - /* FIR iobase */ - outb(0x2b, conf_reg); - fir_io = inb(conf_reg+1) << 3; - if (fir_io) { - ret = 0; - *ioaddr = fir_io; - } + /* Value for IR port */ + if (mode && mode < 4) { + /* SIR iobase */ + outb(0x25, cfg_base); + info->sir_base = inb(cfg_base+1) << 2; + + /* FIR iobase */ + outb(0x2b, cfg_base); + fir_io = inb(cfg_base+1) << 3; + if (fir_io) { + ret = 0; + info->fir_base = fir_io; } } + } + + /* Exit configuration */ + outb(0xaa, cfg_base); + + return ret; +} + +/* + * Function ircc_probe_58 (chip, info) + * + * Probes for the SMC FDC37N958 + * + */ +static int ircc_probe_58(smc_chip_t *chip, chipio_t *info) +{ + int cfg_base = info->cfg_base; + __u8 devid; + int ret = -ENODEV; + int fir_io; + + IRDA_DEBUG(0, __FUNCTION__ "()\n"); + + /* Enter configuration */ + outb(chip->entr1, cfg_base); + outb(chip->entr2, cfg_base); + + outb(chip->cid_index, cfg_base); + devid = inb(cfg_base+1); + IRDA_DEBUG(0, __FUNCTION__ "(), devid=0x%02x\n",devid); + + /* Check for expected device ID; are there others? */ + if (devid == chip->cid_value) { + /* Select logical device (UART2) */ + outb(0x07, cfg_base); + outb(0x05, cfg_base + 1); + + /* SIR iobase */ + outb(0x60, cfg_base); + info->sir_base = inb(cfg_base + 1) << 8; + outb(0x61, cfg_base); + info->sir_base |= inb(cfg_base + 1); + + /* Read FIR base */ + outb(0x62, cfg_base); + fir_io = inb(cfg_base + 1) << 8; + outb(0x63, cfg_base); + fir_io |= inb(cfg_base + 1); + outb(0x2b, cfg_base); + if (fir_io) { + ret = 0; + info->fir_base = fir_io; + } + } + + /* Exit configuration */ + outb(0xaa, cfg_base); - /* Exit configuration */ - outb(0xaa, conf_reg); - } return ret; } @@ -371,34 +452,32 @@ static int ircc_probe_smc(int *ioaddr, int *ioaddr2) * Returns non-negative on success. * */ -static int ircc_probe(int iobase, int iobase2) +static int ircc_probe(int fir_base, int sir_base) { - int version = 1; int low, high, chip, config, dma, irq; - - IRDA_DEBUG(0, __FUNCTION__ "\n"); + int iobase = fir_base; + int version = 1; - /* Power on device */ - outb(inb(iobase+IRCC_MASTER) & ~IRCC_MASTER_POWERDOWN, - iobase+IRCC_MASTER); + IRDA_DEBUG(0, __FUNCTION__ "\n"); register_bank(iobase, 3); - high = inb(iobase+IRCC_ID_HIGH); - low = inb(iobase+IRCC_ID_LOW); - chip = inb(iobase+IRCC_CHIP_ID); + high = inb(iobase+IRCC_ID_HIGH); + low = inb(iobase+IRCC_ID_LOW); + chip = inb(iobase+IRCC_CHIP_ID); version = inb(iobase+IRCC_VERSION); - config = inb(iobase+IRCC_INTERFACE); - irq = config >> 4 & 0x0f; - dma = config & 0x0f; + config = inb(iobase+IRCC_INTERFACE); + irq = config >> 4 & 0x0f; + dma = config & 0x0f; if (high == 0x10 && low == 0xb8 && (chip == 0xf1 || chip == 0xf2)) { - IRDA_DEBUG(0, "SMC IrDA Controller found; IrCC version %d.%d, " - "port 0x%04x, dma %d, interrupt %d\n", - chip & 0x0f, version, iobase, dma, irq); + MESSAGE("SMC IrDA Controller found; IrCC version %d.%d, " + "port 0x%03x, dma=%d, irq=%d\n", + chip & 0x0f, version, iobase, dma, irq); } else - return -1; + return -ENODEV; - outb(0, iobase+IRCC_MASTER); + /* Power on device */ + outb(0x00, iobase+IRCC_MASTER); return config; } @@ -411,7 +490,7 @@ static int ircc_probe(int iobase, int iobase2) */ static void ircc_change_speed(void *priv, __u32 speed) { - int iobase, ir_mode, select, fast; + int iobase, ir_mode, ctrl, fast; struct ircc_cb *self = (struct ircc_cb *) priv; struct net_device *dev; @@ -420,49 +499,39 @@ static void ircc_change_speed(void *priv, __u32 speed) ASSERT(self != NULL, return;); dev = self->netdev; - iobase = self->io.iobase; + iobase = self->io.fir_base; /* Update accounting for new speed */ self->io.speed = speed; + outb(IRCC_MASTER_RESET, iobase+IRCC_MASTER); + outb(0x00, iobase+IRCC_MASTER); + switch (speed) { case 9600: case 19200: case 38400: case 57600: - case 115200: - IRDA_DEBUG(0, __FUNCTION__ - "(), using irport to change speed to %d\n", speed); - - register_bank(iobase, 0); - outb(0, iobase+IRCC_IER); - outb(IRCC_MASTER_RESET, iobase+IRCC_MASTER); - outb(IRCC_MASTER_INT_EN, iobase+IRCC_MASTER); - - dev->hard_start_xmit = &irport_hard_xmit; - - /* We must give the interrupt back to irport */ - self->irport->interrupt = irport_interrupt; - - irport_start(self->irport); - irport_change_speed(self->irport, speed); - return; + case 115200: + ir_mode = IRCC_CFGA_IRDA_SIR_A; + ctrl = 0; + fast = 0; break; case 576000: ir_mode = IRCC_CFGA_IRDA_HDLC; - select = 0; + ctrl = IRCC_CRC; fast = 0; IRDA_DEBUG(0, __FUNCTION__ "(), handling baud of 576000\n"); break; case 1152000: ir_mode = IRCC_CFGA_IRDA_HDLC; - select = IRCC_1152; + ctrl = IRCC_1152 | IRCC_CRC; fast = 0; IRDA_DEBUG(0, __FUNCTION__ "(), handling baud of 1152000\n"); break; case 4000000: ir_mode = IRCC_CFGA_IRDA_4PPM; - select = 0; + ctrl = IRCC_CRC; fast = IRCC_LCR_A_FAST; IRDA_DEBUG(0, __FUNCTION__ "(), handling baud of 4000000\n"); break; @@ -471,39 +540,55 @@ static void ircc_change_speed(void *priv, __u32 speed) speed); return; } - - outb(IRCC_MASTER_RESET, iobase+IRCC_MASTER); - + register_bank(iobase, 0); outb(0, iobase+IRCC_IER); - - irport_stop(self->irport); + outb(IRCC_MASTER_INT_EN, iobase+IRCC_MASTER); + + /* Make special FIR init if necessary */ + if (speed > 115200) { + irport_stop(self->irport); - /* Install FIR transmit handler */ - dev->hard_start_xmit = &ircc_hard_xmit; + /* Install FIR transmit handler */ + dev->hard_start_xmit = &ircc_hard_xmit; - /* Need to steal the interrupt as well */ - self->irport->interrupt = &ircc_interrupt; + /* + * Don't know why we have to do this, but FIR interrupts + * stops working if we remove it. + */ + /* outb(UART_MCR_OUT2, self->io.sir_base + UART_MCR); */ + /* Be ready for incomming frames */ + ircc_dma_receive(self, iobase); + } else { + /* Install SIR transmit handler */ + dev->hard_start_xmit = &irport_hard_xmit; + irport_start(self->irport); + + IRDA_DEBUG(0, __FUNCTION__ + "(), using irport to change speed to %d\n", speed); + irport_change_speed(self->irport, speed); + } dev->tbusy = 0; - + register_bank(iobase, 1); outb(((inb(iobase+IRCC_SCE_CFGA) & 0x87) | ir_mode), iobase+IRCC_SCE_CFGA); - - outb(((inb(iobase+IRCC_SCE_CFGB) & 0x3f) | IRCC_CFGB_IR), - iobase+IRCC_SCE_CFGB); +#ifdef SMC_669 /* Uses pin 88/89 for Rx/Tx */ + outb(((inb(iobase+IRCC_SCE_CFGB) & 0x3f) | IRCC_CFGB_MUX_COM), + iobase+IRCC_SCE_CFGB); +#else + outb(((inb(iobase+IRCC_SCE_CFGB) & 0x3f) | IRCC_CFGB_MUX_IR), + iobase+IRCC_SCE_CFGB); +#endif (void) inb(iobase+IRCC_FIFO_THRESHOLD); outb(64, iobase+IRCC_FIFO_THRESHOLD); - + register_bank(iobase, 4); - - outb((inb(iobase+IRCC_CONTROL) & 0x30) | select | IRCC_CRC, - iobase+IRCC_CONTROL); - + outb((inb(iobase+IRCC_CONTROL) & 0x30) | ctrl, iobase+IRCC_CONTROL); + register_bank(iobase, 0); - outb(fast, iobase+IRCC_LCR_A); } @@ -517,21 +602,20 @@ static int ircc_hard_xmit(struct sk_buff *skb, struct net_device *dev) { struct irport_cb *irport; struct ircc_cb *self; + unsigned long flags; + __u32 speed; int iobase; int mtt; - __u32 speed; irport = (struct irport_cb *) dev->priv; self = (struct ircc_cb *) irport->priv; - ASSERT(self != NULL, return 0;); - iobase = self->io.iobase; + iobase = self->io.fir_base; - IRDA_DEBUG(2, __FUNCTION__ "(%ld), skb->len=%d\n", jiffies, - (int) skb->len); + spin_lock_irqsave(&self->lock, flags); - /* Check if we need to change the speed */ + /* Check if we need to change the speed after this frame */ if ((speed = irda_get_speed(skb)) != self->io.speed) self->new_speed = speed; @@ -541,19 +625,28 @@ static int ircc_hard_xmit(struct sk_buff *skb, struct net_device *dev) memcpy(self->tx_buff.head, skb->data, skb->len); - /* Make sure that the length is a multiple of 16 bits */ - if (skb->len & 0x01) - skb->len++; - self->tx_buff.len = skb->len; self->tx_buff.data = self->tx_buff.head; mtt = irda_get_mtt(skb); - if (mtt) - udelay(mtt); - - ircc_dma_xmit(self, iobase); + if (mtt) { + int bofs; + + /* + * Compute who many BOFS (STA or PA's) we need to waste the + * min turn time given the speed of the link. + */ + bofs = mtt * (self->io.speed / 1000) / 8000; + if (bofs > 4095) + bofs = 4095; + + ircc_dma_xmit(self, iobase, bofs); + } else { + /* Transmit frame */ + ircc_dma_xmit(self, iobase, 0); + } + spin_unlock_irqrestore(&self->lock, flags); dev_kfree_skb(skb); return 0; @@ -565,44 +658,49 @@ static int ircc_hard_xmit(struct sk_buff *skb, struct net_device *dev) * Transmit data using DMA * */ -static void ircc_dma_xmit(struct ircc_cb *self, int iobase) +static void ircc_dma_xmit(struct ircc_cb *self, int iobase, int bofs) { - IRDA_DEBUG(2, __FUNCTION__ "\n"); + __u8 ctrl; - ASSERT(self != NULL, return;); - - iobase = self->io.iobase; + IRDA_DEBUG(2, __FUNCTION__ "\n"); +#if 0 + /* Disable Rx */ + register_bank(iobase, 0); + outb(0x00, iobase+IRCC_LCR_B); +#endif + register_bank(iobase, 1); + outb(inb(iobase+IRCC_SCE_CFGB) & ~IRCC_CFGB_DMA_ENABLE, + iobase+IRCC_SCE_CFGB); - setup_dma(self->io.dma, self->tx_buff.data, self->tx_buff.len, - DMA_TX_MODE); - self->io.direction = IO_XMIT; - outb(0x08, self->io.iobase2+4); - + /* Set BOF additional count for generating the min turn time */ register_bank(iobase, 4); - outb((inb(iobase+IRCC_CONTROL) & 0xf0), iobase+IRCC_CONTROL); - - outb(2, iobase+IRCC_BOF_COUNT_LO); - outb(0, iobase+IRCC_BRICKWALL_CNT_LO); -#if 1 - outb(self->tx_buff.len >> 8, iobase+IRCC_BRICKWALL_TX_CNT_HI); + outb(bofs & 0xff, iobase+IRCC_BOF_COUNT_LO); + ctrl = inb(iobase+IRCC_CONTROL) & 0xf0; + outb(ctrl | ((bofs >> 8) & 0x0f), iobase+IRCC_BOF_COUNT_HI); + + /* Set max Tx frame size */ + outb(self->tx_buff.len >> 8, iobase+IRCC_TX_SIZE_HI); outb(self->tx_buff.len & 0xff, iobase+IRCC_TX_SIZE_LO); -#else - outb(0, iobase+IRCC_BRICKWALL_TX_CNT_HI); - outb(0, iobase+IRCC_TX_SIZE_LO); -#endif + /* Setup DMA controller (must be done after enabling chip DMA) */ + setup_dma(self->io.dma, self->tx_buff.data, self->tx_buff.len, + DMA_TX_MODE); + + outb(UART_MCR_OUT2, self->io.sir_base + UART_MCR); + /* Enable burst mode chip Tx DMA */ register_bank(iobase, 1); - outb(inb(iobase+IRCC_SCE_CFGB) | IRCC_CFGB_DMA_ENABLE, - iobase+IRCC_SCE_CFGB); + outb(inb(iobase+IRCC_SCE_CFGB) | IRCC_CFGB_DMA_ENABLE | + IRCC_CFGB_DMA_BURST, iobase+IRCC_SCE_CFGB); + /* Enable interrupt */ + outb(IRCC_MASTER_INT_EN, iobase+IRCC_MASTER); register_bank(iobase, 0); - outb(IRCC_IER_ACTIVE_FRAME | IRCC_IER_EOM, iobase+IRCC_IER); - outb(IRCC_LCR_B_SCE_TRANSMIT|IRCC_LCR_B_SIP_ENABLE, iobase+IRCC_LCR_B); - outb(IRCC_MASTER_INT_EN, iobase+IRCC_MASTER); + /* Enable transmit */ + outb(IRCC_LCR_B_SCE_TRANSMIT|IRCC_LCR_B_SIP_ENABLE, iobase+IRCC_LCR_B); } /* @@ -612,39 +710,36 @@ static void ircc_dma_xmit(struct ircc_cb *self, int iobase) * by the interrupt handler * */ -static void ircc_dma_xmit_complete(struct ircc_cb *self, int underrun) +static void ircc_dma_xmit_complete(struct ircc_cb *self, int iobase) { - int iobase, d; - IRDA_DEBUG(2, __FUNCTION__ "\n"); - - ASSERT(self != NULL, return;); - - register_bank(self->io.iobase, 1); - - outb(inb(self->io.iobase+IRCC_SCE_CFGB) & IRCC_CFGB_DMA_ENABLE, - self->io.iobase+IRCC_SCE_CFGB); - - d = get_dma_residue(self->io.dma); - - IRDA_DEBUG(0, __FUNCTION__ - ": dma residue = %d, len=%d, sent=%d\n", - d, self->tx_buff.len, self->tx_buff.len - d); - - iobase = self->io.iobase; +#if 0 + /* Disable Tx */ + register_bank(iobase, 0); + outb(0x00, iobase+IRCC_LCR_B); +#endif + register_bank(self->io.fir_base, 1); + outb(inb(self->io.fir_base+IRCC_SCE_CFGB) & ~IRCC_CFGB_DMA_ENABLE, + self->io.fir_base+IRCC_SCE_CFGB); /* Check for underrrun! */ - if (underrun) { + register_bank(iobase, 0); + if (inb(iobase+IRCC_LSR) & IRCC_LSR_UNDERRUN) { self->irport->stats.tx_errors++; - self->irport->stats.tx_fifo_errors++; + self->irport->stats.tx_fifo_errors++; + + /* Reset error condition */ + register_bank(iobase, 0); + outb(IRCC_MASTER_ERROR_RESET, iobase+IRCC_MASTER); + outb(0x00, iobase+IRCC_MASTER); } else { self->irport->stats.tx_packets++; self->irport->stats.tx_bytes += self->tx_buff.len; } + /* Check if it's time to change the speed */ if (self->new_speed) { - ircc_change_speed(self, self->new_speed); - + ircc_change_speed(self, self->new_speed); self->new_speed = 0; } @@ -662,39 +757,31 @@ static void ircc_dma_xmit_complete(struct ircc_cb *self, int underrun) * if it starts to receive a frame. * */ -static int ircc_dma_receive(struct ircc_cb *self) -{ - int iobase; - - IRDA_DEBUG(2, __FUNCTION__ "\n"); - - ASSERT(self != NULL, return -1;); - - iobase= self->io.iobase; +static int ircc_dma_receive(struct ircc_cb *self, int iobase) +{ + /* Turn off chip DMA */ + //register_bank(iobase, 1); + //outb(inb(iobase+IRCC_SCE_CFGB) & ~IRCC_CFGB_DMA_ENABLE, + // iobase+IRCC_SCE_CFGB); setup_dma(self->io.dma, self->rx_buff.data, self->rx_buff.truesize, - DMA_RX_MODE); - - /* driver->media_busy = FALSE; */ + DMA_RX_MODE); + /* Set max Rx frame size */ + register_bank(iobase, 4); + outb((2050 >> 8) & 0x0f, iobase+IRCC_RX_SIZE_HI); + outb(2050 & 0xff, iobase+IRCC_RX_SIZE_LO); + self->io.direction = IO_RECV; self->rx_buff.data = self->rx_buff.head; -#if 0 - self->rx_buff.offset = 0; -#endif - - register_bank(iobase, 4); - outb(inb(iobase+IRCC_CONTROL) & 0xf0, iobase+IRCC_CONTROL); - outb(2, iobase+IRCC_BOF_COUNT_LO); - outb(0, iobase+IRCC_BRICKWALL_CNT_LO); - outb(0, iobase+IRCC_BRICKWALL_TX_CNT_HI); - outb(0, iobase+IRCC_TX_SIZE_LO); - outb(0, iobase+IRCC_RX_SIZE_HI); - outb(0, iobase+IRCC_RX_SIZE_LO); + /* Setup DMA controller */ + + /* Enable receiver */ register_bank(iobase, 0); outb(IRCC_LCR_B_SCE_RECEIVE | IRCC_LCR_B_SIP_ENABLE, iobase+IRCC_LCR_B); + /* Enable burst mode chip Rx DMA */ register_bank(iobase, 1); outb(inb(iobase+IRCC_SCE_CFGB) | IRCC_CFGB_DMA_ENABLE | IRCC_CFGB_DMA_BURST, iobase+IRCC_SCE_CFGB); @@ -709,45 +796,54 @@ static int ircc_dma_receive(struct ircc_cb *self) * * */ -static int ircc_dma_receive_complete(struct ircc_cb *self, int iobase) +static void ircc_dma_receive_complete(struct ircc_cb *self, int iobase) { + unsigned long flags; struct sk_buff *skb; int len, msgcnt; IRDA_DEBUG(2, __FUNCTION__ "\n"); +#if 0 + /* Disable Rx */ + register_bank(iobase, 0); + outb(0x00, iobase+IRCC_LCR_B); +#endif + register_bank(iobase, 0); + msgcnt = inb(iobase+IRCC_LCR_B) & 0x08; - msgcnt = inb(self->io.iobase+IRCC_LCR_B) & 0x08; - - IRDA_DEBUG(0, __FUNCTION__ ": dma count = %d\n", + IRDA_DEBUG(2, __FUNCTION__ ": dma count = %d\n", get_dma_residue(self->io.dma)); - len = self->rx_buff.truesize - get_dma_residue(self->io.dma) - 4; - - IRDA_DEBUG(0, __FUNCTION__ ": msgcnt = %d, len=%d\n", msgcnt, len); + len = self->rx_buff.truesize - get_dma_residue(self->io.dma); + + /* Remove CRC */ + if (self->io.speed < 4000000) + len -= 2; + else + len -= 4; + + if ((len < 2) && (len > 2050)) { + WARNING(__FUNCTION__ "(), bogus len=%d\n", len); + return; + } + IRDA_DEBUG(2, __FUNCTION__ ": msgcnt = %d, len=%d\n", msgcnt, len); skb = dev_alloc_skb(len+1); if (!skb) { WARNING(__FUNCTION__ "(), memory squeeze, dropping frame.\n"); - return FALSE; - } - + return; + } /* Make sure IP header gets aligned */ skb_reserve(skb, 1); - skb_put(skb, len); - memcpy(skb->data, self->rx_buff.data, len); + memcpy(skb_put(skb, len), self->rx_buff.data, len); self->irport->stats.rx_packets++; + self->irport->stats.rx_bytes += len; skb->dev = self->netdev; skb->mac.raw = skb->data; skb->protocol = htons(ETH_P_IRDA); netif_rx(skb); - - register_bank(self->io.iobase, 1); - outb(inb(self->io.iobase+IRCC_SCE_CFGB) & ~IRCC_CFGB_DMA_ENABLE, - self->io.iobase+IRCC_SCE_CFGB); - - return TRUE; } /* @@ -758,58 +854,54 @@ static int ircc_dma_receive_complete(struct ircc_cb *self, int iobase) */ static void ircc_interrupt(int irq, void *dev_id, struct pt_regs *regs) { - int iobase, iir; struct net_device *dev = (struct net_device *) dev_id; + struct irport_cb *irport; struct ircc_cb *self; + int iobase, iir; if (dev == NULL) { printk(KERN_WARNING "%s: irq %d for unknown device.\n", driver_name, irq); return; } - - self = (struct ircc_cb *) dev->priv; + irport = (struct irport_cb *) dev->priv; + ASSERT(irport != NULL, return;); + self = (struct ircc_cb *) irport->priv; + ASSERT(self != NULL, return;); - iobase = self->io.iobase; + /* Check if we should use the SIR interrupt handler */ + if (self->io.speed < 576000) { + irport_interrupt(irq, dev_id, regs); + return; + } + iobase = self->io.fir_base; + spin_lock(&self->lock); dev->interrupt = 1; - outb(0, iobase+IRCC_MASTER); - register_bank(iobase, 0); iir = inb(iobase+IRCC_IIR); /* Disable interrupts */ outb(0, iobase+IRCC_IER); - IRDA_DEBUG(0, __FUNCTION__ "(), iir = 0x%02x\n", iir); + IRDA_DEBUG(2, __FUNCTION__ "(), iir = 0x%02x\n", iir); if (iir & IRCC_IIR_EOM) { - IRDA_DEBUG(0, __FUNCTION__ "(), IRCC_IIR_EOM\n"); - if (self->io.direction == IO_RECV) ircc_dma_receive_complete(self, iobase); else ircc_dma_xmit_complete(self, iobase); - ircc_dma_receive(self); - } - if (iir & IRCC_IIR_ACTIVE_FRAME) { - IRDA_DEBUG(0, __FUNCTION__ "(), IRCC_IIR_ACTIVE_FRAME\n"); - self->rx_buff.state = INSIDE_FRAME; -#if 0 - ircc_dma_receive(self); -#endif - } - if (iir & IRCC_IIR_RAW_MODE) { - IRDA_DEBUG(0, __FUNCTION__ "(), IIR RAW mode interrupt.\n"); + ircc_dma_receive(self, iobase); } + /* Enable interrupts again */ register_bank(iobase, 0); outb(IRCC_IER_ACTIVE_FRAME|IRCC_IER_EOM, iobase+IRCC_IER); - outb(IRCC_MASTER_INT_EN, iobase+IRCC_MASTER); dev->interrupt = 0; + spin_unlock(&self->lock); } /* @@ -855,7 +947,7 @@ static int ircc_net_open(struct net_device *dev) ASSERT(self != NULL, return 0;); - iobase = self->io.iobase; + iobase = self->io.fir_base; irport_net_open(dev); /* irport allocates the irq */ @@ -894,7 +986,7 @@ static int ircc_net_close(struct net_device *dev) ASSERT(self != NULL, return 0;); - iobase = self->io.iobase; + iobase = self->io.fir_base; irport_net_close(dev); @@ -907,6 +999,69 @@ static int ircc_net_close(struct net_device *dev) return 0; } +#ifdef CONFIG_APM +static void ircc_suspend(struct ircc_cb *self) +{ + int i = 10; + + MESSAGE("%s, Suspending\n", driver_name); + + if (self->io.suspended) + return; + + ircc_net_close(self->netdev); + + self->io.suspended = 1; +} + +static void ircc_wakeup(struct ircc_cb *self) +{ + struct net_device *dev = self->netdev; + unsigned long flags; + + if (!self->io.suspended) + return; + + save_flags(flags); + cli(); + + ircc_net_open(self->netdev); + + restore_flags(flags); + MESSAGE("%s, Waking up\n", driver_name); +} + +static int ircc_apmproc(apm_event_t event) +{ + static int down = 0; /* Filter out double events */ + int i; + + switch (event) { + case APM_SYS_SUSPEND: + case APM_USER_SUSPEND: + if (!down) { + for (i=0; i<4; i++) { + if (dev_self[i]) + ircc_suspend(dev_self[i]); + } + } + down = 1; + break; + case APM_NORMAL_RESUME: + case APM_CRITICAL_RESUME: + if (down) { + for (i=0; i<4; i++) { + if (dev_self[i]) + ircc_wakeup(dev_self[i]); + } + } + down = 0; + break; + } + return 0; +} +#endif /* CONFIG_APM */ + #ifdef MODULE MODULE_AUTHOR("Thomas Davis <tadavis@jps.net>"); MODULE_DESCRIPTION("SMC IrCC controller driver"); diff --git a/drivers/net/irda/toshoboe.c b/drivers/net/irda/toshoboe.c index cbf06af5f..a7b97af37 100644 --- a/drivers/net/irda/toshoboe.c +++ b/drivers/net/irda/toshoboe.c @@ -32,37 +32,6 @@ static char *rcsid = "$Id: toshoboe.c,v 1.91 1999/06/29 14:21:06 root Exp $"; -/* - * $Log: toshoboe.c,v $ - * Revision 1.9 1999/06/29 14:21:06 root - * *** empty log message *** - * - * Revision 1.8 1999/06/29 14:15:08 root - * *** empty log message *** - * - * Revision 1.7 1999/06/29 13:46:42 root - * *** empty log message *** - * - * Revision 1.6 1999/06/29 12:31:03 root - * *** empty log message *** - * - * Revision 1.5 1999/05/12 12:24:39 root - * *** empty log message *** - * - * Revision 1.4 1999/05/12 11:55:08 root - * *** empty log message *** - * - * Revision 1.3 1999/05/09 01:33:12 root - * *** empty log message *** - * - * Revision 1.2 1999/05/09 01:30:38 root - * *** empty log message *** - * - * Revision 1.1 1999/05/09 01:25:04 root - * Initial revision - * - */ - /* Define this to have only one frame in the XMIT or RECV queue */ /* Toshiba's drivers do this, but it disables back to back tansfers */ /* I think that the chip may have some problems certainly, I have */ @@ -682,7 +651,7 @@ toshoboe_close (struct toshoboe_cb *self) toshoboe_disablebm (self); } - release_region (self->io.iobase, self->io.io_ext); + release_region (self->io.sir_base, self->io.sir_ext); for (i = 0; i < TX_SLOTS; ++i) @@ -754,17 +723,17 @@ toshoboe_open (struct pci_dev *pci_dev) self->pdev = pci_dev; self->base = pci_dev->resource[0].start; - self->io.iobase = self->base; + self->io.sir_base = self->base; self->io.irq = pci_dev->irq; - self->io.io_ext = CHIP_IO_EXTENT; + self->io.sir_ext = CHIP_IO_EXTENT; self->io.speed = 9600; /* Lock the port that we need */ - i = check_region (self->io.iobase, self->io.io_ext); + i = check_region (self->io.sir_base, self->io.sir_ext); if (i < 0) { IRDA_DEBUG (0, __FUNCTION__ "(), can't get iobase of 0x%03x\n", - self->io.iobase); + self->io.sir_base); dev_self[i] = NULL; kfree (self); @@ -864,13 +833,12 @@ toshoboe_open (struct pci_dev *pci_dev) } - request_region (self->io.iobase, self->io.io_ext, driver_name); + request_region (self->io.sir_base, self->io.sir_ext, driver_name); if (!(dev = dev_alloc("irda%d", &err))) { ERROR(__FUNCTION__ "(), dev_alloc() failed!\n"); return -ENOMEM; } - dev->priv = (void *) self; self->netdev = dev; @@ -1025,7 +993,7 @@ int __init toshoboe_init (void) if (pci_dev) { printk (KERN_WARNING "ToshOboe: Found 701 chip at 0x%0lx irq %d\n", - pci_dev->resource[0].start, + pci_dev->resource[0].start, pci_dev->irq); if (!toshoboe_open (pci_dev)) diff --git a/drivers/net/irda/w83977af_ir.c b/drivers/net/irda/w83977af_ir.c index 1bc2d9fa9..7f781719c 100644 --- a/drivers/net/irda/w83977af_ir.c +++ b/drivers/net/irda/w83977af_ir.c @@ -6,7 +6,7 @@ * Status: Experimental. * Author: Paul VanderSpek * Created at: Wed Nov 4 11:46:16 1998 - * Modified at: Wed Jan 5 15:11:21 2000 + * Modified at: Fri Jan 28 12:10:59 2000 * Modified by: Dag Brattli <dagb@cs.uit.no> * * Copyright (c) 1998-2000 Dag Brattli <dagb@cs.uit.no> @@ -183,21 +183,21 @@ int w83977af_open(int i, unsigned int iobase, unsigned int irq, dev_self[i] = self; /* Initialize IO */ - self->io.iobase = iobase; + self->io.fir_base = iobase; self->io.irq = irq; - self->io.io_ext = CHIP_IO_EXTENT; + self->io.fir_ext = CHIP_IO_EXTENT; self->io.dma = dma; self->io.fifo_size = 32; /* Lock the port that we need */ - ret = check_region(self->io.iobase, self->io.io_ext); + ret = check_region(self->io.fir_base, self->io.fir_ext); if (ret < 0) { IRDA_DEBUG(0, __FUNCTION__ "(), can't get iobase of 0x%03x\n", - self->io.iobase); + self->io.fir_base); /* w83977af_cleanup( self); */ return -ENODEV; } - request_region(self->io.iobase, self->io.io_ext, driver_name); + request_region(self->io.fir_base, self->io.fir_ext, driver_name); /* Initialize QoS for this device */ irda_init_max_qos_capabilies(&self->qos); @@ -243,9 +243,6 @@ int w83977af_open(int i, unsigned int iobase, unsigned int irq, ERROR(__FUNCTION__ "(), dev_alloc() failed!\n"); return -ENOMEM; } - /* dev_alloc doesn't clear the struct, so lets do a little hack */ - memset(((__u8*)dev)+sizeof(char*),0,sizeof(struct net_device)-sizeof(char*)); - dev->priv = (void *) self; self->netdev = dev; @@ -282,7 +279,7 @@ static int w83977af_close(struct w83977af_ir *self) IRDA_DEBUG(0, __FUNCTION__ "()\n"); - iobase = self->io.iobase; + iobase = self->io.fir_base; #ifdef CONFIG_USE_W977_PNP /* enter PnP configuration mode */ @@ -301,14 +298,12 @@ static int w83977af_close(struct w83977af_ir *self) rtnl_lock(); unregister_netdevice(self->netdev); rtnl_unlock(); - /* Must free the old-style 2.2.x device */ - kfree(self->netdev); } /* Release the PORT that this driver is using */ IRDA_DEBUG(0 , __FUNCTION__ "(), Releasing Region %03x\n", - self->io.iobase); - release_region(self->io.iobase, self->io.io_ext); + self->io.fir_base); + release_region(self->io.fir_base, self->io.fir_ext); if (self->tx_buff.head) kfree(self->tx_buff.head); @@ -426,7 +421,7 @@ void w83977af_change_speed(struct w83977af_ir *self, __u32 speed) int iobase; __u8 set; - iobase = self->io.iobase; + iobase = self->io.fir_base; /* Update accounting for new speed */ self->io.speed = speed; @@ -510,7 +505,7 @@ int w83977af_hard_xmit(struct sk_buff *skb, struct net_device *dev) self = (struct w83977af_ir *) dev->priv; - iobase = self->io.iobase; + iobase = self->io.fir_base; IRDA_DEBUG(4, __FUNCTION__ "(%ld), skb->len=%d\n", jiffies, (int) skb->len); @@ -692,7 +687,7 @@ void w83977af_dma_xmit_complete(struct w83977af_ir *self) ASSERT(self != NULL, return;); - iobase = self->io.iobase; + iobase = self->io.fir_base; /* Save current set */ set = inb(iobase+SSR); @@ -748,7 +743,7 @@ int w83977af_dma_receive(struct w83977af_ir *self) IRDA_DEBUG(4, __FUNCTION__ "\n"); - iobase= self->io.iobase; + iobase= self->io.fir_base; /* Save current set */ set = inb(iobase+SSR); @@ -822,12 +817,12 @@ int w83977af_dma_receive_complete(struct w83977af_ir *self) st_fifo = &self->st_fifo; - iobase = self->io.iobase; + iobase = self->io.fir_base; /* Save current set */ set = inb(iobase+SSR); - iobase = self->io.iobase; + iobase = self->io.fir_base; /* Read status FIFO */ switch_bank(iobase, SET5); @@ -948,7 +943,7 @@ static void w83977af_pio_receive(struct w83977af_ir *self) ASSERT(self != NULL, return;); - iobase = self->io.iobase; + iobase = self->io.fir_base; /* Receive all characters in Rx FIFO */ do { @@ -973,11 +968,11 @@ static __u8 w83977af_sir_interrupt(struct w83977af_ir *self, int isr) IRDA_DEBUG(4, __FUNCTION__ "(), isr=%#x\n", isr); - iobase = self->io.iobase; + iobase = self->io.fir_base; /* Transmit FIFO low on data */ if (isr & ISR_TXTH_I) { /* Write data left in transmit buffer */ - actual = w83977af_pio_write(self->io.iobase, + actual = w83977af_pio_write(self->io.fir_base, self->tx_buff.data, self->tx_buff.len, self->io.fifo_size); @@ -1042,7 +1037,7 @@ static __u8 w83977af_fir_interrupt(struct w83977af_ir *self, int isr) __u8 set; int iobase; - iobase = self->io.iobase; + iobase = self->io.fir_base; set = inb(iobase+SSR); /* End of frame detected in FIFO */ @@ -1131,7 +1126,7 @@ static void w83977af_interrupt(int irq, void *dev_id, struct pt_regs *regs) dev->interrupt = 1; - iobase = self->io.iobase; + iobase = self->io.fir_base; /* Save current bank */ set = inb(iobase+SSR); @@ -1171,7 +1166,7 @@ static int w83977af_is_receiving(struct w83977af_ir *self) ASSERT(self != NULL, return FALSE;); if (self->io.speed > 115200) { - iobase = self->io.iobase; + iobase = self->io.fir_base; /* Check if rx FIFO is not empty */ set = inb(iobase+SSR); @@ -1225,7 +1220,7 @@ static int w83977af_net_open(struct net_device *dev) ASSERT(self != NULL, return 0;); - iobase = self->io.iobase; + iobase = self->io.fir_base; if (request_irq(self->io.irq, w83977af_interrupt, 0, dev->name, (void *) dev)) { @@ -1290,7 +1285,7 @@ static int w83977af_net_close(struct net_device *dev) ASSERT(self != NULL, return 0;); - iobase = self->io.iobase; + iobase = self->io.fir_base; /* Stop device */ dev->tbusy = 1; diff --git a/drivers/net/mac89x0.c b/drivers/net/mac89x0.c new file mode 100644 index 000000000..ce43ad752 --- /dev/null +++ b/drivers/net/mac89x0.c @@ -0,0 +1,678 @@ +/* cs89x0.c: A Crystal Semiconductor CS89[02]0 driver for linux. */ +/* + Written 1996 by Russell Nelson, with reference to skeleton.c + written 1993-1994 by Donald Becker. + + This software may be used and distributed according to the terms + of the GNU Public License, incorporated herein by reference. + + The author may be reached at nelson@crynwr.com, Crynwr + Software, 11 Grant St., Potsdam, NY 13676 + + Changelog: + + Mike Cruse : mcruse@cti-ltd.com + : Changes for Linux 2.0 compatibility. + : Added dev_id parameter in net_interrupt(), + : request_irq() and free_irq(). Just NULL for now. + + Mike Cruse : Added MOD_INC_USE_COUNT and MOD_DEC_USE_COUNT macros + : in net_open() and net_close() so kerneld would know + : that the module is in use and wouldn't eject the + : driver prematurely. + + Mike Cruse : Rewrote init_module() and cleanup_module using 8390.c + : as an example. Disabled autoprobing in init_module(), + : not a good thing to do to other devices while Linux + : is running from all accounts. + + Alan Cox : Removed 1.2 support, added 2.1 extra counters. + + David Huggins-Daines <dhd@debian.org> + + Split this off into mac89x0.c, and gutted it of all parts which are + not relevant to the existing CS8900 cards on the Macintosh + (i.e. basically the Daynaport CS and LC cards). To be precise: + + * Removed all the media-detection stuff, because these cards are + TP-only. + + * Lobotomized the ISA interrupt bogosity, because these cards use + a hardwired NuBus interrupt and a magic ISAIRQ value in the card. + + * Basically eliminated everything not relevant to getting the + cards minimally functioning on the Macintosh. + + I might add that these cards are badly designed even from the Mac + standpoint, in that Dayna, in their infinite wisdom, used NuBus slot + I/O space and NuBus interrupts for these cards, but neglected to + provide anything even remotely resembling a NuBus ROM. Therefore we + have to probe for them in a brain-damaged ISA-like fashion. +*/ + +static char *version = +"cs89x0.c:v1.02 11/26/96 Russell Nelson <nelson@crynwr.com>\n"; + +/* ======================= configure the driver here ======================= */ + +/* use 0 for production, 1 for verification, >2 for debug */ +#ifndef NET_DEBUG +#define NET_DEBUG 0 +#endif + +/* ======================= end of configuration ======================= */ + + +/* Always include 'config.h' first in case the user wants to turn on + or override something. */ +#ifdef MODULE +#include <linux/module.h> +#include <linux/version.h> +#else +#define MOD_INC_USE_COUNT +#define MOD_DEC_USE_COUNT +#endif + +#define PRINTK(x) printk x + +/* + Sources: + + Crynwr packet driver epktisa. + + Crystal Semiconductor data sheets. + +*/ + +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/types.h> +#include <linux/fcntl.h> +#include <linux/interrupt.h> +#include <linux/ptrace.h> +#include <linux/ioport.h> +#include <linux/in.h> +#include <linux/malloc.h> +#include <linux/string.h> +#include <linux/nubus.h> +#include <asm/system.h> +#include <asm/bitops.h> +#include <asm/io.h> +#include <asm/hwtest.h> +#include <asm/macints.h> +#include <linux/errno.h> +#include <linux/init.h> + +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> +#include "cs89x0.h" + +static unsigned int net_debug = NET_DEBUG; + +/* Information that need to be kept for each board. */ +struct net_local { + struct net_device_stats stats; + int chip_type; /* one of: CS8900, CS8920, CS8920M */ + char chip_revision; /* revision letter of the chip ('A'...) */ + int send_cmd; /* the propercommand used to send a packet. */ + int rx_mode; + int curr_rx_cfg; + int send_underrun; /* keep track of how many underruns in a row we get */ + struct sk_buff *skb; +}; + +/* Index to functions, as function prototypes. */ + +extern int mac89x0_probe(struct net_device *dev); +extern void reset_chip(struct net_device *dev); +static int net_open(struct net_device *dev); +static int net_send_packet(struct sk_buff *skb, struct net_device *dev); +static void net_interrupt(int irq, void *dev_id, struct pt_regs *regs); +static void set_multicast_list(struct net_device *dev); +static void net_rx(struct net_device *dev); +static int net_close(struct net_device *dev); +static struct net_device_stats *net_get_stats(struct net_device *dev); +static int set_mac_address(struct net_device *dev, void *addr); + + +/* Example routines you must write ;->. */ +#define tx_done(dev) 1 + +/* For reading/writing registers ISA-style */ +static int inline +readreg_io(struct net_device *dev, int portno) +{ + writew(swab16(portno), dev->base_addr + ADD_PORT); + return swab16(readw(dev->base_addr + DATA_PORT)); +} + +static void inline +writereg_io(struct net_device *dev, int portno, int value) +{ + writew(swab16(portno), dev->base_addr + ADD_PORT); + writew(swab16(value), dev->base_addr + DATA_PORT); +} + +/* These are for reading/writing registers in shared memory */ +static int inline +readreg(struct net_device *dev, int portno) +{ + return swab16(readw(dev->mem_start + portno)); +} + +static void inline +writereg(struct net_device *dev, int portno, int value) +{ + writew(swab16(value), dev->mem_start + portno); +} + +/* Probe for the CS8900 card in slot E. We won't bother looking + anywhere else until we have a really good reason to do so. */ +int __init mac89x0_probe(struct net_device *dev) +{ + static int once_is_enough = 0; + struct net_local *lp; + static unsigned version_printed = 0; + int i, slot; + unsigned rev_type = 0; + unsigned long ioaddr; + unsigned short sig; + + if (once_is_enough) + return ENODEV; + once_is_enough = 1; + + /* We might have to parameterize this later */ + slot = 0xE; + /* Get out now if there's a real NuBus card in slot E */ + if (nubus_find_slot(slot, NULL) != NULL) + return ENODEV; + + /* The pseudo-ISA bits always live at offset 0x300 (gee, + wonder why...) */ + ioaddr = (unsigned long) + nubus_slot_addr(slot) | (((slot&0xf) << 20) + DEFAULTIOBASE); + { + unsigned long flags; + int card_present; + + save_flags(flags); + cli(); + card_present = hwreg_present((void*) ioaddr+4) + && hwreg_present((void*) ioaddr + DATA_PORT); + restore_flags(flags); + + if (!card_present) + return ENODEV; + } + + writew(0, ioaddr + ADD_PORT); + sig = readw(ioaddr + DATA_PORT); + if (sig != swab16(CHIP_EISA_ID_SIG)) + return ENODEV; + + /* Initialize the net_device structure. */ + if (dev->priv == NULL) { + dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL); + memset(dev->priv, 0, sizeof(struct net_local)); + } + lp = (struct net_local *)dev->priv; + + /* Fill in the 'dev' fields. */ + dev->base_addr = ioaddr; + dev->mem_start = (unsigned long) + nubus_slot_addr(slot) | (((slot&0xf) << 20) + MMIOBASE); + dev->mem_end = dev->mem_start + 0x1000; + + /* Turn on shared memory */ + writereg_io(dev, PP_BusCTL, MEMORY_ON); + + /* get the chip type */ + rev_type = readreg(dev, PRODUCT_ID_ADD); + lp->chip_type = rev_type &~ REVISON_BITS; + lp->chip_revision = ((rev_type & REVISON_BITS) >> 8) + 'A'; + + /* Check the chip type and revision in order to set the correct send command + CS8920 revision C and CS8900 revision F can use the faster send. */ + lp->send_cmd = TX_AFTER_381; + if (lp->chip_type == CS8900 && lp->chip_revision >= 'F') + lp->send_cmd = TX_NOW; + if (lp->chip_type != CS8900 && lp->chip_revision >= 'C') + lp->send_cmd = TX_NOW; + + if (net_debug && version_printed++ == 0) + printk(version); + + printk(KERN_INFO "%s: cs89%c0%s rev %c found at %#8lx", + dev->name, + lp->chip_type==CS8900?'0':'2', + lp->chip_type==CS8920M?"M":"", + lp->chip_revision, + dev->base_addr); + + /* Try to read the MAC address */ + if ((readreg(dev, PP_SelfST) & (EEPROM_PRESENT | EEPROM_OK)) == 0) { + printk("\nmac89x0: No EEPROM, giving up now.\n"); + return ENODEV; + } else { + for (i = 0; i < ETH_ALEN; i += 2) { + /* Big-endian (why??!) */ + unsigned short s = readreg(dev, PP_IA + i); + dev->dev_addr[i] = s >> 8; + dev->dev_addr[i+1] = s & 0xff; + } + } + + dev->irq = SLOT2IRQ(slot); + printk(" IRQ %d ADDR ", dev->irq); + + /* print the ethernet address. */ + for (i = 0; i < ETH_ALEN; i++) + printk("%2.2x%s", dev->dev_addr[i], + ((i < ETH_ALEN-1) ? ":" : "")); + + dev->open = net_open; + dev->stop = net_close; + dev->hard_start_xmit = net_send_packet; + dev->get_stats = net_get_stats; + dev->set_multicast_list = &set_multicast_list; + dev->set_mac_address = &set_mac_address; + + /* Fill in the fields of the net_device structure with ethernet values. */ + ether_setup(dev); + + printk("\n"); + return 0; +} + +/* This is useful for something, but I don't know what yet. */ +void __init reset_chip(struct net_device *dev) +{ + int reset_start_time; + + writereg(dev, PP_SelfCTL, readreg(dev, PP_SelfCTL) | POWER_ON_RESET); + + /* wait 30 ms */ + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(30*HZ/1000); + + /* Wait until the chip is reset */ + reset_start_time = jiffies; + while( (readreg(dev, PP_SelfST) & INIT_DONE) == 0 && jiffies - reset_start_time < 2) + ; +} + +/* Open/initialize the board. This is called (in the current kernel) + sometime after booting when the 'ifconfig' program is run. + + This routine should set everything up anew at each open, even + registers that "should" only need to be set once at boot, so that + there is non-reboot way to recover if something goes wrong. + */ +static int +net_open(struct net_device *dev) +{ + struct net_local *lp = (struct net_local *)dev->priv; + int i; + + /* Disable the interrupt for now */ + writereg(dev, PP_BusCTL, readreg(dev, PP_BusCTL) & ~ENABLE_IRQ); + + /* Grab the interrupt */ + if (request_irq(dev->irq, &net_interrupt, 0, "cs89x0", dev)) + return -EAGAIN; + + /* Set up the IRQ - Apparently magic */ + if (lp->chip_type == CS8900) + writereg(dev, PP_CS8900_ISAINT, 0); + else + writereg(dev, PP_CS8920_ISAINT, 0); + + /* set the Ethernet address */ + for (i=0; i < ETH_ALEN/2; i++) + writereg(dev, PP_IA+i*2, dev->dev_addr[i*2] | (dev->dev_addr[i*2+1] << 8)); + + /* Turn on both receive and transmit operations */ + writereg(dev, PP_LineCTL, readreg(dev, PP_LineCTL) | SERIAL_RX_ON | SERIAL_TX_ON); + + /* Receive only error free packets addressed to this card */ + lp->rx_mode = 0; + writereg(dev, PP_RxCTL, DEF_RX_ACCEPT); + + lp->curr_rx_cfg = RX_OK_ENBL | RX_CRC_ERROR_ENBL; + + writereg(dev, PP_RxCFG, lp->curr_rx_cfg); + + writereg(dev, PP_TxCFG, TX_LOST_CRS_ENBL | TX_SQE_ERROR_ENBL | TX_OK_ENBL | + TX_LATE_COL_ENBL | TX_JBR_ENBL | TX_ANY_COL_ENBL | TX_16_COL_ENBL); + + writereg(dev, PP_BufCFG, READY_FOR_TX_ENBL | RX_MISS_COUNT_OVRFLOW_ENBL | + TX_COL_COUNT_OVRFLOW_ENBL | TX_UNDERRUN_ENBL); + + /* now that we've got our act together, enable everything */ + writereg(dev, PP_BusCTL, readreg(dev, PP_BusCTL) | ENABLE_IRQ); + dev->tbusy = 0; + dev->interrupt = 0; + dev->start = 1; + MOD_INC_USE_COUNT; + return 0; +} + +static int +net_send_packet(struct sk_buff *skb, struct net_device *dev) +{ + if (dev->tbusy) { + /* If we get here, some higher level has decided we are broken. + There should really be a "kick me" function call instead. */ + int tickssofar = jiffies - dev->trans_start; + if (tickssofar < 5) + return 1; + if (net_debug > 0) printk("%s: transmit timed out, %s?\n", dev->name, + tx_done(dev) ? "IRQ conflict" : "network cable problem"); + /* Try to restart the adaptor. */ + dev->tbusy=0; + dev->trans_start = jiffies; + } + + /* Block a timer-based transmit from overlapping. This could better be + done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */ + if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) + printk("%s: Transmitter access conflict.\n", dev->name); + else { + struct net_local *lp = (struct net_local *)dev->priv; + unsigned long flags; + + if (net_debug > 3) + printk("%s: sent %d byte packet of type %x\n", + dev->name, skb->len, + (skb->data[ETH_ALEN+ETH_ALEN] << 8) + | skb->data[ETH_ALEN+ETH_ALEN+1]); + + /* keep the upload from being interrupted, since we + ask the chip to start transmitting before the + whole packet has been completely uploaded. */ + save_flags(flags); + cli(); + + /* initiate a transmit sequence */ + writereg(dev, PP_TxCMD, lp->send_cmd); + writereg(dev, PP_TxLength, skb->len); + + /* Test to see if the chip has allocated memory for the packet */ + if ((readreg(dev, PP_BusST) & READY_FOR_TX_NOW) == 0) { + /* Gasp! It hasn't. But that shouldn't happen since + we're waiting for TxOk, so return 1 and requeue this packet. */ + restore_flags(flags); + return 1; + } + + /* Write the contents of the packet */ + memcpy_toio(dev->mem_start + PP_TxFrame, skb->data, skb->len+1); + + restore_flags(flags); + dev->trans_start = jiffies; + } + dev_kfree_skb (skb); + + return 0; +} + +/* The typical workload of the driver: + Handle the network interface interrupts. */ +static void net_interrupt(int irq, void *dev_id, struct pt_regs * regs) +{ + struct net_device *dev = dev_id; + struct net_local *lp; + int ioaddr, status; + + if (dev == NULL) { + printk ("net_interrupt(): irq %d for unknown device.\n", irq); + return; + } + if (dev->interrupt) + printk("%s: Re-entering the interrupt handler.\n", dev->name); + dev->interrupt = 1; + + ioaddr = dev->base_addr; + lp = (struct net_local *)dev->priv; + + /* we MUST read all the events out of the ISQ, otherwise we'll never + get interrupted again. As a consequence, we can't have any limit + on the number of times we loop in the interrupt handler. The + hardware guarantees that eventually we'll run out of events. Of + course, if you're on a slow machine, and packets are arriving + faster than you can read them off, you're screwed. Hasta la + vista, baby! */ + while ((status = swab16(readw(dev->base_addr + ISQ_PORT)))) { + if (net_debug > 4)printk("%s: event=%04x\n", dev->name, status); + switch(status & ISQ_EVENT_MASK) { + case ISQ_RECEIVER_EVENT: + /* Got a packet(s). */ + net_rx(dev); + break; + case ISQ_TRANSMITTER_EVENT: + lp->stats.tx_packets++; + dev->tbusy = 0; + mark_bh(NET_BH); /* Inform upper layers. */ + if ((status & TX_OK) == 0) lp->stats.tx_errors++; + if (status & TX_LOST_CRS) lp->stats.tx_carrier_errors++; + if (status & TX_SQE_ERROR) lp->stats.tx_heartbeat_errors++; + if (status & TX_LATE_COL) lp->stats.tx_window_errors++; + if (status & TX_16_COL) lp->stats.tx_aborted_errors++; + break; + case ISQ_BUFFER_EVENT: + if (status & READY_FOR_TX) { + /* we tried to transmit a packet earlier, + but inexplicably ran out of buffers. + That shouldn't happen since we only ever + load one packet. Shrug. Do the right + thing anyway. */ + dev->tbusy = 0; + mark_bh(NET_BH); /* Inform upper layers. */ + } + if (status & TX_UNDERRUN) { + if (net_debug > 0) printk("%s: transmit underrun\n", dev->name); + lp->send_underrun++; + if (lp->send_underrun == 3) lp->send_cmd = TX_AFTER_381; + else if (lp->send_underrun == 6) lp->send_cmd = TX_AFTER_ALL; + } + break; + case ISQ_RX_MISS_EVENT: + lp->stats.rx_missed_errors += (status >>6); + break; + case ISQ_TX_COL_EVENT: + lp->stats.collisions += (status >>6); + break; + } + } + dev->interrupt = 0; + return; +} + +/* We have a good packet(s), get it/them out of the buffers. */ +static void +net_rx(struct net_device *dev) +{ + struct net_local *lp = (struct net_local *)dev->priv; + struct sk_buff *skb; + int status, length; + + status = readreg(dev, PP_RxStatus); + if ((status & RX_OK) == 0) { + lp->stats.rx_errors++; + if (status & RX_RUNT) lp->stats.rx_length_errors++; + if (status & RX_EXTRA_DATA) lp->stats.rx_length_errors++; + if (status & RX_CRC_ERROR) if (!(status & (RX_EXTRA_DATA|RX_RUNT))) + /* per str 172 */ + lp->stats.rx_crc_errors++; + if (status & RX_DRIBBLE) lp->stats.rx_frame_errors++; + return; + } + + length = readreg(dev, PP_RxLength); + /* Malloc up new buffer. */ + skb = alloc_skb(length, GFP_ATOMIC); + if (skb == NULL) { + printk("%s: Memory squeeze, dropping packet.\n", dev->name); + lp->stats.rx_dropped++; + return; + } + skb->len = length; + skb->dev = dev; + + memcpy_fromio(skb->data, dev->mem_start + PP_RxFrame, length); + + if (net_debug > 3)printk("%s: received %d byte packet of type %x\n", + dev->name, length, + (skb->data[ETH_ALEN+ETH_ALEN] << 8) + | skb->data[ETH_ALEN+ETH_ALEN+1]); + + skb->protocol=eth_type_trans(skb,dev); + netif_rx(skb); + lp->stats.rx_packets++; + lp->stats.rx_bytes+=skb->len; + return; +} + +/* The inverse routine to net_open(). */ +static int +net_close(struct net_device *dev) +{ + + writereg(dev, PP_RxCFG, 0); + writereg(dev, PP_TxCFG, 0); + writereg(dev, PP_BufCFG, 0); + writereg(dev, PP_BusCTL, 0); + + dev->start = 0; + + free_irq(dev->irq, dev); + + /* Update the statistics here. */ + + MOD_DEC_USE_COUNT; + return 0; + +} + +/* Get the current statistics. This may be called with the card open or + closed. */ +static struct net_device_stats * +net_get_stats(struct net_device *dev) +{ + struct net_local *lp = (struct net_local *)dev->priv; + + cli(); + /* Update the statistics from the device registers. */ + lp->stats.rx_missed_errors += (readreg(dev, PP_RxMiss) >> 6); + lp->stats.collisions += (readreg(dev, PP_TxCol) >> 6); + sti(); + + return &lp->stats; +} + +static void set_multicast_list(struct net_device *dev) +{ + struct net_local *lp = (struct net_local *)dev->priv; + + if(dev->flags&IFF_PROMISC) + { + lp->rx_mode = RX_ALL_ACCEPT; + } + else if((dev->flags&IFF_ALLMULTI)||dev->mc_list) + { + /* The multicast-accept list is initialized to accept-all, and we + rely on higher-level filtering for now. */ + lp->rx_mode = RX_MULTCAST_ACCEPT; + } + else + lp->rx_mode = 0; + + writereg(dev, PP_RxCTL, DEF_RX_ACCEPT | lp->rx_mode); + + /* in promiscuous mode, we accept errored packets, so we have to enable interrupts on them also */ + writereg(dev, PP_RxCFG, lp->curr_rx_cfg | + (lp->rx_mode == RX_ALL_ACCEPT? (RX_CRC_ERROR_ENBL|RX_RUNT_ENBL|RX_EXTRA_DATA_ENBL) : 0)); +} + + +static int set_mac_address(struct net_device *dev, void *addr) +{ + int i; + if (dev->start) + return -EBUSY; + printk("%s: Setting MAC address to ", dev->name); + for (i = 0; i < 6; i++) + printk(" %2.2x", dev->dev_addr[i] = ((unsigned char *)addr)[i]); + printk(".\n"); + /* set the Ethernet address */ + for (i=0; i < ETH_ALEN/2; i++) + writereg(dev, PP_IA+i*2, dev->dev_addr[i*2] | (dev->dev_addr[i*2+1] << 8)); + + return 0; +} + +#ifdef MODULE + +static char namespace[16] = ""; +static struct net_device dev_cs89x0 = { + NULL, + 0, 0, 0, 0, + 0, 0, + 0, 0, 0, NULL, NULL }; + +static int debug=0; + +MODULE_PARM(debug, "i"); + +EXPORT_NO_SYMBOLS; + +int +init_module(void) +{ + struct net_local *lp; + + net_debug = debug; + dev_cs89x0.name = namespace; + dev_cs89x0.init = mac89x0_probe; + dev_cs89x0.priv = kmalloc(sizeof(struct net_local), GFP_KERNEL); + memset(dev_cs89x0.priv, 0, sizeof(struct net_local)); + lp = (struct net_local *)dev_cs89x0.priv; + + if (register_netdev(&dev_cs89x0) != 0) { + printk(KERN_WARNING "mac89x0.c: No card found\n"); + return -ENXIO; + } + return 0; +} + +void +cleanup_module(void) +{ + +#endif +#ifdef MODULE + writew(0, dev_cs89x0.base_addr + ADD_PORT); +#endif +#ifdef MODULE + + if (dev_cs89x0.priv != NULL) { + /* Free up the private structure, or leak memory :-) */ + unregister_netdev(&dev_cs89x0); + kfree(dev_cs89x0.priv); + dev_cs89x0.priv = NULL; /* gets re-allocated by cs89x0_probe1 */ + } +} +#endif /* MODULE */ + +/* + * Local variables: + * compile-command: "m68k-linux-gcc -D__KERNEL__ -I../../include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -pipe -fno-strength-reduce -ffixed-a2 -DMODULE -DMODVERSIONS -include ../../include/linux/modversions.h -c -o mac89x0.o mac89x0.c" + * version-control: t + * kept-new-versions: 5 + * c-indent-level: 8 + * tab-width: 8 + * End: + * + */ diff --git a/drivers/net/macmace.c b/drivers/net/macmace.c new file mode 100644 index 000000000..a7c347a7d --- /dev/null +++ b/drivers/net/macmace.c @@ -0,0 +1,825 @@ +/* + * Driver for the Macintosh 68K onboard MACE controller with PSC + * driven DMA. The MACE driver code is derived from mace.c. The + * Mac68k theory of operation is courtesy of the MacBSD wizards. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Copyright (C) 1996 Paul Mackerras. + * Copyright (C) 1998 Alan Cox <alan@redhat.com> + */ + + +#include <linux/kernel.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/delay.h> +#include <linux/string.h> +#include <linux/timer.h> +#include <asm/io.h> +#include <asm/pgtable.h> +#include <asm/irq.h> +#include <asm/macintosh.h> +#include <asm/macints.h> +#include <asm/mac_psc.h> +#include "mace.h" + +#define N_RX_RING 8 +#define N_TX_RING 2 +#define MAX_TX_ACTIVE 1 +#define NCMDS_TX 1 /* dma commands per element in tx ring */ +#define RX_BUFLEN (ETH_FRAME_LEN + 8) +#define TX_TIMEOUT HZ /* 1 second */ + +/* Bits in transmit DMA status */ +#define TX_DMA_ERR 0x80 + +/* The MACE is simply wired down on a Mac68K box */ + +#define MACE_BASE (void *)(0x50F1C000) +#define MACE_PROM (void *)(0x50F08001) + +struct mace68k_data +{ + volatile struct mace *mace; + volatile unsigned char *tx_ring; + volatile unsigned char *rx_ring; + int dma_intr; + unsigned char maccc; + struct net_device_stats stats; + struct timer_list tx_timeout; + int timeout_active; + int rx_slot, rx_done; + int tx_slot, tx_count; +}; + +struct mace_frame +{ + u16 len; + u16 status; + u16 rntpc; + u16 rcvcc; + u32 pad1; + u32 pad2; + u8 data[1]; + /* And frame continues.. */ +}; + +#define PRIV_BYTES sizeof(struct mace68k_data) + +extern void psc_debug_dump(void); + +static int mace68k_open(struct net_device *dev); +static int mace68k_close(struct net_device *dev); +static int mace68k_xmit_start(struct sk_buff *skb, struct net_device *dev); +static struct net_device_stats *mace68k_stats(struct net_device *dev); +static void mace68k_set_multicast(struct net_device *dev); +static void mace68k_reset(struct net_device *dev); +static int mace68k_set_address(struct net_device *dev, void *addr); +static void mace68k_interrupt(int irq, void *dev_id, struct pt_regs *regs); +static void mace68k_dma_intr(int irq, void *dev_id, struct pt_regs *regs); +static void mace68k_set_timeout(struct net_device *dev); +static void mace68k_tx_timeout(unsigned long data); + +/* + * PSC DMA engine control. As you'd expect on a macintosh its + * more like a lawnmower engine supplied without instructions + * + * The basic theory of operation appears to be as follows. + * + * There are two sets of receive DMA registers and two sets + * of transmit DMA registers. Instead of the more traditional + * "ring buffer" approach the Mac68K DMA engine expects you + * to be loading one chain while the other runs, and then + * to flip register set. Each entry in the chain is a fixed + * length. + */ + +/* + * Load a receive DMA channel with a base address and ring length + */ + +static void psc_load_rxdma_base(int set, void *base) +{ + psc_write_word(PSC_ENETRD_CMD + set, 0x0100); + psc_write_long(PSC_ENETRD_ADDR + set, (u32)base); + psc_write_long(PSC_ENETRD_LEN + set, N_RX_RING); + psc_write_word(PSC_ENETRD_CMD + set, 0x9800); +} + +/* + * Reset the receive DMA subsystem + */ + +static void mace68k_rxdma_reset(struct net_device *dev) +{ + struct mace68k_data *mp = (struct mace68k_data *) dev->priv; + volatile struct mace *mace = mp->mace; + u8 mcc = mace->maccc; + + /* + * Turn off receive + */ + + mcc&=~ENRCV; + mace->maccc=mcc; + + /* + * Program the DMA + */ + + psc_write_word(PSC_ENETRD_CTL, 0x8800); + psc_load_rxdma_base(0x0, (void *)virt_to_bus(mp->rx_ring)); + psc_write_word(PSC_ENETRD_CTL, 0x0400); + + psc_write_word(PSC_ENETRD_CTL, 0x8800); + psc_load_rxdma_base(0x10, (void *)virt_to_bus(mp->rx_ring)); + psc_write_word(PSC_ENETRD_CTL, 0x0400); + + mace->maccc=mcc|ENRCV; + +#if 0 + psc_write_word(PSC_ENETRD_CTL, 0x9800); + psc_write_word(PSC_ENETRD_CTL+0x10, 0x9800); +#endif +} + +/* + * Reset the transmit DMA subsystem + */ + +static void mace68k_txdma_reset(struct net_device *dev) +{ + struct mace68k_data *mp = (struct mace68k_data *) dev->priv; + volatile struct mace *mace = mp->mace; + u8 mcc = mace->maccc; + + psc_write_word(PSC_ENETWR_CTL,0x8800); + + mace->maccc = mcc&~ENXMT; + psc_write_word(PSC_ENETWR_CTL,0x0400); + mace->maccc = mcc; +} + +/* + * Disable DMA + */ + +static void mace68k_dma_off(struct net_device *dev) +{ + psc_write_word(PSC_ENETRD_CTL, 0x8800); + psc_write_word(PSC_ENETRD_CTL, 0x1000); + psc_write_word(PSC_ENETRD_CMD, 0x1100); + psc_write_word(PSC_ENETRD_CMD+0x10, 0x1100); + + psc_write_word(PSC_ENETWR_CTL, 0x8800); + psc_write_word(PSC_ENETWR_CTL, 0x1000); + psc_write_word(PSC_ENETWR_CMD, 0x1100); + psc_write_word(PSC_ENETWR_CMD+0x10, 0x1100); +} + +/* Bit-reverse one byte of an ethernet hardware address. */ + +static int bitrev(int b) +{ + int d = 0, i; + + for (i = 0; i < 8; ++i, b >>= 1) + d = (d << 1) | (b & 1); + return d; +} + +/* + * Not really much of a probe. The hardware table tells us if this + * model of Macintrash has a MACE (AV macintoshes) + */ + +int mace68k_probe(struct net_device *unused) +{ + int j; + static int once=0; + struct mace68k_data *mp; + unsigned char *addr; + struct net_device *dev; + unsigned char checksum = 0; + + /* + * There can be only one... + */ + + if (once) return -ENODEV; + + once = 1; + + if (macintosh_config->ether_type != MAC_ETHER_MACE) return -ENODEV; + + printk("MACE ethernet should be present "); + + dev = init_etherdev(0, PRIV_BYTES); + if(dev==NULL) + { + printk("no free memory.\n"); + return -ENOMEM; + } + mp = (struct mace68k_data *) dev->priv; + dev->base_addr = (u32)MACE_BASE; + mp->mace = (volatile struct mace *) MACE_BASE; + + printk("at 0x%p", mp->mace); + + /* + * 16K RX ring and 4K TX ring should do nicely + */ + + mp->rx_ring=(void *)__get_free_pages(GFP_KERNEL, 2); + mp->tx_ring=(void *)__get_free_page(GFP_KERNEL); + + printk("."); + + if(mp->tx_ring==NULL || mp->rx_ring==NULL) + { + if(mp->tx_ring) + free_page((u32)mp->tx_ring); +// if(mp->rx_ring) +// __free_pages(mp->rx_ring,2); + printk("\nNo memory for ring buffers.\n"); + return -ENOMEM; + } + + /* We want the receive data to be uncached. We dont care about the + byte reading order */ + + printk("."); + kernel_set_cachemode((void *)mp->rx_ring, 16384, IOMAP_NOCACHE_NONSER); + + printk("."); + /* The transmit buffer needs to be write through */ + kernel_set_cachemode((void *)mp->tx_ring, 4096, IOMAP_WRITETHROUGH); + + printk(" Ok\n"); + dev->irq = IRQ_MAC_MACE; + printk(KERN_INFO "%s: MACE at", dev->name); + + /* + * The PROM contains 8 bytes which total 0xFF when XOR'd + * together. Due to the usual peculiar apple brain damage + * the bytes are spaced out in a strange boundary and the + * bits are reversed. + */ + + addr = (void *)MACE_PROM; + + for (j = 0; j < 6; ++j) + { + u8 v=bitrev(addr[j<<4]); + checksum^=v; + dev->dev_addr[j] = v; + printk("%c%.2x", (j ? ':' : ' '), dev->dev_addr[j]); + } + for (; j < 8; ++j) + { + checksum^=bitrev(addr[j<<4]); + } + + if(checksum!=0xFF) + { + printk(" (invalid checksum)\n"); + return -ENODEV; + } + printk("\n"); + + memset(&mp->stats, 0, sizeof(mp->stats)); + init_timer(&mp->tx_timeout); + mp->timeout_active = 0; + + dev->open = mace68k_open; + dev->stop = mace68k_close; + dev->hard_start_xmit = mace68k_xmit_start; + dev->get_stats = mace68k_stats; + dev->set_multicast_list = mace68k_set_multicast; + dev->set_mac_address = mace68k_set_address; + + ether_setup(dev); + + mp = (struct mace68k_data *) dev->priv; + mp->maccc = ENXMT | ENRCV; + mp->dma_intr = IRQ_MAC_MACE_DMA; + + psc_write_word(PSC_ENETWR_CTL, 0x9000); + psc_write_word(PSC_ENETRD_CTL, 0x9000); + psc_write_word(PSC_ENETWR_CTL, 0x0400); + psc_write_word(PSC_ENETRD_CTL, 0x0400); + + /* apple's driver doesn't seem to do this */ + /* except at driver shutdown time... */ +#if 0 + mace68k_dma_off(dev); +#endif + + return 0; +} + +/* + * Reset a MACE controller + */ + +static void mace68k_reset(struct net_device *dev) +{ + struct mace68k_data *mp = (struct mace68k_data *) dev->priv; + volatile struct mace *mb = mp->mace; + int i; + + /* soft-reset the chip */ + i = 200; + while (--i) { + mb->biucc = SWRST; + if (mb->biucc & SWRST) { + udelay(10); + continue; + } + break; + } + if (!i) { + printk(KERN_ERR "mace: cannot reset chip!\n"); + return; + } + + mb->biucc = XMTSP_64; + mb->imr = 0xff; /* disable all intrs for now */ + i = mb->ir; + mb->maccc = 0; /* turn off tx, rx */ + mb->utr = RTRD; + mb->fifocc = RCVFW_64; + mb->xmtfc = AUTO_PAD_XMIT; /* auto-pad short frames */ + + /* load up the hardware address */ + + mb->iac = ADDRCHG | PHYADDR; + + while ((mb->iac & ADDRCHG) != 0); + + for (i = 0; i < 6; ++i) + mb->padr = dev->dev_addr[i]; + + /* clear the multicast filter */ + mb->iac = ADDRCHG | LOGADDR; + + while ((mb->iac & ADDRCHG) != 0); + + for (i = 0; i < 8; ++i) + mb->ladrf = 0; + + mb->plscc = PORTSEL_GPSI + ENPLSIO; +} + +/* + * Load the address on a mace controller. + */ + +static int mace68k_set_address(struct net_device *dev, void *addr) +{ + unsigned char *p = addr; + struct mace68k_data *mp = (struct mace68k_data *) dev->priv; + volatile struct mace *mb = mp->mace; + int i; + unsigned long flags; + + save_flags(flags); + cli(); + + /* load up the hardware address */ + mb->iac = ADDRCHG | PHYADDR; + while ((mb->iac & ADDRCHG) != 0); + + for (i = 0; i < 6; ++i) + mb->padr = dev->dev_addr[i] = p[i]; + /* note: setting ADDRCHG clears ENRCV */ + mb->maccc = mp->maccc; + restore_flags(flags); + return 0; +} + +/* + * Open the Macintosh MACE. Most of this is playing with the DMA + * engine. The ethernet chip is quite friendly. + */ + +static int mace68k_open(struct net_device *dev) +{ + struct mace68k_data *mp = (struct mace68k_data *) dev->priv; + volatile struct mace *mb = mp->mace; + + /* reset the chip */ + mace68k_reset(dev); + + mp->rx_done = 0; + mace68k_rxdma_reset(dev); + + /* + * The interrupt is fixed and comes off the PSC. + */ + + if (request_irq(dev->irq, mace68k_interrupt, 0, "68K MACE", dev)) + { + printk(KERN_ERR "MACE: can't get irq %d\n", dev->irq); + return -EAGAIN; + } + + /* + * Ditto the DMA interrupt. + */ + + if (request_irq(IRQ_MAC_MACE_DMA, mace68k_dma_intr, 0, "68K MACE DMA", + dev)) + { + printk(KERN_ERR "MACE: can't get irq %d\n", IRQ_MAC_MACE_DMA); + return -EAGAIN; + } + + /* Activate the Mac DMA engine */ + + mp->tx_slot = 0; /* Using register set 0 */ + mp->tx_count = 1; /* 1 Buffer ready for use */ + mace68k_txdma_reset(dev); + + /* turn it on! */ + mb->maccc = mp->maccc; + /* enable all interrupts except receive interrupts */ + mb->imr = RCVINT; + return 0; +} + +/* + * Shut down the mace and its interrupt channel + */ + +static int mace68k_close(struct net_device *dev) +{ + struct mace68k_data *mp = (struct mace68k_data *) dev->priv; + volatile struct mace *mb = mp->mace; + + /* disable rx and tx */ + mb->maccc = 0; + mb->imr = 0xff; /* disable all intrs */ + + /* disable rx and tx dma */ + + mace68k_dma_off(dev); + + free_irq(dev->irq, dev); + free_irq(IRQ_MAC_MACE_DMA, dev); + return 0; +} + +static inline void mace68k_set_timeout(struct net_device *dev) +{ + struct mace68k_data *mp = (struct mace68k_data *) dev->priv; + unsigned long flags; + + save_flags(flags); + cli(); + if (mp->timeout_active) + del_timer(&mp->tx_timeout); + mp->tx_timeout.expires = jiffies + TX_TIMEOUT; + mp->tx_timeout.function = mace68k_tx_timeout; + mp->tx_timeout.data = (unsigned long) dev; + add_timer(&mp->tx_timeout); + mp->timeout_active = 1; + restore_flags(flags); +} + +/* + * Transmit a frame + */ + +static int mace68k_xmit_start(struct sk_buff *skb, struct net_device *dev) +{ + struct mace68k_data *mp = (struct mace68k_data *) dev->priv; + /* + * This may need atomic types ??? + */ + + printk("mace68k_xmit_start: mp->tx_count = %d, dev->tbusy = %d, mp->tx_ring = %p (%p)\n", + mp->tx_count, dev->tbusy, + mp->tx_ring, virt_to_bus(mp->tx_ring)); + psc_debug_dump(); + + if(mp->tx_count == 0) + { + dev->tbusy=1; + mace68k_dma_intr(IRQ_MAC_MACE_DMA, dev, NULL); + return 1; + } + mp->tx_count--; + + /* + * FIXME: + * This is hackish. The memcpy probably isnt needed but + * the rules for alignment are not known. Ideally we'd like + * to just blast the skb directly to ethernet. We also don't + * use the ring properly - just a one frame buffer. That + * also requires cache pushes ;). + */ + memcpy((void *)mp->tx_ring, skb, skb->len); + psc_write_long(PSC_ENETWR_ADDR + mp->tx_slot, virt_to_bus(mp->tx_ring)); + psc_write_long(PSC_ENETWR_LEN + mp->tx_slot, skb->len); + psc_write_word(PSC_ENETWR_CMD + mp->tx_slot, 0x9800); + mp->stats.tx_packets++; + mp->stats.tx_bytes+=skb->len; + dev_kfree_skb(skb); + return 0; +} + +static struct net_device_stats *mace68k_stats(struct net_device *dev) +{ + struct mace68k_data *p = (struct mace68k_data *) dev->priv; + return &p->stats; +} + +/* + * CRC polynomial - used in working out multicast filter bits. + */ +#define CRC_POLY 0xedb88320 + +static void mace68k_set_multicast(struct net_device *dev) +{ + struct mace68k_data *mp = (struct mace68k_data *) dev->priv; + volatile struct mace *mb = mp->mace; + int i, j, k, b; + unsigned long crc; + + mp->maccc &= ~PROM; + if (dev->flags & IFF_PROMISC) + { + mp->maccc |= PROM; + } else + { + unsigned char multicast_filter[8]; + struct dev_mc_list *dmi = dev->mc_list; + + if (dev->flags & IFF_ALLMULTI) + { + for (i = 0; i < 8; i++) + multicast_filter[i] = 0xff; + } else + { + for (i = 0; i < 8; i++) + multicast_filter[i] = 0; + for (i = 0; i < dev->mc_count; i++) + { + crc = ~0; + for (j = 0; j < 6; ++j) + { + b = dmi->dmi_addr[j]; + for (k = 0; k < 8; ++k) + { + if ((crc ^ b) & 1) + crc = (crc >> 1) ^ CRC_POLY; + else + crc >>= 1; + b >>= 1; + } + } + j = crc >> 26; /* bit number in multicast_filter */ + multicast_filter[j >> 3] |= 1 << (j & 7); + dmi = dmi->next; + } + } +#if 0 + printk("Multicast filter :"); + for (i = 0; i < 8; i++) + printk("%02x ", multicast_filter[i]); + printk("\n"); +#endif + + mb->iac = ADDRCHG | LOGADDR; + while ((mb->iac & ADDRCHG) != 0); + + for (i = 0; i < 8; ++i) + mb->ladrf = multicast_filter[i]; + } + /* reset maccc */ + mb->maccc = mp->maccc; +} + +/* + * Miscellaneous interrupts are handled here. We may end up + * having to bash the chip on the head for bad errors + */ + +static void mace68k_handle_misc_intrs(struct mace68k_data *mp, int intr) +{ + volatile struct mace *mb = mp->mace; + static int mace68k_babbles, mace68k_jabbers; + + if (intr & MPCO) + mp->stats.rx_missed_errors += 256; + mp->stats.rx_missed_errors += mb->mpc; /* reading clears it */ + if (intr & RNTPCO) + mp->stats.rx_length_errors += 256; + mp->stats.rx_length_errors += mb->rntpc; /* reading clears it */ + if (intr & CERR) + ++mp->stats.tx_heartbeat_errors; + if (intr & BABBLE) + if (mace68k_babbles++ < 4) + printk(KERN_DEBUG "mace: babbling transmitter\n"); + if (intr & JABBER) + if (mace68k_jabbers++ < 4) + printk(KERN_DEBUG "mace: jabbering transceiver\n"); +} + +/* + * A transmit error has occured. (We kick the transmit side from + * the DMA completion) + */ + +static void mace68k_xmit_error(struct net_device *dev) +{ + struct mace68k_data *mp = (struct mace68k_data *) dev->priv; + volatile struct mace *mb = mp->mace; + u8 xmtfs, xmtrc; + + xmtfs = mb->xmtfs; + xmtrc = mb->xmtrc; + + if(xmtfs & XMTSV) + { + if(xmtfs & UFLO) + { + printk("%s: DMA underrun.\n", dev->name); + mp->stats.tx_errors++; + mp->stats.tx_fifo_errors++; + mace68k_reset(dev); + } + if(xmtfs & RTRY) + mp->stats.collisions++; + } + mark_bh(NET_BH); +} + +/* + * A receive interrupt occured. + */ + +static void mace68k_recv_interrupt(struct net_device *dev) +{ +// struct mace68k_data *mp = (struct mace68k_data *) dev->priv; +// volatile struct mace *mb = mp->mace; +} + +/* + * Process the chip interrupt + */ + +static void mace68k_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct net_device *dev = (struct net_device *) dev_id; + struct mace68k_data *mp = (struct mace68k_data *) dev->priv; + volatile struct mace *mb = mp->mace; + u8 ir; + + ir = mb->ir; + mace68k_handle_misc_intrs(mp, ir); + + if(ir&XMTINT) + mace68k_xmit_error(dev); + if(ir&RCVINT) + mace68k_recv_interrupt(dev); +} + +static void mace68k_tx_timeout(unsigned long data) +{ +// struct net_device *dev = (struct net_device *) data; +// struct mace68k_data *mp = (struct mace68k_data *) dev->priv; +// volatile struct mace *mb = mp->mace; +} + +/* + * Handle a newly arrived frame + */ + +static void mace_dma_rx_frame(struct net_device *dev, struct mace_frame *mf) +{ + struct mace68k_data *mp = (struct mace68k_data *) dev->priv; + struct sk_buff *skb; + + if(mf->status&RS_OFLO) + { + printk("%s: fifo overflow.\n", dev->name); + mp->stats.rx_errors++; + mp->stats.rx_fifo_errors++; + } + if(mf->status&(RS_CLSN|RS_FRAMERR|RS_FCSERR)) + mp->stats.rx_errors++; + + if(mf->status&RS_CLSN) + mp->stats.collisions++; + if(mf->status&RS_FRAMERR) + mp->stats.rx_frame_errors++; + if(mf->status&RS_FCSERR) + mp->stats.rx_crc_errors++; + + skb = dev_alloc_skb(mf->len+2); + if(skb==NULL) + { + mp->stats.rx_dropped++; + return; + } + skb_reserve(skb,2); + memcpy(skb_put(skb, mf->len), mf->data, mf->len); + + skb->protocol = eth_type_trans(skb, dev); + netif_rx(skb); + mp->stats.rx_packets++; + mp->stats.rx_bytes+=mf->len; +} + +/* + * The PSC has passed us a DMA interrupt event. + */ + +static void mace68k_dma_intr(int irq, void *dev_id, struct pt_regs *regs) +{ + struct net_device *dev = (struct net_device *) dev_id; + struct mace68k_data *mp = (struct mace68k_data *) dev->priv; + +#if 0 + u32 psc_status; + + /* It seems this must be allowed to stabilise ?? */ + + while((psc_status=psc_read_long(0x0804))!=psc_read_long(0x0804)); + + /* + * Was this an ethernet event ? + */ + + if(psc_status&0x60000000) + { +#endif + /* + * Process the read queue + */ + + u16 psc_status = psc_read_word(PSC_ENETRD_CTL); + + printk("mace68k_dma_intr: PSC_ENETRD_CTL = %04X\n", (uint) psc_status); + + if (psc_status & 0x2000) { + mace68k_rxdma_reset(dev); + mp->rx_done = 0; + } else if (psc_status & 0x100) { + int left; + + psc_write_word(PSC_ENETRD_CMD + mp->rx_slot, 0x1100); + left=psc_read_long(PSC_ENETRD_LEN + mp->rx_slot); + /* read packets */ + + while(mp->rx_done < left) + { + struct mace_frame *mf=((struct mace_frame *) + mp->rx_ring)+mp->rx_done++; + mace_dma_rx_frame(dev, mf); + } + + if(left == 0) /* Out of DMA room */ + { + psc_load_rxdma_base(mp->rx_slot, + (void *)virt_to_phys(mp->rx_ring)); + mp->rx_slot^=16; + mp->rx_done = 0; + } + else + { + psc_write_word(PSC_ENETRD_CMD+mp->rx_slot, + 0x9800); + } + + } + + /* + * Process the write queue + */ + + psc_status = psc_read_word(PSC_ENETWR_CTL); + printk("mace68k_dma_intr: PSC_ENETWR_CTL = %04X\n", (uint) psc_status); + + /* apple's driver seems to loop over this until neither */ + /* condition is true. - jmt */ + + if (psc_status & 0x2000) { + mace68k_txdma_reset(dev); + } else if (psc_status & 0x0100) { + psc_write_word(PSC_ENETWR_CMD + mp->tx_slot, 0x0100); + mp->tx_slot ^=16; + mp->tx_count++; + dev->tbusy = 0; + mark_bh(NET_BH); + } +#if 0 + } +#endif +} diff --git a/drivers/net/macsonic.c b/drivers/net/macsonic.c index d14ccf3fb..dbb9129d7 100644 --- a/drivers/net/macsonic.c +++ b/drivers/net/macsonic.c @@ -42,153 +42,245 @@ #include <asm/pgtable.h> #include <asm/segment.h> #include <asm/io.h> +#include <asm/hwtest.h> #include <asm/dma.h> #include <asm/macintosh.h> +#include <asm/macints.h> +#include <asm/mac_via.h> #include <linux/errno.h> #include <linux/netdevice.h> #include <linux/etherdevice.h> #include <linux/skbuff.h> - -#include <config/macsonic.h> +#include <linux/module.h> #define SREGS_PAD(n) u16 n; #include "sonic.h" -extern int mac_onboard_sonic_probe(void); +static int sonic_debug = 0; +static int sonic_version_printed = 0; -static int setup_debug = -1; -static int setup_offset = -1; -static int setup_shift = -1; +extern int macsonic_probe(struct net_device* dev); +extern int mac_onboard_sonic_probe(struct net_device* dev); +extern int mac_nubus_sonic_probe(struct net_device* dev); -/* - * This seems to be the right default for the Q800 - */ +/* For onboard SONIC */ +#define ONBOARD_SONIC_REGISTERS 0x50F0A000 +#define ONBOARD_SONIC_PROM_BASE 0x50f08000 -static int reg_offset = 0; -static int reg_shift = 0; +enum macsonic_type { + MACSONIC_DUODOCK, + MACSONIC_APPLE, + MACSONIC_APPLE16, + MACSONIC_DAYNA, + MACSONIC_DAYNALINK +}; -/* - * Macros to access SONIC registers - */ - -#define MAC_SONIC_REGISTERS 0x50F0A000 -#define MAC_SONIC_PROM_BASE 0x50f08000 -#define MAC_SONIC_IRQ 9 /* Nubus 9 */ +/* For the built-in SONIC in the Duo Dock */ +#define DUODOCK_SONIC_REGISTERS 0xe10000 +#define DUODOCK_SONIC_PROM_BASE 0xe12000 -/* - * FIXME: We may need to invert the byte ordering. These should - * be ok for other aspects as they are uncached spaces. - * The original macros from jazzsonic.c works for me - * on my LC 630, YMMV /Andreas Ehliar - */ +/* For Apple-style NuBus SONIC */ +#define APPLE_SONIC_REGISTERS 0 +#define APPLE_SONIC_PROM_BASE 0x40000 -#if 0 -#define SONIC_READ(reg) \ - *((volatile unsigned int *)base_addr+((reg)<<2)+2) +/* Daynalink LC SONIC */ +#define DAYNALINK_PROM_BASE 0x400000 -#define SONIC_WRITE(reg,val) \ - *((volatile unsigned int *)base_addr+((reg)<<2)+2) = val -#else -#define SONIC_READ(reg) \ - *((volatile unsigned int *)base_addr+reg) +/* For Dayna-style NuBus SONIC (haven't seen one yet) */ +#define DAYNA_SONIC_REGISTERS 0x180000 +/* This is what OpenBSD says. However, this is definitely in NuBus + ROM space so we should be able to get it by walking the NuBus + resource directories */ +#define DAYNA_SONIC_MAC_ADDR 0xffe004 -#define SONIC_WRITE(reg,val) \ - *((volatile unsigned int *)base_addr+reg) = val -#endif +#define SONIC_READ_PROM(addr) readb(prom_addr+addr) + +int __init macsonic_probe(struct net_device* dev) +{ + int rv; + + /* This will catch fatal stuff like -ENOMEM as well as success */ + if ((rv = mac_onboard_sonic_probe(dev)) != -ENODEV) + return rv; + return mac_nubus_sonic_probe(dev); +} -#define SONIC_READ_PROM(addr) \ - *((volatile unsigned char *)prom_addr+addr) /* - * Function : mac_sonic_setup(char *str, int *ints) - * - * Purpose : booter command line initialization of the overrides array, - * - * Inputs : str - unused, ints - array of integer parameters with ints[0] - * equal to the number of ints. - * - * Currently unused in the new driver; need to add settable parameters to the - * detect function. - * + * For reversing the PROM address */ -void mac_sonic_setup(char *str, int *ints) { - /* Format of macsonic parameter is: - * macsonic=<debug>,<offset>,<shift> - * Negative values mean don't change. - */ - - /* Grmbl... the standard parameter parsing can't handle negative numbers - * :-( So let's do it ourselves! - */ +static unsigned char nibbletab[] = {0, 8, 4, 12, 2, 10, 6, 14, + 1, 9, 5, 13, 3, 11, 7, 15}; - int i = ints[0]+1, fact; +static inline void bit_reverse_addr(unsigned char addr[6]) +{ + int i; - while( str && (isdigit(*str) || *str == '-') && i <= 10) { - if (*str == '-') - fact = -1, ++str; - else - fact = 1; - ints[i++] = simple_strtoul( str, NULL, 0 ) * fact; - if ((str = strchr( str, ',' )) != NULL) - ++str; - } - ints[0] = i-1; - - if (ints[0] < 1) { - printk( "mac_sonic_setup: no arguments!\n" ); - return; - } + for(i = 0; i < 6; i++) + addr[i] = ((nibbletab[addr[i] & 0xf] << 4) | + nibbletab[(addr[i] >> 4) &0xf]); +} + +int __init macsonic_init(struct net_device* dev) +{ + struct sonic_local* lp = (struct sonic_local *)dev->priv; + int i; - if (ints[0] >= 1) { - /* 0 <= n <= 2 */ - if (ints[1] >= 0 && ints[1] <= 8) - setup_debug = ints[1]; - else if (ints[1] > 16) - printk( "mac_sonic_setup: invalid debug level %d !\n", ints[1] ); + /* Allocate the entire chunk of memory for the descriptors. + Note that this cannot cross a 64K boundary. */ + for (i = 0; i < 20; i++) { + unsigned long desc_base, desc_top; + if ((lp->sonic_desc = + kmalloc(SIZEOF_SONIC_DESC + * SONIC_BUS_SCALE(lp->dma_bitmode), GFP_DMA)) == NULL) { + printk(KERN_ERR "%s: couldn't allocate descriptor buffers\n", dev->name); + } + desc_base = (unsigned long) lp->sonic_desc; + desc_top = desc_base + SIZEOF_SONIC_DESC * SONIC_BUS_SCALE(lp->dma_bitmode); + if ((desc_top & 0xffff) >= (desc_base & 0xffff)) + break; + /* Hmm. try again (FIXME: does this actually work?) */ + kfree(lp->sonic_desc); + printk(KERN_DEBUG + "%s: didn't get continguous chunk [%08lx - %08lx], trying again\n", + dev->name, desc_base, desc_top); } - if (ints[0] >= 2) { - /* 0 <= n <= 2 */ - if (ints[2] >= 0 && ints[2] <= 16) - setup_offset = ints[2]; - else if (ints[2] > 16) - printk( "mac_sonic_setup: invalid offset %d !\n", ints[2] ); + + if (lp->sonic_desc == NULL) { + printk(KERN_ERR "%s: tried 20 times to allocate descriptor buffers, giving up.\n", + dev->name); + return -ENOMEM; + } + + /* Now set up the pointers to point to the appropriate places */ + lp->cda = lp->sonic_desc; + lp->tda = lp->cda + (SIZEOF_SONIC_CDA * SONIC_BUS_SCALE(lp->dma_bitmode)); + lp->rda = lp->tda + (SIZEOF_SONIC_TD * SONIC_NUM_TDS + * SONIC_BUS_SCALE(lp->dma_bitmode)); + lp->rra = lp->rda + (SIZEOF_SONIC_RD * SONIC_NUM_RDS + * SONIC_BUS_SCALE(lp->dma_bitmode)); + + /* FIXME, maybe we should use skbs */ + if ((lp->rba = (char *) + kmalloc(SONIC_NUM_RRS * SONIC_RBSIZE, GFP_DMA)) == NULL) { + printk(KERN_ERR "%s: couldn't allocate receive buffers\n", dev->name); + return -ENOMEM; } - if (ints[0] >= 3) { - /* 0 <= n <= 2 */ - if (ints[3] >= 0 && ints[3] <= 16) - setup_shift = ints[3]; - else if (ints[3] > 16) - printk( "mac_sonic_setup: invalid shift %d !\n", ints[3] ); + + { + int rs, ds; + + /* almost always 12*4096, but let's not take chances */ + rs = ((SONIC_NUM_RRS * SONIC_RBSIZE + 4095) / 4096) * 4096; + /* almost always under a page, but let's not take chances */ + ds = ((SIZEOF_SONIC_DESC + 4095) / 4096) * 4096; + kernel_set_cachemode(lp->rba, rs, IOMAP_NOCACHE_SER); + kernel_set_cachemode(lp->sonic_desc, ds, IOMAP_NOCACHE_SER); } + +#if 0 + flush_cache_all(); +#endif + + dev->open = sonic_open; + dev->stop = sonic_close; + dev->hard_start_xmit = sonic_send_packet; + dev->get_stats = sonic_get_stats; + dev->set_multicast_list = &sonic_multicast_list; + + /* + * clear tally counter + */ + sonic_write(dev, SONIC_CRCT, 0xffff); + sonic_write(dev, SONIC_FAET, 0xffff); + sonic_write(dev, SONIC_MPT, 0xffff); + + /* Fill in the fields of the device structure with ethernet values. */ + ether_setup(dev); + return 0; } -static int sonic_debug = 0; +int __init mac_onboard_sonic_ethernet_addr(struct net_device* dev) +{ + const int prom_addr = ONBOARD_SONIC_PROM_BASE; + int i; -/* - * For reversing the PROM address - */ + /* On NuBus boards we can sometimes look in the ROM resources. + No such luck for comm-slot/onboard. */ + for(i = 0; i < 6; i++) + dev->dev_addr[i] = SONIC_READ_PROM(i); -static unsigned char nibbletab[] = {0, 8, 4, 12, 2, 10, 6, 14, - 1, 9, 5, 13, 3, 11, 7, 15}; + /* Most of the time, the address is bit-reversed. The NetBSD + source has a rather long and detailed historical account of + why this is so. */ + if (memcmp(dev->dev_addr, "\x08\x00\x07", 3) && + memcmp(dev->dev_addr, "\x00\xA0\x40", 3) && + memcmp(dev->dev_addr, "\x00\x05\x02", 3)) + bit_reverse_addr(dev->dev_addr); + else + return 0; + + /* If we still have what seems to be a bogus address, we'll + look in the CAM. The top entry should be ours. */ + /* Danger! This only works if MacOS has already initialized + the card... */ + if (memcmp(dev->dev_addr, "\x08\x00\x07", 3) && + memcmp(dev->dev_addr, "\x00\xA0\x40", 3) && + memcmp(dev->dev_addr, "\x00\x05\x02", 3)) + { + unsigned short val; + + printk(KERN_INFO "macsonic: PROM seems to be wrong, trying CAM entry 15\n"); + + sonic_write(dev, SONIC_CMD, SONIC_CR_RST); + sonic_write(dev, SONIC_CEP, 15); + + val = sonic_read(dev, SONIC_CAP2); + dev->dev_addr[5] = val >> 8; + dev->dev_addr[4] = val & 0xff; + val = sonic_read(dev, SONIC_CAP1); + dev->dev_addr[3] = val >> 8; + dev->dev_addr[2] = val & 0xff; + val = sonic_read(dev, SONIC_CAP0); + dev->dev_addr[1] = val >> 8; + dev->dev_addr[0] = val & 0xff; + + printk(KERN_INFO "HW Address from CAM 15: "); + for (i = 0; i < 6; i++) { + printk("%2.2x", dev->dev_addr[i]); + if (i < 5) + printk(":"); + } + printk("\n"); + } else return 0; -int __init mac_onboard_sonic_probe(void) + if (memcmp(dev->dev_addr, "\x08\x00\x07", 3) && + memcmp(dev->dev_addr, "\x00\xA0\x40", 3) && + memcmp(dev->dev_addr, "\x00\x05\x02", 3)) + { + /* + * Still nonsense ... messed up someplace! + */ + printk(KERN_ERR "macsonic: ERROR (INVALID MAC)\n"); + return -EIO; + } else return 0; +} + +int __init mac_onboard_sonic_probe(struct net_device* dev) { - struct net_device *dev; - unsigned int silicon_revision; - unsigned int val; - struct sonic_local *lp; + /* Bwahahaha */ + static int once_is_more_than_enough = 0; + struct sonic_local* lp; int i; - int base_addr = MAC_SONIC_REGISTERS; - int prom_addr = MAC_SONIC_PROM_BASE; - static int one=0; - if (!MACH_IS_MAC) + if (once_is_more_than_enough) return -ENODEV; + once_is_more_than_enough = 1; - if(++one!=1) /* Only one is allowed */ + if (!MACH_IS_MAC) return -ENODEV; printk(KERN_INFO "Checking for internal Macintosh ethernet (SONIC).. "); @@ -198,288 +290,332 @@ int __init mac_onboard_sonic_probe(void) printk("none.\n"); return -ENODEV; } - - printk("yes\n"); - - if (setup_debug >= 0) - sonic_debug = setup_debug; - /* - * This may depend on the actual Mac model ... works for me. - */ - reg_offset = - (setup_offset >= 0) ? setup_offset : 0; - reg_shift = - (setup_shift >= 0) ? setup_shift : 0; + /* Bogus probing, on the models which may or may not have + Ethernet (BTW, the Ethernet *is* always at the same + address, and nothing else lives there, at least if Apple's + documentation is to be believed) */ + if (macintosh_config->ident == MAC_MODEL_Q630 || + macintosh_config->ident == MAC_MODEL_P588 || + macintosh_config->ident == MAC_MODEL_C610) { + unsigned long flags; + int card_present; + + save_flags(flags); + cli(); + card_present = hwreg_present((void*)ONBOARD_SONIC_REGISTERS); + restore_flags(flags); + + if (!card_present) { + printk("none.\n"); + return -ENODEV; + } + } - /* - * get the Silicon Revision ID. If this is one of the known - * one assume that we found a SONIC ethernet controller at - * the expected location. - * (This is not implemented in the Macintosh driver yet; need - * to collect values from various sources. Mine is 0x4 ...) - */ + printk("yes\n"); - silicon_revision = SONIC_READ(SONIC_SR); - if (sonic_debug > 1) - printk("SONIC Silicon Revision = 0x%04x\n", silicon_revision); + if (dev) { + dev = init_etherdev(dev, sizeof(struct sonic_local)); + /* methinks this will always be true but better safe than sorry */ + if (dev->priv == NULL) + dev->priv = kmalloc(sizeof(struct sonic_local), GFP_KERNEL); + } else { + dev = init_etherdev(NULL, sizeof(struct sonic_local)); + } - /* - * We need to allocate sonic_local later on, making sure it's - * aligned on a 64k boundary. So, no space for dev->priv allocated - * here ... - */ - dev = init_etherdev(0,0); - - if(dev==NULL) + if (dev == NULL) return -ENOMEM; - printk("%s: %s found at 0x%08x, ", - dev->name, "SONIC ethernet", base_addr); + lp = (struct sonic_local*) dev->priv; + memset(lp, 0, sizeof(struct sonic_local)); + /* Danger! My arms are flailing wildly! You *must* set this + before using sonic_read() */ - if (sonic_debug > 1) - printk("using offset %d shift %d,", reg_offset, reg_shift); + dev->base_addr = ONBOARD_SONIC_REGISTERS; + if (via_alt_mapping) + dev->irq = IRQ_AUTO_3; + else + dev->irq = IRQ_NUBUS_9; - /* Fill in the 'dev' fields. */ - dev->base_addr = base_addr; - dev->irq = MAC_SONIC_IRQ; + if (!sonic_version_printed) { + printk(KERN_INFO "%s", version); + sonic_version_printed = 1; + } + printk(KERN_INFO "%s: onboard / comm-slot SONIC at 0x%08lx\n", + dev->name, dev->base_addr); + + /* Now do a song and dance routine in an attempt to determine + the bus width */ + + /* The PowerBook's SONIC is 16 bit always. */ + if (macintosh_config->ident == MAC_MODEL_PB520) { + lp->reg_offset = 0; + lp->dma_bitmode = 0; + } else { + /* Some of the comm-slot cards are 16 bit. But some + of them are not. The 32-bit cards use offset 2 and + pad with zeroes or sometimes ones (I think...) + Therefore, if we try offset 0 and get a silicon + revision of 0, we assume 16 bit. */ + int sr; + + /* Technically this is not necessary since we zeroed + it above */ + lp->reg_offset = 0; + lp->dma_bitmode = 0; + sr = sonic_read(dev, SONIC_SR); + if (sr == 0 || sr == 0xffff) { + lp->reg_offset = 2; + /* 83932 is 0x0004, 83934 is 0x0100 or 0x0101 */ + sr = sonic_read(dev, SONIC_SR); + lp->dma_bitmode = 1; + + } + printk(KERN_INFO + "%s: revision 0x%04x, using %d bit DMA and register offset %d\n", + dev->name, sr, lp->dma_bitmode?32:16, lp->reg_offset); + } - /* - * Put the sonic into software reset, then - * retrieve and print the ethernet address. - */ - SONIC_WRITE(SONIC_CMD, SONIC_CR_RST); + /* Software reset, then initialize control registers. */ + sonic_write(dev, SONIC_CMD, SONIC_CR_RST); + sonic_write(dev, SONIC_DCR, SONIC_DCR_BMS | + SONIC_DCR_RFT1 | SONIC_DCR_TFT0 | SONIC_DCR_EXBUS | + (lp->dma_bitmode ? SONIC_DCR_DW : 0)); + /* This *must* be written back to in order to restore the + extended programmable output bits */ + sonic_write(dev, SONIC_DCR2, 0); - /* - * We can't trust MacOS to initialise things it seems. - */ + /* Clear *and* disable interrupts to be on the safe side */ + sonic_write(dev, SONIC_ISR,0x7fff); + sonic_write(dev, SONIC_IMR,0); - if (sonic_debug > 1) - printk("SONIC_DCR was %X\n",SONIC_READ(SONIC_DCR)); - - SONIC_WRITE(SONIC_DCR, - SONIC_DCR_RFT1 | SONIC_DCR_TFT0 | SONIC_DCR_EXBUS | SONIC_DCR_DW); + /* Now look for the MAC address. */ + if (mac_onboard_sonic_ethernet_addr(dev) != 0) + return -ENODEV; - /* - * We don't want floating spare IRQ's around, not on - * level triggered systems! - * Strange though - writing to the ISR only clears currently - * pending IRQs, but doesn't disable them... Does this make - * a difference?? Seems it does ... - */ -#if 1 - SONIC_WRITE(SONIC_ISR,0x7fff); - SONIC_WRITE(SONIC_IMR,0); -#else - SONIC_WRITE(SONIC_ISR, SONIC_IMR_DEFAULT); -#endif - - /* This is how it is done in jazzsonic.c - * It doesn't seem to work here though. - */ - if (sonic_debug > 2) { - printk("Retreiving CAM entry 0. This should be the HW address.\n"); - - SONIC_WRITE(SONIC_CEP, 0); - for (i = 0; i < 3; i++) - { - val = SONIC_READ(SONIC_CAP0 - i); - dev->dev_addr[i * 2] = val; - dev->dev_addr[i * 2 + 1] = val >> 8; - } + printk(KERN_INFO "MAC "); + for (i = 0; i < 6; i++) { + printk("%2.2x", dev->dev_addr[i]); + if (i < 5) + printk(":"); + } - printk("HW Address from CAM 0: "); - for (i = 0; i < 6; i++) - { - printk("%2.2x", dev->dev_addr[i]); - if (i < 5) - printk(":"); - } - printk("\n"); + printk(" IRQ %d\n", dev->irq); - printk("Retreiving CAM entry 15. Another candidate...\n"); + /* Shared init code */ + return macsonic_init(dev); +} - /* - * MacOS seems to use CAM entry 15 ... - */ - SONIC_WRITE(SONIC_CEP, 15); - for (i = 0; i < 3; i++) - { - val = SONIC_READ(SONIC_CAP0 - i); - dev->dev_addr[i * 2] = val; - dev->dev_addr[i * 2 + 1] = val >> 8; - } +int __init mac_nubus_sonic_ethernet_addr(struct net_device* dev, + unsigned long prom_addr, + int id) +{ + int i; + for(i = 0; i < 6; i++) + dev->dev_addr[i] = SONIC_READ_PROM(i); + /* For now we are going to assume that they're all bit-reversed */ + bit_reverse_addr(dev->dev_addr); - printk("HW Address from CAM 15: "); - for (i = 0; i < 6; i++) - { - printk("%2.2x", dev->dev_addr[i]); - if (i < 5) - printk(":"); - } - printk("\n"); - } + return 0; +} - /* - * if we can read the PROM, we're safe :-) - */ - if (sonic_debug > 1) - printk("Retreiving HW address from the PROM: "); - - for(i=0;i<6;i++){ - dev->dev_addr[i]=SONIC_READ_PROM(i); - } - if (sonic_debug > 1) { - for (i = 0; i < 6; i++) - { - printk("%2.2x", dev->dev_addr[i]); - if (i < 5) - printk(":"); - } - printk("\n"); +int __init macsonic_ident(struct nubus_dev* ndev) +{ + if (ndev->dr_hw == NUBUS_DRHW_ASANTE_LC && + ndev->dr_sw == NUBUS_DRSW_SONIC_LC) + return MACSONIC_DAYNALINK; + if (ndev->dr_hw == NUBUS_DRHW_SONIC && + ndev->dr_sw == NUBUS_DRSW_APPLE) { + /* There has to be a better way to do this... */ + if (strstr(ndev->board->name, "DuoDock")) + return MACSONIC_DUODOCK; + else + return MACSONIC_APPLE; } - /* - * If its not one of these we have - * screwed up on this Mac model - */ + return -1; +} - if (memcmp(dev->dev_addr, "\x08\x00\x07", 3) && - memcmp(dev->dev_addr, "\x00\xA0\x40", 3) && - memcmp(dev->dev_addr, "\x00\x05\x02", 3)) +int __init mac_nubus_sonic_probe(struct net_device* dev) +{ + static int slots = 0; + struct nubus_dev* ndev = NULL; + struct sonic_local* lp; + unsigned long base_addr, prom_addr; + u16 sonic_dcr; + int id; + int i; + int reg_offset, dma_bitmode; + + /* Find the first SONIC that hasn't been initialized already */ + while ((ndev = nubus_find_type(NUBUS_CAT_NETWORK, + NUBUS_TYPE_ETHERNET, ndev)) != NULL) { - /* - * Try bit reversed - */ - for(i=0;i<6;i++){ - val = SONIC_READ_PROM(i); - dev->dev_addr[i]=(nibbletab[val & 0xf] << 4) | - nibbletab[(val >> 4) &0xf]; - } - if (sonic_debug > 1) { - printk("Trying bit reversed: "); - for (i = 0; i < 6; i++) - { - printk("%2.2x", dev->dev_addr[i]); - if (i < 5) - printk(":"); - } - printk("\n"); - } - if (memcmp(dev->dev_addr, "\x08\x00\x07", 3) && - memcmp(dev->dev_addr, "\x00\xA0\x40", 3) && - memcmp(dev->dev_addr, "\x00\x05\x02", 3)) - { - /* - * Still nonsense ... messed up someplace! - */ - printk("ERROR (INVALID MAC)\n"); - return -EIO; - } + /* Have we seen it already? */ + if (slots & (1<<ndev->board->slot)) + continue; + slots |= 1<<ndev->board->slot; + + /* Is it one of ours? */ + if ((id = macsonic_ident(ndev)) != -1) + break; } - printk(" MAC "); - for (i = 0; i < 6; i++) - { + if (ndev == NULL) + return -ENODEV; + + switch (id) { + case MACSONIC_DUODOCK: + base_addr = ndev->board->slot_addr + DUODOCK_SONIC_REGISTERS; + prom_addr = ndev->board->slot_addr + DUODOCK_SONIC_PROM_BASE; + sonic_dcr = SONIC_DCR_EXBUS | SONIC_DCR_RFT0 | SONIC_DCR_RFT1 + | SONIC_DCR_TFT0; + reg_offset = 2; + dma_bitmode = 1; + break; + case MACSONIC_APPLE: + base_addr = ndev->board->slot_addr + APPLE_SONIC_REGISTERS; + prom_addr = ndev->board->slot_addr + APPLE_SONIC_PROM_BASE; + sonic_dcr = SONIC_DCR_BMS | SONIC_DCR_RFT1 | SONIC_DCR_TFT0; + reg_offset = 0; + dma_bitmode = 1; + break; + case MACSONIC_APPLE16: + base_addr = ndev->board->slot_addr + APPLE_SONIC_REGISTERS; + prom_addr = ndev->board->slot_addr + APPLE_SONIC_PROM_BASE; + sonic_dcr = SONIC_DCR_EXBUS + | SONIC_DCR_RFT1 | SONIC_DCR_TFT0 + | SONIC_DCR_PO1 | SONIC_DCR_BMS; + reg_offset = 0; + dma_bitmode = 0; + break; + case MACSONIC_DAYNALINK: + base_addr = ndev->board->slot_addr + APPLE_SONIC_REGISTERS; + prom_addr = ndev->board->slot_addr + DAYNALINK_PROM_BASE; + sonic_dcr = SONIC_DCR_RFT1 | SONIC_DCR_TFT0 + | SONIC_DCR_PO1 | SONIC_DCR_BMS; + reg_offset = 0; + dma_bitmode = 0; + break; + case MACSONIC_DAYNA: + base_addr = ndev->board->slot_addr + DAYNA_SONIC_REGISTERS; + prom_addr = ndev->board->slot_addr + DAYNA_SONIC_MAC_ADDR; + sonic_dcr = SONIC_DCR_BMS + | SONIC_DCR_RFT1 | SONIC_DCR_TFT0 | SONIC_DCR_PO1; + reg_offset = 0; + dma_bitmode = 0; + break; + default: + printk(KERN_ERR "macsonic: WTF, id is %d\n", id); + return -ENODEV; + } + + if (dev) { + dev = init_etherdev(dev, sizeof(struct sonic_local)); + /* methinks this will always be true but better safe than sorry */ + if (dev->priv == NULL) + dev->priv = kmalloc(sizeof(struct sonic_local), GFP_KERNEL); + } else { + dev = init_etherdev(NULL, sizeof(struct sonic_local)); + } + + if (dev == NULL) + return -ENOMEM; + + lp = (struct sonic_local*) dev->priv; + memset(lp, 0, sizeof(struct sonic_local)); + /* Danger! My arms are flailing wildly! You *must* set this + before using sonic_read() */ + lp->reg_offset = reg_offset; + lp->dma_bitmode = dma_bitmode; + dev->base_addr = base_addr; + dev->irq = SLOT2IRQ(ndev->board->slot); + + if (!sonic_version_printed) { + printk(KERN_INFO "%s", version); + sonic_version_printed = 1; + } + printk(KERN_INFO "%s: %s in slot %X\n", + dev->name, ndev->board->name, ndev->board->slot); + printk(KERN_INFO "%s: revision 0x%04x, using %d bit DMA and register offset %d\n", + dev->name, sonic_read(dev, SONIC_SR), dma_bitmode?32:16, reg_offset); + + /* Software reset, then initialize control registers. */ + sonic_write(dev, SONIC_CMD, SONIC_CR_RST); + sonic_write(dev, SONIC_DCR, sonic_dcr + | (dma_bitmode ? SONIC_DCR_DW : 0)); + + /* Clear *and* disable interrupts to be on the safe side */ + sonic_write(dev, SONIC_ISR,0x7fff); + sonic_write(dev, SONIC_IMR,0); + + /* Now look for the MAC address. */ + if (mac_nubus_sonic_ethernet_addr(dev, prom_addr, id) != 0) + return -ENODEV; + + printk(KERN_INFO "MAC "); + for (i = 0; i < 6; i++) { printk("%2.2x", dev->dev_addr[i]); if (i < 5) printk(":"); } + printk(" IRQ %d\n", dev->irq); - printk(" IRQ %d\n", MAC_SONIC_IRQ); - - /* Initialize the device structure. */ - if (dev->priv == NULL) - { - if (sonic_debug > 2) { - printk("Allocating memory for dev->priv aka lp\n"); - printk("Memory to allocate: %d\n",sizeof(*lp)); - } - /* - * the memory be located in the same 64kb segment - */ - lp = NULL; - i = 0; - do - { - lp = (struct sonic_local *) kmalloc(sizeof(*lp), GFP_KERNEL); - if ((unsigned long) lp >> 16 != ((unsigned long) lp + sizeof(*lp)) >> 16) - { - /* FIXME, free the memory later */ - kfree(lp); - lp = NULL; - } - } - while (lp == NULL && i++ < 20); + /* Shared init code */ + return macsonic_init(dev); +} - if (lp == NULL) - { - printk("%s: couldn't allocate memory for descriptors\n", - dev->name); - return -ENOMEM; - } +#ifdef MODULE +static char namespace[16] = ""; +static struct net_device dev_macsonic = { + NULL, + 0, 0, 0, 0, + 0, 0, + 0, 0, 0, NULL, NULL }; - if (sonic_debug > 2) { - printk("Memory allocated after %d tries\n",i); - } +MODULE_PARM(sonic_debug, "i"); - /* XXX sonic_local has the TDA, RRA, RDA, don't cache */ - kernel_set_cachemode((u32)lp, 8192, IOMAP_NOCACHE_SER); - memset(lp, 0, sizeof(struct sonic_local)); +EXPORT_NO_SYMBOLS; - lp->cda_laddr = (u32)lp; - if (sonic_debug > 2) { - printk("memory allocated for sonic at 0x%x\n",lp); - } - lp->tda_laddr = lp->cda_laddr + sizeof(lp->cda); - lp->rra_laddr = lp->tda_laddr + sizeof(lp->tda); - lp->rda_laddr = lp->rra_laddr + sizeof(lp->rra); - - /* allocate receive buffer area */ - /* FIXME, maybe we should use skbs */ - if ((lp->rba = (char *) kmalloc(SONIC_NUM_RRS * SONIC_RBSIZE, GFP_KERNEL)) == NULL) - { - printk("%s: couldn't allocate receive buffers\n", dev->name); - return -ENOMEM; - } - /* XXX RBA written by Sonic, not cached either */ - kernel_set_cachemode((u32)lp->rba, 6*8192, IOMAP_NOCACHE_SER); - lp->rba_laddr = (u32)lp->rba; - flush_cache_all(); - dev->priv = (struct sonic_local *) lp; - } - lp = (struct sonic_local *) dev->priv; - dev->open = sonic_open; - dev->stop = sonic_close; - dev->hard_start_xmit = sonic_send_packet; - dev->get_stats = sonic_get_stats; - dev->set_multicast_list = &sonic_multicast_list; +int +init_module(void) +{ + dev_macsonic.name = namespace; + dev_macsonic.init = macsonic_probe; - /* Fill in the fields of the device structure with ethernet values. */ - ether_setup(dev); + if (register_netdev(&dev_macsonic) != 0) { + printk(KERN_WARNING "macsonic.c: No card found\n"); + return -ENXIO; + } return 0; } -/* - * SONIC uses a nubus IRQ - */ +void +cleanup_module(void) +{ + if (dev_macsonic.priv != NULL) { + unregister_netdev(&dev_macsonic); + kfree(dev_macsonic.priv); + dev_macsonic.priv = NULL; + } +} +#endif /* MODULE */ -#define sonic_request_irq(irq, vec, flags, name, dev) \ - nubus_request_irq(irq, dev, vec) -#define sonic_free_irq(irq,id) nubus_free_irq(irq) -/* - * No funnies on memory mapping. - */ +#define vdma_alloc(foo, bar) ((u32)foo) +#define vdma_free(baz) +#define sonic_chiptomem(bat) (bat) +#define PHYSADDR(quux) (quux) -#define sonic_chiptomem(x) (x) +#include "sonic.c" /* - * No VDMA on a Macintosh. So we need request no such facility. + * Local variables: + * compile-command: "m68k-linux-gcc -D__KERNEL__ -I../../include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -pipe -fno-strength-reduce -ffixed-a2 -DMODULE -DMODVERSIONS -include ../../include/linux/modversions.h -c -o macsonic.o macsonic.c" + * version-control: t + * kept-new-versions: 5 + * c-indent-level: 8 + * tab-width: 8 + * End: + * */ - -#define vdma_alloc(x,y) ((u32)(x)) -#define vdma_free(x) -#define PHYSADDR(x) (x) - -#include "sonic.c" diff --git a/drivers/net/sun3lance.c b/drivers/net/sun3lance.c index 4fff08b7f..006e36f4b 100644 --- a/drivers/net/sun3lance.c +++ b/drivers/net/sun3lance.c @@ -21,7 +21,7 @@ */ -static char *version = "sun3lance.c: v1.0 12/12/96 Sam Creasey (sammy@users.qual.net)\n"; +static char *version = "sun3lance.c: v1.1 11/17/1999 Sam Creasey (sammy@oh.verio.com)\n"; #include <linux/module.h> @@ -263,6 +263,8 @@ static int __init lance_probe( struct net_device *dev) int i; static int did_version = 0; int found = 0; + volatile unsigned short *ioaddr_probe; + unsigned short tmp1, tmp2; /* LANCE_OBIO can be found within the IO pmeg with some effort */ for(ioaddr = 0xfe00000; ioaddr < (0xfe00000 + @@ -282,6 +284,23 @@ static int __init lance_probe( struct net_device *dev) if(!found) return 0; + /* test to see if there's really a lance here */ + /* (CSRO_INIT shouldn't be readable) */ + + ioaddr_probe = (volatile unsigned short *)ioaddr; + tmp1 = ioaddr_probe[0]; + tmp2 = ioaddr_probe[1]; + + ioaddr_probe[1] = CSR0; + ioaddr_probe[0] = CSR0_INIT | CSR0_STOP; + + if(ioaddr_probe[0] != CSR0_STOP) { + ioaddr_probe[0] = tmp1; + ioaddr_probe[1] = tmp2; + + return 0; + } + init_etherdev( dev, sizeof(struct lance_private) ); if (!dev->priv) dev->priv = kmalloc( sizeof(struct lance_private), GFP_KERNEL ); diff --git a/drivers/net/sunbmac.c b/drivers/net/sunbmac.c index 10e466a5d..b86c8fdb6 100644 --- a/drivers/net/sunbmac.c +++ b/drivers/net/sunbmac.c @@ -1,4 +1,4 @@ -/* $Id: sunbmac.c,v 1.12 1999/12/15 14:07:58 davem Exp $ +/* $Id: sunbmac.c,v 1.13 2000/01/28 13:42:29 jj Exp $ * sunbmac.c: Driver for Sparc BigMAC 100baseT ethernet adapters. * * Copyright (C) 1997, 1998, 1999 David S. Miller (davem@redhat.com) diff --git a/drivers/net/sunhme.c b/drivers/net/sunhme.c index eb76a9fee..65d8315d9 100644 --- a/drivers/net/sunhme.c +++ b/drivers/net/sunhme.c @@ -1,4 +1,4 @@ -/* $Id: sunhme.c,v 1.84 1999/12/15 14:08:03 davem Exp $ +/* $Id: sunhme.c,v 1.85 2000/01/28 13:42:27 jj Exp $ * sunhme.c: Sparc HME/BigMac 10/100baseT half/full duplex auto switching, * auto carrier detecting ethernet driver. Also known as the * "Happy Meal Ethernet" found on SunSwift SBUS cards. diff --git a/drivers/net/sunlance.c b/drivers/net/sunlance.c index 3c499ba38..45b671ae7 100644 --- a/drivers/net/sunlance.c +++ b/drivers/net/sunlance.c @@ -1,4 +1,4 @@ -/* $Id: sunlance.c,v 1.92 1999/12/15 14:08:09 davem Exp $ +/* $Id: sunlance.c,v 1.93 2000/01/28 13:42:31 jj Exp $ * lance.c: Linux/Sparc/Lance driver * * Written 1995, 1996 by Miguel de Icaza diff --git a/drivers/net/sunqe.c b/drivers/net/sunqe.c index 2a170a0a6..53e0c756f 100644 --- a/drivers/net/sunqe.c +++ b/drivers/net/sunqe.c @@ -1,4 +1,4 @@ -/* $Id: sunqe.c,v 1.40 1999/12/15 14:08:13 davem Exp $ +/* $Id: sunqe.c,v 1.41 2000/01/28 13:42:30 jj Exp $ * sunqe.c: Sparc QuadEthernet 10baseT SBUS card driver. * Once again I am out to prove that every ethernet * controller out there can be most efficiently programmed diff --git a/drivers/sbus/audio/cs4231.c b/drivers/sbus/audio/cs4231.c index 442c25206..82e0fc494 100644 --- a/drivers/sbus/audio/cs4231.c +++ b/drivers/sbus/audio/cs4231.c @@ -1,4 +1,4 @@ -/* $Id: cs4231.c,v 1.41 1999/12/19 23:28:03 davem Exp $ +/* $Id: cs4231.c,v 1.42 2000/01/28 13:42:48 jj Exp $ * drivers/sbus/audio/cs4231.c * * Copyright 1996, 1997, 1998, 1999 Derrick J Brashear (shadow@andrew.cmu.edu) diff --git a/drivers/sbus/audio/dbri.c b/drivers/sbus/audio/dbri.c index 0b73b8e0d..a3f083ead 100644 --- a/drivers/sbus/audio/dbri.c +++ b/drivers/sbus/audio/dbri.c @@ -1,4 +1,4 @@ -/* $Id: dbri.c,v 1.17 2000/01/20 07:57:47 anton Exp $ +/* $Id: dbri.c,v 1.18 2000/01/28 13:42:50 jj Exp $ * drivers/sbus/audio/dbri.c * * Copyright (C) 1997 Rudolf Koenig (rfkoenig@immd4.informatik.uni-erlangen.de) diff --git a/drivers/sbus/char/zs.c b/drivers/sbus/char/zs.c index d0ec266c0..77f031cce 100644 --- a/drivers/sbus/char/zs.c +++ b/drivers/sbus/char/zs.c @@ -1,4 +1,4 @@ -/* $Id: zs.c,v 1.52 1999/12/15 14:29:20 davem Exp $ +/* $Id: zs.c,v 1.53 2000/01/29 01:29:38 anton Exp $ * zs.c: Zilog serial port driver for the Sparc. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -1928,7 +1928,7 @@ int zs_open(struct tty_struct *tty, struct file * filp) static void show_serial_version(void) { - char *revision = "$Revision: 1.52 $"; + char *revision = "$Revision: 1.53 $"; char *version, *p; version = strchr(revision, ' '); @@ -2076,11 +2076,6 @@ static struct sun_zslayout * __init get_zs(int chip) static int irq = 0; int chipid = chip; -#if CONFIG_AP1000 - printk("No zs chip\n"); - return NULL; -#endif - iospace = 0; if(chip < 0 || chip >= NUM_SERIAL) panic("get_zs bogon zs chip number"); @@ -2407,11 +2402,6 @@ int __init zs_init(void) struct sun_serial *info; char dummy; -#if CONFIG_AP1000 - printk("not doing zs_init()\n"); - return 0; -#endif - /* Setup base handler, and timer table. */ init_bh(SERIAL_BH, do_serial_bh); timer_table[RS_TIMER].fn = zs_timer; diff --git a/drivers/scsi/esp.c b/drivers/scsi/esp.c index fd315c1f7..61ce25c36 100644 --- a/drivers/scsi/esp.c +++ b/drivers/scsi/esp.c @@ -1,4 +1,4 @@ -/* $Id: esp.c,v 1.89 1999/12/23 01:46:14 davem Exp $ +/* $Id: esp.c,v 1.90 2000/01/28 13:42:56 jj Exp $ * esp.c: EnhancedScsiProcessor Sun SCSI driver code. * * Copyright (C) 1995, 1998 David S. Miller (davem@caip.rutgers.edu) diff --git a/drivers/scsi/scsi_merge.c b/drivers/scsi/scsi_merge.c index 290d33d6c..119634641 100644 --- a/drivers/scsi/scsi_merge.c +++ b/drivers/scsi/scsi_merge.c @@ -87,7 +87,7 @@ static int dump_stats(struct request *req, * Dump the information that we have. We know we have an * inconsistency. */ - printk("nr_segments is %lx\n", req->nr_segments); + printk("nr_segments is %x\n", req->nr_segments); printk("counted segments is %x\n", segments); printk("Flags %d %d\n", use_clustering, dma_host); for (bh = req->bh; bh->b_reqnext != NULL; bh = bh->b_reqnext) diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 0790a1978..ccbacf127 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -373,6 +373,7 @@ static int sd_init_command(Scsi_Cmnd * SCpnt) static int sd_open(struct inode *inode, struct file *filp) { int target; + Scsi_Device * SDev; target = DEVICE_NR(inode->i_rdev); SCSI_LOG_HLQUEUE(1, printk("target=%d, max=%d\n", target, sd_template.dev_max)); @@ -412,13 +413,14 @@ static int sd_open(struct inode *inode, struct file *filp) if ((rscsi_disks[target].write_prot) && (filp->f_mode & 2)) return -EROFS; } + SDev = rscsi_disks[target].device; /* * It is possible that the disk changing stuff resulted in the device * being taken offline. If this is the case, report this to the user, * and don't pretend that * the open actually succeeded. */ - if (!rscsi_disks[target].device->online) { + if (!SDev->online) { return -ENXIO; } /* @@ -428,13 +430,14 @@ static int sd_open(struct inode *inode, struct file *filp) if (sd_sizes[SD_PARTITION(inode->i_rdev)] == 0) return -ENXIO; - if (rscsi_disks[target].device->removable) - if (!rscsi_disks[target].device->access_count) - sd_ioctl(inode, NULL, SCSI_IOCTL_DOORLOCK, 0); + if (SDev->removable) + if (!SDev->access_count) + if (scsi_block_when_processing_errors(SDev)) + scsi_ioctl(SDev, SCSI_IOCTL_DOORLOCK, NULL); - rscsi_disks[target].device->access_count++; - if (rscsi_disks[target].device->host->hostt->module) - __MOD_INC_USE_COUNT(rscsi_disks[target].device->host->hostt->module); + SDev->access_count++; + if (SDev->host->hostt->module) + __MOD_INC_USE_COUNT(SDev->host->hostt->module); if (sd_template.module) __MOD_INC_USE_COUNT(sd_template.module); return 0; @@ -443,17 +446,20 @@ static int sd_open(struct inode *inode, struct file *filp) static int sd_release(struct inode *inode, struct file *file) { int target; + Scsi_Device * SDev; target = DEVICE_NR(inode->i_rdev); + SDev = rscsi_disks[target].device; - rscsi_disks[target].device->access_count--; + SDev->access_count--; - if (rscsi_disks[target].device->removable) { - if (!rscsi_disks[target].device->access_count) - sd_ioctl(inode, NULL, SCSI_IOCTL_DOORUNLOCK, 0); + if (SDev->removable) { + if (!SDev->access_count) + if (scsi_block_when_processing_errors(SDev)) + scsi_ioctl(SDev, SCSI_IOCTL_DOORUNLOCK, NULL); } - if (rscsi_disks[target].device->host->hostt->module) - __MOD_DEC_USE_COUNT(rscsi_disks[target].device->host->hostt->module); + if (SDev->host->hostt->module) + __MOD_DEC_USE_COUNT(SDev->host->hostt->module); if (sd_template.module) __MOD_DEC_USE_COUNT(sd_template.module); return 0; @@ -580,17 +586,17 @@ static int check_scsidisk_media_change(kdev_t full_dev) { int retval; int target; - struct inode inode; int flag = 0; + Scsi_Device * SDev; target = DEVICE_NR(full_dev); + SDev = rscsi_disks[target].device; - if (target >= sd_template.dev_max || - !rscsi_disks[target].device) { + if (target >= sd_template.dev_max || !SDev) { printk("SCSI disk request error: invalid device.\n"); return 0; } - if (!rscsi_disks[target].device->removable) + if (!SDev->removable) return 0; /* @@ -599,13 +605,12 @@ static int check_scsidisk_media_change(kdev_t full_dev) * can deal with it then. It is only because of unrecoverable errors * that we would ever take a device offline in the first place. */ - if (rscsi_disks[target].device->online == FALSE) { + if (SDev->online == FALSE) { rscsi_disks[target].ready = 0; - rscsi_disks[target].device->changed = 1; + SDev->changed = 1; return 1; /* This will force a flush, if called from * check_disk_change */ } - inode.i_rdev = full_dev; /* This is all we really need here */ /* Using Start/Stop enables differentiation between drive with * no cartridge loaded - NOT READY, drive with changed cartridge - @@ -613,7 +618,9 @@ static int check_scsidisk_media_change(kdev_t full_dev) * This also handles drives that auto spin down. eg iomega jaz 1GB * as this will spin up the drive. */ - retval = sd_ioctl(&inode, NULL, SCSI_IOCTL_START_UNIT, 0); + retval = -ENODEV; + if (scsi_block_when_processing_errors(SDev)) + retval = scsi_ioctl(SDev, SCSI_IOCTL_START_UNIT, NULL); if (retval) { /* Unable to test, unit probably not ready. * This usually means there is no disc in the @@ -622,7 +629,7 @@ static int check_scsidisk_media_change(kdev_t full_dev) * again. */ rscsi_disks[target].ready = 0; - rscsi_disks[target].device->changed = 1; + SDev->changed = 1; return 1; /* This will force a flush, if called from * check_disk_change */ } @@ -634,9 +641,9 @@ static int check_scsidisk_media_change(kdev_t full_dev) rscsi_disks[target].ready = 1; /* FLOPTICAL */ - retval = rscsi_disks[target].device->changed; + retval = SDev->changed; if (!flag) - rscsi_disks[target].device->changed = 0; + SDev->changed = 0; return retval; } diff --git a/drivers/sound/Config.in b/drivers/sound/Config.in index 4859685c6..c218ca69d 100644 --- a/drivers/sound/Config.in +++ b/drivers/sound/Config.in @@ -26,7 +26,7 @@ if [ "$CONFIG_VISWS" = "y" ]; then dep_tristate ' SGI Visual Workstation Sound' CONFIG_SOUND_VWSND $CONFIG_SOUND fi -dep_tristate ' Trident 4DWave-DX/NX' CONFIG_SOUND_TRIDENT $CONFIG_SOUND +dep_tristate ' Trident 4DWave DX/NX or SiS 7018 PCI Audio Core' CONFIG_SOUND_TRIDENT $CONFIG_SOUND dep_tristate ' Support for Turtle Beach MultiSound Classic, Tahiti, Monterey' CONFIG_SOUND_MSNDCLAS $CONFIG_SOUND if [ "$CONFIG_SOUND_MSNDCLAS" = "y" -o "$CONFIG_SOUND_MSNDCLAS" = "m" ]; then diff --git a/drivers/sound/Makefile b/drivers/sound/Makefile index 50ba920b9..17f404531 100644 --- a/drivers/sound/Makefile +++ b/drivers/sound/Makefile @@ -25,7 +25,7 @@ endif export-objs := ad1848.o audio_syms.o midi_syms.o mpu401.o \ msnd.o opl3.o sb_card.o sequencer_syms.o \ sound_core.o sound_syms.o uart401.o ad1816.o \ - nm256_audio.o ac97.o + nm256_audio.o ac97.o ac97_codec.o @@ -84,7 +84,7 @@ obj-$(CONFIG_SOUND_ES1370) += es1370.o obj-$(CONFIG_SOUND_ES1371) += es1371.o obj-$(CONFIG_SOUND_ESSSOLO1) += esssolo1.o obj-$(CONFIG_SOUND_MAESTRO) += maestro.o -obj-$(CONFIG_SOUND_TRIDENT) += trident.o +obj-$(CONFIG_SOUND_TRIDENT) += trident.o ac97_codec.o # Declare multi-part drivers. diff --git a/drivers/sound/ac97_codec.c b/drivers/sound/ac97_codec.c new file mode 100644 index 000000000..a1177c6df --- /dev/null +++ b/drivers/sound/ac97_codec.c @@ -0,0 +1,440 @@ +/* + * ac97_codec.c: Generic AC97 mixer module + * + * Derived from ac97 mixer in maestro and trident driver. + * + * Copyright 2000 Silicon Integrated System Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * History + * Jan 14 2000 Ollie Lho <ollie@sis.com.tw> + * Isloated from trident.c to support multiple ac97 codec + */ +#include <linux/module.h> +#include <linux/version.h> +#include <linux/kernel.h> + +#include "ac97_codec.h" + +static int ac97_read_mixer(struct ac97_codec *codec, int oss_channel); +static void ac97_write_mixer(struct ac97_codec *codec, int oss_channel, + unsigned int left, unsigned int right); +static void ac97_set_mixer(struct ac97_codec *codec, unsigned int oss_mixer, unsigned int val ); +static int ac97_recmask_io(struct ac97_codec *codec, int rw, int mask); +static int ac97_mixer_ioctl(struct ac97_codec *codec, unsigned int cmd, unsigned long arg); + +static struct { + unsigned int id; + char *name; + int (*init) (struct ac97_codec *codec); +} snd_ac97_codec_ids[] = { + {0x414B4D00, "Asahi Kasei AK4540" , NULL}, + {0x41445340, "Analog Devices AD1881" , NULL}, + {0x43525900, "Cirrus Logic CS4297" , NULL}, + {0x43525913, "Cirrus Logic CS4297A" , NULL}, + {0x43525931, "Cirrus Logic CS4299" , NULL}, + {0x4e534331, "National Semiconductor LM4549", NULL}, + {0x83847600, "SigmaTel STAC????" , NULL}, + {0x83847604, "SigmaTel STAC9701/3/4/5", NULL}, + {0x83847605, "SigmaTel STAC9704" , NULL}, + {0x83847608, "SigmaTel STAC9708" , NULL}, + {0x83847609, "SigmaTel STAC9721/23" , NULL}, + {0x00000000, NULL, NULL} +}; + + +/* this table has default mixer values for all OSS mixers. */ +static struct mixer_defaults { + int mixer; + unsigned int value; +} mixer_defaults[SOUND_MIXER_NRDEVICES] = { + /* all values 0 -> 100 in bytes */ + {SOUND_MIXER_VOLUME, 0x3232}, + {SOUND_MIXER_BASS, 0x3232}, + {SOUND_MIXER_TREBLE, 0x3232}, + {SOUND_MIXER_PCM, 0x3232}, + {SOUND_MIXER_SPEAKER, 0x3232}, + {SOUND_MIXER_LINE, 0x3232}, + {SOUND_MIXER_MIC, 0x3232}, + {SOUND_MIXER_CD, 0x3232}, + {SOUND_MIXER_ALTPCM, 0x3232}, + {SOUND_MIXER_IGAIN, 0x3232}, + {SOUND_MIXER_LINE1, 0x3232}, + {SOUND_MIXER_PHONEIN, 0x3232}, + {SOUND_MIXER_PHONEOUT, 0x3232}, + {SOUND_MIXER_VIDEO, 0x3232}, + {-1,0} +}; + +/* table to scale scale from OSS mixer value to AC97 mixer register value */ +static struct ac97_mixer_hw { + unsigned char offset; + int scale; +} ac97_hw[SOUND_MIXER_NRDEVICES]= { + [SOUND_MIXER_VOLUME] = {AC97_MASTER_VOL_STEREO,63}, + [SOUND_MIXER_BASS] = {AC97_MASTER_TONE, 15}, + [SOUND_MIXER_TREBLE] = {AC97_MASTER_TONE, 15}, + [SOUND_MIXER_PCM] = {AC97_PCMOUT_VOL, 31}, + [SOUND_MIXER_SPEAKER] = {AC97_PCBEEP_VOL, 15}, + [SOUND_MIXER_LINE] = {AC97_LINEIN_VOL, 31}, + [SOUND_MIXER_MIC] = {AC97_MIC_VOL, 31}, + [SOUND_MIXER_CD] = {AC97_CD_VOL, 31}, + [SOUND_MIXER_ALTPCM] = {AC97_HEADPHONE_VOL, 63}, + [SOUND_MIXER_IGAIN] = {AC97_RECORD_GAIN, 31}, + [SOUND_MIXER_LINE1] = {AC97_AUX_VOL, 31}, + [SOUND_MIXER_PHONEIN] = {AC97_PHONE_VOL, 15}, + [SOUND_MIXER_PHONEOUT] = {AC97_MASTER_VOL_MONO, 63}, + [SOUND_MIXER_VIDEO] = {AC97_VIDEO_VOL, 31}, +}; + +/* the following tables allow us to go from OSS <-> ac97 quickly. */ +enum ac97_recsettings { + AC97_REC_MIC=0, + AC97_REC_CD, + AC97_REC_VIDEO, + AC97_REC_AUX, + AC97_REC_LINE, + AC97_REC_STEREO, /* combination of all enabled outputs.. */ + AC97_REC_MONO, /*.. or the mono equivalent */ + AC97_REC_PHONE +}; + +static unsigned int ac97_rm2oss[] = { + [AC97_REC_MIC] = SOUND_MIXER_MIC, + [AC97_REC_CD] = SOUND_MIXER_CD, + [AC97_REC_VIDEO] = SOUND_MIXER_VIDEO, + [AC97_REC_AUX] = SOUND_MIXER_LINE1, + [AC97_REC_LINE] = SOUND_MIXER_LINE, + [AC97_REC_PHONE] = SOUND_MIXER_PHONEIN +}; + +/* indexed by bit position */ +static unsigned int ac97_oss_rm[] = { + [SOUND_MIXER_MIC] = AC97_REC_MIC, + [SOUND_MIXER_CD] = AC97_REC_CD, + [SOUND_MIXER_VIDEO] = AC97_REC_VIDEO, + [SOUND_MIXER_LINE1] = AC97_REC_AUX, + [SOUND_MIXER_LINE] = AC97_REC_LINE, + [SOUND_MIXER_PHONEIN] = AC97_REC_PHONE +}; + +/* reads the given OSS mixer from the ac97 the caller must have insured that the ac97 knows + about that given mixer, and should be holding a spinlock for the card */ +static int ac97_read_mixer(struct ac97_codec *codec, int oss_channel) +{ + u16 val; + int ret = 0; + struct ac97_mixer_hw *mh = &ac97_hw[oss_channel]; + + val = codec->codec_read(codec , mh->offset); + + if (AC97_STEREO_MASK & (1 << oss_channel)) { + /* nice stereo mixers .. */ + int left,right; + + left = (val >> 8) & 0x7f; + right = val & 0x7f; + + if (oss_channel == SOUND_MIXER_IGAIN) { + right = (right * 100) / mh->scale; + left = (left * 100) / mh->scale; + } else { + right = 100 - ((right * 100) / mh->scale); + left = 100 - ((left * 100) / mh->scale); + } + + ret = left | (right << 8); + } else if (oss_channel == SOUND_MIXER_SPEAKER) { + ret = 100 - ((((val & 0x1e)>>1) * 100) / mh->scale); + } else if (oss_channel == SOUND_MIXER_MIC) { + ret = 100 - (((val & 0x1f) * 100) / mh->scale); + /* the low bit is optional in the tone sliders and masking + it lets us avoid the 0xf 'bypass'.. */ + } else if (oss_channel == SOUND_MIXER_BASS) { + ret = 100 - ((((val >> 8) & 0xe) * 100) / mh->scale); + } else if (oss_channel == SOUND_MIXER_TREBLE) { + ret = 100 - (((val & 0xe) * 100) / mh->scale); + } + +#ifdef DEBUG + printk("ac97_codec: read OSS mixer %2d (ac97 register 0x%02x), " + "0x%04x -> 0x%04x\n", oss_channel, mh->offset, val, ret); +#endif + + return ret; +} + +/* write the OSS encoded volume to the given OSS encoded mixer, again caller's job to + make sure all is well in arg land, call with spinlock held */ +static void ac97_write_mixer(struct ac97_codec *codec, int oss_channel, + unsigned int left, unsigned int right) +{ + u16 val = 0; + struct ac97_mixer_hw *mh = &ac97_hw[oss_channel]; + +#ifdef DEBUG + printk("ac97_codec: wrote OSS mixer %2d (%s ac97 register 0x%02x), " + "left vol:%2d, right vol:%2d:", + oss_channel, codec->id ? "Secondary" : "Primary", + mh->offset, left, right); +#endif + + if (AC97_STEREO_MASK & (1 << oss_channel)) { + /* stereo mixers */ + if (oss_channel == SOUND_MIXER_IGAIN) { + right = (right * mh->scale) / 100; + left = (left * mh->scale) / 100; + } else { + right = ((100 - right) * mh->scale) / 100; + left = ((100 - left) * mh->scale) / 100; + } + val = (left << 8) | right; + } else if (oss_channel == SOUND_MIXER_SPEAKER) { + val = (((100 - left) * mh->scale) / 100) << 1; + } else if (oss_channel == SOUND_MIXER_MIC) { + val = codec->codec_read(codec , mh->offset) & ~0x801f; + val |= (((100 - left) * mh->scale) / 100); + /* the low bit is optional in the tone sliders and masking + it lets us avoid the 0xf 'bypass'.. */ + } else if (oss_channel == SOUND_MIXER_BASS) { + val = codec->codec_read(codec , mh->offset) & ~0x0f00; + val |= ((((100 - left) * mh->scale) / 100) << 8) & 0x0e00; + } else if (oss_channel == SOUND_MIXER_TREBLE) { + val = codec->codec_read(codec , mh->offset) & ~0x000f; + val |= (((100 - left) * mh->scale) / 100) & 0x000e; + } + +#ifdef DEBUG + printk(" 0x%04x", val); +#endif + codec->codec_write(codec, mh->offset, val); + +#ifdef DEBUG + val = codec->codec_read(codec, mh->offset); + printk(" -> 0x%04x\n", val); +#endif +} + +/* a thin wrapper for write_mixer */ +static void ac97_set_mixer(struct ac97_codec *codec, unsigned int oss_mixer, unsigned int val ) +{ + unsigned int left,right; + + /* cleanse input a little */ + right = ((val >> 8) & 0xff) ; + left = (val & 0xff) ; + + if (right > 100) right = 100; + if (left > 100) left = 100; + + codec->mixer_state[oss_mixer] = (right << 8) | left; + codec->write_mixer(codec, oss_mixer, left, right); +} + +/* read or write the recmask, the ac97 can really have left and right recording + inputs independantly set, but OSS doesn't seem to want us to express that to + the user. the caller guarantees that we have a supported bit set, and they + must be holding the card's spinlock */ +static int ac97_recmask_io(struct ac97_codec *codec, int rw, int mask) +{ + unsigned int val; + + if (rw) { + /* read it from the card */ + val = codec->codec_read(codec, 0x1a) & 0x7; + return ac97_rm2oss[val]; + } + + /* else, write the first set in the mask as the + output */ + + val = ffs(mask); + val = ac97_oss_rm[val-1]; + val |= val << 8; /* set both channels */ + +#ifdef DEBUG + printk("ac97_codec: setting ac97 recmask to 0x%x\n", val); +#endif + + codec->codec_write(codec, 0x1a, val); + + return 0; +}; + +static int ac97_mixer_ioctl(struct ac97_codec *codec, unsigned int cmd, unsigned long arg) +{ + int i, val = 0; + + if (cmd == SOUND_MIXER_INFO) { + mixer_info info; + strncpy(info.id, codec->name, sizeof(info.id)); + strncpy(info.name, codec->name, sizeof(info.name)); + info.modify_counter = codec->modcnt; + if (copy_to_user((void *)arg, &info, sizeof(info))) + return -EFAULT; + return 0; + } + if (cmd == SOUND_OLD_MIXER_INFO) { + _old_mixer_info info; + strncpy(info.id, codec->name, sizeof(info.id)); + strncpy(info.name, codec->name, sizeof(info.name)); + if (copy_to_user((void *)arg, &info, sizeof(info))) + return -EFAULT; + return 0; + } + + if (_IOC_TYPE(cmd) != 'M' || _IOC_SIZE(cmd) != sizeof(int)) + return -EINVAL; + + if (cmd == OSS_GETVERSION) + return put_user(SOUND_VERSION, (int *)arg); + + if (_IOC_DIR(cmd) == _IOC_READ) { + switch (_IOC_NR(cmd)) { + case SOUND_MIXER_RECSRC: /* give them the current record source */ + if (!codec->recmask_io) { + val = 0; + } else { + val = codec->recmask_io(codec, 1, 0); + } + break; + + case SOUND_MIXER_DEVMASK: /* give them the supported mixers */ + val = codec->supported_mixers; + break; + + case SOUND_MIXER_RECMASK: /* Arg contains a bit for each supported recording source */ + val = codec->record_sources; + break; + + case SOUND_MIXER_STEREODEVS: /* Mixer channels supporting stereo */ + val = codec->stereo_mixers; + break; + + case SOUND_MIXER_CAPS: + val = SOUND_CAP_EXCL_INPUT; + break; + + default: /* read a specific mixer */ + i = _IOC_NR(cmd); + + if (!supported_mixer(codec, i)) + return -EINVAL; + + /* do we ever want to touch the hardware? */ + /* val = codec->read_mixer(card,i); */ + val = codec->mixer_state[i]; + break; + } + return put_user(val,(int *)arg); + } + + if (_IOC_DIR(cmd) == (_IOC_WRITE|_IOC_READ)) { + codec->modcnt++; + get_user_ret(val, (int *)arg, -EFAULT); + + switch (_IOC_NR(cmd)) { + case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ + if (!codec->recmask_io) return -EINVAL; + if (!(val &= codec->record_sources)) return -EINVAL; + + codec->recmask_io(codec, 0, val); + + return 0; + default: /* write a specific mixer */ + i = _IOC_NR(cmd); + + if (!supported_mixer(codec, i)) + return -EINVAL; + + ac97_set_mixer(codec, i, val); + + return 0; + } + } + return -EINVAL; +} + +int ac97_probe_codec(struct ac97_codec *codec) +{ + u16 id1, id2, cap; + int i; + + /* probing AC97 codec, AC97 2.0 says that bit 15 of register 0x00 (reset) should + be read zero. Probing of AC97 in this way is not reliable, it is not even SAFE !! */ + codec->codec_write(codec, AC97_RESET, 0L); + if ((cap = codec->codec_read(codec, AC97_RESET)) & 0x8000) + return 0; + + id1 = codec->codec_read(codec, AC97_VENDOR_ID1); + id2 = codec->codec_read(codec, AC97_VENDOR_ID2); + for (i = 0; i < sizeof (snd_ac97_codec_ids); i++) { + if (snd_ac97_codec_ids[i].id == ((id1 << 16) | id2)) { + codec->name = snd_ac97_codec_ids[i].name; + codec->codec_init = snd_ac97_codec_ids[i].init; + break; + } + } + if (codec->name == NULL) + codec->name = "Unknown"; + printk(KERN_INFO "ac97_codec: ac97 vendor id1: 0x%04x, id2: 0x%04x (%s)\n", + id1, id2, codec->name); + printk(KERN_INFO "ac97_codec: capability: 0x%04x\n", cap); + + /* mixer masks */ + codec->supported_mixers = AC97_SUPPORTED_MASK; + codec->stereo_mixers = AC97_STEREO_MASK; + codec->record_sources = AC97_RECORD_MASK; + if (!(cap & 0x04)) + codec->supported_mixers &= ~(SOUND_MASK_BASS|SOUND_MASK_TREBLE); + + /* generic OSS to AC97 wrapper */ + codec->read_mixer = ac97_read_mixer; + codec->write_mixer = ac97_write_mixer; + codec->recmask_io = ac97_recmask_io; + codec->mixer_ioctl = ac97_mixer_ioctl; + + /* initialize volume level */ + codec->codec_write(codec, AC97_MASTER_VOL_STEREO, 0L); + codec->codec_write(codec, AC97_PCMOUT_VOL, 0L); + + /* codec specific initialization for 4-6 channel output */ + if (codec->id != 0 && codec->codec_init != NULL) { + codec->codec_init(codec); + } + + /* initilize mixer channel volumes */ + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { + struct mixer_defaults *md = &mixer_defaults[i]; + if (md->mixer == -1) + break; + if (!supported_mixer(codec, md->mixer)) + continue; + ac97_set_mixer(codec, md->mixer, md->value); + } + + return 1; +} + +static int sigmatel_init(struct ac97_codec * codec) +{ + codec->codec_write(codec, AC97_SURROUND_MASTER, 0L); + /* initialize SigmaTel STAC9721/23 */ + codec->codec_write(codec, 0x74, 0x01); + return 1; +} + +EXPORT_SYMBOL(ac97_probe_codec); diff --git a/drivers/sound/ac97_codec.h b/drivers/sound/ac97_codec.h new file mode 100644 index 000000000..36d2b052a --- /dev/null +++ b/drivers/sound/ac97_codec.h @@ -0,0 +1,157 @@ +#ifndef _AC97_CODEC_H_ +#define _AC97_CODEC_H_ + +#include "sound_config.h" +#include "sound_calls.h" + +/* AC97 1.0 */ +#define AC97_RESET 0x0000 // +#define AC97_MASTER_VOL_STEREO 0x0002 // Line Out +#define AC97_HEADPHONE_VOL 0x0004 // +#define AC97_MASTER_VOL_MONO 0x0006 // TAD Output +#define AC97_MASTER_TONE 0x0008 // +#define AC97_PCBEEP_VOL 0x000a // none +#define AC97_PHONE_VOL 0x000c // TAD Input (mono) +#define AC97_MIC_VOL 0x000e // MIC Input (mono) +#define AC97_LINEIN_VOL 0x0010 // Line Input (stereo) +#define AC97_CD_VOL 0x0012 // CD Input (stereo) +#define AC97_VIDEO_VOL 0x0014 // none +#define AC97_AUX_VOL 0x0016 // Aux Input (stereo) +#define AC97_PCMOUT_VOL 0x0018 // Wave Output (stereo) +#define AC97_RECORD_SELECT 0x001a // +#define AC97_RECORD_GAIN 0x001c +#define AC97_RECORD_GAIN_MIC 0x001e +#define AC97_GENERAL_PURPOSE 0x0020 +#define AC97_3D_CONTROL 0x0022 +#define AC97_MODEM_RATE 0x0024 +#define AC97_POWER_CONTROL 0x0026 + +/* AC'97 2.0 */ +#define AC97_EXTENDED_ID 0x0028 /* Extended Audio ID */ +#define AC97_EXTENDED_STATUS 0x002A /* Extended Audio Status */ +#define AC97_PCM_FRONT_DAC_RATE 0x002C /* PCM Front DAC Rate */ +#define AC97_PCM_SURR_DAC_RATE 0x002E /* PCM Surround DAC Rate */ +#define AC97_PCM_LFE_DAC_RATE 0x0030 /* PCM LFE DAC Rate */ +#define AC97_PCM_LR_DAC_RATE 0x0032 /* PCM LR DAC Rate */ +#define AC97_PCM_MIC_ADC_RATE 0x0034 /* PCM MIC ADC Rate */ +#define AC97_CENTER_LFE_MASTER 0x0036 /* Center + LFE Master Volume */ +#define AC97_SURROUND_MASTER 0x0038 /* Surround (Rear) Master Volume */ +#define AC97_RESERVED_3A 0x003A /* Reserved */ + +/* range 0x3c-0x58 - MODEM */ + +/* registers 0x005a - 0x007a are vendor reserved */ + +#define AC97_VENDOR_ID1 0x007c +#define AC97_VENDOR_ID2 0x007e + +/* volume control bit defines */ +#define AC97_MUTE 0x8000 +#define AC97_MICBOOST 0x0040 +#define AC97_LEFTVOL 0x3f00 +#define AC97_RIGHTVOL 0x003f + +/* record mux defines */ +#define AC97_RECMUX_MIC 0x0000 +#define AC97_RECMUX_CD 0x0101 +#define AC97_RECMUX_VIDEO 0x0202 /* not used */ +#define AC97_RECMUX_AUX 0x0303 +#define AC97_RECMUX_LINE 0x0404 +#define AC97_RECMUX_STEREO_MIX 0x0505 +#define AC97_RECMUX_MONO_MIX 0x0606 +#define AC97_RECMUX_PHONE 0x0707 + + +/* general purpose register bit defines */ +#define AC97_GP_LPBK 0x0080 /* Loopback mode */ +#define AC97_GP_MS 0x0100 /* Mic Select 0=Mic1, 1=Mic2 */ +#define AC97_GP_MIX 0x0200 /* Mono output select 0=Mix, 1=Mic */ +#define AC97_GP_RLBK 0x0400 /* Remote Loopback - Modem line codec */ +#define AC97_GP_LLBK 0x0800 /* Local Loopback - Modem Line codec */ +#define AC97_GP_LD 0x1000 /* Loudness 1=on */ +#define AC97_GP_3D 0x2000 /* 3D Enhancement 1=on */ +#define AC97_GP_ST 0x4000 /* Stereo Enhancement 1=on */ +#define AC97_GP_POP 0x8000 /* Pcm Out Path, 0=pre 3D, 1=post 3D */ + + +/* powerdown control and status bit defines */ + +/* status */ +#define AC97_PWR_MDM 0x0010 /* Modem section ready */ +#define AC97_PWR_REF 0x0008 /* Vref nominal */ +#define AC97_PWR_ANL 0x0004 /* Analog section ready */ +#define AC97_PWR_DAC 0x0002 /* DAC section ready */ +#define AC97_PWR_ADC 0x0001 /* ADC section ready */ + +/* control */ +#define AC97_PWR_PR0 0x0100 /* ADC and Mux powerdown */ +#define AC97_PWR_PR1 0x0200 /* DAC powerdown */ +#define AC97_PWR_PR2 0x0400 /* Output mixer powerdown (Vref on) */ +#define AC97_PWR_PR3 0x0800 /* Output mixer powerdown (Vref off) */ +#define AC97_PWR_PR4 0x1000 /* AC-link powerdown */ +#define AC97_PWR_PR5 0x2000 /* Internal Clk disable */ +#define AC97_PWR_PR6 0x4000 /* HP amp powerdown */ +#define AC97_PWR_PR7 0x8000 /* Modem off - if supported */ + +/* useful power states */ +#define AC97_PWR_D0 0x0000 /* everything on */ +#define AC97_PWR_D1 AC97_PWR_PR0|AC97_PWR_PR1|AC97_PWR_PR4 +#define AC97_PWR_D2 AC97_PWR_PR0|AC97_PWR_PR1|AC97_PWR_PR2|AC97_PWR_PR3|AC97_PWR_PR4 +#define AC97_PWR_D3 AC97_PWR_PR0|AC97_PWR_PR1|AC97_PWR_PR2|AC97_PWR_PR3|AC97_PWR_PR4 +#define AC97_PWR_ANLOFF AC97_PWR_PR2|AC97_PWR_PR3 /* analog section off */ + +/* Total number of defined registers. */ +#define AC97_REG_CNT 64 + + +/* OSS interface to the ac97s.. */ +#define AC97_STEREO_MASK (SOUND_MASK_VOLUME|SOUND_MASK_PCM|\ + SOUND_MASK_LINE|SOUND_MASK_CD|\ + SOUND_MIXER_ALTPCM|SOUND_MASK_IGAIN|\ + SOUND_MASK_LINE1|SOUND_MASK_VIDEO) + +#define AC97_SUPPORTED_MASK (AC97_STEREO_MASK | \ + SOUND_MASK_BASS|SOUND_MASK_TREBLE|\ + SOUND_MASK_SPEAKER|SOUND_MASK_MIC|\ + SOUND_MIXER_PHONEIN|SOUND_MIXER_PHONEOUT) + +#define AC97_RECORD_MASK (SOUND_MASK_MIC|\ + SOUND_MASK_CD| SOUND_MASK_VIDEO| SOUND_MASK_LINE1| SOUND_MASK_LINE|\ + SOUND_MASK_PHONEIN) + +#define supported_mixer(CODEC,FOO) ( CODEC->supported_mixers & (1<<FOO) ) + +struct ac97_codec { + /* AC97 controller connected with */ + void *private_data; + + char *name; + int id; + int dev_mixer; + + /* codec specific init/reset routines, used mainly for 4 or 6 channel support */ + int (*codec_init) (struct ac97_codec *codec); + + /* controller specific lower leverl ac97 accessing routines */ + u16 (*codec_read) (struct ac97_codec *codec, u8 reg); + void (*codec_write) (struct ac97_codec *codec, u8 reg, u16 val); + + /* OSS mixer masks */ + int modcnt; + int supported_mixers; + int stereo_mixers; + int record_sources; + + /* OSS mixer interface */ + int (*read_mixer) (struct ac97_codec *codec, int oss_channel); + void (*write_mixer)(struct ac97_codec *codec, int oss_channel, + unsigned int left, unsigned int right); + int (*recmask_io) (struct ac97_codec *codec, int rw, int mask); + int (*mixer_ioctl)(struct ac97_codec *codec, unsigned int cmd, unsigned long arg); + + /* saved OSS mixer states */ + unsigned int mixer_state[SOUND_MIXER_NRDEVICES]; +}; + +extern int ac97_probe_codec(struct ac97_codec *); +#endif diff --git a/drivers/sound/trident.c b/drivers/sound/trident.c index c8c0cd0c3..2f808c92c 100644 --- a/drivers/sound/trident.c +++ b/drivers/sound/trident.c @@ -29,16 +29,36 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * History + * v0.11 Jan 27 2000 Ollie Lho + * DMA bug, scheduler latency, second try + * v0.10 Jan 24 2000 Ollie Lho + * DMA bug fixed, found kernel scheduling problem + * v0.09 Jan 20 2000 Ollie Lho + * Clean up of channel register access routine (prepare for channel binding) + * v0.08 Jan 14 2000 Ollie Lho + * Isolation of AC97 codec code + * v0.07 Jan 13 2000 Ollie Lho + * Get rid of ugly old low level access routines (e.g. CHRegs.lp****) + * v0.06 Jan 11 2000 Ollie Lho + * Preliminary support for dual (more ?) AC97 codecs * v0.05 Jan 08 2000 Luca Montecchiani <m.luca@iname.com> - * adapt to 2.3.x new __setup/__initcall + * adapt to 2.3.x new __setup/__init call * v0.04 Dec 31 1999 Ollie Lho - * Multiple Open, useing Middle Loop Interrupt to smooth playback + * Multiple Open, using Middle Loop Interrupt to smooth playback * v0.03 Dec 24 1999 Ollie Lho * mem leak in prog_dmabuf and dealloc_dmabuf removed * v0.02 Dec 15 1999 Ollie Lho * SiS 7018 support added, playback O.K. * v0.01 Alan Cox et. al. * Initial Release in kernel 2.3.30, does not work + * + * ToDo + * Clean up of low level channel register access code. (done) + * Fix the bug on dma buffer management in update_ptr, read/write, drain_dac (done) + * Dual AC97 codecs support (done partially, need channel binding to test) + * Recording support + * Mmap support + * "Channel Binding" ioctl extension */ #include <linux/config.h> @@ -66,34 +86,40 @@ #endif #include "trident.h" -#include "ac97.h" +#include "ac97_codec.h" #undef DEBUG -#define DRIVER_VERSION "0.05" - -#define TRIDENT_FMT_STEREO 0x01 -#define TRIDENT_FMT_16BIT 0x02 -#define TRIDENT_FMT_MASK 0x03 -#define TRIDENT_DAC_SHIFT 0 -#define TRIDENT_ADC_SHIFT 4 - -#define TRIDENT_ENABLE_PE 1 -#define TRIDENT_ENABLE_RE 2 -#define DAC_RUNNING 1 -#define ADC_RUNNING 2 +#define DRIVER_VERSION "0.11" +/* magic numbers to protect our data structures */ #define TRIDENT_CARD_MAGIC 0x5072696E /* "Prin" */ #define TRIDENT_STATE_MAGIC 0x63657373 /* "cess" */ -/* number of instances of opening /dev/dsp, can your CPU handle this ? */ -#define NR_DSPS 32 +/* The first 32 channels are called Bank A. They are (should be) reserved + for MIDI synthesizer. But since that is not supported yet, we can (ab)use + them to play PCM samples */ +#undef ABUSE_BANK_A + +/* maxinum number of instances of opening /dev/dspN, can your CPU handle this ? + NOTE: If /dev/dsp is opened O_RDWR (i.e. full duplex) it will consume 2 HW + channels */ +#ifdef ABUSE_BANK_A +#define NR_HW_CH 64 +#else +#define NR_HW_CH 32 +#endif + +/* maxinum nuber of AC97 codecs connected, AC97 2.0 defined 4, but 7018 and 4D-NX only + have 2 SDATA_IN lines (currently) */ +#define NR_AC97 2 +/* minor number of /dev/dspW */ #define SND_DEV_DSP16 5 static const unsigned sample_size[] = { 1, 2, 2, 4 }; static const unsigned sample_shift[] = { 0, 1, 1, 2 }; -static const char *sample_format[] = {"8 bits Mono", "8 bits Stereo", "16 bits Mono", "16 bits Stereo"}; + static const char invalid_magic[] = KERN_CRIT "trident: invalid magic value in %s\n"; struct pci_audio_info { @@ -108,52 +134,12 @@ static struct pci_audio_info pci_audio_devices[] = { {PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_7018, "SiS 7018 PCI Audio"} }; -static struct { - unsigned int id; - char *name; -} snd_ac97_codec_ids[] = { - {0x414B4D00, "Asahi Kasei AK4540" }, - {0x41445340, "Analog Devices AD1881" }, - {0x43525900, "Cirrus Logic CS4297" }, - {0x43525913, "Cirrus Logic CS4297A" }, - {0x43525931, "Cirrus Logic CS4299" }, - {0x4e534331, "National Semiconductor LM4549"}, - {0x83847600, "SigmaTel STAC????" }, - {0x83847604, "SigmaTel STAC9701/3/4/5"}, - {0x83847605, "SigmaTel STAC9704" }, - {0x83847608, "SigmaTel STAC9708" }, - {0x83847609, "SigmaTel STAC9721/23" }, - {0x00000000, NULL} -}; - -typedef struct tChannelControl -{ - // register data - unsigned int * lpChStart; - unsigned int * lpChStop; - unsigned int * lpChAint; - unsigned int * lpChAinten; - - // register addresses - unsigned int * lpAChStart; - unsigned int * lpAChStop; - unsigned int * lpAChAint; - unsigned int * lpAChAinten; - - unsigned int data[16]; - -} CHANNELCONTROL; - /* "software" or virtual channel, an instance of opened /dev/dsp */ struct trident_state { unsigned int magic; struct trident_card *card; /* Card info */ - /* wave stuff */ - unsigned int rateadc, ratedac; - unsigned char fmt, enable; - - /* single opne lock mechanism, should be removed */ + /* single open lock mechanism, only used for recording */ struct semaphore open_sem; wait_queue_head_t open_wait; @@ -164,26 +150,33 @@ struct trident_state { int virt; struct dmabuf { + /* wave sample stuff */ + unsigned int rate; + unsigned char fmt, enable; + + /* hardware channel */ + struct trident_channel *channel; + + /* OSS buffer manangemeent stuff */ void *rawbuf; unsigned buforder; unsigned numfrag; unsigned fragshift; - /* hardware channel number */ - int chan; + /* our buffer acts like a circular ring */ + unsigned hwptr; /* where dma last started, update by update_ptr */ + unsigned swptr; /* where driver last clear/filled, updated by read/write */ + int count; /* bytes to be comsumed by dma machine */ + unsigned total_bytes; /* total bytes dmaed by hardware */ - /* XXX zab - swptr only in here so that it can be referenced by - clear_advance, as far as I can tell :( */ - unsigned hwptr, swptr; - unsigned total_bytes; - int count; - unsigned error; /* over/underrun */ - wait_queue_head_t wait; + unsigned error; /* number of over/underruns */ + wait_queue_head_t wait; /* put process on wait queue when no more space in buffer */ /* redundant, but makes calculations easier */ unsigned fragsize; unsigned dmasize; unsigned fragsamples; + /* OSS stuff */ unsigned mapped:1; unsigned ready:1; @@ -192,44 +185,45 @@ struct trident_state { int ossmaxfrags; unsigned subdivision; } dma_dac, dma_adc; - - u8 bDMAStart; - }; /* hardware channels */ struct trident_channel { - int chan; /* channel number */ - u32 lba; - u32 eso; + int num; /* channel number */ + u32 lba; /* reg 0xe4 */ + u32 eso; /* reg 0xe8 */ u32 delta; - u16 attribute; - + u16 attribute; /* reg 0xec */ + u16 fm_vol; + u32 control; /* reg 0xf0 */ }; -struct trident_pcm_bank { - /* registers to control bank operations */ +struct trident_pcm_bank_address { u32 start; u32 stop; u32 aint; u32 aint_en; +}; +static struct trident_pcm_bank_address bank_a_addrs = +{ + T4D_START_A, + T4D_STOP_A, + T4D_AINT_A, + T4D_AINTEN_A +}; +static struct trident_pcm_bank_address bank_b_addrs = +{ + T4D_START_B, + T4D_STOP_B, + T4D_AINT_B, + T4D_AINTEN_B +}; +struct trident_pcm_bank { + /* register addresses to control bank operations */ + struct trident_pcm_bank_address *addresses; /* each bank has 32 channels */ u32 bitmap; /* channel allocation bitmap */ - //struct trident_channel channels[32]; -}; - -struct trident_mixer { - int modcnt; - int supported_mixers; - int stereo_mixers; - int record_sources; - - /* the caller must guarantee arg sanity before calling these */ - /* int (*read_mixer)(struct trident_card *card, int index);*/ - void (*write_mixer)(struct trident_card *card,int mixer, unsigned int left, - unsigned int right); - int (*recmask_io)(struct trident_card *card,int rw,int mask); - unsigned int mixer_state[SOUND_MIXER_NRDEVICES]; + struct trident_channel channels[32]; }; struct trident_card { @@ -239,7 +233,7 @@ struct trident_card { struct trident_card *next; /* The trident has a certain amount of cross channel interaction - so we use a single per card lock */ + so we use a single per card lock */ spinlock_t lock; /* PCI device stuff */ @@ -249,286 +243,225 @@ struct trident_card { /* soundcore stuff */ int dev_audio; - int dev_mixer; - struct trident_mixer mix; - struct trident_state *channels[NR_DSPS]; + /* structures for abstraction of hardware facilities, codecs, banks and channels*/ + struct ac97_codec *ac97_codec[NR_AC97]; + struct trident_pcm_bank banks[NR_BANKS]; + struct trident_state *states[NR_HW_CH]; /* hardware resources */ unsigned long iobase; u32 irq; - - /* hardware channel allocation bitmap */ - u32 bitmap[2]; - - /* ugly stupid thing, remove ASAP */ - CHANNELCONTROL ChRegs; - int ChanDwordCount; }; static struct trident_card *devs = NULL; -/* - * Trident support library routines - */ - -/*--------------------------------------------------------------------------- - void ResetAinten( struct trident_state *trident, int ChannelNum) - - Description: This routine will disable interrupts and ack any - existing interrupts for specified channel. - - Parameters: trident - pointer to target device class for 4DWave. - ChannelNum - channel number - - returns: TRUE if everything went ok, else FALSE. - - ---------------------------------------------------------------------------*/ - -static void ResetAinten(struct trident_card * trident, int ChannelNum) -{ - unsigned int dwMask; - unsigned int x = ChannelNum >> 5; - unsigned int ChanDwordCount = trident->ChanDwordCount; - - IReadAinten(&trident->ChRegs); - dwMask = 1 << (ChannelNum & 0x1f); - trident->ChRegs.lpChAinten[x] &= ~dwMask; - IWriteAinten(&trident->ChRegs); - // Ack the channel in case the interrupt was set before we disable it. - outl(dwMask, TRID_REG(trident, trident->ChRegs.lpAChAint[x])); -} +static void trident_ac97_set(struct ac97_codec *codec, u8 reg, u16 val); +static u16 trident_ac97_get(struct ac97_codec *codec, u8 reg); -/*--------------------------------------------------------------------------- - void EnableEndInterrupts( struct trident_card *trident) - - Description: This routine will enable end of loop interrupts. - End of loop interrupts will occur when a running - channel reaches ESO. - - Parameters: trident - pointer to target device class for 4DWave. - - returns: TRUE if everything went ok, else FALSE. - - ---------------------------------------------------------------------------*/ - -static int trident_enable_end_interrupts(struct trident_card * trident) +static int trident_open_mixdev(struct inode *inode, struct file *file); +static int trident_release_mixdev(struct inode *inode, struct file *file); +static int trident_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg); +static loff_t trident_llseek(struct file *file, loff_t offset, int origin); + +static int trident_enable_loop_interrupts(struct trident_card * card) { u32 global_control; - global_control = inl(TRID_REG(trident, T4D_LFO_GC_CIR)); + global_control = inl(TRID_REG(card, T4D_LFO_GC_CIR)); - switch (trident->pci_id) + switch (card->pci_id) { case PCI_DEVICE_ID_SI_7018: - global_control |= (ENDLP_IE | BANK_B_EN); + global_control |= (ENDLP_IE | MIDLP_IE| BANK_B_EN); break; case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX: case PCI_DEVICE_ID_TRIDENT_4DWAVE_NX: - global_control |= ENDLP_IE; + global_control |= (ENDLP_IE | MIDLP_IE); break; default: return FALSE; } - outl(global_control, TRID_REG(trident, T4D_LFO_GC_CIR)); + outl(global_control, TRID_REG(card, T4D_LFO_GC_CIR)); #ifdef DEBUG - printk("trident: Enable End Interrupts, globctl = 0x%08X\n", global_control); + printk("trident: Enable Loop Interrupts, globctl = 0x%08X\n", + global_control); #endif return (TRUE); } -static int trident_enable_middle_interrupts(struct trident_card * trident) +static int trident_disable_loop_interrupts(struct trident_card * card) { u32 global_control; - global_control = inl(TRID_REG(trident, T4D_LFO_GC_CIR)); - - switch (trident->pci_id) - { - case PCI_DEVICE_ID_SI_7018: - global_control |= (MIDLP_IE | BANK_B_EN); - break; - case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX: - case PCI_DEVICE_ID_TRIDENT_4DWAVE_NX: - default: - global_control |= MIDLP_IE; - break; - } - - outl(global_control, TRID_REG(trident, T4D_LFO_GC_CIR)); + global_control = inl(TRID_REG(card, T4D_LFO_GC_CIR)); + global_control &= ~(ENDLP_IE | MIDLP_IE); + outl(global_control, TRID_REG(card, T4D_LFO_GC_CIR)); #ifdef DEBUG - printk("trident: Enable Middle Interrupts, globctl = 0x%08X\n", global_control); + printk("trident: Disabled Loop Interrupts, globctl = 0x%08X\n", + global_control); #endif return (TRUE); } -/*--------------------------------------------------------------------------- - void DisableEndInterrupts( struct trident_card *trident) - - Description: This routine will disable end of loop interrupts. - End of loop interrupts will occur when a running - channel reaches ESO. - - Parameters: - trident - pointer to target device class for 4DWave. - - returns: TRUE if everything went ok, else FALSE. - - ---------------------------------------------------------------------------*/ - -static int trident_disable_end_interrupts(struct trident_card * trident) + +static void trident_enable_voice_irq(struct trident_card * card, unsigned int channel) { - u32 global_control; + unsigned int mask = 1 << (channel & 0x1f); + struct trident_pcm_bank *bank = &card->banks[channel >> 5]; + u32 reg, addr = bank->addresses->aint_en; - global_control = inl(TRID_REG(trident, T4D_LFO_GC_CIR)); - global_control &= ~ENDLP_IE; - outl(global_control, TRID_REG(trident, T4D_LFO_GC_CIR)); + reg = inl(TRID_REG(card, addr)); + reg |= mask; + outl(reg, TRID_REG(card, addr)); #ifdef DEBUG - printk("trident: Disabled End Interrupts, globctl = 0x%08X\n", global_control); + reg = inl(TRID_REG(card, T4D_AINTEN_B)); + printk("trident: enabled IRQ on channel %d, AINTEN_B = 0x%08x\n", + channel, reg); #endif - return (TRUE); } -static int trident_disable_middle_interrupts(struct trident_card * trident) +static void trident_disable_voice_irq(struct trident_card * card, unsigned int channel) { - u32 global_control; - - global_control = inl(TRID_REG(trident, T4D_LFO_GC_CIR)); - global_control &= ~MIDLP_IE; - outl(global_control, TRID_REG(trident, T4D_LFO_GC_CIR)); + unsigned int mask = 1 << (channel & 0x1f); + struct trident_pcm_bank *bank = &card->banks[channel >> 5]; + u32 reg, addr = bank->addresses->aint_en; + + reg = inl(TRID_REG(card, addr)); + reg &= ~mask; + outl(reg, TRID_REG(card, addr)); + + /* Ack the channel in case the interrupt was set before we disable it. */ + outl(mask, TRID_REG(card, bank->addresses->aint)); #ifdef DEBUG - printk("trident: Disabled Middle Interrupts, globctl = 0x%08X\n", global_control); + reg = inl(TRID_REG(card, T4D_AINTEN_B)); + printk("trident: disabled IRQ on channel %d, AINTEN_B = 0x%08x\n", + channel, reg); #endif - return (TRUE); } -/*--------------------------------------------------------------------------- - void trident_enable_voice_irq( unsigned int HwChannel ) - - Description: Enable an interrupt channel, any channel 0 thru n. - This routine automatically handles the fact that there are - more than 32 channels available. - - Parameters : HwChannel - Channel number 0 thru n. - trident - pointer to target device class for 4DWave. - - Return Value: None. - - ---------------------------------------------------------------------------*/ -void trident_enable_voice_irq(struct trident_card * trident, unsigned int channel) + +static void trident_start_voice(struct trident_card * card, unsigned int channel) { - unsigned int bank, mask, ChanDwordCount; + unsigned int mask = 1 << (channel & 0x1f); + struct trident_pcm_bank *bank = &card->banks[channel >> 5]; + u32 addr = bank->addresses->start; + +#ifdef DEBUG u32 reg; +#endif - bank = channel >> 5; - mask = 1 << (channel & 0x1f); + outl(mask, TRID_REG(card, addr)); - ChanDwordCount = trident->ChanDwordCount; +#ifdef DEBUG + reg = inl(TRID_REG(card, T4D_START_B)); + printk("trident: start voice on channel %d, START_B = 0x%08x\n", + channel, reg); +#endif +} - IReadAinten(&trident->ChRegs); - trident->ChRegs.lpChAinten[bank] |= mask; - IWriteAinten(&trident->ChRegs); +static void trident_stop_voice(struct trident_card * card, unsigned int channel) +{ + unsigned int mask = 1 << (channel & 0x1f); + struct trident_pcm_bank *bank = &card->banks[channel >> 5]; + u32 addr = bank->addresses->stop; #ifdef DEBUG - reg = inl(TRID_REG(trident, T4D_AINTEN_B)); - printk("trident: enabled IRQ on channel %d\n", channel); + u32 reg; +#endif + + outl(mask, TRID_REG(card, addr)); + +#ifdef DEBUG + reg = inl(TRID_REG(card, T4D_STOP_B)); + printk("trident: stop voice on channel %d, STOP_B = 0x%08x\n", + channel, reg); #endif } -/*--------------------------------------------------------------------------- - void trident_disable_voice_irq( unsigned int HwChannel ) - - Description: Disable an interrupt channel, any channel 0 thru n. - This routine automatically handles the fact that there are - more than 32 channels available. - - Parameters : HwChannel - Channel number 0 thru n. - trident - pointer to target device class for 4DWave. - - Return Value: None. - - ---------------------------------------------------------------------------*/ -void trident_disable_voice_irq(struct trident_card * trident, unsigned int channel) +static int trident_check_channel_interrupt(struct trident_card * card, int channel) { - unsigned int bank, mask, ChanDwordCount; - u32 reg; + unsigned int mask = 1 << (channel & 0x1f); + struct trident_pcm_bank *bank = &card->banks[channel >> 5]; + u32 reg, addr = bank->addresses->aint; - bank = channel >> 5; - mask = 1 << (channel & 0x1f); + reg = inl(TRID_REG(card, addr)); + +#ifdef DEBUG + if (reg & mask) + printk("trident: channel %d has interrupt, AINT_B = 0x%08x\n", + channel, reg); +#endif + return (reg & mask) ? TRUE : FALSE; +} + +static void trident_ack_channel_interrupt(struct trident_card * card, int channel) +{ + unsigned int mask = 1 << (channel & 0x1f); + struct trident_pcm_bank *bank = &card->banks[channel >> 5]; + u32 reg, addr = bank->addresses->aint; - ChanDwordCount = trident->ChanDwordCount; - IReadAinten(&trident->ChRegs); - trident->ChRegs.lpChAinten[bank] &= ~mask; - IWriteAinten(&trident->ChRegs); + reg = inl(TRID_REG(card, addr)); + reg &= mask; + outl(reg, TRID_REG(card, addr)); #ifdef DEBUG - reg = inl(TRID_REG(trident, T4D_AINTEN_B)); - printk("trident: disabled IRQ on channel %d\n", channel); + reg = inl(TRID_REG(card, T4D_AINT_B)); + printk("trident: Ack channel %d interrupt, AINT_B = 0x%08x\n", + channel, reg); #endif } -/*--------------------------------------------------------------------------- - unsigned int AllocateChannelPCM( void ) - - Description: Allocate hardware channel by reverse order (63-0). - - Parameters : trident - pointer to target device class for 4DWave. - - Return Value: hardware channel - 0-63 or -1 when no channel is available - - ---------------------------------------------------------------------------*/ - -static int trident_alloc_pcm_channel(struct trident_card *trident) +static struct trident_channel * trident_alloc_pcm_channel(struct trident_card *card) { + struct trident_pcm_bank *bank; int idx; - if (trident->bitmap[BANK_B] == ~0UL) { + bank = &card->banks[BANK_B]; + if (bank->bitmap == ~0UL) { /* no more free channels avaliable */ printk(KERN_ERR "trident: no more channels available on Bank B.\n"); - return -1; +#ifdef ABUSE_BANK_A + goto bank_a; +#endif + return NULL; } for (idx = 31; idx >= 0; idx--) { - if (!(trident->bitmap[BANK_B] & (1 << idx))) { - trident->bitmap[BANK_B] |= 1 << idx; - return idx + 32; + if (!(bank->bitmap & (1 << idx))) { + struct trident_channel *channel = &bank->channels[idx]; + bank->bitmap |= 1 << idx; + channel->num = idx + 32; + return channel; } } #ifdef ABUSE_BANK_A /* channels in Bank A should be reserved for synthesizer not for normal use (channels in Bank A can't record) */ - if (trident->bitmap[BANK_A] == ~0UL) { + bank_a: + bank = &card->banks[BANK_A]; + if (bank->bitmap == ~0UL) { /* no more free channels avaliable */ - printk(KERN_ERR "trident: no channels available on Bank A.\n"); - return -1; + printk(KERN_ERR "trident: no more channels available on Bank A.\n"); + return NULL; } for (idx = 31; idx >= 0; idx--) { - if (!(trident->bitmap[BANK_A] & (1 << idx))) { - trident->bitmap[BANK_A] |= 1 << idx; - return idx; + if (!(bank->bitmap & (1 << idx))) { + struct trident_channel *channel = &bank->channels[idx]; + banks->bitmap |= 1 << idx; + channel->num = idx; + return channels; } } #endif - - return -1; + return NULL; } -/*--------------------------------------------------------------------------- - void FreeChannelPCM( int channel ) - - Description: Free hardware channel. - - Parameters : trident - pointer to target device class for 4DWave. - channel - hardware channel number 0-63 - - Return Value: none - - ---------------------------------------------------------------------------*/ - -static void trident_free_pcm_channel(struct trident_card *trident, int channel) +static void trident_free_pcm_channel(struct trident_card *card, int channel) { int bank; @@ -543,236 +476,74 @@ static void trident_free_pcm_channel(struct trident_card *trident, int channel) bank = channel >> 5; channel = channel & 0x1f; - if (trident->bitmap[bank] & (1 << (channel))) { - trident->bitmap[bank] &= ~(1 << (channel)); + if (card->banks[bank].bitmap & (1 << (channel))) { + card->banks[bank].bitmap &= ~(1 << (channel)); } } -/*--------------------------------------------------------------------------- - void trident_start_voice( ULONG HwChannel ) - - Description: Start a channel, any channel 0 thru n. - This routine automatically handles the fact that there are - more than 32 channels available. - - Parameters : HwChannel - Channel number 0 thru n. - trident - pointer to target device class for 4DWave. - - Return Value: None. - - ---------------------------------------------------------------------------*/ -void trident_start_voice(struct trident_card * trident, unsigned int channel) -{ - unsigned int bank = channel >> 5; - unsigned int mask = 1 << (channel & 0x1f); - - outl(mask, TRID_REG(trident, trident->ChRegs.lpAChStart[bank])); -#ifdef DEBUG - printk("trident: start voice on channel %d\n", channel); -#endif -} - -/*--------------------------------------------------------------------------- - void trident_stop_voice( ULONG HwChannel ) - - Description: Stop a channel, any channel 0 thru n. - This routine automatically handles the fact that there are - more than 32 channels available. - - Parameters : HwChannel - Channel number 0 thru n. - trident - pointer to target device class for 4DWave. - - Return Value: None. - - ---------------------------------------------------------------------------*/ -void trident_stop_voice(struct trident_card * trident, unsigned int channel) +/* called with spin lock held */ +static int trident_load_channel_registers(struct trident_card *card, u32 *data, unsigned int channel) { - unsigned int bank = channel >> 5; - unsigned int mask = 1 << (channel & 0x1f); - - outl(mask, TRID_REG(trident, trident->ChRegs.lpAChStop[bank])); -#ifdef DEBUG - printk("trident: stop voice on channel %d\n", channel); -#endif -} - -/*--------------------------------------------------------------------------- - int DidChannelInterrupt( ) - - Description: Check if interrupt channel occurred. - - Parameters : trident - pointer to target device class for 4DWave. - - Return Value: TRUE if interrupt occurred, else FALSE. - - ---------------------------------------------------------------------------*/ -static int trident_check_channel_interrupt(struct trident_card * trident, int channel) -{ - unsigned int ChanDwordCount = NUM_BANKS; - unsigned int bank = channel >> 5; - unsigned int mask = 1 << (channel & 0x1f); - - ReadAint(&trident->ChRegs); - -#ifdef DEBUG - if (trident->ChRegs.lpChAint[bank] & mask) - printk("trident: channel %d has interrupt\n", channel); -#endif - return (trident->ChRegs.lpChAint[bank] & mask) ? TRUE : FALSE; -} - -/*--------------------------------------------------------------------------- - void AckChannelInterrupt( ) - - Description: Acknowledge the interrupt bit for channel intrs. - - Parameters : trident - pointer to target device class for 4DWave. - - Return Value: None - - ---------------------------------------------------------------------------*/ -static void trident_ack_channel_interrupt(struct trident_card * trident, int channel) -{ - unsigned int ChanDwordCount = NUM_BANKS; - unsigned int bank = channel >> 5; - unsigned int mask = 1 << (channel & 0x1f); - - ReadAint(&trident->ChRegs); - trident->ChRegs.lpChAint[bank] &= mask; - IWriteAint(&trident->ChRegs); -} - -/*--------------------------------------------------------------------------- - int trident_load_hw_delta( unsigned int HwChannel, unsigned int Delta ) - - Description: This routine writes Delta to the hardware. - - Parameters: Delta - data to write (2 Bytes only) - HwChannel - Hardware channel to write to. - trident - pointer to target device class for 4DWave. - - Returns: TRUE if all goes well, else FALSE. - - ---------------------------------------------------------------------------*/ -static int trident_load_hw_delta (struct trident_card * trident, unsigned int channel, - unsigned short delta) -{ - /* select a channel for output */ - outb(channel, TRID_REG(trident, T4D_LFO_GC_CIR)); + int i; - switch (trident->pci_id) - { - case PCI_DEVICE_ID_SI_7018: - case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX: - outw((u16) delta, TRID_REG(trident, CH_DX_ESO_DELTA)); - break; - case PCI_DEVICE_ID_TRIDENT_4DWAVE_NX: - outb(delta & 0xff, TRID_REG(trident, CH_NX_DELTA_CSO + 3)); - outb((delta >> 8)& 0xff, TRID_REG(trident, CH_NX_DELTA_ESO + 3)); - break; - default: + if (channel > 63) return FALSE; - } - return TRUE; -} -/*--------------------------------------------------------------------------- - int LoadVirtualChannel( ULONG *Data, ULONG HwChannel) - - Description: This routine writes all required channel registers to hardware. - - Parameters: *Data - a pointer to the data to write (5 ULONGS always). - HwChannel - Hardware channel to write to. - trident - pointer to target device class for 4DWave. - - Returns: TRUE if all goes well, else FALSE. - - ---------------------------------------------------------------------------*/ -static int LoadVirtualChannel(struct trident_card * trident, unsigned int *Data, unsigned int HwChannel) -{ - unsigned int ChanData[CHANNEL_REGS]; - unsigned int ULONGSToDo = CHANNEL_REGS; - unsigned int i; - unsigned int Address = CHANNEL_START; - - /* Copy the data first... Hack... Before mucking with Volume! */ - memcpy((unsigned char *) ChanData, (unsigned char *) Data, ULONGSToDo * 4); - - outb((unsigned char) HwChannel, TRID_REG(trident, T4D_LFO_GC_CIR)); + /* select hardware channel to write */ + outb(channel, TRID_REG(card, T4D_LFO_GC_CIR)); + /* output the channel registers */ + for (i = 0; i < CHANNEL_REGS; i++) { + outl(data[i], TRID_REG(card, CHANNEL_START + 4*i)); + } - for (i = 0; i < ULONGSToDo; i++, Address += 4) - outl(ChanData[i], TRID_REG(trident, Address)); -#ifdef DEBUG - printk("(trident) load virtual channel %d\n", HwChannel); -#endif return TRUE; } -/*--------------------------------------------------------------------------- - trident_write_voice_regs - - Description: This routine will write the 5 hardware channel registers - to hardware. - - Paramters: trident - pointer to target device class for 4DWave. - Channel - Real or Virtual channel number. - Each register field. - - Returns: TRUE if all goes well, else FALSE. - - ---------------------------------------------------------------------------*/ -int trident_write_voice_regs(struct trident_card * trident, - unsigned int Channel, - unsigned int LBA, - unsigned int CSO, - unsigned int ESO, - unsigned int DELTA, - unsigned int ALPHA_FMS, - unsigned int FMC_RVOL_CVOL, - unsigned int GVSEL, - unsigned int PAN, - unsigned int VOL, - unsigned int CTRL, - unsigned int EC) +/* called with spin lock held */ +static int trident_write_voice_regs(struct trident_state *state, unsigned int rec) { - unsigned int ChanData[CHANNEL_REGS + 1], FmcRvolCvol; + unsigned int data[CHANNEL_REGS + 1]; + struct trident_channel *channel; - ChanData[1] = LBA; - ChanData[4] = (GVSEL << 31) | - ((PAN & 0x0000007f) << 24) | - ((VOL & 0x000000ff) << 16) | - ((CTRL & 0x0000000f) << 12) | - (EC & 0x00000fff); + if (rec) + channel = state->dma_adc.channel; + else + channel = state->dma_dac.channel; - FmcRvolCvol = FMC_RVOL_CVOL & 0x0000ffff; + data[1] = channel->lba; + data[4] = channel->control; - switch (trident->pci_id) + switch (state->card->pci_id) { case PCI_DEVICE_ID_SI_7018: + data[0] = 0; /* Current Sample Offset */ + data[2] = (channel->eso << 16) | (channel->delta & 0xffff); + data[3] = (channel->attribute << 16) | (channel->fm_vol & 0xffff); + break; case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX: - ChanData[0] = (CSO << 16) | (ALPHA_FMS & 0x0000ffff); - ChanData[2] = (ESO << 16) | (DELTA & 0x0000ffff); - ChanData[3] = FmcRvolCvol; + data[0] = 0; /* Current Sample Offset */ + data[2] = (channel->eso << 16) | (channel->delta & 0xffff); + data[3] = channel->fm_vol & 0xffff; break; case PCI_DEVICE_ID_TRIDENT_4DWAVE_NX: - ChanData[0] = (DELTA << 24) | (CSO & 0x00ffffff); - ChanData[2] = ((DELTA << 16) & 0xff000000) | (ESO & 0x00ffffff); - ChanData[3] = (ALPHA_FMS << 16) | FmcRvolCvol; + data[0] = (channel->delta << 24); + data[2] = ((channel->delta << 24) & 0xff000000) | (channel->eso & 0x00ffffff); + data[3] = channel->fm_vol & 0xffff; break; + default: + return FALSE; } - LoadVirtualChannel(trident, ChanData, Channel); - - return TRUE; + return trident_load_channel_registers(state->card, data, channel->num); } static int compute_rate(u32 rate) { int delta; - // We special case 44100 and 8000 since rounding with the equation - // does not give us an accurate enough value. For 11025 and 22050 - // the equation gives us the best answer. All other frequencies will - // also use the equation. JDW + /* We special case 44100 and 8000 since rounding with the equation + does not give us an accurate enough value. For 11025 and 22050 + the equation gives us the best answer. All other frequencies will + also use the equation. JDW */ if (rate == 44100) delta = 0xeb3; else if (rate == 8000) @@ -784,838 +555,267 @@ static int compute_rate(u32 rate) return delta; } -/*--------------------------------------------------------------------------- - trident_set_dac_rate - - Description: This routine will set the sample rate for playback. - - Paramters: trident - pointer to target device class for 4DWave. - rate - desired sample rate - set - actually write hardware if set is true. - - Returns: The rate allowed by device. - - ---------------------------------------------------------------------------*/ - -static unsigned int trident_set_dac_rate(struct trident_state * trident, - unsigned int rate, int set) -{ - u16 delta; +/* set playback sample rate */ +static unsigned int trident_set_dac_rate(struct trident_state * state, unsigned int rate) +{ + struct dmabuf *dmabuf = &state->dma_dac; if (rate > 48000) rate = 48000; if (rate < 4000) rate = 4000; - delta = compute_rate(rate); - trident->ratedac = rate; + dmabuf->rate = rate; + dmabuf->channel->delta = compute_rate(rate); - if (set) - trident_load_hw_delta(trident->card, trident->dma_dac.chan, - delta); -#ifdef DEBUG - printk("trident: called trident_set_dac_rate : rate = %d, " - "set = %d, delta = 0x%04x\n", rate, set, delta); + trident_write_voice_regs(state, 0); + +#ifdef DEBUG + printk("trident: called trident_set_dac_rate : rate = %d\n", rate); #endif return rate; } -/*--------------------------------------------------------------------------- - trident_set_adc_rate - - Description: This routine will set the sample rate for capture. - - Paramters: trident - pointer to target device class for 4DWave. - rate - desired sample rate - set - actually write hardware if set is true. - - Returns: The rate allowed by device. - - ---------------------------------------------------------------------------*/ - -static unsigned int trident_set_adc_rate(struct trident_state * trident, - unsigned int rate, int set) +/* set recording sample rate */ +static unsigned int trident_set_adc_rate(struct trident_state * state, unsigned int rate) { - u16 delta; - + struct dmabuf *dmabuf = &state->dma_adc; if (rate > 48000) rate = 48000; if (rate < 4000) rate = 4000; - delta = compute_rate(rate); - trident->ratedac = rate; - -#if 0 /* It seems that 4D-Wave can not use wave tables channels for recording */ - if (set) - trident_load_hw_delta(trident->card, trident->dma_adc.chan, - delta); -#endif -#ifdef DEBUG - printk("trident: called trident_set_adc_rate : rate = %d, " - "set = %d, delta = 0x%04x\n", rate, set, delta); -#endif - return rate; -} - -extern __inline__ unsigned ld2(unsigned int x) -{ - unsigned r = 0; - - if (x >= 0x10000) { - x >>= 16; - r += 16; - } - if (x >= 0x100) { - x >>= 8; - r += 8; - } - if (x >= 0x10) { - x >>= 4; - r += 4; - } - if (x >= 4) { - x >>= 2; - r += 2; - } - if (x >= 2) - r++; - return r; -} - -/* Write AC97 mixer registers */ -static void trident_ac97_set(struct trident_card *trident, u8 cmd, u16 val) -{ - unsigned int address, mask, busy; - unsigned short count = 0xffff; - u32 data; - - data = ((u32) val) << 16; - - switch (trident->pci_id) - { - default: - case PCI_DEVICE_ID_SI_7018: - address = SI_AC97_WRITE; - mask = SI_AC97_BUSY_WRITE | SI_AC97_AUDIO_BUSY; - busy = SI_AC97_BUSY_WRITE; - break; - case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX: - address = DX_ACR0_AC97_W; - mask = busy = DX_AC97_BUSY_WRITE; - break; - case PCI_DEVICE_ID_TRIDENT_4DWAVE_NX: - address = NX_ACR1_AC97_W; - mask = busy = NX_AC97_BUSY_WRITE; - break; - } - - do { - if ((inw(TRID_REG(trident, address)) & busy) == 0) - break; - } while (count--); - - data |= (mask | (cmd & AC97_REG_ADDR)); - - if (count == 0) { - printk(KERN_ERR "trident: AC97 CODEC write timed out.\n"); - return; - } - outl(data, TRID_REG(trident, address)); -} - -/* Read AC97 codec registers */ -static u16 trident_ac97_get(struct trident_card *trident, u8 cmd) -{ - unsigned int address, mask, busy; - unsigned short count = 0xffff; - u32 data; - - switch (trident->pci_id) - { - default: - case PCI_DEVICE_ID_SI_7018: - address = SI_AC97_READ; - mask = SI_AC97_BUSY_READ | SI_AC97_AUDIO_BUSY; - busy = SI_AC97_BUSY_READ; - break; - case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX: - address = DX_ACR1_AC97_R; - mask = busy = DX_AC97_BUSY_READ; - break; - case PCI_DEVICE_ID_TRIDENT_4DWAVE_NX: - address = NX_ACR2_AC97_R_PRIMARY; - mask = NX_AC97_BUSY_READ; - busy = 0x0c00; - break; - } + dmabuf->rate = rate; + dmabuf->channel->delta = compute_rate(rate); - data = (mask | (cmd & AC97_REG_ADDR)); - outl(data, TRID_REG(trident, address)); - - do { - data = inl(TRID_REG(trident, address)); - if ((data & busy) == 0) - break; - } while (count--); - - if (count == 0) { - printk(KERN_ERR "trident: AC97 CODEC read timed out.\n"); - data = 0; - } - return ((u16) (data >> 16)); -} - -/* OSS interface to the ac97s.. */ - -#define AC97_STEREO_MASK (SOUND_MASK_VOLUME|\ - SOUND_MASK_PCM|SOUND_MASK_LINE|SOUND_MASK_CD|\ - SOUND_MASK_VIDEO|SOUND_MASK_LINE1|SOUND_MASK_IGAIN) - -#define AC97_SUPPORTED_MASK (AC97_STEREO_MASK | \ - SOUND_MASK_BASS|SOUND_MASK_TREBLE|SOUND_MASK_MIC|\ - SOUND_MASK_SPEAKER) - -#define AC97_RECORD_MASK (SOUND_MASK_MIC|\ - SOUND_MASK_CD| SOUND_MASK_VIDEO| SOUND_MASK_LINE1| SOUND_MASK_LINE|\ - SOUND_MASK_PHONEIN) - -#define supported_mixer(CARD,FOO) ( CARD->mix.supported_mixers & (1<<FOO) ) - -/* this table has default mixer values for all OSS mixers. - be sure to fill it in if you add oss mixers - to anyone's supported mixer defines */ - -/* possible __init */ -static struct mixer_defaults { - int mixer; - unsigned int value; -} mixer_defaults[SOUND_MIXER_NRDEVICES] = { - /* all values 0 -> 100 in bytes */ - {SOUND_MIXER_VOLUME, 0x3232}, - {SOUND_MIXER_BASS, 0x3232}, - {SOUND_MIXER_TREBLE, 0x3232}, - {SOUND_MIXER_SPEAKER, 0x3232}, - {SOUND_MIXER_MIC, 0x3232}, - {SOUND_MIXER_LINE, 0x3232}, - {SOUND_MIXER_CD, 0x3232}, - {SOUND_MIXER_VIDEO, 0x3232}, - {SOUND_MIXER_LINE1, 0x3232}, - {SOUND_MIXER_PCM, 0x3232}, - {SOUND_MIXER_IGAIN, 0x3232}, - {-1,0} -}; - -static struct ac97_mixer_hw { - unsigned char offset; - int scale; -} ac97_hw[SOUND_MIXER_NRDEVICES]= { - [SOUND_MIXER_VOLUME] = {AC97_MASTER_VOL_STEREO,63}, - [SOUND_MIXER_BASS] = {AC97_MASTER_TONE, 15}, - [SOUND_MIXER_TREBLE] = {AC97_MASTER_TONE, 15}, - [SOUND_MIXER_SPEAKER] = {AC97_PCBEEP_VOL, 15}, - [SOUND_MIXER_MIC] = {AC97_MIC_VOL, 31}, - [SOUND_MIXER_LINE] = {AC97_LINEIN_VOL, 31}, - [SOUND_MIXER_CD] = {AC97_CD_VOL, 31}, - [SOUND_MIXER_VIDEO] = {AC97_VIDEO_VOL, 31}, - [SOUND_MIXER_LINE1] = {AC97_AUX_VOL, 31}, - [SOUND_MIXER_PCM] = {AC97_PCMOUT_VOL, 31}, - [SOUND_MIXER_IGAIN] = {AC97_RECORD_GAIN, 31} -}; - -#if 0 /* *shrug* removed simply because we never used it. - feel free to implement again if needed */ - -/* reads the given OSS mixer from the ac97 - the caller must have insured that the ac97 knows - about that given mixer, and should be holding a - spinlock for the card */ -static int ac97_read_mixer(struct trident_card *card, int mixer) -{ - u16 val; - int ret = 0; - struct ac97_mixer_hw *mh = &ac97_hw[mixer]; - - val = trident_ac97_get(card , mh->offset); - - if (AC97_STEREO_MASK & (1<<mixer)) { - /* nice stereo mixers .. */ - int left,right; - - left = (val >> 8) & 0x7f; - right = val & 0x7f; - - if (mixer == SOUND_MIXER_IGAIN) { - right = (right * 100) / mh->scale; - left = (left * 100) / mh->scale; - else { - right = 100 - ((right * 100) / mh->scale); - left = 100 - ((left * 100) / mh->scale); - } - - ret = left | (right << 8); - } else if (mixer == SOUND_MIXER_SPEAKER) { - ret = 100 - ((((val & 0x1e)>>1) * 100) / mh->scale); - } else if (mixer == SOUND_MIXER_MIC) { - ret = 100 - (((val & 0x1f) * 100) / mh->scale); - /* the low bit is optional in the tone sliders and masking - it lets us avoid the 0xf 'bypass'.. */ - } else if (mixer == SOUND_MIXER_BASS) { - ret = 100 - ((((val >> 8) & 0xe) * 100) / mh->scale); - } else if (mixer == SOUND_MIXER_TREBLE) { - ret = 100 - (((val & 0xe) * 100) / mh->scale); - } + trident_write_voice_regs(state, 1); #ifdef DEBUG - printk("trident: read OSS mixer %2d (ac97 register 0x%02x), " - "0x%04x -> 0x%04x\n", mixer, mh->offset, val, ret); + printk("trident: called trident_set_adc_rate : rate = %d\n", rate); #endif - - return ret; + return rate; } -#endif -/* write the OSS encoded volume to the given OSS encoded mixer, - again caller's job to make sure all is well in arg land, - call with spinlock held */ -static void ac97_write_mixer(struct trident_card *card, int mixer, - unsigned int left, unsigned int right) +/* prepare channel attributes for playback */ +static void trident_play_setup(struct trident_state *state) { - u16 val = 0; - struct ac97_mixer_hw *mh = &ac97_hw[mixer]; + struct dmabuf *dmabuf = &state->dma_dac; + struct trident_channel *channel = dmabuf->channel; -#ifdef DEBUG - printk("trident: wrote OSS mixer %2d (ac97 register 0x%02x), " - "left vol:%2d, right vol:%2d:", - mixer, mh->offset, left, right); -#endif + channel->lba = virt_to_bus(dmabuf->rawbuf); + channel->delta = compute_rate(dmabuf->rate); - if (AC97_STEREO_MASK & (1 << mixer)) { - /* stereo mixers */ - if (mixer == SOUND_MIXER_IGAIN) { - right = (right * mh->scale) / 100; - left = (left * mh->scale) / 100; - } else { - right = ((100 - right) * mh->scale) / 100; - left = ((100 - left) * mh->scale) / 100; - } + channel->eso = dmabuf->dmasize >> sample_shift[dmabuf->fmt]; + channel->eso -= 1; - val = (left << 8) | right; - } else if (mixer == SOUND_MIXER_SPEAKER) { - val = (((100 - left) * mh->scale) / 100) << 1; - } else if (mixer == SOUND_MIXER_MIC) { - val = trident_ac97_get(card , mh->offset) & ~0x801f; - val |= (((100 - left) * mh->scale) / 100); - /* the low bit is optional in the tone sliders and masking - it lets us avoid the 0xf 'bypass'.. */ - } else if (mixer == SOUND_MIXER_BASS) { - val = trident_ac97_get(card , mh->offset) & ~0x0f00; - val |= ((((100 - left) * mh->scale) / 100) << 8) & 0x0e00; - } else if (mixer == SOUND_MIXER_TREBLE) { - val = trident_ac97_get(card , mh->offset) & ~0x000f; - val |= (((100 - left) * mh->scale) / 100) & 0x000e; + if (state->card->pci_id == PCI_DEVICE_ID_SI_7018) { + /* FIXME: channel attributes are configured by ioctls, but it is not implemented + so just set to ZERO for the moment */ + channel->attribute = 0; + } else { + channel->attribute = 0; } -#ifdef DEBUG - printk(" 0x%04x", val); -#endif - trident_ac97_set(card, mh->offset, val); - -#ifdef DEBUG - val = trident_ac97_get(card, mh->offset); - printk(" -> 0x%04x\n", val); -#endif -} - -/* the following tables allow us to go from - OSS <-> ac97 quickly. */ - -enum ac97_recsettings { - AC97_REC_MIC=0, - AC97_REC_CD, - AC97_REC_VIDEO, - AC97_REC_AUX, - AC97_REC_LINE, - AC97_REC_STEREO, /* combination of all enabled outputs.. */ - AC97_REC_MONO, /*.. or the mono equivalent */ - AC97_REC_PHONE -}; - -static unsigned int ac97_rm2oss[] = { - [AC97_REC_MIC] = SOUND_MIXER_MIC, - [AC97_REC_CD] = SOUND_MIXER_CD, - [AC97_REC_VIDEO] = SOUND_MIXER_VIDEO, - [AC97_REC_AUX] = SOUND_MIXER_LINE1, - [AC97_REC_LINE] = SOUND_MIXER_LINE, - [AC97_REC_PHONE] = SOUND_MIXER_PHONEIN -}; - -/* indexed by bit position */ -static unsigned int ac97_oss_rm[] = { - [SOUND_MIXER_MIC] = AC97_REC_MIC, - [SOUND_MIXER_CD] = AC97_REC_CD, - [SOUND_MIXER_VIDEO] = AC97_REC_VIDEO, - [SOUND_MIXER_LINE1] = AC97_REC_AUX, - [SOUND_MIXER_LINE] = AC97_REC_LINE, - [SOUND_MIXER_PHONEIN] = AC97_REC_PHONE -}; + channel->fm_vol = 0x0; -/* read or write the recmask - the ac97 can really have left and right recording - inputs independantly set, but OSS doesn't seem to - want us to express that to the user. - the caller guarantees that we have a supported bit set, - and they must be holding the card's spinlock */ -static int ac97_recmask_io(struct trident_card *card, int rw, int mask) -{ - unsigned int val; - - if (rw) { - /* read it from the card */ - val = trident_ac97_get(card, 0x1a) & 0x7; - return ac97_rm2oss[val]; - } - - /* else, write the first set in the mask as the - output */ - - val = ffs(mask); - val = ac97_oss_rm[val-1]; - val |= val << 8; /* set both channels */ -#ifdef DEBUG - printk("trident: setting ac97 recmask to 0x%x\n", val); -#endif - trident_ac97_set(card, 0x1a, val); - - return 0; -}; - -/* AC97 codec initialisation. */ -static u16 trident_ac97_init(struct trident_card *trident) -{ - u16 id1, id2; - char *ac97_name = NULL; - int i; - - /* initialize controller side of AC link */ - switch (trident->pci_id) - { - case PCI_DEVICE_ID_SI_7018: - /* disable AC97 GPIO interrupt */ - outl(0x00, TRID_REG(trident, SI_AC97_GPIO)); - /* stop AC97 cold reset process */ - outl(0x00014000, TRID_REG(trident, SI_SERIAL_INTF_CTRL)); - break; - case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX: - /* playback on */ - outl(0x02, TRID_REG(trident, DX_ACR2_AC97_COM_STAT)); - break; - case PCI_DEVICE_ID_TRIDENT_4DWAVE_NX: - /* enable AC97 Output Slot 3,4 (PCM Left/Right Playback) */ - outl(0x02, TRID_REG(trident, NX_ACR0_AC97_COM_STAT)); - break; - } - - /* get some information about our AC97 codec */ - id1 = trident_ac97_get(trident, AC97_VENDOR_ID1); - id2 = trident_ac97_get(trident, AC97_VENDOR_ID2); - for (i = 0; i < sizeof (snd_ac97_codec_ids); i++) { - if (snd_ac97_codec_ids[i].id == ((id1 << 16) | id2)) { - ac97_name = snd_ac97_codec_ids[i].name; - break; - } - } - if (ac97_name == NULL) - ac97_name = "Unknown"; - printk(KERN_INFO "trident: ac97 vendor id1: 0x%04x, id2: 0x%04x (%s)\n", - id1, id2, ac97_name); - - /* initialize volume level */ - trident_ac97_set(trident, AC97_RESET, 0L); - trident_ac97_set(trident, AC97_MASTER_VOL_STEREO, 0L); - trident_ac97_set(trident, AC97_PCMOUT_VOL, 0L); - - /* set appropriate masks and function pointers */ - trident->mix.supported_mixers = AC97_SUPPORTED_MASK; - trident->mix.stereo_mixers = AC97_STEREO_MASK; - trident->mix.record_sources = AC97_RECORD_MASK; - /* FIXME: trident->mix.read_mixer = ac97_read_mixer; */ - trident->mix.write_mixer = ac97_write_mixer; - trident->mix.recmask_io = ac97_recmask_io; - - return 0; -} - -/* this function only update fmt field in trident_state, the hardware channel attribute - will be update in trident_play(rec)_setup() which will be called every time a new - sample is played(recorded) */ -static void set_fmt(struct trident_state *s, unsigned char mask, unsigned char data) -{ - s->fmt = (s->fmt & mask) | data; -} - -/* the mode passed should be already shifted and masked */ -/* trident_play_setup: initialize channel for play back, mode specify the format of samples to - be played. - default values: -*/ - -static void trident_play_setup(struct trident_state *trident, int mode, u32 rate, - void *buffer, int size) -{ - unsigned int LBA; - unsigned int Delta; - unsigned int ESO; - unsigned int CTRL; - unsigned int FMC_RVOL_CVOL; - unsigned int GVSEL; - unsigned int PAN; - unsigned int VOL; - unsigned int EC; - - /* set Loop Begin Address */ - LBA = virt_to_bus(buffer); - Delta = compute_rate(rate); - - /* set ESO */ - ESO = size; - if (mode & TRIDENT_FMT_16BIT) - ESO /= 2; - if (mode & TRIDENT_FMT_STEREO) - ESO /= 2; - ESO = ESO - 1; - - /* loop mode enable */ - CTRL = 0x00000001; - if (mode & TRIDENT_FMT_16BIT) { + channel->control = CHANNEL_LOOP; + if (dmabuf->fmt & TRIDENT_FMT_16BIT) { /* 16-bits */ - CTRL |= 0x00000008; + channel->control |= CHANNEL_16BITS; /* signed */ - CTRL |= 0x00000002; + channel->control |= CHANNEL_SIGNED; } - if (mode & TRIDENT_FMT_STEREO) + if (dmabuf->fmt & TRIDENT_FMT_STEREO) /* stereo */ - CTRL |= 0x00000004; - - /* FIXME: some difference between 4D and 7018 in FMC_RVOL_CVOL */ - /* right vol: mute, ledt vol: mute */ - FMC_RVOL_CVOL = 0x0000ffff; - GVSEL = 1; - PAN = 0; - VOL = 0; - EC = 0; - - trident_write_voice_regs(trident->card, - trident->dma_dac.chan, - LBA, - 0, /* cso */ - ESO, - Delta, - 0, /* alpha */ - FMC_RVOL_CVOL, - GVSEL, - PAN, - VOL, - CTRL, - EC); - + channel->control |= CHANNEL_STEREO; +#ifdef DEBUG + printk("trident: trident_play_setup, LBA = 0x%08x, " + "Delat = 0x%08x, ESO = 0x%08x, Control = 0x%08x\n", + channel->lba, channel->delta, channel->eso, channel->control); +#endif + trident_write_voice_regs(state, 0); } -/* - * Native record driver - */ -/* FIXME: Not exammed yet */ -/* again, passed mode is alrady shifted/masked */ - -static void trident_rec_setup(struct trident_state *trident, int mode, u32 rate, - void *buffer, int size) +/* prepare channel attributes for recording */ +static void trident_rec_setup(struct trident_state *state) { - unsigned int LBA; - unsigned int Delta; - unsigned int ESO; - unsigned int CTRL; - unsigned int FMC_RVOL_CVOL; - unsigned int GVSEL; - unsigned int PAN; - unsigned int VOL; - unsigned int EC; - unsigned char bValue; - unsigned short wValue; - unsigned int dwValue; - unsigned short wRecCODECSamples; - unsigned int dwChanFlags; - struct trident_card *card = trident->card; - -#ifdef DEBUG - printk("trident: trident_rec_setup called\n"); -#endif + u16 w; + struct trident_card *card = state->card; + struct dmabuf *dmabuf = &state->dma_adc; + struct trident_channel *channel = dmabuf->channel; - // Enable AC-97 ADC (capture), disable capture interrupt + /* Enable AC-97 ADC (capture) */ switch (card->pci_id) { case PCI_DEVICE_ID_SI_7018: /* for 7018, the ac97 is always in playback/record (duplex) mode */ break; case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX: - bValue = inb(TRID_REG(card, DX_ACR2_AC97_COM_STAT)); - outb(bValue | 0x48, TRID_REG(card, DX_ACR2_AC97_COM_STAT)); + w = inb(TRID_REG(card, DX_ACR2_AC97_COM_STAT)); + outb(w | 0x48, TRID_REG(card, DX_ACR2_AC97_COM_STAT)); break; case PCI_DEVICE_ID_TRIDENT_4DWAVE_NX: - wValue = inw(TRID_REG(card, T4D_MISCINT)); - outw(wValue | 0x1000, TRID_REG(card, T4D_MISCINT)); + w = inw(TRID_REG(card, T4D_MISCINT)); + outw(w | 0x1000, TRID_REG(card, T4D_MISCINT)); break; + default: + return; } - // Initilize the channel and set channel Mode - outb(0, TRID_REG(card, LEGACY_DMAR15)); - - // Set DMA channel operation mode register - bValue = inb(TRID_REG(card, LEGACY_DMAR11)) & 0x03; - outb(bValue | 0x54, TRID_REG(card, LEGACY_DMAR11)); - - // Set channel buffer Address - LBA = virt_to_bus(buffer); - outl(LBA, TRID_REG(card, LEGACY_DMAR0)); - - /* set ESO */ - ESO = size; + channel->lba = virt_to_bus(dmabuf->rawbuf); + channel->delta = compute_rate(dmabuf->rate); - dwValue = inl(TRID_REG(card, LEGACY_DMAR4)) & 0xff000000; - dwValue |= (ESO - 1) & 0x0000ffff; - outl(dwValue, TRID_REG(card, LEGACY_DMAR4)); + channel->eso = dmabuf->dmasize >> sample_shift[dmabuf->fmt]; + channel->eso -= 1; - // Set channel sample rate , 4.12 format - dwValue = (((unsigned int) 48000L << 12) / (unsigned long) (rate)); - outw((unsigned short) dwValue, TRID_REG(card, T4D_SBDELTA_DELTA_R)); - - // Set channel interrupt blk length - if (mode & TRIDENT_FMT_16BIT) { - wRecCODECSamples = (unsigned short) ((ESO >> 1) - 1); - dwChanFlags = 0xffffb000; + if (state->card->pci_id == PCI_DEVICE_ID_SI_7018) { + /* FIXME: channel attributes are configured by ioctls, but it is not implemented + so just set to ZERO for the moment */ + channel->attribute = 0; } else { - wRecCODECSamples = (unsigned short) (ESO - 1); - dwChanFlags = 0xffff1000; + channel->attribute = 0; } - dwValue = ((unsigned int) wRecCODECSamples) << 16; - dwValue |= (unsigned int) (wRecCODECSamples) & 0x0000ffff; - outl(dwValue, TRID_REG(card, T4D_SBBL_SBCL)); - - // Right now, set format and start to run capturing, - // continuous run loop enable. - trident->bDMAStart = 0x19; // 0001 1001b - - if (mode & TRIDENT_FMT_16BIT) - trident->bDMAStart |= 0xa0; - if (mode & TRIDENT_FMT_STEREO) - trident->bDMAStart |= 0x40; - - // Prepare capture intr channel - - Delta = ((((unsigned int) rate) << 12) / ((unsigned long) (48000L))); - - /* set Loop Back Address */ - LBA = virt_to_bus(buffer); - - /* set ESO */ - ESO = size; - if (mode & TRIDENT_FMT_16BIT) - ESO /= 2; - if (mode & TRIDENT_FMT_STEREO) - ESO /= 2; - - ESO = ESO - 1; - //snd_printk("trid: ESO = %d\n", ESO); - - /* set ctrl mode - CTRL default: 8-bit (unsigned) mono, loop mode enabled - */ - CTRL = 0x00000001; - if (mode & TRIDENT_FMT_16BIT) - CTRL |= 0x00000008; // 16-bit data - /* XXX DO UNSIGNED XXX */ - //if (!(mode & SND_PCM1_MODE_U)) - // CTRL |= 0x00000002; // signed data - if (mode& TRIDENT_FMT_STEREO) - CTRL |= 0x00000004; // stereo data - - FMC_RVOL_CVOL = 0x0000ffff; - GVSEL = 1; - PAN = 0xff; - VOL = 0xff; - EC = 0; - - trident_write_voice_regs(card, - trident->dma_adc.chan, - LBA, - 0, /* cso */ - ESO, - Delta, - 0, /* alpha */ - FMC_RVOL_CVOL, - GVSEL, - PAN, - VOL, - CTRL, - EC); - + channel->fm_vol = 0x0; + + channel->control = CHANNEL_LOOP; + if (dmabuf->fmt & TRIDENT_FMT_16BIT) { + /* 16-bits */ + channel->control |= CHANNEL_16BITS; + /* signed */ + channel->control |= CHANNEL_SIGNED; + } + if (dmabuf->fmt & TRIDENT_FMT_STEREO) + /* stereo */ + channel->control |= CHANNEL_STEREO; +#ifdef DEBUG + printk("trident: trident_rec_setup, LBA = 0x%08x, " + "Delat = 0x%08x, ESO = 0x%08x, Control = 0x%08x\n", + channel->lba, channel->delta, channel->eso, channel->control); +#endif + trident_write_voice_regs(state, 1); } -/* get current playback pointer */ -__inline__ unsigned int get_dmaa(struct trident_state *trident) +/* get current playback/recording dma buffer pointer (byte offset from LBA), + called with spinlock held! */ +extern __inline__ unsigned trident_get_dma_addr(struct trident_state *state, unsigned rec) { + struct dmabuf *dmabuf; u32 cso; - u32 eso; -#if 0 - /* FIXME: does this mean that FULL duplex is not supported ? */ - if (!(trident->enable & ADC_RUNNING)) + + if (rec) + dmabuf = &state->dma_adc; + else + dmabuf = &state->dma_dac; + + if (!dmabuf->enable) return 0; -#endif - outb(trident->dma_dac.chan, TRID_REG(trident->card, T4D_LFO_GC_CIR)); - switch (trident->card->pci_id) + outb(dmabuf->channel->num, TRID_REG(state->card, T4D_LFO_GC_CIR)); + + switch (state->card->pci_id) { case PCI_DEVICE_ID_SI_7018: case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX: /* 16 bits ESO, CSO for 7018 and DX */ - cso = inw(TRID_REG(trident->card, CH_DX_CSO_ALPHA_FMS + 2)); - eso = inw(TRID_REG(trident->card, CH_DX_ESO_DELTA + 2)); + cso = inw(TRID_REG(state->card, CH_DX_CSO_ALPHA_FMS + 2)); break; case PCI_DEVICE_ID_TRIDENT_4DWAVE_NX: /* 24 bits ESO, CSO for NX */ - cso = inl(TRID_REG(trident->card, CH_NX_DELTA_CSO)) & 0x00ffffff; - eso = inl(TRID_REG(trident->card, CH_NX_DELTA_ESO)) & 0x00ffffff; + cso = inl(TRID_REG(state->card, CH_NX_DELTA_CSO)) & 0x00ffffff; break; default: return 0; } #ifdef DEBUG - printk("trident: get_dmaa: chip reported channel: %d, cso = %d, eso = %d\n", - trident->dma_dac.chan, cso, eso); + printk("trident: trident_get_dma_addr: chip reported channel: %d, cso = %d\n", + dmabuf->channel->num, cso); #endif /* ESO and CSO are in units of Samples, convert to byte offset */ - if (cso > eso) - cso = eso; - if (trident->fmt & TRIDENT_FMT_16BIT) - cso *= 2; - if (trident->fmt & TRIDENT_FMT_STEREO) - cso *= 2; - return cso; + cso <<= sample_shift[dmabuf->fmt]; + + return (cso % dmabuf->dmasize); } -/* get current recording pointer */ -extern __inline__ unsigned get_dmac(struct trident_state *trident) +/* Stop recording (lock held) */ +extern __inline__ void __stop_adc(struct trident_state *state) { - u32 cso; -#if 0 - /* FIXME: does this mean that FULL duplex is not supported ? */ - if (!(trident->enable & DAC_RUNNING)) - return 0; -#endif - outb(trident->dma_adc.chan, TRID_REG(trident->card, T4D_LFO_GC_CIR)); + struct dmabuf *dmabuf = &state->dma_adc; + unsigned int chan_num = dmabuf->channel->num; + struct trident_card *card = state->card; - switch (trident->card->pci_id) - { - default: - case PCI_DEVICE_ID_SI_7018: - case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX: - /* 16 bits ESO, CSO for 7018 and DX */ - cso = inw(TRID_REG(trident->card, CH_DX_CSO_ALPHA_FMS + 2)); - break; - case PCI_DEVICE_ID_TRIDENT_4DWAVE_NX: - /* 24 bits ESO, CSO for NX */ - cso = inl(TRID_REG(trident->card, CH_NX_DELTA_CSO)) & 0x00ffffff; - break; - } - -#ifdef DEBUG - printk("(trident) get_dmac: chip reported cso = %d\n", cso); -#endif - /* ESO and CSO are in units of Samples, convert to byte offset */ - if (trident->fmt & TRIDENT_FMT_16BIT) - cso *= 2; - if (trident->fmt & TRIDENT_FMT_STEREO) - cso *= 2; - return cso; + dmabuf->enable &= ~DMA_RUNNING; + trident_stop_voice(card, chan_num); + trident_disable_voice_irq(card, chan_num); } -/* Stop recording (lock held) */ -extern inline void __stop_adc(struct trident_state *s) +static void stop_adc(struct trident_state *state) { - struct trident_card *trident = s->card; -#ifdef DEBUG - printk("(trident) stopping ADC\n"); -#endif - s->enable &= ~ADC_RUNNING; - trident_disable_voice_irq(trident, s->dma_adc.chan); - outb(0x00, TRID_REG(trident, T4D_SBCTRL_SBE2R_SBDD)); - trident_disable_voice_irq(trident, s->dma_adc.chan); - trident_stop_voice(trident, s->dma_adc.chan); - ResetAinten(trident, s->dma_adc.chan); -} + struct trident_card *card = state->card; + unsigned long flags; + + spin_lock_irqsave(&card->lock, flags); + __stop_adc(state); + spin_unlock_irqrestore(&card->lock, flags); +} -extern inline void stop_adc(struct trident_state *s) +static void start_adc(struct trident_state *state) { + struct dmabuf *dmabuf = &state->dma_adc; + unsigned int chan_num = dmabuf->channel->num; + struct trident_card *card = state->card; unsigned long flags; - struct trident_card *trident = s->card; - spin_lock_irqsave(&trident->lock, flags); - __stop_adc(s); - spin_unlock_irqrestore(&trident->lock, flags); -} + spin_lock_irqsave(&card->lock, flags); + if ((dmabuf->mapped || + dmabuf->count < (signed)(dmabuf->dmasize - 2*dmabuf->fragsize)) + && dmabuf->ready) { + dmabuf->enable |= DMA_RUNNING; + trident_enable_voice_irq(card, chan_num); + trident_start_voice(card, chan_num); + } + spin_unlock_irqrestore(&card->lock, flags); +} /* stop playback (lock held) */ -extern inline void __stop_dac(struct trident_state *state) +extern __inline__ void __stop_dac(struct trident_state *state) { - struct trident_card *trident = state->card; - trident_stop_voice(trident, state->dma_dac.chan); - trident_disable_voice_irq(trident, state->dma_dac.chan); - state->enable &= ~DAC_RUNNING; -} + struct dmabuf *dmabuf = &state->dma_dac; + unsigned int chan_num = dmabuf->channel->num; + struct trident_card *card = state->card; -extern inline void stop_dac(struct trident_state *state) + dmabuf->enable &= ~DMA_RUNNING; + trident_stop_voice(card, chan_num); + trident_disable_voice_irq(card, chan_num); +} + +static void stop_dac(struct trident_state *state) { - struct trident_card *trident = state->card; + struct trident_card *card = state->card; unsigned long flags; - spin_lock_irqsave(&trident->lock, flags); + spin_lock_irqsave(&card->lock, flags); __stop_dac(state); - spin_unlock_irqrestore(&trident->lock, flags); + spin_unlock_irqrestore(&card->lock, flags); } static void start_dac(struct trident_state *state) { + struct dmabuf *dmabuf = &state->dma_dac; + unsigned int chan_num = dmabuf->channel->num; + struct trident_card *card = state->card; unsigned long flags; - struct trident_card *trident = state->card; - spin_lock_irqsave(&state->card->lock, flags); - if ((state->dma_dac.mapped || state->dma_dac.count > 0) && state->dma_dac.ready) { - state->enable |= DAC_RUNNING; - trident_enable_voice_irq(trident, state->dma_dac.chan); - trident_start_voice(trident, state->dma_dac.chan); + spin_lock_irqsave(&card->lock, flags); + if ((dmabuf->mapped || dmabuf->count > 0) && dmabuf->ready) { + dmabuf->enable |= DMA_RUNNING; + trident_enable_voice_irq(card, chan_num); + trident_start_voice(card, chan_num); } - spin_unlock_irqrestore(&state->card->lock, flags); -} - -static void start_adc(struct trident_state *s) -{ - unsigned long flags; - - spin_lock_irqsave(&s->card->lock, flags); - if ((s->dma_adc.mapped || s->dma_adc.count < (signed)(s->dma_adc.dmasize - 2*s->dma_adc.fragsize)) - && s->dma_adc.ready) { - s->enable |= ADC_RUNNING; - trident_enable_voice_irq(s->card, s->dma_adc.chan); - outb(s->bDMAStart, TRID_REG(s->card, T4D_SBCTRL_SBE2R_SBDD)); - trident_start_voice(s->card, s->dma_adc.chan); -#ifdef DEBUG - printk("(trident) starting ADC\n"); -#endif - } - spin_unlock_irqrestore(&s->card->lock, flags); -} + spin_unlock_irqrestore(&card->lock, flags); +} #define DMABUF_DEFAULTORDER (15-PAGE_SHIFT) #define DMABUF_MINORDER 1 @@ -1623,10 +823,16 @@ static void start_adc(struct trident_state *s) /* allocate DMA buffer, playback and recording buffer should be allocated seperately */ static int alloc_dmabuf(struct trident_state *state, unsigned rec) { + struct dmabuf *dmabuf; void *rawbuf; int order; unsigned long map, mapend; + if (rec) + dmabuf = &state->dma_adc; + else + dmabuf = &state->dma_dac; + /* alloc as big a chunk as we can, FIXME: is this necessary ?? */ for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--) if ((rawbuf = (void *)__get_free_pages(GFP_KERNEL, order))) @@ -1634,7 +840,7 @@ static int alloc_dmabuf(struct trident_state *state, unsigned rec) if (!rawbuf) return -ENOMEM; #ifdef DEBUG - printk("trident: allocated %ld (%d) bytes at %p\n", + printk("trident: allocated %ld (order = %d) bytes at %p\n", PAGE_SIZE << order, order, rawbuf); #endif @@ -1648,17 +854,10 @@ static int alloc_dmabuf(struct trident_state *state, unsigned rec) return -ENOMEM; } - if (rec) { - state->dma_adc.ready = state->dma_adc.mapped = 0; - state->dma_adc.rawbuf = rawbuf; - state->dma_adc.buforder = order; - } - else { - state->dma_dac.ready = state->dma_dac.mapped = 0; - state->dma_dac.rawbuf = rawbuf; - state->dma_dac.buforder = order; - } - + dmabuf->ready = dmabuf->mapped = 0; + dmabuf->rawbuf = rawbuf; + dmabuf->buforder = order; + /* now mark the pages as reserved; otherwise remap_page_range doesn't do what we want */ mapend = MAP_NR(rawbuf + (PAGE_SIZE << order) - 1); for (map = MAP_NR(rawbuf); map <= mapend; map++) @@ -1668,180 +867,237 @@ static int alloc_dmabuf(struct trident_state *state, unsigned rec) } /* free DMA buffer */ -static void dealloc_dmabuf(struct dmabuf *db) +static void dealloc_dmabuf(struct dmabuf *dmabuf) { unsigned long map, mapend; - if (db->rawbuf) { + if (dmabuf->rawbuf) { /* undo marking the pages as reserved */ - mapend = MAP_NR(db->rawbuf + (PAGE_SIZE << db->buforder) - 1); - for (map = MAP_NR(db->rawbuf); map <= mapend; map++) + mapend = MAP_NR(dmabuf->rawbuf + (PAGE_SIZE << dmabuf->buforder) - 1); + for (map = MAP_NR(dmabuf->rawbuf); map <= mapend; map++) clear_bit(PG_reserved, &mem_map[map].flags); - free_pages((unsigned long)db->rawbuf, db->buforder); + free_pages((unsigned long)dmabuf->rawbuf, dmabuf->buforder); } - db->rawbuf = NULL; - db->mapped = db->ready = 0; + dmabuf->rawbuf = NULL; + dmabuf->mapped = dmabuf->ready = 0; } static int prog_dmabuf(struct trident_state *state, unsigned rec) { - struct dmabuf *db = rec ? &state->dma_adc : &state->dma_dac; - unsigned rate = rec ? state->rateadc : state->ratedac; + struct dmabuf *dmabuf; unsigned bytepersec; - unsigned bufs; - unsigned char fmt; + unsigned bufsize; unsigned long flags; int ret; + if (rec) + dmabuf = &state->dma_adc; + else + dmabuf = &state->dma_dac; + spin_lock_irqsave(&state->card->lock, flags); - fmt = state->fmt; - if (rec) { - state->enable &= ~TRIDENT_ENABLE_RE; - fmt >>= TRIDENT_ADC_SHIFT; - } else { - state->enable &= ~TRIDENT_ENABLE_PE; - fmt >>= TRIDENT_DAC_SHIFT; - } + dmabuf->hwptr = dmabuf->swptr = dmabuf->total_bytes = 0; + dmabuf->count = dmabuf->error = dmabuf->endcleared = 0; spin_unlock_irqrestore(&state->card->lock, flags); - fmt &= TRIDENT_FMT_MASK; - - db->hwptr = db->swptr = db->total_bytes = 0; - db->count = db->error = db->endcleared = 0; - /* allocate DMA buffer if not allocated yet */ - if (!db->rawbuf) + if (!dmabuf->rawbuf) if ((ret = alloc_dmabuf(state, rec))) return ret; - bytepersec = rate << sample_shift[fmt]; - bufs = PAGE_SIZE << db->buforder; - if (db->ossfragshift) { - if ((1000 << db->ossfragshift) < bytepersec) - db->fragshift = ld2(bytepersec/1000); + /* FIXME: figure out all this OSS fragment stuff */ + bytepersec = dmabuf->rate << sample_shift[dmabuf->fmt]; + bufsize = PAGE_SIZE << dmabuf->buforder; + if (dmabuf->ossfragshift) { + if ((1000 << dmabuf->ossfragshift) < bytepersec) + dmabuf->fragshift = ld2(bytepersec/1000); else - db->fragshift = db->ossfragshift; + dmabuf->fragshift = dmabuf->ossfragshift; } else { /* lets hand out reasonable big ass buffers by default */ - db->fragshift = (db->buforder + PAGE_SHIFT -2); + dmabuf->fragshift = (dmabuf->buforder + PAGE_SHIFT -2); } - db->numfrag = bufs >> db->fragshift; - while (db->numfrag < 4 && db->fragshift > 3) { - db->fragshift--; - db->numfrag = bufs >> db->fragshift; + dmabuf->numfrag = bufsize >> dmabuf->fragshift; + while (dmabuf->numfrag < 4 && dmabuf->fragshift > 3) { + dmabuf->fragshift--; + dmabuf->numfrag = bufsize >> dmabuf->fragshift; } - db->fragsize = 1 << db->fragshift; - if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag) - db->numfrag = db->ossmaxfrags; - db->fragsamples = db->fragsize >> sample_shift[fmt]; - db->dmasize = db->numfrag << db->fragshift; + dmabuf->fragsize = 1 << dmabuf->fragshift; + if (dmabuf->ossmaxfrags >= 4 && dmabuf->ossmaxfrags < dmabuf->numfrag) + dmabuf->numfrag = dmabuf->ossmaxfrags; + dmabuf->fragsamples = dmabuf->fragsize >> sample_shift[dmabuf->fmt]; + dmabuf->dmasize = dmabuf->numfrag << dmabuf->fragshift; - memset(db->rawbuf, (fmt & TRIDENT_FMT_16BIT) ? 0 : 0x80, db->dmasize); + memset(dmabuf->rawbuf, (dmabuf->fmt & TRIDENT_FMT_16BIT) ? 0 : 0x80, + dmabuf->dmasize); spin_lock_irqsave(&state->card->lock, flags); if (rec) { - trident_rec_setup(state, fmt, state->rateadc, - db->rawbuf, db->numfrag << db->fragshift); + trident_rec_setup(state); } else { - trident_play_setup(state, fmt, state->ratedac, - db->rawbuf, db->numfrag << db->fragshift); + trident_play_setup(state); } spin_unlock_irqrestore(&state->card->lock, flags); /* set the ready flag for the dma buffer */ - db->ready = 1; + dmabuf->ready = 1; #ifdef DEBUG printk("trident: prog_dmabuf, sample rate = %d, format = %d, numfrag = %d, " "fragsize = %d dmasize = %d\n", - rate, fmt, db->numfrag, db->fragsize, db->dmasize); + dmabuf->rate, dmabuf->fmt, dmabuf->numfrag, + dmabuf->fragsize, dmabuf->dmasize); #endif return 0; } -extern __inline__ void clear_advance(struct trident_state *s) +/* we are doing quantum mechanics here, the buffer can only be empty, half or full filled i.e. + |------------|------------| or |xxxxxxxxxxxx|------------| or |xxxxxxxxxxxx|xxxxxxxxxxxx| + but we almost always get this + |xxxxxx------|------------| or |xxxxxxxxxxxx|xxxxx-------| + so we have to clear the tail space to "silence" + |xxxxxx000000|------------| or |xxxxxxxxxxxx|xxxxxx000000| +*/ +static void trident_clear_tail(struct trident_state *state) { - unsigned char c = ((s->fmt >> TRIDENT_DAC_SHIFT) & TRIDENT_FMT_16BIT) ? 0 : 0x80; - unsigned char *buf = s->dma_dac.rawbuf; - unsigned bsize = s->dma_dac.dmasize; - unsigned bptr = s->dma_dac.swptr; - unsigned len = s->dma_dac.fragsize; - - if (bptr + len > bsize) { - unsigned x = bsize - bptr; - memset(buf + bptr, c, x); - /* account for wrapping? */ - bptr = 0; - len -= x; + struct dmabuf *dmabuf = &state->dma_dac; + unsigned swptr; + unsigned char silence = (dmabuf->fmt & TRIDENT_FMT_16BIT) ? 0 : 0x80; + unsigned int len; + unsigned long flags; + + spin_lock_irqsave(&state->card->lock, flags); + swptr = dmabuf->swptr; + spin_unlock_irqrestore(&state->card->lock, flags); + + if (swptr == 0 || swptr == dmabuf->dmasize / 2 || swptr == dmabuf->dmasize) + return; + + + if (swptr < dmabuf->dmasize/2) + len = dmabuf->dmasize/2 - swptr; + else + len = dmabuf->dmasize - swptr; + + memset(dmabuf->rawbuf + swptr, silence, len); + + spin_lock_irqsave(&state->card->lock, flags); + dmabuf->swptr += len; + dmabuf->count += len; + spin_unlock_irqrestore(&state->card->lock, flags); + + /* restart the dma machine in case it is halted */ + start_dac(state); +} + +static int drain_dac(struct trident_state *state, int nonblock) +{ + DECLARE_WAITQUEUE(wait, current); + struct dmabuf *dmabuf = &state->dma_dac; + unsigned long flags; + unsigned long tmo; + int count; + + if (dmabuf->mapped || !dmabuf->ready) + return 0; + + add_wait_queue(&dmabuf->wait, &wait); + for (;;) { + /* It seems that we have to set the current state to TASK_INTERRUPTIBLE + every time to make the process really go to sleep */ + current->state = TASK_INTERRUPTIBLE; + + spin_lock_irqsave(&state->card->lock, flags); + count = dmabuf->count; + spin_unlock_irqrestore(&state->card->lock, flags); + + if (count <= 0) + break; + + if (signal_pending(current)) + break; + + if (nonblock) { + remove_wait_queue(&dmabuf->wait, &wait); + current->state = TASK_RUNNING; + return -EBUSY; + } + + /* No matter how much data left in the buffer, we have to wait untill + CSO == ESO/2 or CSO == ESO when address engine interrupts */ + tmo = (dmabuf->dmasize * HZ) / dmabuf->rate; + tmo >>= sample_shift[dmabuf->fmt]; + if (!schedule_timeout(tmo ? tmo : 1) && tmo){ + printk(KERN_ERR "trident: drain_dac, dma timeout?\n"); + break; + } } - memset(buf + bptr, c, len); + remove_wait_queue(&dmabuf->wait, &wait); + current->state = TASK_RUNNING; + if (signal_pending(current)) + return -ERESTARTSYS; + + return 0; } /* call with spinlock held! */ -static void trident_update_ptr(struct trident_state *s) +static void trident_update_ptr(struct trident_state *state) { + struct dmabuf *dmabuf; unsigned hwptr; int diff; /* update ADC pointer */ - if (s->dma_adc.ready) { - hwptr = get_dmac(s) % s->dma_adc.dmasize; - diff = (s->dma_adc.dmasize + hwptr - s->dma_adc.hwptr) % - s->dma_adc.dmasize; - s->dma_adc.hwptr = hwptr; - s->dma_adc.total_bytes += diff; - s->dma_adc.count += diff; - if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) - wake_up(&s->dma_adc.wait); - if (!s->dma_adc.mapped) { - if (s->dma_adc.count > (signed)(s->dma_adc.dmasize - ((3 * s->dma_adc.fragsize) >> 1))) { - s->enable &= ~TRIDENT_ENABLE_RE; - __stop_adc(s); - s->dma_adc.error++; + if (state->dma_adc.ready) { + dmabuf = &state->dma_adc; + hwptr = trident_get_dma_addr(state, 1); + diff = (dmabuf->dmasize + hwptr - dmabuf->hwptr) % dmabuf->dmasize; + + dmabuf->hwptr = hwptr; + dmabuf->total_bytes += diff; + dmabuf->count += diff; + + if (dmabuf->count >= (signed)dmabuf->fragsize) + wake_up(&dmabuf->wait); + if (!dmabuf->mapped) { + if (dmabuf->count > (signed)(dmabuf->dmasize - ((3 * dmabuf->fragsize) >> 1))) { + __stop_adc(state); + dmabuf->error++; } } } /* update DAC pointer */ - if (s->dma_dac.ready) { - hwptr = get_dmaa(s) % s->dma_dac.dmasize; - diff = (s->dma_dac.dmasize + hwptr - s->dma_dac.hwptr) % - s->dma_dac.dmasize; - s->dma_dac.hwptr = hwptr; - s->dma_dac.total_bytes += diff; - if (s->dma_dac.mapped) { - s->dma_dac.count += diff; - if (s->dma_dac.count >= (signed)s->dma_dac.fragsize) - wake_up(&s->dma_dac.wait); - } + if (state->dma_dac.ready) { + dmabuf = &state->dma_dac; + hwptr = trident_get_dma_addr(state, 0); + diff = (dmabuf->dmasize + hwptr - dmabuf->hwptr) % dmabuf->dmasize; + + dmabuf->hwptr = hwptr; + dmabuf->total_bytes += diff; + + if (dmabuf->mapped) { + dmabuf->count += diff; + if (dmabuf->count >= (signed)dmabuf->fragsize) + wake_up(&dmabuf->wait); + } else { - s->dma_dac.count -= diff; - if (s->dma_dac.count <= 0) { - s->enable &= ~TRIDENT_ENABLE_PE; - /* Lock already held */ - __stop_dac(s); - /* brute force everyone back in sync, sigh */ - s->dma_dac.count = 0; - s->dma_dac.swptr = 0; - s->dma_dac.hwptr = 0; - s->dma_dac.error++; - } - else if (s->dma_dac.count <= (signed)s->dma_dac.fragsize && - !s->dma_dac.endcleared) { - clear_advance(s); - s->dma_dac.endcleared = 1; + dmabuf->count -= diff; + if (dmabuf->count < 0 || dmabuf->count > dmabuf->dmasize) { + /* buffer underrun or buffer overrun, we have no way to recover + it here, just stop the machine and let the process force hwptr + and swptr to sync */ + __stop_dac(state); + dmabuf->error++; } - - if (s->dma_dac.count + (signed)s->dma_dac.fragsize <= (signed)s->dma_dac.dmasize) - wake_up(&s->dma_dac.wait); + /* since dma machine only interrupts at ESO and ESO/2, we sure have at + least half of dma buffer free, so wake up the process unconditionally */ + wake_up(&dmabuf->wait); } } } -/* - * Trident interrupt handlers. - */ static void trident_interrupt(int irq, void *dev_id, struct pt_regs *regs) { struct trident_state *state; @@ -1858,19 +1114,17 @@ static void trident_interrupt(int irq, void *dev_id, struct pt_regs *regs) if (event & ADDRESS_IRQ) { /* Update the pointers for all channels we are running. */ - /* FIXME: improve interrupt latency !!! */ - for (i = 0; i < NR_DSPS; i++) { - state = card->channels[i]; + /* FIXME: should read interrupt status only once */ + for (i = 0; i < NR_HW_CH; i++) { if (trident_check_channel_interrupt(card, 63 - i)) { trident_ack_channel_interrupt(card, 63 - i); - if (state != NULL) + if ((state = card->states[i]) != NULL) { trident_update_ptr(state); - else { - /* Spurious ? */ - printk("trident: spurious channel irq %d.\n", + } else { + printk("trident: spurious channel irq %d.\n", 63 - i); trident_stop_voice(card, i); - trident_disable_voice_irq(card, i); + trident_disable_voice_irq(card, i); } } } @@ -1886,254 +1140,29 @@ static void trident_interrupt(int irq, void *dev_id, struct pt_regs *regs) spin_unlock(&card->lock); } -static void set_mixer(struct trident_card *card,unsigned int mixer, unsigned int val ) -{ - unsigned int left,right; - - /* cleanse input a little */ - right = ((val >> 8) & 0xff) ; - left = (val & 0xff) ; - - if (right > 100) right = 100; - if (left > 100) left = 100; - - card->mix.mixer_state[mixer] = (right << 8) | left; - card->mix.write_mixer(card, mixer, left, right); -} - -static int mixer_ioctl(struct trident_card *card, unsigned int cmd, unsigned long arg) -{ - unsigned long flags; - int i, val = 0; - - VALIDATE_CARD(card); - if (cmd == SOUND_MIXER_INFO) { - mixer_info info; - strncpy(info.id, card->pci_info->name, sizeof(info.id)); - strncpy(info.name, card->pci_info->name, sizeof(info.name)); - info.modify_counter = card->mix.modcnt; - if (copy_to_user((void *)arg, &info, sizeof(info))) - return -EFAULT; - return 0; - } - if (cmd == SOUND_OLD_MIXER_INFO) { - _old_mixer_info info; - strncpy(info.id, card->pci_info->name, sizeof(info.id)); - strncpy(info.name, card->pci_info->name, sizeof(info.name)); - if (copy_to_user((void *)arg, &info, sizeof(info))) - return -EFAULT; - return 0; - } - - if (_IOC_TYPE(cmd) != 'M' || _IOC_SIZE(cmd) != sizeof(int)) - return -EINVAL; - - if (cmd == OSS_GETVERSION) - return put_user(SOUND_VERSION, (int *)arg); - - if (_IOC_DIR(cmd) == _IOC_READ) { - switch (_IOC_NR(cmd)) { - case SOUND_MIXER_RECSRC: /* give them the current record source */ - if (!card->mix.recmask_io) { - val = 0; - } else { - spin_lock_irqsave(&card->lock, flags); - val = card->mix.recmask_io(card,1,0); - spin_unlock_irqrestore(&card->lock, flags); - } - break; - - case SOUND_MIXER_DEVMASK: /* give them the supported mixers */ - val = card->mix.supported_mixers; - break; - - case SOUND_MIXER_RECMASK: /* Arg contains a bit for each supported recording source */ - val = card->mix.record_sources; - break; - - case SOUND_MIXER_STEREODEVS: /* Mixer channels supporting stereo */ - val = card->mix.stereo_mixers; - break; - - case SOUND_MIXER_CAPS: - val = SOUND_CAP_EXCL_INPUT; - break; - - default: /* read a specific mixer */ - i = _IOC_NR(cmd); - - if (!supported_mixer(card,i)) - return -EINVAL; - - /* do we ever want to touch the hardware? */ - /* spin_lock_irqsave(&s->lock, flags); - val = card->mix.read_mixer(card,i); - spin_unlock_irqrestore(&s->lock, flags);*/ - - val = card->mix.mixer_state[i]; - /* printk("returned 0x%x for mixer %d\n",val,i);*/ - break; - } - return put_user(val,(int *)arg); - } - - if (_IOC_DIR(cmd) == (_IOC_WRITE|_IOC_READ)) { - card->mix.modcnt++; - get_user_ret(val, (int *)arg, -EFAULT); - - switch (_IOC_NR(cmd)) { - case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ - if (!card->mix.recmask_io) return -EINVAL; - if (!(val &= card->mix.record_sources)) return -EINVAL; - - spin_lock_irqsave(&card->lock, flags); - card->mix.recmask_io(card, 0, val); - spin_unlock_irqrestore(&card->lock, flags); - - return 0; - default: /* write a specific mixer */ - i = _IOC_NR(cmd); - - if (!supported_mixer(card, i)) - return -EINVAL; - - spin_lock_irqsave(&card->lock, flags); - set_mixer(card, i, val); - spin_unlock_irqrestore(&card->lock, flags); - - return 0; - } - } - return -EINVAL; -} - -static int trident_open_mixdev(struct inode *inode, struct file *file) -{ - int minor = MINOR(inode->i_rdev); - struct trident_card *card = devs; - - while (card && card->dev_mixer != minor) - card = card->next; - if (!card) - return -ENODEV; - - file->private_data = card; - - //FIXME put back in - //MOD_INC_USE_COUNT; - return 0; -} - -static int trident_release_mixdev(struct inode *inode, struct file *file) -{ - struct trident_card *card = (struct trident_card *)file->private_data; - - VALIDATE_CARD(card); - - //FIXME put back in - //MOD_DEC_USE_COUNT; - return 0; -} - -static int trident_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, - unsigned long arg) -{ - struct trident_card *card = (struct trident_card *)file->private_data; - - VALIDATE_CARD(card); - return mixer_ioctl(card, cmd, arg); -} - static loff_t trident_llseek(struct file *file, loff_t offset, int origin) { return -ESPIPE; } -static /*const*/ struct file_operations trident_mixer_fops = { - &trident_llseek, - NULL, /* read */ - NULL, /* write */ - NULL, /* readdir */ - NULL, /* poll */ - &trident_ioctl_mixdev, - NULL, /* mmap */ - &trident_open_mixdev, - NULL, /* flush */ - &trident_release_mixdev, - NULL, /* fsync */ - NULL, /* fasync */ - NULL, /* lock */ -}; - -/* drain the DAC buffer - FIXME: This function will block (forever ??) when using - XMMS Qsound plugin and direct cat sample.wav > /dev/dsp - This behavior is when drain_dac is called by trident_release. */ -static int drain_dac(struct trident_state *s, int nonblock) -{ - DECLARE_WAITQUEUE(wait, current); - unsigned long flags; - int count; - signed long tmo; - - if (s->dma_dac.mapped || !s->dma_dac.ready) - return 0; - - current->state = TASK_INTERRUPTIBLE; - add_wait_queue(&s->dma_dac.wait, &wait); - for (;;) { - spin_lock_irqsave(&s->card->lock, flags); - count = s->dma_dac.count; - spin_unlock_irqrestore(&s->card->lock, flags); - - if (count <= 0) - break; - - if (signal_pending(current)) - break; - - if (nonblock) { - remove_wait_queue(&s->dma_dac.wait, &wait); - current->state = TASK_RUNNING; - return -EBUSY; - } - - tmo = (count * HZ) / s->ratedac; - tmo >>= sample_shift[(s->fmt >> TRIDENT_DAC_SHIFT) & TRIDENT_FMT_MASK]; - - /* XXX this is just broken. someone is waking us up alot, - or schedule_timeout is broken. - or something. who cares. - zach */ - if (!schedule_timeout(tmo ? tmo : 1) && tmo) - printk(KERN_ERR "trident: drain_dac, " - "dma timed out? jiffies = %ld\n", - jiffies); - } - remove_wait_queue(&s->dma_dac.wait, &wait); - current->state = TASK_RUNNING; - if (signal_pending(current)) - return -ERESTARTSYS; - - return 0; -} - /* in this loop, dma_adc.count signifies the amount of data thats waiting to be copied to the user's buffer. it is filled by the interrupt handler and drained by this loop. */ static ssize_t trident_read(struct file *file, char *buffer, size_t count, loff_t *ppos) { struct trident_state *state = (struct trident_state *)file->private_data; + struct dmabuf *dmabuf = &state->dma_dac; ssize_t ret; unsigned long flags; unsigned swptr; int cnt; - + VALIDATE_STATE(state); if (ppos != &file->f_pos) return -ESPIPE; - if (state->dma_adc.mapped) + if (dmabuf->mapped) return -ENXIO; - if (!state->dma_adc.ready && (ret = prog_dmabuf(state, 1))) + if (!dmabuf->ready && (ret = prog_dmabuf(state, 1))) return ret; if (!access_ok(VERIFY_WRITE, buffer, count)) return -EFAULT; @@ -2141,12 +1170,10 @@ static ssize_t trident_read(struct file *file, char *buffer, size_t count, loff_ while (count > 0) { spin_lock_irqsave(&state->card->lock, flags); - /* remember, all these things are expressed in bytes to be - sent to the user.. hence the evil / 2 down below */ - swptr = state->dma_adc.swptr; - cnt = state->dma_adc.dmasize - swptr; - if (state->dma_adc.count < cnt) - cnt = state->dma_adc.count; + swptr = dmabuf->swptr; + cnt = dmabuf->dmasize - swptr; + if (dmabuf->count < cnt) + cnt = dmabuf->count; spin_unlock_irqrestore(&state->card->lock, flags); if (cnt > count) @@ -2158,20 +1185,17 @@ static ssize_t trident_read(struct file *file, char *buffer, size_t count, loff_ ret = ret ? ret : -EAGAIN; return ret; } - if (!interruptible_sleep_on_timeout(&state->dma_adc.wait, HZ)) { - printk(KERN_DEBUG "(trident) read: chip lockup? " + if (!interruptible_sleep_on_timeout(&dmabuf->wait, HZ)) { + printk(KERN_ERR + "(trident) read: chip lockup? " "dmasz %u fragsz %u count %i hwptr %u swptr %u\n", - state->dma_adc.dmasize, - state->dma_adc.fragsize, - state->dma_adc.count, - state->dma_adc.hwptr, - state->dma_adc.swptr); + dmabuf->dmasize, dmabuf->fragsize, dmabuf->count, + dmabuf->hwptr, dmabuf->swptr); stop_adc(state); - spin_lock_irqsave(&state->card->lock, flags); - state->dma_adc.count = 0; - state->dma_adc.hwptr = 0; - state->dma_adc.swptr = 0; + dmabuf->count = 0; + dmabuf->hwptr = 0; + dmabuf->swptr = 0; spin_unlock_irqrestore(&state->card->lock, flags); } if (signal_pending(current)) { @@ -2180,17 +1204,19 @@ static ssize_t trident_read(struct file *file, char *buffer, size_t count, loff_ } continue; } - - if (copy_to_user(buffer, state->dma_adc.rawbuf + swptr, cnt)) { + + if (copy_to_user(buffer, dmabuf->rawbuf + swptr, cnt)) { ret = ret ? ret : -EFAULT; return ret; } - swptr = (swptr + cnt) % state->dma_adc.dmasize; + swptr = (swptr + cnt) % dmabuf->dmasize; + spin_lock_irqsave(&state->card->lock, flags); - state->dma_adc.swptr = swptr; - state->dma_adc.count -= cnt; + dmabuf->swptr = swptr; + dmabuf->count -= cnt; spin_unlock_irqrestore(&state->card->lock, flags); + count -= cnt; buffer += cnt; ret += cnt; @@ -2203,11 +1229,11 @@ static ssize_t trident_read(struct file *file, char *buffer, size_t count, loff_ static ssize_t trident_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) { struct trident_state *state = (struct trident_state *)file->private_data; + struct dmabuf *dmabuf = &state->dma_dac; ssize_t ret; unsigned long flags; unsigned swptr; int cnt; - int mode = (state->fmt >> TRIDENT_DAC_SHIFT) & TRIDENT_FMT_MASK; #ifdef DEBUG printk("trident: trident_write called, count = %d\n", count); @@ -2216,9 +1242,9 @@ static ssize_t trident_write(struct file *file, const char *buffer, size_t count VALIDATE_STATE(state); if (ppos != &file->f_pos) return -ESPIPE; - if (state->dma_dac.mapped) + if (dmabuf->mapped) return -ENXIO; - if (!state->dma_dac.ready && (ret = prog_dmabuf(state, 0))) + if (!dmabuf->ready && (ret = prog_dmabuf(state, 0))) return ret; if (!access_ok(VERIFY_READ, buffer, count)) return -EFAULT; @@ -2226,41 +1252,47 @@ static ssize_t trident_write(struct file *file, const char *buffer, size_t count while (count > 0) { spin_lock_irqsave(&state->card->lock, flags); - if (state->dma_dac.count < 0) { - state->dma_dac.count = 0; - state->dma_dac.swptr = state->dma_dac.hwptr; + if (dmabuf->count < 0) { + /* buffer underrun, we are recovering from sleep_on_timeout, + resync hwptr and swptr */ + dmabuf->count = 0; + dmabuf->swptr = dmabuf->hwptr; } - swptr = state->dma_dac.swptr; - cnt = state->dma_dac.dmasize - swptr; - if (state->dma_dac.count + cnt > state->dma_dac.dmasize) - cnt = state->dma_dac.dmasize - state->dma_dac.count; + swptr = dmabuf->swptr; + cnt = dmabuf->dmasize - swptr; + if (dmabuf->count + cnt > dmabuf->dmasize) + cnt = dmabuf->dmasize - dmabuf->count; spin_unlock_irqrestore(&state->card->lock, flags); if (cnt > count) cnt = count; if (cnt <= 0) { - /* buffer is full, wait for it to be played */ + unsigned long tmo; + /* buffer is full, start the dma machine and wait for data to be played */ start_dac(state); if (file->f_flags & O_NONBLOCK) { if (!ret) ret = -EAGAIN; return ret; } - if (!interruptible_sleep_on_timeout(&state->dma_dac.wait, HZ)) { - printk(KERN_ERR - "trident: write: chip lockup? " - "dmasz %u fragsz %u count %i " - "hwptr %u swptr %u\n", - state->dma_dac.dmasize, - state->dma_dac.fragsize, - state->dma_dac.count, - state->dma_dac.hwptr, - state->dma_dac.swptr); - stop_dac(state); - spin_lock_irqsave(&state->card->lock, flags); - state->dma_dac.count = 0; - state->dma_dac.hwptr = 0; - state->dma_dac.swptr = 0; - spin_unlock_irqrestore(&state->card->lock, flags); + /* No matter how much data left in the buffer, we have to wait untill + CSO == ESO/2 or CSO == ESO when address engine interrupts */ + tmo = (dmabuf->dmasize * HZ) / (dmabuf->rate * 2); + tmo >>= sample_shift[dmabuf->fmt]; + /* There are two situations when sleep_on_timeout returns, one is when the + interrupt is serviced correctly and the process is waked up by ISR ON TIME. + Another is when timeout is expired, which means that either interrupt is NOT + serviced correctly (pending interrupt) or it is TOO LATE for the process to + be scheduled to run (scheduler latency) which results in a (potential) buffer + underrun. And worse, there is NOTHING we can do to prevent it. */ + if (!interruptible_sleep_on_timeout(&dmabuf->wait, tmo)) { +#ifdef DEBUG + printk(KERN_ERR "trident: schedule timeout, " + "dmasz %u fragsz %u count %i hwptr %u swptr %u\n", + dmabuf->dmasize, dmabuf->fragsize, dmabuf->count, + dmabuf->hwptr, dmabuf->swptr); +#endif + /* a buffer underrun, we delay the recovery untill next time the + while loop begin and we REALLY have data to play */ } if (signal_pending(current)) { if (!ret) ret = -ERESTARTSYS; @@ -2268,18 +1300,17 @@ static ssize_t trident_write(struct file *file, const char *buffer, size_t count } continue; } - if (copy_from_user(state->dma_dac.rawbuf + swptr, buffer, cnt)) { - if (!ret) - ret = -EFAULT; + if (copy_from_user(dmabuf->rawbuf + swptr, buffer, cnt)) { + if (!ret) ret = -EFAULT; return ret; } - swptr = (swptr + cnt) % state->dma_dac.dmasize; + swptr = (swptr + cnt) % dmabuf->dmasize; spin_lock_irqsave(&state->card->lock, flags); - state->dma_dac.swptr = swptr; - state->dma_dac.count += cnt; - state->dma_dac.endcleared = 0; + dmabuf->swptr = swptr; + dmabuf->count += cnt; + dmabuf->endcleared = 0; spin_unlock_irqrestore(&state->card->lock, flags); count -= cnt; @@ -2304,7 +1335,6 @@ static unsigned int trident_poll(struct file *file, struct poll_table_struct *wa spin_lock_irqsave(&s->card->lock, flags); trident_update_ptr(s); - if (file->f_mode & FMODE_READ) { if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) mask |= POLLIN | POLLRDNORM; @@ -2319,56 +1349,56 @@ static unsigned int trident_poll(struct file *file, struct poll_table_struct *wa } } spin_unlock_irqrestore(&s->card->lock, flags); + return mask; } -/* this needs to be fixed to deal with the dual apus/buffers */ -#if 0 static int trident_mmap(struct file *file, struct vm_area_struct *vma) { - struct trident_state *s = (struct trident_state *)file->private_data; - struct dmabuf *db; + struct trident_state *state = (struct trident_state *)file->private_data; + struct dmabuf *dmabuf; int ret; unsigned long size; - VALIDATE_STATE(s); + VALIDATE_STATE(state); if (vma->vm_flags & VM_WRITE) { - if ((ret = prog_dmabuf(s, 0)) != 0) + if ((ret = prog_dmabuf(state, 0)) != 0) return ret; - db = &s->dma_dac; + dmabuf = &state->dma_dac; } else if (vma->vm_flags & VM_READ) { - if ((ret = prog_dmabuf(s, 1)) != 0) + if ((ret = prog_dmabuf(state, 1)) != 0) return ret; - db = &s->dma_adc; + dmabuf = &state->dma_adc; } else return -EINVAL; - if (vma->vm_offset != 0) + + if (vma->vm_pgoff != 0) return -EINVAL; size = vma->vm_end - vma->vm_start; - if (size > (PAGE_SIZE << db->buforder)) + if (size > (PAGE_SIZE << dmabuf->buforder)) return -EINVAL; - if (remap_page_range(vma->vm_start, virt_to_phys(db->rawbuf), size, vma->vm_page_prot)) + if (remap_page_range(vma->vm_start, virt_to_phys(dmabuf->rawbuf), + size, vma->vm_page_prot)) return -EAGAIN; - db->mapped = 1; + dmabuf->mapped = 1; + return 0; } -#endif static int trident_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { - struct trident_state *s = (struct trident_state *)file->private_data; + struct trident_state *state = (struct trident_state *)file->private_data; unsigned long flags; audio_buf_info abinfo; count_info cinfo; int val, mapped, ret; - unsigned char fmtm, fmtd; - VALIDATE_STATE(s); - mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac.mapped) || - ((file->f_mode & FMODE_READ) && s->dma_adc.mapped); + VALIDATE_STATE(state); + mapped = ((file->f_mode & FMODE_WRITE) && state->dma_dac.mapped) || + ((file->f_mode & FMODE_READ) && state->dma_adc.mapped); #ifdef DEBUG - printk("trident: trident_ioctl, command = %2d, arg = 0x%08x\n",_IOC_NR(cmd), - arg ? *(int *)arg : 0); + printk("trident: trident_ioctl, command = %2d, arg = 0x%08x\n", + _IOC_NR(cmd), arg ? *(int *)arg : 0); #endif switch (cmd) @@ -2378,189 +1408,218 @@ static int trident_ioctl(struct inode *inode, struct file *file, unsigned int cm case SNDCTL_DSP_RESET: if (file->f_mode & FMODE_WRITE) { - stop_dac(s); + stop_dac(state); synchronize_irq(); - s->dma_dac.swptr = s->dma_dac.hwptr = 0; - s->dma_dac.count = s->dma_dac.total_bytes = 0; + state->dma_dac.ready = 0; + state->dma_dac.swptr = state->dma_dac.hwptr = 0; + state->dma_dac.count = state->dma_dac.total_bytes = 0; } if (file->f_mode & FMODE_READ) { - stop_adc(s); + stop_adc(state); synchronize_irq(); - s->dma_adc.swptr = s->dma_adc.hwptr = 0; - s->dma_adc.count = s->dma_adc.total_bytes = 0; + state->dma_adc.ready = 0; + state->dma_adc.swptr = state->dma_adc.hwptr = 0; + state->dma_adc.count = state->dma_adc.total_bytes = 0; } return 0; case SNDCTL_DSP_SYNC: if (file->f_mode & FMODE_WRITE) - return drain_dac(s, file->f_flags & O_NONBLOCK); + return drain_dac(state, file->f_flags & O_NONBLOCK); return 0; - case SNDCTL_DSP_SPEED: + case SNDCTL_DSP_SPEED: /* set smaple rate */ get_user_ret(val, (int *)arg, -EFAULT); if (val >= 0) { - if (file->f_mode & FMODE_READ) { - stop_adc(s); - s->dma_adc.ready = 0; - trident_set_adc_rate(s, val, 1); - } if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - s->dma_dac.ready = 0; - trident_set_dac_rate(s, val, 1); + stop_dac(state); + state->dma_dac.ready = 0; + spin_lock_irqsave(&state->card->lock, flags); + trident_set_dac_rate(state, val); + spin_unlock_irqrestore(&state->card->lock, flags); + } + if (file->f_mode & FMODE_READ) { + stop_adc(state); + state->dma_adc.ready = 0; + spin_lock_irqsave(&state->card->lock, flags); + trident_set_adc_rate(state, val); + spin_unlock_irqrestore(&state->card->lock, flags); } } - return put_user((file->f_mode & FMODE_READ) ? s->rateadc : s->ratedac, + return put_user((file->f_mode & FMODE_READ) ? state->dma_adc.rate : + state->dma_dac.rate, (int *)arg); - case SNDCTL_DSP_STEREO: + case SNDCTL_DSP_STEREO: /* set stereo or mono channel */ get_user_ret(val, (int *)arg, -EFAULT); - fmtd = 0; - fmtm = ~0; - if (file->f_mode & FMODE_READ) { - stop_adc(s); - s->dma_adc.ready = 0; + if (file->f_mode & FMODE_WRITE) { + stop_dac(state); + state->dma_dac.ready = 0; if (val) - fmtd |= TRIDENT_FMT_STEREO << TRIDENT_ADC_SHIFT; + state->dma_dac.fmt |= TRIDENT_FMT_STEREO; else - fmtm &= ~(TRIDENT_FMT_STEREO << TRIDENT_ADC_SHIFT); + state->dma_dac.fmt &= ~TRIDENT_FMT_STEREO; } - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - s->dma_dac.ready = 0; + if (file->f_mode & FMODE_READ) { + stop_adc(state); + state->dma_adc.ready = 0; if (val) - fmtd |= TRIDENT_FMT_STEREO << TRIDENT_DAC_SHIFT; + state->dma_adc.fmt |= TRIDENT_FMT_STEREO; else - fmtm &= ~(TRIDENT_FMT_STEREO << TRIDENT_DAC_SHIFT); + state->dma_adc.fmt &= ~TRIDENT_FMT_STEREO; } - set_fmt(s, fmtm, fmtd); return 0; case SNDCTL_DSP_GETBLKSIZE: if (file->f_mode & FMODE_WRITE) { - if ((val = prog_dmabuf(s, 0))) + if ((val = prog_dmabuf(state, 0))) return val; - return put_user(s->dma_dac.fragsize, (int *)arg); + return put_user(state->dma_dac.fragsize, (int *)arg); + } + if (file->f_mode & FMODE_READ) { + if ((val = prog_dmabuf(state, 1))) + return val; + return put_user(state->dma_adc.fragsize, (int *)arg); } - if ((val = prog_dmabuf(s, 1))) - return val; - return put_user(s->dma_adc.fragsize, (int *)arg); - case SNDCTL_DSP_GETFMTS: /* Returns a mask */ - return put_user(AFMT_S8|AFMT_S16_LE, (int *)arg); - case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/ + case SNDCTL_DSP_GETFMTS: /* Returns a mask of supported sample format*/ + return put_user(AFMT_S16_LE|AFMT_U16_LE|AFMT_S8|AFMT_U8, (int *)arg); + + case SNDCTL_DSP_SETFMT: /* Select sample format */ get_user_ret(val, (int *)arg, -EFAULT); if (val != AFMT_QUERY) { - fmtd = 0; - fmtm = ~0; - if (file->f_mode & FMODE_READ) { - stop_adc(s); - s->dma_adc.ready = 0; - /* fixed at 16bit for now */ - fmtd |= TRIDENT_FMT_16BIT << TRIDENT_ADC_SHIFT; -#if 0 + if (file->f_mode & FMODE_WRITE) { + stop_dac(state); + state->dma_dac.ready = 0; if (val == AFMT_S16_LE) - fmtd |= TRIDENT_FMT_16BIT << TRIDENT_ADC_SHIFT; + state->dma_dac.fmt |= TRIDENT_FMT_16BIT; else - fmtm &= ~(TRIDENT_FMT_16BIT << TRIDENT_ADC_SHIFT); -#endif + state->dma_dac.fmt &= ~TRIDENT_FMT_16BIT; } - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - s->dma_dac.ready = 0; + if (file->f_mode & FMODE_READ) { + stop_adc(state); + state->dma_adc.ready = 0; if (val == AFMT_S16_LE) - fmtd |= TRIDENT_FMT_16BIT << TRIDENT_DAC_SHIFT; + state->dma_adc.fmt |= TRIDENT_FMT_16BIT; else - fmtm &= ~(TRIDENT_FMT_16BIT << TRIDENT_DAC_SHIFT); + state->dma_adc.fmt &= ~TRIDENT_FMT_16BIT; } - set_fmt(s, fmtm, fmtd); } - return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? - (TRIDENT_FMT_16BIT << TRIDENT_ADC_SHIFT) : - (TRIDENT_FMT_16BIT << TRIDENT_DAC_SHIFT))) ? - AFMT_S16_LE : AFMT_S8, (int *)arg); + if (file->f_mode & FMODE_WRITE) + return put_user((state->dma_dac.fmt & TRIDENT_FMT_16BIT) ? + AFMT_S16_LE : AFMT_U8, (int *)arg); + else + return put_user((state->dma_adc.fmt & TRIDENT_FMT_16BIT) ? + AFMT_S16_LE : AFMT_U8, (int *)arg); case SNDCTL_DSP_CHANNELS: get_user_ret(val, (int *)arg, -EFAULT); if (val != 0) { - fmtd = 0; - fmtm = ~0; - - if (file->f_mode & FMODE_READ) { - stop_adc(s); - s->dma_adc.ready = 0; + if (file->f_mode & FMODE_WRITE) { + stop_dac(state); + state->dma_dac.ready = 0; if (val >= 2) - fmtd |= TRIDENT_FMT_STEREO << TRIDENT_ADC_SHIFT; + state->dma_dac.fmt |= TRIDENT_FMT_STEREO; else - fmtm &= ~(TRIDENT_FMT_STEREO << TRIDENT_ADC_SHIFT); + state->dma_dac.fmt &= ~TRIDENT_FMT_STEREO; } - - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - s->dma_dac.ready = 0; + if (file->f_mode & FMODE_READ) { + stop_adc(state); + state->dma_adc.ready = 0; if (val >= 2) - fmtd |= TRIDENT_FMT_STEREO << TRIDENT_DAC_SHIFT; + state->dma_adc.fmt |= TRIDENT_FMT_STEREO; else - fmtm &= ~(TRIDENT_FMT_STEREO << TRIDENT_DAC_SHIFT); + state->dma_adc.fmt &= ~TRIDENT_FMT_STEREO; } - set_fmt(s, fmtm, fmtd); } - return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? - (TRIDENT_FMT_STEREO << TRIDENT_ADC_SHIFT) : - (TRIDENT_FMT_STEREO << TRIDENT_DAC_SHIFT))) ? 2 : 1, (int *)arg); + if (file->f_mode & FMODE_WRITE) + return put_user((state->dma_dac.fmt & TRIDENT_FMT_STEREO) ? 2 : 1, + (int *)arg); + else + return put_user((state->dma_adc.fmt & TRIDENT_FMT_STEREO) ? 2 : 1, + (int *)arg); case SNDCTL_DSP_POST: + /* FIXME: the same as RESET ?? */ return 0; case SNDCTL_DSP_SUBDIVIDE: - if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) || - (file->f_mode & FMODE_WRITE && s->dma_dac.subdivision)) + if ((file->f_mode & FMODE_READ && state->dma_adc.subdivision) || + (file->f_mode & FMODE_WRITE && state->dma_dac.subdivision)) return -EINVAL; get_user_ret(val, (int *)arg, -EFAULT); if (val != 1 && val != 2 && val != 4) return -EINVAL; if (file->f_mode & FMODE_READ) - s->dma_adc.subdivision = val; + state->dma_adc.subdivision = val; if (file->f_mode & FMODE_WRITE) - s->dma_dac.subdivision = val; + state->dma_dac.subdivision = val; return 0; case SNDCTL_DSP_SETFRAGMENT: get_user_ret(val, (int *)arg, -EFAULT); if (file->f_mode & FMODE_READ) { - s->dma_adc.ossfragshift = val & 0xffff; - s->dma_adc.ossmaxfrags = (val >> 16) & 0xffff; - if (s->dma_adc.ossfragshift < 4) - s->dma_adc.ossfragshift = 4; - if (s->dma_adc.ossfragshift > 15) - s->dma_adc.ossfragshift = 15; - if (s->dma_adc.ossmaxfrags < 4) - s->dma_adc.ossmaxfrags = 4; + state->dma_adc.ossfragshift = val & 0xffff; + state->dma_adc.ossmaxfrags = (val >> 16) & 0xffff; + if (state->dma_adc.ossfragshift < 4) + state->dma_adc.ossfragshift = 4; + if (state->dma_adc.ossfragshift > 15) + state->dma_adc.ossfragshift = 15; + if (state->dma_adc.ossmaxfrags < 4) + state->dma_adc.ossmaxfrags = 4; } if (file->f_mode & FMODE_WRITE) { - s->dma_dac.ossfragshift = val & 0xffff; - s->dma_dac.ossmaxfrags = (val >> 16) & 0xffff; - if (s->dma_dac.ossfragshift < 4) - s->dma_dac.ossfragshift = 4; - if (s->dma_dac.ossfragshift > 15) - s->dma_dac.ossfragshift = 15; - if (s->dma_dac.ossmaxfrags < 4) - s->dma_dac.ossmaxfrags = 4; + state->dma_dac.ossfragshift = val & 0xffff; + state->dma_dac.ossmaxfrags = (val >> 16) & 0xffff; + if (state->dma_dac.ossfragshift < 4) + state->dma_dac.ossfragshift = 4; + if (state->dma_dac.ossfragshift > 15) + state->dma_dac.ossfragshift = 15; + if (state->dma_dac.ossmaxfrags < 4) + state->dma_dac.ossmaxfrags = 4; } return 0; - case SNDCTL_DSP_GETCAPS: - return put_user(0/* DSP_CAP_DUPLEX | DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP */, - (int *)arg); - - case SNDCTL_DSP_SETDUPLEX: - /* XXX fix */ + case SNDCTL_DSP_GETOSPACE: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + if (!state->dma_dac.enable && (val = prog_dmabuf(state, 0)) != 0) + return val; + spin_lock_irqsave(&state->card->lock, flags); + trident_update_ptr(state); + abinfo.fragsize = state->dma_dac.fragsize; + abinfo.bytes = state->dma_dac.dmasize - state->dma_dac.count; + abinfo.fragstotal = state->dma_dac.numfrag; + abinfo.fragments = abinfo.bytes >> state->dma_dac.fragshift; + spin_unlock_irqrestore(&state->card->lock, flags); + return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_GETISPACE: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + if (!state->dma_adc.enable && (val = prog_dmabuf(state, 1)) != 0) + return val; + spin_lock_irqsave(&state->card->lock, flags); + trident_update_ptr(state); + abinfo.fragsize = state->dma_adc.fragsize; + abinfo.bytes = state->dma_adc.count; + abinfo.fragstotal = state->dma_adc.numfrag; + abinfo.fragments = abinfo.bytes >> state->dma_adc.fragshift; + spin_unlock_irqrestore(&state->card->lock, flags); + return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_NONBLOCK: + file->f_flags |= O_NONBLOCK; return 0; + case SNDCTL_DSP_GETCAPS: + return put_user(/* DSP_CAP_DUPLEX|*/DSP_CAP_REALTIME|DSP_CAP_TRIGGER|DSP_CAP_MMAP, + (int *)arg); + case SNDCTL_DSP_GETTRIGGER: val = 0; - if (file->f_mode & FMODE_READ && s->enable & TRIDENT_ENABLE_RE) + if (file->f_mode & FMODE_READ && state->dma_adc.enable) val |= PCM_ENABLE_INPUT; - if (file->f_mode & FMODE_WRITE && s->enable & TRIDENT_ENABLE_PE) + if (file->f_mode & FMODE_WRITE && state->dma_dac.enable) val |= PCM_ENABLE_OUTPUT; return put_user(val, (int *)arg); @@ -2568,106 +1627,86 @@ static int trident_ioctl(struct inode *inode, struct file *file, unsigned int cm get_user_ret(val, (int *)arg, -EFAULT); if (file->f_mode & FMODE_READ) { if (val & PCM_ENABLE_INPUT) { - if (!s->dma_adc.ready && (ret = prog_dmabuf(s, 1))) + if (!state->dma_adc.ready && (ret = prog_dmabuf(state, 1))) return ret; - start_adc(s); + start_adc(state); } else - stop_adc(s); + stop_adc(state); } if (file->f_mode & FMODE_WRITE) { if (val & PCM_ENABLE_OUTPUT) { - if (!s->dma_dac.ready && (ret = prog_dmabuf(s, 0))) + if (!state->dma_dac.ready && (ret = prog_dmabuf(state, 0))) return ret; - start_dac(s); + start_dac(state); } else - stop_dac(s); + stop_dac(state); } return 0; - case SNDCTL_DSP_GETOSPACE: - if (!(file->f_mode & FMODE_WRITE)) - return -EINVAL; - if (!(s->enable & TRIDENT_ENABLE_PE) && (val = prog_dmabuf(s, 0)) != 0) - return val; - spin_lock_irqsave(&s->card->lock, flags); - trident_update_ptr(s); - abinfo.fragsize = s->dma_dac.fragsize; - abinfo.bytes = s->dma_dac.dmasize - s->dma_dac.count; - abinfo.fragstotal = s->dma_dac.numfrag; - abinfo.fragments = abinfo.bytes >> s->dma_dac.fragshift; - spin_unlock_irqrestore(&s->card->lock, flags); - return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; - - case SNDCTL_DSP_GETISPACE: - if (!(file->f_mode & FMODE_READ)) - return -EINVAL; - if (!(s->enable & TRIDENT_ENABLE_RE) && (val = prog_dmabuf(s, 1)) != 0) - return val; - spin_lock_irqsave(&s->card->lock, flags); - trident_update_ptr(s); - abinfo.fragsize = s->dma_adc.fragsize; - abinfo.bytes = s->dma_adc.count; - abinfo.fragstotal = s->dma_adc.numfrag; - abinfo.fragments = abinfo.bytes >> s->dma_adc.fragshift; - spin_unlock_irqrestore(&s->card->lock, flags); - return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; - - case SNDCTL_DSP_NONBLOCK: - file->f_flags |= O_NONBLOCK; - return 0; - - case SNDCTL_DSP_GETODELAY: - if (!(file->f_mode & FMODE_WRITE)) - return -EINVAL; - spin_lock_irqsave(&s->card->lock, flags); - trident_update_ptr(s); - val = s->dma_dac.count; - spin_unlock_irqrestore(&s->card->lock, flags); - return put_user(val, (int *)arg); - case SNDCTL_DSP_GETIPTR: if (!(file->f_mode & FMODE_READ)) return -EINVAL; - spin_lock_irqsave(&s->card->lock, flags); - trident_update_ptr(s); - cinfo.bytes = s->dma_adc.total_bytes; - cinfo.blocks = s->dma_adc.count >> s->dma_adc.fragshift; - cinfo.ptr = s->dma_adc.hwptr; - if (s->dma_adc.mapped) - s->dma_adc.count &= s->dma_adc.fragsize-1; - spin_unlock_irqrestore(&s->card->lock, flags); + spin_lock_irqsave(&state->card->lock, flags); + trident_update_ptr(state); + cinfo.bytes = state->dma_adc.total_bytes; + cinfo.blocks = state->dma_adc.count >> state->dma_adc.fragshift; + cinfo.ptr = state->dma_adc.hwptr; + if (state->dma_adc.mapped) + state->dma_adc.count &= state->dma_adc.fragsize-1; + spin_unlock_irqrestore(&state->card->lock, flags); return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); case SNDCTL_DSP_GETOPTR: if (!(file->f_mode & FMODE_WRITE)) return -EINVAL; - spin_lock_irqsave(&s->card->lock, flags); - trident_update_ptr(s); - cinfo.bytes = s->dma_dac.total_bytes; - cinfo.blocks = s->dma_dac.count >> s->dma_dac.fragshift; - cinfo.ptr = s->dma_dac.hwptr; - if (s->dma_dac.mapped) - s->dma_dac.count &= s->dma_dac.fragsize-1; - spin_unlock_irqrestore(&s->card->lock, flags); + spin_lock_irqsave(&state->card->lock, flags); + trident_update_ptr(state); + cinfo.bytes = state->dma_dac.total_bytes; + cinfo.blocks = state->dma_dac.count >> state->dma_dac.fragshift; + cinfo.ptr = state->dma_dac.hwptr; + if (state->dma_dac.mapped) + state->dma_dac.count &= state->dma_dac.fragsize-1; + spin_unlock_irqrestore(&state->card->lock, flags); return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); + case SNDCTL_DSP_SETDUPLEX: + /* XXX fix */ + return 0; + + case SNDCTL_DSP_GETODELAY: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + spin_lock_irqsave(&state->card->lock, flags); + trident_update_ptr(state); + val = state->dma_dac.count; + spin_unlock_irqrestore(&state->card->lock, flags); + return put_user(val, (int *)arg); + case SOUND_PCM_READ_RATE: - return put_user((file->f_mode & FMODE_READ) ? s->rateadc : s->ratedac, (int *)arg); + return put_user((file->f_mode & FMODE_READ) ? state->dma_adc.rate : + state->dma_dac.rate, (int *)arg); case SOUND_PCM_READ_CHANNELS: - return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? - (TRIDENT_FMT_STEREO << TRIDENT_ADC_SHIFT) : - (TRIDENT_FMT_STEREO << TRIDENT_DAC_SHIFT))) ? 2 : 1, (int *)arg); + if (file->f_mode & FMODE_WRITE) + return put_user((state->dma_dac.fmt & TRIDENT_FMT_STEREO) ? 2 : 1, + (int *)arg); + else + return put_user((state->dma_adc.fmt & TRIDENT_FMT_STEREO) ? 2 : 1, + (int *)arg); case SOUND_PCM_READ_BITS: - return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? - (TRIDENT_FMT_16BIT << TRIDENT_ADC_SHIFT) : - (TRIDENT_FMT_16BIT << TRIDENT_DAC_SHIFT))) ? 16 : 8, (int *)arg); - - case SOUND_PCM_WRITE_FILTER: + if (file->f_mode & FMODE_WRITE) + return put_user((state->dma_dac.fmt & TRIDENT_FMT_16BIT) ? + AFMT_S16_LE : AFMT_U8, (int *)arg); + else + return put_user((state->dma_adc.fmt & TRIDENT_FMT_16BIT) ? + AFMT_S16_LE : AFMT_U8, (int *)arg); + case SNDCTL_DSP_MAPINBUF: + case SNDCTL_DSP_MAPOUTBUF: case SNDCTL_DSP_SETSYNCRO: + case SOUND_PCM_WRITE_FILTER: case SOUND_PCM_READ_FILTER: return -EINVAL; @@ -2681,13 +1720,12 @@ static int trident_open(struct inode *inode, struct file *file) int minor = MINOR(inode->i_rdev); struct trident_card *card = devs; struct trident_state *state = NULL; - unsigned char fmtm = ~0, fmts = 0; /* find an avaiable virtual channel (instance of /dev/dsp) */ while (card != NULL) { - for (i = 0; i < NR_DSPS; i++) { - if (card->channels[i] == NULL) { - state = card->channels[i] = (struct trident_state *) + for (i = 0; i < NR_HW_CH; i++) { + if (card->states[i] == NULL) { + state = card->states[i] = (struct trident_state *) kmalloc(sizeof(struct trident_state), GFP_KERNEL); if (state == NULL) return -ENOMEM; @@ -2704,18 +1742,18 @@ static int trident_open(struct inode *inode, struct file *file) found_virt: /* found a free virtual channel, allocate hardware channels */ if (file->f_mode & FMODE_READ) - if ((state->dma_adc.chan = trident_alloc_pcm_channel(card)) == -1) { - kfree (card->channels[i]); - card->channels[i] = NULL;; + if ((state->dma_adc.channel = trident_alloc_pcm_channel(card)) == NULL) { + kfree (card->states[i]); + card->states[i] = NULL;; return -ENODEV; } if (file->f_mode & FMODE_WRITE) - if ((state->dma_dac.chan = trident_alloc_pcm_channel(card)) == -1) { - kfree (card->channels[i]); - card->channels[i] = NULL; + if ((state->dma_dac.channel = trident_alloc_pcm_channel(card)) == NULL) { + kfree (card->states[i]); + card->states[i] = NULL; if (file->f_mode & FMODE_READ) /* free previously allocated hardware channel */ - trident_free_pcm_channel(card, state->dma_adc.chan); + trident_free_pcm_channel(card, state->dma_adc.channel->num); return -ENODEV; } @@ -2732,30 +1770,27 @@ static int trident_open(struct inode *inode, struct file *file) /* set default sample format, Refer to OSS Programmer's Guide */ if (file->f_mode & FMODE_READ) { - /* fmtm &= ~((TRIDENT_FMT_STEREO | TRIDENT_FMT_16BIT) << TRIDENT_ADC_SHIFT); - if ((minor & 0xf) == SND_DEV_DSP16) - fmts |= TRIDENT_FMT_16BIT << TRIDENT_ADC_SHIFT; */ - fmtm = (TRIDENT_FMT_STEREO|TRIDENT_FMT_16BIT) << TRIDENT_ADC_SHIFT; + /* FIXME: Trident 4d can only record in singed 16-bits stereo, 48kHz sample */ + state->dma_adc.fmt = TRIDENT_FMT_STEREO|TRIDENT_FMT_16BIT; state->dma_adc.ossfragshift = 0; state->dma_adc.ossmaxfrags = 0; state->dma_adc.subdivision = 0; - trident_set_adc_rate(state, 8000, 0); + trident_set_adc_rate(state, 48000); } /* according to OSS document, /dev/dsp should be default to unsigned 8-bits, mono, with sample rate 8kHz and /dev/dspW will accept 16-bits sample */ if (file->f_mode & FMODE_WRITE) { - fmtm &= ~((TRIDENT_FMT_STEREO | TRIDENT_FMT_16BIT) << TRIDENT_DAC_SHIFT); + state->dma_dac.fmt &= ~TRIDENT_FMT_MASK; if ((minor & 0xf) == SND_DEV_DSP16) - fmts |= TRIDENT_FMT_16BIT << TRIDENT_DAC_SHIFT; + state->dma_dac.fmt |= TRIDENT_FMT_16BIT; state->dma_dac.ossfragshift = 0; state->dma_dac.ossmaxfrags = 0; state->dma_dac.subdivision = 0; - trident_set_dac_rate(state, 8000, 1); + trident_set_dac_rate(state, 8000); } - set_fmt(state, fmtm, fmts); - state->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); + state->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); up(&state->open_sem); //FIXME put back in @@ -2768,8 +1803,12 @@ static int trident_release(struct inode *inode, struct file *file) struct trident_state *state = (struct trident_state *)file->private_data; VALIDATE_STATE(state); - if (file->f_mode & FMODE_WRITE) + + + if (file->f_mode & FMODE_WRITE) { + trident_clear_tail(state); drain_dac(state, file->f_flags & O_NONBLOCK); + } /* stop DMA state machine and free DMA buffers/channels */ down(&state->open_sem); @@ -2777,16 +1816,16 @@ static int trident_release(struct inode *inode, struct file *file) if (file->f_mode & FMODE_WRITE) { stop_dac(state); dealloc_dmabuf(&state->dma_dac); - trident_free_pcm_channel(state->card, state->dma_dac.chan); + trident_free_pcm_channel(state->card, state->dma_dac.channel->num); } if (file->f_mode & FMODE_READ) { stop_adc(state); dealloc_dmabuf(&state->dma_adc); - trident_free_pcm_channel(state->card, state->dma_adc.chan); + trident_free_pcm_channel(state->card, state->dma_adc.channel->num); } - kfree(state->card->channels[state->virt]); - state->card->channels[state->virt] = NULL; + kfree(state->card->states[state->virt]); + state->card->states[state->virt] = NULL; state->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE); /* we're covered by the open_sem */ @@ -2796,6 +1835,7 @@ static int trident_release(struct inode *inode, struct file *file) //MOD_DEC_USE_COUNT; return 0; } + static /*const*/ struct file_operations trident_audio_fops = { &trident_llseek, &trident_read, @@ -2803,7 +1843,7 @@ static /*const*/ struct file_operations trident_audio_fops = { NULL, /* readdir */ &trident_poll, &trident_ioctl, - NULL, /* XXX &trident_mmap, */ + &trident_mmap, &trident_open, NULL, /* flush */ &trident_release, @@ -2812,15 +1852,238 @@ static /*const*/ struct file_operations trident_audio_fops = { NULL, /* lock */ }; -/* install the driver, we do not allocate hardware channel nor DMA buffer now, they are defered - untill open time */ -static int trident_install(struct pci_dev *pcidev, struct pci_audio_info *pci_info) +/* trident specific AC97 functions */ +/* Write AC97 mixer registers */ +static void trident_ac97_set(struct ac97_codec *codec, u8 reg, u16 val) +{ + struct trident_card *card = (struct trident_card *)codec->private_data; + unsigned int address, mask, busy; + unsigned short count = 0xffff; + unsigned long flags; + u32 data; + + data = ((u32) val) << 16; + + switch (card->pci_id) + { + default: + case PCI_DEVICE_ID_SI_7018: + address = SI_AC97_WRITE; + mask = SI_AC97_BUSY_WRITE | SI_AC97_AUDIO_BUSY; + if (codec->id) + mask |= SI_AC97_SECONDARY; + busy = SI_AC97_BUSY_WRITE; + break; + case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX: + address = DX_ACR0_AC97_W; + mask = busy = DX_AC97_BUSY_WRITE; + break; + case PCI_DEVICE_ID_TRIDENT_4DWAVE_NX: + address = NX_ACR1_AC97_W; + mask = NX_AC97_BUSY_WRITE; + if (codec->id) + mask |= NX_AC97_WRITE_SECONDARY; + busy = NX_AC97_BUSY_WRITE; + break; + } + + spin_lock_irqsave(&card->lock, flags); + do { + if ((inw(TRID_REG(card, address)) & busy) == 0) + break; + } while (count--); + + + data |= (mask | (reg & AC97_REG_ADDR)); + + if (count == 0) { + printk(KERN_ERR "trident: AC97 CODEC write timed out.\n"); + spin_unlock_irqrestore(&card->lock, flags); + return; + } + + outl(data, TRID_REG(card, address)); + spin_unlock_irqrestore(&card->lock, flags); +} + +/* Read AC97 codec registers */ +static u16 trident_ac97_get(struct ac97_codec *codec, u8 reg) +{ + struct trident_card *card = (struct trident_card *)codec->private_data; + unsigned int address, mask, busy; + unsigned short count = 0xffff; + unsigned long flags; + u32 data; + + switch (card->pci_id) + { + default: + case PCI_DEVICE_ID_SI_7018: + address = SI_AC97_READ; + mask = SI_AC97_BUSY_READ | SI_AC97_AUDIO_BUSY; + if (codec->id) + mask |= SI_AC97_SECONDARY; + busy = SI_AC97_BUSY_READ; + break; + case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX: + address = DX_ACR1_AC97_R; + mask = busy = DX_AC97_BUSY_READ; + break; + case PCI_DEVICE_ID_TRIDENT_4DWAVE_NX: + if (codec->id) + address = NX_ACR3_AC97_R_SECONDARY; + else + address = NX_ACR2_AC97_R_PRIMARY; + mask = NX_AC97_BUSY_READ; + busy = 0x0c00; + break; + } + + data = (mask | (reg & AC97_REG_ADDR)); + + spin_lock_irqsave(&card->lock, flags); + outl(data, TRID_REG(card, address)); + do { + data = inl(TRID_REG(card, address)); + if ((data & busy) == 0) + break; + } while (count--); + spin_unlock_irqrestore(&card->lock, flags); + + if (count == 0) { + printk(KERN_ERR "trident: AC97 CODEC read timed out.\n"); + data = 0; + } + return ((u16) (data >> 16)); +} + +/* OSS /dev/mixer file operation methods */ +static int trident_open_mixdev(struct inode *inode, struct file *file) { int i; + int minor = MINOR(inode->i_rdev); + struct trident_card *card = devs; + + for (card = devs; card != NULL; card = card->next) + for (i = 0; i < NR_AC97; i++) + if (card->ac97_codec[i] != NULL && + card->ac97_codec[i]->dev_mixer == minor) + goto match; + + if (!card) + return -ENODEV; + + match: + file->private_data = card->ac97_codec[i]; + + //FIXME put back in + //MOD_INC_USE_COUNT; + return 0; +} + +static int trident_release_mixdev(struct inode *inode, struct file *file) +{ + //struct ac97_codec *codec = (struct ac97_codec *)file->private_data; + + //FIXME put back in + //MOD_DEC_USE_COUNT; + return 0; +} + +static int trident_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct ac97_codec *codec = (struct ac97_codec *)file->private_data; + + return codec->mixer_ioctl(codec, cmd, arg); +} + +static /*const*/ struct file_operations trident_mixer_fops = { + &trident_llseek, + NULL, /* read */ + NULL, /* write */ + NULL, /* readdir */ + NULL, /* poll */ + &trident_ioctl_mixdev, + NULL, /* mmap */ + &trident_open_mixdev, + NULL, /* flush */ + &trident_release_mixdev, + NULL, /* fsync */ + NULL, /* fasync */ + NULL, /* lock */ +}; + +/* AC97 codec initialisation. */ +static int __init trident_ac97_init(struct trident_card *card) +{ + int num_ac97 = 0; + int ready_2nd = 0; + struct ac97_codec *codec; + + /* initialize controller side of AC link, and find out if secondary codes + really exist */ + switch (card->pci_id) + { + case PCI_DEVICE_ID_SI_7018: + /* disable AC97 GPIO interrupt */ + outl(0x00, TRID_REG(card, SI_AC97_GPIO)); + /* stop AC97 cold reset process */ + outl(PCMOUT|SECONDARY_ID, TRID_REG(card, SI_SERIAL_INTF_CTRL)); + ready_2nd = inl(TRID_REG(card, SI_SERIAL_INTF_CTRL)); + ready_2nd &= SI_AC97_SECONDARY_READY; + break; + case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX: + /* playback on */ + outl(DX_AC97_PLAYBACK, TRID_REG(card, DX_ACR2_AC97_COM_STAT)); + break; + case PCI_DEVICE_ID_TRIDENT_4DWAVE_NX: + /* enable AC97 Output Slot 3,4 (PCM Left/Right Playback) */ + outl(NX_AC97_PCM_OUTPUT, TRID_REG(card, NX_ACR0_AC97_COM_STAT)); + ready_2nd = inl(TRID_REG(card, NX_ACR0_AC97_COM_STAT)); + ready_2nd &= NX_AC97_SECONDARY_READY; + break; + } + + for (num_ac97 = 0; num_ac97 < NR_AC97; num_ac97++) { + if ((codec = kmalloc(sizeof(struct ac97_codec), GFP_KERNEL)) == NULL) + return -1; + memset(codec, 0, sizeof(struct ac97_codec)); + + /* initialize some basic codec information, other fields will be filled + in ac97_probe_codec */ + codec->private_data = card; + codec->id = num_ac97; + /* controller specific low level AC97 access function */ + codec->codec_read = trident_ac97_get; + codec->codec_write = trident_ac97_set; + + if (ac97_probe_codec(codec) == 0) + break; + + if ((codec->dev_mixer = register_sound_mixer(&trident_mixer_fops, -1)) < 0) { + printk(KERN_ERR "trident: couldn't register mixer!\n"); + kfree(codec); + break; + } + + card->ac97_codec[num_ac97] = codec; + + /* if there is no secondary codec at all, don't probe any more */ + if (!ready_2nd) + return num_ac97+1; + } + + return num_ac97; +} + +/* install the driver, we do not allocate hardware channel nor DMA buffer now, they are defered + untill "ACCESS" time (in prog_dmabuf calles by open/read/write/ioctl/mmap) */ +static int __init trident_install(struct pci_dev *pcidev, struct pci_audio_info *pci_info) +{ u16 w; unsigned long iobase; struct trident_card *card; - u32 ChanDwordCount; iobase = pcidev->resource[0].start; if (check_region(iobase, 256)) { @@ -2847,30 +2110,12 @@ static int trident_install(struct pci_dev *pcidev, struct pci_audio_info *pci_in card->irq = pcidev->irq; card->next = devs; card->magic = TRIDENT_CARD_MAGIC; + card->banks[BANK_A].addresses = &bank_a_addrs; + card->banks[BANK_A].bitmap = 0UL; + card->banks[BANK_B].addresses = &bank_b_addrs; + card->banks[BANK_B].bitmap = 0UL; spin_lock_init(&card->lock); - devs = card; - - /* ungly stupid thing, remove ASAP */ - ChanDwordCount = card->ChanDwordCount = 2; - card->ChRegs.lpChStart = card->ChRegs.data; - card->ChRegs.lpChStop = card->ChRegs.lpChStart + ChanDwordCount; - card->ChRegs.lpChAint = card->ChRegs.lpChStop + ChanDwordCount; - card->ChRegs.lpChAinten = card->ChRegs.lpChAint + ChanDwordCount; - card->ChRegs.lpAChStart = card->ChRegs.lpChAinten + ChanDwordCount; - card->ChRegs.lpAChStop = card->ChRegs.lpAChStart + ChanDwordCount; - card->ChRegs.lpAChAint = card->ChRegs.lpAChStop + ChanDwordCount; - card->ChRegs.lpAChAinten = card->ChRegs.lpAChAint + ChanDwordCount; - // Assign Bank A addresses. - card->ChRegs.lpAChStart[0] = T4D_START_A; - card->ChRegs.lpAChStop[0] = T4D_STOP_A; - card->ChRegs.lpAChAint[0] = T4D_AINT_A; - card->ChRegs.lpAChAinten[0] = T4D_AINTEN_A; - /* Assign Bank B addresses */ - card->ChRegs.lpAChStart[1] = T4D_START_B; - card->ChRegs.lpAChStop[1] = T4D_STOP_B; - card->ChRegs.lpAChAint[1] = T4D_AINT_B; - card->ChRegs.lpAChAinten[1] = T4D_AINTEN_B; - + devs = card; printk(KERN_INFO "trident: %s found at IO 0x%04lx, IRQ %d\n", card->pci_info->name, card->iobase, card->irq); @@ -2883,11 +2128,6 @@ static int trident_install(struct pci_dev *pcidev, struct pci_audio_info *pci_in kfree(card); return 0; } - - /* initilize AC97 codec */ - trident_ac97_init(card); - outl(0x00, TRID_REG(card, T4D_MUSICVOL_WAVEVOL)); - /* register /dev/dsp */ if ((card->dev_audio = register_sound_dsp(&trident_audio_fops, -1)) < 0) { printk(KERN_ERR "trident: coundn't register DSP device!\n"); @@ -2896,29 +2136,18 @@ static int trident_install(struct pci_dev *pcidev, struct pci_audio_info *pci_in kfree(card); return 0; } - /* register /dev/mixer */ - if ((card->dev_mixer = register_sound_mixer(&trident_mixer_fops, -1)) < 0) { - printk(KERN_ERR "trident: couldn't register mixer!\n"); + /* initilize AC97 codec and register /dev/mixer */ + if (trident_ac97_init(card) <= 0) { unregister_sound_dsp(card->dev_audio); release_region(iobase, 256); free_irq(card->irq, card); kfree(card); return 0; - } else { - /* initilize mixer channels */ - for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { - struct mixer_defaults *md = &mixer_defaults[i]; - if (md->mixer == -1) - break; - if (!supported_mixer(card, md->mixer)) - continue; - set_mixer(card, md->mixer, md->value); - } } + outl(0x00, TRID_REG(card, T4D_MUSICVOL_WAVEVOL)); /* Enable Address Engine Interrupts */ - trident_enable_end_interrupts(card); - trident_enable_middle_interrupts(card); + trident_enable_loop_interrupts(card); return 1; } @@ -2952,23 +2181,23 @@ static int __init init_trident(void) MODULE_AUTHOR("Alan Cox, Aaron Holtzman, Ollie Lho"); MODULE_DESCRIPTION("Trident 4DWave/SiS 7018 PCI Audio Driver"); -#ifdef DEBUG -MODULE_PARM(debug,"i"); -#endif - static void __exit cleanup_trident(void) { while (devs != NULL) { + int i; /* Kill interrupts, and SP/DIF */ - trident_disable_end_interrupts(devs); - trident_enable_middle_interrupts(devs); + trident_disable_loop_interrupts(devs); /* free hardware resources */ free_irq(devs->irq, devs); release_region(devs->iobase, 256); /* unregister audio devices */ - unregister_sound_mixer(devs->dev_mixer); + for (i = 0; i < NR_AC97; i++) + if (devs->ac97_codec[i] != NULL) { + unregister_sound_mixer(devs->ac97_codec[i]->dev_mixer); + kfree (devs->ac97_codec[i]); + } unregister_sound_dsp(devs->dev_audio); kfree(devs); @@ -2978,5 +2207,3 @@ static void __exit cleanup_trident(void) module_init(init_trident); module_exit(cleanup_trident); - - diff --git a/drivers/sound/trident.h b/drivers/sound/trident.h index a22e60650..4504cb303 100644 --- a/drivers/sound/trident.h +++ b/drivers/sound/trident.h @@ -23,6 +23,7 @@ * */ +/* PCI vendor and device ID */ #ifndef PCI_VENDOR_ID_TRIDENT #define PCI_VENDOR_ID_TRIDENT 0x1023 #endif @@ -43,118 +44,101 @@ #define PCI_DEVICE_ID_SI_7018 0x7018 #endif -/* - * Direct registers - */ - #ifndef FALSE -#define FALSE 0 -#define TRUE 1 +#define FALSE 0 +#define TRUE 1 #endif -#define TRID_REG( trident, x ) ( (trident) -> iobase + (x) ) - #define CHANNEL_REGS 5 #define CHANNEL_START 0xe0 // The first bytes of the contiguous register space. -#define BANK_A 0 -#define BANK_B 1 -#define NUM_BANKS 2 - -#define ID_4DWAVE_DX 0x2000 -#define ID_4DWAVE_NX 0x2001 -#define ID_SI_7018 0x7018 - -// Register definitions - -// Global registers - -// T2 legacy dma control registers. -#define LEGACY_DMAR0 0x00 // ADR0 -#define LEGACY_DMAR4 0x04 // CNT0 -#define LEGACY_DMAR11 0x0b // MOD -#define LEGACY_DMAR15 0x0f // MMR - -#define T4D_START_A 0x80 -#define T4D_STOP_A 0x84 -#define T4D_DLY_A 0x88 -#define T4D_SIGN_CSO_A 0x8c -#define T4D_CSPF_A 0x90 -#define T4D_CEBC_A 0x94 -#define T4D_AINT_A 0x98 -#define T4D_EINT_A 0x9c -#define T4D_LFO_GC_CIR 0xa0 -#define T4D_AINTEN_A 0xa4 -#define T4D_MUSICVOL_WAVEVOL 0xa8 -#define T4D_SBDELTA_DELTA_R 0xac -#define T4D_MISCINT 0xb0 -#define T4D_START_B 0xb4 -#define T4D_STOP_B 0xb8 -#define T4D_CSPF_B 0xbc -#define T4D_SBBL_SBCL 0xc0 -#define T4D_SBCTRL_SBE2R_SBDD 0xc4 -#define T4D_STIMER 0xc8 -#define T4D_LFO_B_I2S_DELTA 0xcc -#define T4D_AINT_B 0xd8 -#define T4D_AINTEN_B 0xdc - -// MPU-401 UART -#define T4D_MPU401_BASE 0x20 -#define T4D_MPUR0 0x20 -#define T4D_MPUR1 0x21 -#define T4D_MPUR2 0x22 -#define T4D_MPUR3 0x23 - -// S/PDIF Registers -#define NX_SPCTRL_SPCSO 0x24 -#define NX_SPLBA 0x28 -#define NX_SPESO 0x2c -#define NX_SPCSTATUS 0x64 - -// Channel Registers - -#define CH_DX_CSO_ALPHA_FMS 0xe0 -#define CH_DX_ESO_DELTA 0xe8 -#define CH_DX_FMC_RVOL_CVOL 0xec - -#define CH_NX_DELTA_CSO 0xe0 -#define CH_NX_DELTA_ESO 0xe8 -#define CH_NX_ALPHA_FMS_FMC_RVOL_CVOL 0xec - -#define CH_LBA 0xe4 -#define CH_GVSEL_PAN_VOL_CTRL_EC 0xf0 - -// AC-97 Registers - -#define DX_ACR0_AC97_W 0x40 -#define DX_ACR1_AC97_R 0x44 -#define DX_ACR2_AC97_COM_STAT 0x48 - -#define NX_ACR0_AC97_COM_STAT 0x40 -#define NX_ACR1_AC97_W 0x44 -#define NX_ACR2_AC97_R_PRIMARY 0x48 -#define NX_ACR3_AC97_R_SECONDARY 0x4c - -#define SI_AC97_WRITE 0x40 -#define SI_AC97_READ 0x44 -#define SI_SERIAL_INTF_CTRL 0x48 -#define SI_AC97_GPIO 0x4c +#define BANK_A 0 +#define BANK_B 1 +#define NR_BANKS 2 + +#define TRIDENT_FMT_STEREO 0x01 +#define TRIDENT_FMT_16BIT 0x02 +#define TRIDENT_FMT_MASK 0x03 + +#define DMA_ENABLE 0x01 +#define DMA_RUNNING 0x02 + +/* Register Addresses */ + +/* operational registers common to DX, NX, 7018 */ +enum trident_op_registers { + T4D_START_A = 0x80, T4D_STOP_A = 0x84, + T4D_DLY_A = 0x88, T4D_SIGN_CSO_A = 0x8c, + T4D_CSPF_A = 0x90, T4D_CEBC_A = 0x94, + T4D_AINT_A = 0x98, T4D_EINT_A = 0x9c, + T4D_LFO_GC_CIR = 0xa0, T4D_AINTEN_A = 0xa4, + T4D_MUSICVOL_WAVEVOL = 0xa8, T4D_SBDELTA_DELTA_R = 0xac, + T4D_MISCINT = 0xb0, T4D_START_B = 0xb4, + T4D_STOP_B = 0xb8, T4D_CSPF_B = 0xbc, + T4D_SBBL_SBCL = 0xc0, T4D_SBCTRL_SBE2R_SBDD = 0xc4, + T4D_STIMER = 0xc8, T4D_LFO_B_I2S_DELTA = 0xcc, + T4D_AINT_B = 0xd8, T4D_AINTEN_B = 0xdc +}; -#define AC97_SIGMATEL_DAC2INVERT 0x6E -#define AC97_SIGMATEL_BIAS1 0x70 -#define AC97_SIGMATEL_BIAS2 0x72 -#define AC97_SIGMATEL_CIC1 0x76 -#define AC97_SIGMATEL_CIC2 0x78 +/* S/PDIF Operational Registers for 4D-NX */ +enum nx_spdif_registers { + NX_SPCTRL_SPCSO = 0x24, NX_SPLBA = 0x28, + NX_SPESO = 0x2c, NX_SPCSTATUS = 0x64 +}; + +/* OP registers to access each hardware channel */ +enum channel_registers { + CH_DX_CSO_ALPHA_FMS = 0xe0, CH_DX_ESO_DELTA = 0xe8, + CH_DX_FMC_RVOL_CVOL = 0xec, + CH_NX_DELTA_CSO = 0xe0, CH_NX_DELTA_ESO = 0xe8, + CH_NX_ALPHA_FMS_FMC_RVOL_CVOL = 0xec, + CH_LBA = 0xe4, + CH_GVSEL_PAN_VOL_CTRL_EC = 0xf0 +}; + +/* registers to read/write/control AC97 codec */ +enum dx_ac97_registers { + DX_ACR0_AC97_W = 0x40, DX_ACR1_AC97_R = 0x44, + DX_ACR2_AC97_COM_STAT = 0x48 +}; -#define SI_AC97_BUSY_WRITE 0x8000 -#define SI_AC97_AUDIO_BUSY 0x4000 -#define DX_AC97_BUSY_WRITE 0x8000 -#define NX_AC97_BUSY_WRITE 0x0800 -#define SI_AC97_BUSY_READ 0x8000 -#define DX_AC97_BUSY_READ 0x8000 -#define NX_AC97_BUSY_READ 0x0800 +enum nx_ac97_registers { + NX_ACR0_AC97_COM_STAT = 0x40, NX_ACR1_AC97_W = 0x44, + NX_ACR2_AC97_R_PRIMARY = 0x48, NX_ACR3_AC97_R_SECONDARY = 0x4c +}; + +enum si_ac97_registers { + SI_AC97_WRITE = 0x40, SI_AC97_READ = 0x44, + SI_SERIAL_INTF_CTRL = 0x48, SI_AC97_GPIO = 0x4c +}; + +/* Bit mask for operational registers */ #define AC97_REG_ADDR 0x000000ff +enum sis7018_ac97_bits { + SI_AC97_BUSY_WRITE = 0x8000, SI_AC97_BUSY_READ = 0x8000, + SI_AC97_AUDIO_BUSY = 0x4000, SI_AC97_MODEM_BUSY = 0x2000, + SI_AC97_SECONDARY = 0x0080 +}; + +enum trident_dx_ac97_bits { + DX_AC97_BUSY_WRITE = 0x8000, DX_AC97_BUSY_READ = 0x8000, + DX_AC97_READY = 0x0010, DX_AC97_RECORD = 0x0008, + DX_AC97_PLAYBACK = 0x0002 +}; + +enum trident_nx_ac97_bits { + /* ACR1-3 */ + NX_AC97_BUSY_WRITE = 0x0800, NX_AC97_BUSY_READ = 0x0800, + NX_AC97_WRITE_SECONDARY = 0x0100, + /* ACR0 */ + NX_AC97_SECONDARY_READY = 0x0040, NX_AC97_SECONDARY_RECORD = 0x0020, + NX_AC97_SURROUND_OUTPUT = 0x0010, + NX_AC97_PRIMARY_READY = 0x0008, NX_AC97_PRIMARY_RECORD = 0x0004, + NX_AC97_PCM_OUTPUT = 0x0002, + NX_AC97_WARM_RESET = 0x0001 +}; + enum serial_intf_ctrl_bits { WARM_REST = 0x00000001, COLD_RESET = 0x00000002, I2S_CLOCK = 0x00000004, PCM_SEC_AC97= 0x00000008, @@ -162,6 +146,16 @@ enum serial_intf_ctrl_bits { I2S_OUTPUT_EN = 0x00000040, I2S_INPUT_EN = 0x00000080, PCMIN = 0x00000100, LINE1IN = 0x00000200, MICIN = 0x00000400, LINE2IN = 0x00000800, + HEAD_SET_IN = 0x00001000, GPIOIN = 0x00002000, + /* 7018 spec says id = 01 but the demo board routed to 10 + SECONDARY_ID= 0x00008000, */ + SECONDARY_ID= 0x00004000, + PCMOUT = 0x00010000, SURROUT = 0x00020000, + CENTEROUT = 0x00040000, LFEOUT = 0x00080000, + LINE1OUT = 0x00100000, LINE2OUT = 0x00200000, + GPIOOUT = 0x00400000, + SI_AC97_PRIMARY_READY = 0x01000000, + SI_AC97_SECONDARY_READY = 0x02000000, }; enum global_control_bits { @@ -173,6 +167,18 @@ enum global_control_bits { EDROP_IE = 0x00008000, BANK_B_EN = 0x00010000 }; +enum channel_control_bits { + CHANNEL_LOOP = 0x00001000, CHANNEL_SIGNED = 0x00002000, + CHANNEL_STEREO = 0x00004000, CHANNEL_16BITS = 0x00008000, +}; + +enum channel_attribute { + MODEM_LINE1, MODEM_LINE2, PCM_LR, HSET, + I2SLR, CENTER_LFE, SURR_LR, SPDIF_LR, + CHANNEL_PB = 0x00000000, CHANNEL_SPC_PB = 0x40000000, + CHANNEL_REC = 0x80000000, CHANNEL_REC_PB = 0xc0000000 +}; + enum miscint_bits { PB_UNDERRUN_IRO = 0x00000001, REC_OVERRUN_IRQ = 0x00000002, SB_IRQ = 0x00000004, MPU401_IRQ = 0x00000008, @@ -184,31 +190,13 @@ enum miscint_bits { ST_IRQ_EN = 0x00800000, ACGPIO_IRQ = 0x01000000 }; -#define IWriteAinten( x ) \ - {int i; \ - for( i= 0; i < ChanDwordCount; i++) \ - outl((x)->lpChAinten[i], TRID_REG(trident, (x)->lpAChAinten[i]));} - -#define IReadAinten( x ) \ - {int i; \ - for( i= 0; i < ChanDwordCount; i++) \ - (x)->lpChAinten[i] = inl(TRID_REG(trident, (x)->lpAChAinten[i]));} - -#define ReadAint( x ) \ - IReadAint( x ) - -#define WriteAint( x ) \ - IWriteAint( x ) - -#define IWriteAint( x ) \ - {int i; \ - for( i= 0; i < ChanDwordCount; i++) \ - outl((x)->lpChAint[i], TRID_REG(trident, (x)->lpAChAint[i]));} +#define AC97_SIGMATEL_DAC2INVERT 0x6E +#define AC97_SIGMATEL_BIAS1 0x70 +#define AC97_SIGMATEL_BIAS2 0x72 +#define AC97_SIGMATEL_CIC1 0x76 +#define AC97_SIGMATEL_CIC2 0x78 -#define IReadAint( x ) \ - {int i; \ - for( i= 0; i < ChanDwordCount; i++) \ - (x)->lpChAint[i] = inl(TRID_REG(trident, (x)->lpAChAint[i]));} +#define TRID_REG( trident, x ) ( (trident) -> iobase + (x) ) #define VALIDATE_MAGIC(FOO,MAG) \ ({ \ @@ -221,5 +209,31 @@ enum miscint_bits { #define VALIDATE_STATE(a) VALIDATE_MAGIC(a,TRIDENT_STATE_MAGIC) #define VALIDATE_CARD(a) VALIDATE_MAGIC(a,TRIDENT_CARD_MAGIC) + +extern __inline__ unsigned ld2(unsigned int x) +{ + unsigned r = 0; + + if (x >= 0x10000) { + x >>= 16; + r += 16; + } + if (x >= 0x100) { + x >>= 8; + r += 8; + } + if (x >= 0x10) { + x >>= 4; + r += 4; + } + if (x >= 4) { + x >>= 2; + r += 2; + } + if (x >= 2) + r++; + return r; +} + #endif /* __TRID4DWAVE_H */ diff --git a/drivers/usb/Config.in b/drivers/usb/Config.in index aa8c6adb2..bb812ea90 100644 --- a/drivers/usb/Config.in +++ b/drivers/usb/Config.in @@ -9,6 +9,7 @@ if [ ! "$CONFIG_USB" = "n" ]; then comment 'USB Controllers' dep_tristate ' UHCI (Intel PIIX4, VIA, ...) support' CONFIG_USB_UHCI $CONFIG_USB + dep_tristate ' UHCI Alternate Driver (JE) support' CONFIG_USB_UHCI_ALT $CONFIG_USB dep_tristate ' OHCI (Compaq, iMacs, OPTi, SiS, ALi, ...) support' CONFIG_USB_OHCI $CONFIG_USB comment 'Miscellaneous USB options' diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index ee2cee37a..c43a41a31 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -45,6 +45,7 @@ obj- := obj-$(CONFIG_USB) += usbcore.o obj-$(CONFIG_USB_UHCI) += usb-uhci.o +obj-$(CONFIG_USB_UHCI_ALT) += uhci.o obj-$(CONFIG_USB_OHCI) += usb-ohci.o obj-$(CONFIG_USB_MOUSE) += usbmouse.o input.o @@ -107,3 +108,4 @@ usbcore.o: $(usbcore-objs) usb-storage.o: $(usb-storage-objs) $(LD) -r -o $@ $(usb-storage-objs) + diff --git a/drivers/usb/acm.c b/drivers/usb/acm.c index 154d5cd22..1459f1d10 100644 --- a/drivers/usb/acm.c +++ b/drivers/usb/acm.c @@ -1,5 +1,5 @@ /* - * acm.c Version 0.14 + * acm.c Version 0.15 * * Copyright (c) 1999 Armin Fuerst <fuerst@in.tum.de> * Copyright (c) 1999 Pavel Machek <pavel@suse.cz> @@ -17,6 +17,7 @@ * v0.12 - added TIOCM ioctls, added break handling, made struct acm kmalloced * v0.13 - added termios, added hangup * v0.14 - sized down struct acm + * v0.15 - fixed flow control again - characters could be lost */ /* @@ -137,6 +138,7 @@ struct acm { unsigned int writesize; /* max packet size for the output bulk endpoint */ unsigned int used; /* someone has this acm's device open */ unsigned int minor; /* acm minor number */ + unsigned char throttle; /* throttled by tty layer */ unsigned char clocal; /* termios CLOCAL */ }; @@ -210,8 +212,6 @@ static void acm_ctrl_irq(struct urb *urb) dr->request, dr->index, dr->length, data[0], data[1]); return; } - - return; } static void acm_read_bulk(struct urb *urb) @@ -219,24 +219,25 @@ static void acm_read_bulk(struct urb *urb) struct acm *acm = urb->context; struct tty_struct *tty = acm->tty; unsigned char *data = urb->transfer_buffer; - int i; + int i = 0; if (!ACM_READY(acm)) return; - if (!urb->status) { - - for (i = 0; i < urb->actual_length; i++) + if (!urb->status & !acm->throttle) { + for (i = 0; i < urb->actual_length && !acm->throttle; i++) tty_insert_flip_char(tty, data[i], 0); - tty_flip_buffer_push(tty); - } else dbg("nonzero read bulk status received: %d", urb->status); - if (usb_submit_urb(urb)) - dbg("failed resubmitting read urb"); - - return; + if (!acm->throttle) { + urb->actual_length = 0; + if (usb_submit_urb(urb)) + dbg("failed resubmitting read urb"); + } else { + memmove(data, data + i, urb->actual_length - i); + urb->actual_length -= i; + } } static void acm_write_bulk(struct urb *urb) @@ -253,8 +254,6 @@ static void acm_write_bulk(struct urb *urb) (tty->ldisc.write_wakeup)(tty); wake_up_interruptible(&tty->write_wait); - - return; } /* @@ -347,15 +346,16 @@ static void acm_tty_throttle(struct tty_struct *tty) { struct acm *acm = tty->driver_data; if (!ACM_READY(acm)) return; - usb_unlink_urb(&acm->readurb); + acm->throttle = 1; } static void acm_tty_unthrottle(struct tty_struct *tty) { struct acm *acm = tty->driver_data; if (!ACM_READY(acm)) return; - if (usb_submit_urb(&acm->readurb)) - dbg("usb_submit_urb(read bulk) in unthrottle() failed"); + acm->throttle = 0; + if (acm->readurb.status != -EINPROGRESS) + acm_read_bulk(&acm->readurb); } static void acm_tty_break_ctl(struct tty_struct *tty, int state) diff --git a/drivers/usb/ov511.c b/drivers/usb/ov511.c index 6ec72d502..6dacc46b4 100644 --- a/drivers/usb/ov511.c +++ b/drivers/usb/ov511.c @@ -931,7 +931,7 @@ static int ov511_init_isoc(struct usb_ov511 *ov511) err("ov511_init_isoc: usb_run_isoc(0) ret %d", err); err = usb_submit_urb(ov511->sbuf[1].urb); if (err) - err("ov511_init_isoc: usb_run_isoc(1) ret %d\n", err); + err("ov511_init_isoc: usb_run_isoc(1) ret %d", err); ov511->streaming = 1; diff --git a/drivers/usb/printer.c b/drivers/usb/printer.c index 370ed6dd6..a00a9f037 100644 --- a/drivers/usb/printer.c +++ b/drivers/usb/printer.c @@ -125,15 +125,15 @@ static int usblp_check_status(struct usblp *usblp) if (status & LP_PERRORP) { if (status & LP_POUTPA) { - printk(KERN_INFO "usblp%d: out of paper", usblp->minor); + info("usblp%d: out of paper", usblp->minor); return -ENOSPC; } if (~status & LP_PSELECD) { - printk(KERN_INFO "usblp%d: off-line", usblp->minor); + info("usblp%d: off-line", usblp->minor); return -EIO; } if (~status & LP_PERRORP) { - printk(KERN_INFO "usblp%d: on fire", usblp->minor); + info("usblp%d: on fire", usblp->minor); return -EIO; } } @@ -229,18 +229,19 @@ static ssize_t usblp_write(struct file *file, const char *buffer, size_t count, if (usblp->writeurb.status == -EINPROGRESS) { usb_unlink_urb(&usblp->writeurb); - printk(KERN_ERR "usblp%d: timed out\n", usblp->minor); + err("usblp%d: timed out", usblp->minor); return -EIO; } if (!usblp->dev) return -ENODEV; - if (!usblp->writeurb.status) + if (!usblp->writeurb.status) { writecount += usblp->writeurb.transfer_buffer_length; - else { + usblp->writeurb.transfer_buffer_length = 0; + } else { if (!(retval = usblp_check_status(usblp))) { - printk(KERN_ERR "usblp%d: error %d writing to printer\n", + err("usblp%d: error %d writing to printer", usblp->minor, usblp->writeurb.status); return -EIO; } @@ -287,7 +288,7 @@ static ssize_t usblp_read(struct file *file, char *buffer, size_t count, loff_t return -ENODEV; if (usblp->readurb.status) { - printk(KERN_ERR "usblp%d: error %d reading from printer\n", + err("usblp%d: error %d reading from printer", usblp->minor, usblp->readurb.status); usb_submit_urb(&usblp->readurb); return -EIO; @@ -389,7 +390,7 @@ static void *usblp_probe(struct usb_device *dev, unsigned int ifnum) buf + USBLP_BUF_SIZE, USBLP_BUF_SIZE, usblp_bulk, usblp); } - printk(KERN_INFO "usblp%d: USB %sdirectional printer dev %d if %d alt %d\n", + info("usblp%d: USB %sdirectional printer dev %d if %d alt %d", minor, bidir ? "Bi" : "Uni", dev->devnum, ifnum, alts); return usblp_table[minor] = usblp; diff --git a/drivers/usb/scanner.c b/drivers/usb/scanner.c index ec45a6fb9..e707d23e8 100644 --- a/drivers/usb/scanner.c +++ b/drivers/usb/scanner.c @@ -331,7 +331,6 @@ probe_scanner(struct usb_device *dev, unsigned int ifnum) int ep_cnt; - char *ident; char valid_device = 0; char have_bulk_in, have_bulk_out, have_intr; diff --git a/drivers/usb/uhci-debug.h b/drivers/usb/uhci-debug.h index 73d16937a..9d715d4d8 100644 --- a/drivers/usb/uhci-debug.h +++ b/drivers/usb/uhci-debug.h @@ -1,195 +1,260 @@ -#ifdef DEBUG +/* + * UHCI-specific debugging code. Invaluable when something + * goes wrong, but don't get in my face. + * + * Kernel visible pointers are surrounded in []'s and bus + * visible pointers are surrounded in ()'s + * + * (C) Copyright 1999 Linus Torvalds + * (C) Copyright 1999 Johannes Erdfelt + */ -static void uhci_show_qh (puhci_desc_t qh) +#include <linux/kernel.h> +#include <asm/io.h> + +#include "uhci.h" + +void uhci_show_td(struct uhci_td * td) { - if (qh->type != QH_TYPE) { - dbg("qh has not QH_TYPE"); - return; - } - dbg("uhci_show_qh %p (%08lX):", qh, virt_to_bus (qh)); + char *spid; - if (qh->hw.qh.head & UHCI_PTR_TERM) - dbg("Head Terminate"); - else { - if (qh->hw.qh.head & UHCI_PTR_QH) - dbg("Head points to QH"); - else - dbg("Head points to TD"); + printk("%08x ", td->link); + printk("e%d %s%s%s%s%s%s%s%s%s%sLength=%x ", + ((td->status >> 27) & 3), + (td->status & TD_CTRL_SPD) ? "SPD " : "", + (td->status & TD_CTRL_LS) ? "LS " : "", + (td->status & TD_CTRL_IOC) ? "IOC " : "", + (td->status & TD_CTRL_ACTIVE) ? "Active " : "", + (td->status & TD_CTRL_STALLED) ? "Stalled " : "", + (td->status & TD_CTRL_DBUFERR) ? "DataBufErr " : "", + (td->status & TD_CTRL_BABBLE) ? "Babble " : "", + (td->status & TD_CTRL_NAK) ? "NAK " : "", + (td->status & TD_CTRL_CRCTIMEO) ? "CRC/Timeo " : "", + (td->status & TD_CTRL_BITSTUFF) ? "BitStuff " : "", + td->status & 0x7ff); - dbg("head: %08X", qh->hw.qh.head & ~UHCI_PTR_BITS); - } - if (qh->hw.qh.element & UHCI_PTR_TERM) - dbg("Element Terminate"); - else { - - if (qh->hw.qh.element & UHCI_PTR_QH) - dbg("Element points to QH"); - else - dbg("Element points to TD"); - dbg("element: %08X", qh->hw.qh.element & ~UHCI_PTR_BITS); + switch (td->info & 0xff) { + case USB_PID_SETUP: + spid = "SETUP"; + break; + case USB_PID_OUT: + spid = "OUT"; + break; + case USB_PID_IN: + spid = "IN"; + break; + default: + spid = "?"; + break; } + + printk("MaxLen=%x DT%d EndPt=%x Dev=%x, PID=%x(%s) ", + td->info >> 21, + ((td->info >> 19) & 1), + (td->info >> 15) & 15, + (td->info >> 8) & 127, + (td->info & 0xff), + spid); + printk("(buf=%08x)\n", td->buffer); } -#endif -static void uhci_show_td (puhci_desc_t td) +static void uhci_show_sc(int port, unsigned short status) { - char *spid; - warn("uhci_show_td %p (%08lX) ", td, virt_to_bus (td)); - - switch (td->hw.td.info & 0xff) { - case USB_PID_SETUP: - spid = "SETUP"; - break; - case USB_PID_OUT: - spid = " OUT "; - break; - case USB_PID_IN: - spid = " IN "; - break; - default: - spid = " ? "; - break; - } + printk(" stat%d = %04x %s%s%s%s%s%s%s%s\n", + port, + status, + (status & USBPORTSC_SUSP) ? "PortSuspend " : "", + (status & USBPORTSC_PR) ? "PortReset " : "", + (status & USBPORTSC_LSDA) ? "LowSpeed " : "", + (status & USBPORTSC_RD) ? "ResumeDetect " : "", + (status & USBPORTSC_PEC) ? "EnableChange " : "", + (status & USBPORTSC_PE) ? "PortEnabled " : "", + (status & USBPORTSC_CSC) ? "ConnectChange " : "", + (status & USBPORTSC_CCS) ? "PortConnected " : ""); +} - warn("MaxLen=%02x DT%d EndPt=%x Dev=%x, PID=%x(%s) (buf=%08x)", - td->hw.td.info >> 21, - ((td->hw.td.info >> 19) & 1), - (td->hw.td.info >> 15) & 15, - (td->hw.td.info >> 8) & 127, - (td->hw.td.info & 0xff), - spid, - td->hw.td.buffer); - - warn("Len=%02x e%d %s%s%s%s%s%s%s%s%s%s", - td->hw.td.status & 0x7ff, - ((td->hw.td.status >> 27) & 3), - (td->hw.td.status & TD_CTRL_SPD) ? "SPD " : "", - (td->hw.td.status & TD_CTRL_LS) ? "LS " : "", - (td->hw.td.status & TD_CTRL_IOC) ? "IOC " : "", - (td->hw.td.status & TD_CTRL_ACTIVE) ? "Active " : "", - (td->hw.td.status & TD_CTRL_STALLED) ? "Stalled " : "", - (td->hw.td.status & TD_CTRL_DBUFERR) ? "DataBufErr " : "", - (td->hw.td.status & TD_CTRL_BABBLE) ? "Babble " : "", - (td->hw.td.status & TD_CTRL_NAK) ? "NAK " : "", - (td->hw.td.status & TD_CTRL_CRCTIMEO) ? "CRC/Timeo " : "", - (td->hw.td.status & TD_CTRL_BITSTUFF) ? "BitStuff " : "" - ); -#if 1 - if (td->hw.td.link & UHCI_PTR_TERM) - warn("Link Terminate"); - else { - if (td->hw.td.link & UHCI_PTR_QH) - warn("%s, link points to QH @ %08x", - (td->hw.td.link & UHCI_PTR_DEPTH ? "Depth first" : " Breadth first"), - td->hw.td.link & ~UHCI_PTR_BITS); - else - warn("%s, link points to TD @ %08x", - (td->hw.td.link & UHCI_PTR_DEPTH ? "Depth first" : " Breadth first"), - td->hw.td.link & ~UHCI_PTR_BITS); - } -#endif +void uhci_show_status(struct uhci *uhci) +{ + unsigned int io_addr = uhci->io_addr; + unsigned short usbcmd, usbstat, usbint, usbfrnum; + unsigned int flbaseadd; + unsigned char sof; + unsigned short portsc1, portsc2; + + usbcmd = inw(io_addr + 0); + usbstat = inw(io_addr + 2); + usbint = inw(io_addr + 4); + usbfrnum = inw(io_addr + 6); + flbaseadd = inl(io_addr + 8); + sof = inb(io_addr + 12); + portsc1 = inw(io_addr + 16); + portsc2 = inw(io_addr + 18); + + printk(" usbcmd = %04x %s%s%s%s%s%s%s%s\n", + usbcmd, + (usbcmd & USBCMD_MAXP) ? "Maxp64 " : "Maxp32 ", + (usbcmd & USBCMD_CF) ? "CF " : "", + (usbcmd & USBCMD_SWDBG) ? "SWDBG " : "", + (usbcmd & USBCMD_FGR) ? "FGR " : "", + (usbcmd & USBCMD_EGSM) ? "EGSM " : "", + (usbcmd & USBCMD_GRESET) ? "GRESET " : "", + (usbcmd & USBCMD_HCRESET) ? "HCRESET " : "", + (usbcmd & USBCMD_RS) ? "RS " : ""); + + printk(" usbstat = %04x %s%s%s%s%s%s\n", + usbstat, + (usbstat & USBSTS_HCH) ? "HCHalted " : "", + (usbstat & USBSTS_HCPE) ? "HostControllerProcessError " : "", + (usbstat & USBSTS_HSE) ? "HostSystemError " : "", + (usbstat & USBSTS_RD) ? "ResumeDetect " : "", + (usbstat & USBSTS_ERROR) ? "USBError " : "", + (usbstat & USBSTS_USBINT) ? "USBINT " : ""); + + printk(" usbint = %04x\n", usbint); + printk(" usbfrnum = (%d)%03x\n", (usbfrnum >> 10) & 1, + 0xfff & (4*(unsigned int)usbfrnum)); + printk(" flbaseadd = %08x\n", flbaseadd); + printk(" sof = %02x\n", sof); + uhci_show_sc(1, portsc1); + uhci_show_sc(2, portsc2); } -#ifdef DEBUG -static void uhci_show_td_queue (puhci_desc_t td) + +#define uhci_link_to_qh(x) ((struct uhci_qh *) uhci_link_to_td(x)) + +struct uhci_td *uhci_link_to_td(unsigned int link) { - dbg("uhci_show_td_queue %p (%08lX):", td, virt_to_bus (td)); - while (1) { - uhci_show_td (td); - if (td->hw.td.link & UHCI_PTR_TERM) - break; - //if(!(td->hw.td.link&UHCI_PTR_DEPTH)) - // break; - if (td != bus_to_virt (td->hw.td.link & ~UHCI_PTR_BITS)) - td = bus_to_virt (td->hw.td.link & ~UHCI_PTR_BITS); - else { - dbg("td points to itself!"); - break; - } -// schedule(); - } + if (link & UHCI_PTR_TERM) + return NULL; + + return bus_to_virt(link & ~UHCI_PTR_BITS); } -static void uhci_show_queue (puhci_desc_t qh) +void uhci_show_queue(struct uhci_qh *qh) { - dbg("uhci_show_queue %p:", qh); - while (1) { - uhci_show_qh (qh); + struct uhci_td *td, *first; + int i = 0, count = 1000; - if (qh->hw.qh.element & UHCI_PTR_QH) - dbg("Warning: qh->element points to qh!"); - else if (!(qh->hw.qh.element & UHCI_PTR_TERM)) - uhci_show_td_queue (bus_to_virt (qh->hw.qh.element & ~UHCI_PTR_BITS)); + if (qh->element & UHCI_PTR_QH) + printk(" Element points to QH (bug?)\n"); - if (qh->hw.qh.head & UHCI_PTR_TERM) - break; + if (qh->element & UHCI_PTR_DEPTH) + printk(" Depth traverse\n"); - if (qh != bus_to_virt (qh->hw.qh.head & ~UHCI_PTR_BITS)) - qh = bus_to_virt (qh->hw.qh.head & ~UHCI_PTR_BITS); - else { - dbg("qh points to itself!"); + if (qh->element & UHCI_PTR_TERM) + printk(" Terminate\n"); + + if (!(qh->element & ~UHCI_PTR_BITS)) { + printk(" td 0: [NULL]\n"); + return; + } + + first = uhci_link_to_td(qh->element); + + /* Make sure it doesn't runaway */ + for (td = first; td && count > 0; + td = uhci_link_to_td(td->link), --count) { + printk(" td %d: [%p]\n", i++, td); + printk(" "); + uhci_show_td(td); + + if (td == uhci_link_to_td(td->link)) { + printk(KERN_ERR "td links to itself!\n"); break; } } } -static void uhci_show_sc (int port, unsigned short status) +static int uhci_is_skeleton_td(struct uhci *uhci, struct uhci_td *td) { - dbg(" stat%d = %04x %s%s%s%s%s%s%s%s", - port, - status, - (status & USBPORTSC_SUSP) ? "PortSuspend " : "", - (status & USBPORTSC_PR) ? "PortReset " : "", - (status & USBPORTSC_LSDA) ? "LowSpeed " : "", - (status & USBPORTSC_RD) ? "ResumeDetect " : "", - (status & USBPORTSC_PEC) ? "EnableChange " : "", - (status & USBPORTSC_PE) ? "PortEnabled " : "", - (status & USBPORTSC_CSC) ? "ConnectChange " : "", - (status & USBPORTSC_CCS) ? "PortConnected " : ""); + int j; + + for (j = 0; j < UHCI_NUM_SKELTD; j++) + if (td == uhci->skeltd + j) + return 1; + + return 0; } -void uhci_show_status (puhci_t s) +static int uhci_is_skeleton_qh(struct uhci *uhci, struct uhci_qh *qh) { - unsigned int io_addr = s->io_addr; - unsigned short usbcmd, usbstat, usbint, usbfrnum; - unsigned int flbaseadd; - unsigned char sof; - unsigned short portsc1, portsc2; + int j; + + for (j = 0; j < UHCI_NUM_SKELQH; j++) + if (qh == uhci->skelqh + j) + return 1; - usbcmd = inw (io_addr + 0); - usbstat = inw (io_addr + 2); - usbint = inw (io_addr + 4); - usbfrnum = inw (io_addr + 6); - flbaseadd = inl (io_addr + 8); - sof = inb (io_addr + 12); - portsc1 = inw (io_addr + 16); - portsc2 = inw (io_addr + 18); - - dbg(" usbcmd = %04x %s%s%s%s%s%s%s%s", - usbcmd, - (usbcmd & USBCMD_MAXP) ? "Maxp64 " : "Maxp32 ", - (usbcmd & USBCMD_CF) ? "CF " : "", - (usbcmd & USBCMD_SWDBG) ? "SWDBG " : "", - (usbcmd & USBCMD_FGR) ? "FGR " : "", - (usbcmd & USBCMD_EGSM) ? "EGSM " : "", - (usbcmd & USBCMD_GRESET) ? "GRESET " : "", - (usbcmd & USBCMD_HCRESET) ? "HCRESET " : "", - (usbcmd & USBCMD_RS) ? "RS " : ""); - - dbg(" usbstat = %04x %s%s%s%s%s%s", - usbstat, - (usbstat & USBSTS_HCH) ? "HCHalted " : "", - (usbstat & USBSTS_HCPE) ? "HostControllerProcessError " : "", - (usbstat & USBSTS_HSE) ? "HostSystemError " : "", - (usbstat & USBSTS_RD) ? "ResumeDetect " : "", - (usbstat & USBSTS_ERROR) ? "USBError " : "", - (usbstat & USBSTS_USBINT) ? "USBINT " : ""); - - dbg(" usbint = %04x", usbint); - dbg(" usbfrnum = (%d)%03x", (usbfrnum >> 10) & 1, - 0xfff & (4 * (unsigned int) usbfrnum)); - dbg(" flbaseadd = %08x", flbaseadd); - dbg(" sof = %02x", sof); - uhci_show_sc (1, portsc1); - uhci_show_sc (2, portsc2); + return 0; } -#endif + +static const char *td_names[] = {"interrupt1", "interrupt2", "interrupt4", + "interrupt8", "interrupt16", "interrupt32", + "interrupt64", "interrupt128", "interrupt256" }; +static const char *qh_names[] = { "control", "bulk" }; + +void uhci_show_queues(struct uhci *uhci) +{ + int i, isqh; + struct uhci_qh *qh; + struct uhci_td *td; + + for (i = 0; i < UHCI_NUMFRAMES; ++i) { + int shown = 0; + + td = uhci_link_to_td(uhci->fl->frame[i]); + if (td) + isqh = uhci->fl->frame[i] & UHCI_PTR_QH; + while (td && !isqh) { + if (uhci_is_skeleton_td(uhci, td)) + break; + + if (!shown) { + printk(" Frame %d\n", i); + shown = 1; + } + + printk("[%p] ", td); + + uhci_show_td(td); + td = uhci_link_to_td(td->link); + if (td) + isqh = td->link & UHCI_PTR_QH; + } + } + for (i = 0; i < UHCI_NUM_SKELTD; ++i) { + printk(" %s: [%p] (%08x)\n", td_names[i], + &uhci->skeltd[i], + uhci->skeltd[i].link); + + td = uhci_link_to_td(uhci->skeltd[i].link); + if (td) + isqh = uhci->skeltd[i].link & UHCI_PTR_QH; + while (td && !isqh) { + if (uhci_is_skeleton_td(uhci, td)) + break; + + printk("[%p] ", td); + + uhci_show_td(td); + td = uhci_link_to_td(td->link); + if (td) + isqh = td->link & UHCI_PTR_QH; + } + } + for (i = 0; i < UHCI_NUM_SKELQH; ++i) { + printk(" %s: [%p] (%08x) (%08x)\n", qh_names[i], + &uhci->skelqh[i], + uhci->skelqh[i].link, uhci->skelqh[i].element); + + qh = uhci_link_to_qh(uhci->skelqh[i].link); + for (; qh; qh = uhci_link_to_qh(qh->link)) { + if (uhci_is_skeleton_qh(uhci, qh)) + break; + + printk(" [%p] (%08x) (%08x)\n", + qh, qh->link, qh->element); + + uhci_show_queue(qh); + } + } +} + diff --git a/drivers/usb/uhci.c b/drivers/usb/uhci.c new file mode 100644 index 000000000..d18fc30cb --- /dev/null +++ b/drivers/usb/uhci.c @@ -0,0 +1,2214 @@ +/* + * Universal Host Controller Interface driver for USB. + * + * (C) Copyright 1999 Linus Torvalds + * (C) Copyright 1999-2000 Johannes Erdfelt, jerdfelt@sventech.com + * (C) Copyright 1999 Randy Dunlap + * (C) Copyright 1999 Georg Acher, acher@in.tum.de + * (C) Copyright 1999 Deti Fliegl, deti@fliegl.de + * (C) Copyright 1999 Thomas Sailer, sailer@ife.ee.ethz.ch + * (C) Copyright 1999 Roman Weissgaerber, weissg@vienna.at + * + * Intel documents this fairly well, and as far as I know there + * are no royalties or anything like that, but even so there are + * people who decided that they want to do the same thing in a + * completely different way. + * + * WARNING! The USB documentation is downright evil. Most of it + * is just crap, written by a committee. You're better off ignoring + * most of it, the important stuff is: + * - the low-level protocol (fairly simple but lots of small details) + * - working around the horridness of the rest + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/ioport.h> +#include <linux/sched.h> +#include <linux/malloc.h> +#include <linux/smp_lock.h> +#include <linux/errno.h> +#include <linux/unistd.h> +#include <linux/interrupt.h> +#include <linux/spinlock.h> + +#include <asm/uaccess.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/system.h> + +#define DEBUG +#include "usb.h" + +#include "uhci.h" +#include "uhci-debug.h" + +#ifdef CONFIG_APM +#include <linux/apm_bios.h> +static int handle_apm_event(apm_event_t event); +#endif + +static int debug = 1; +MODULE_PARM(debug, "i"); + +static kmem_cache_t *uhci_td_cachep; +static kmem_cache_t *uhci_qh_cachep; + +static LIST_HEAD(uhci_list); + +static int rh_submit_urb(urb_t *urb); +static int rh_unlink_urb(urb_t *urb); +static int uhci_get_current_frame_number(struct usb_device *usb_dev); + +#define min(a,b) (((a)<(b))?(a):(b)) + +/* + * Only the USB core should call uhci_alloc_dev and uhci_free_dev + */ +static int uhci_alloc_dev(struct usb_device *usb_dev) +{ + struct uhci_device *dev; + + /* Allocate the UHCI device private data */ + dev = kmalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return -1; + + /* Initialize "dev" */ + memset(dev, 0, sizeof(*dev)); + + usb_dev->hcpriv = dev; + dev->usb = usb_dev; + atomic_set(&dev->refcnt, 1); + + if (usb_dev->parent) + dev->uhci = usb_to_uhci(usb_dev->parent)->uhci; + + return 0; +} + +static int uhci_free_dev(struct usb_device *usb_dev) +{ + struct uhci_device *dev = usb_to_uhci(usb_dev); + + if (atomic_dec_and_test(&dev->refcnt)) + kfree(dev); + + return 0; +} + +static void uhci_inc_dev_use(struct uhci_device *dev) +{ + atomic_inc(&dev->refcnt); +} + +static void uhci_dec_dev_use(struct uhci_device *dev) +{ + uhci_free_dev(dev->usb); +} + +/* + * UHCI interrupt list operations.. + */ +static void uhci_add_irq_list(struct uhci *uhci, struct uhci_td *td) +{ + unsigned long flags; + + nested_lock(&uhci->irqlist_lock, flags); + list_add(&td->irq_list, &uhci->interrupt_list); + nested_unlock(&uhci->irqlist_lock, flags); +} + +static void uhci_remove_irq_list(struct uhci *uhci, struct uhci_td *td) +{ + unsigned long flags; + + nested_lock(&uhci->irqlist_lock, flags); + if (td->irq_list.next != &td->irq_list) { + list_del(&td->irq_list); + INIT_LIST_HEAD(&td->irq_list); + } + nested_unlock(&uhci->irqlist_lock, flags); +} + +static void uhci_add_urb_list(struct uhci *uhci, struct urb *urb) +{ + unsigned long flags; + + spin_lock_irqsave(&uhci->urblist_lock, flags); + list_add(&urb->urb_list, &uhci->urb_list); + spin_unlock_irqrestore(&uhci->urblist_lock, flags); +} + +static void uhci_remove_urb_list(struct uhci *uhci, struct urb *urb) +{ + unsigned long flags; + + spin_lock_irqsave(&uhci->urblist_lock, flags); + if (urb->urb_list.next != &urb->urb_list) { + list_del(&urb->urb_list); + INIT_LIST_HEAD(&urb->urb_list); + } + spin_unlock_irqrestore(&uhci->urblist_lock, flags); +} + +/* + * We insert Isochronous transfers directly into the frame list at the + * beginning + * The layout looks as follows: + * frame list pointer -> iso td's (if any) -> + * periodic interrupt td (if frame 0) -> irq td's -> control qh -> bulk qh + */ + +static void uhci_insert_td_frame_list(struct uhci *uhci, struct uhci_td *td, unsigned framenum) +{ + unsigned long flags; + struct uhci_td *nexttd; + + framenum %= UHCI_NUMFRAMES; + + spin_lock_irqsave(&uhci->framelist_lock, flags); + td->frameptr = &uhci->fl->frame[framenum]; + td->link = uhci->fl->frame[framenum]; + if (!(td->link & (UHCI_PTR_TERM | UHCI_PTR_QH))) { + nexttd = (struct uhci_td *)uhci_ptr_to_virt(td->link); + td->nexttd = nexttd; + nexttd->prevtd = td; + nexttd->frameptr = NULL; + } + uhci->fl->frame[framenum] = virt_to_bus(td); + spin_unlock_irqrestore(&uhci->framelist_lock, flags); +} + +static void uhci_remove_td(struct uhci *uhci, struct uhci_td *td) +{ + unsigned long flags; + + spin_lock_irqsave(&uhci->framelist_lock, flags); + if (td->frameptr) { + *(td->frameptr) = td->link; + if (td->nexttd) { + td->nexttd->frameptr = td->frameptr; + td->nexttd->prevtd = NULL; + td->nexttd = NULL; + } + td->frameptr = NULL; + } else { + if (td->prevtd) { + td->prevtd->nexttd = td->nexttd; + td->prevtd->link = td->link; + } + if (td->nexttd) + td->nexttd->prevtd = td->prevtd; + td->prevtd = td->nexttd = NULL; + } + td->link = UHCI_PTR_TERM; + spin_unlock_irqrestore(&uhci->framelist_lock, flags); +} + +static void uhci_insert_td(struct uhci *uhci, struct uhci_td *skeltd, struct uhci_td *td) +{ + unsigned long flags; + + spin_lock_irqsave(&uhci->framelist_lock, flags); + + /* Fix the linked list pointers */ + td->nexttd = skeltd->nexttd; + td->prevtd = skeltd; + if (skeltd->nexttd) + skeltd->nexttd->prevtd = td; + skeltd->nexttd = td; + + td->link = skeltd->link; + skeltd->link = virt_to_bus(td); + + spin_unlock_irqrestore(&uhci->framelist_lock, flags); +} + +/* + * Inserts a td into qh list at the top. + */ +static void uhci_insert_tds_in_qh(struct uhci_qh *qh, struct uhci_td *begin) +{ + struct uhci_td *td, *prevtd; + + if (!begin) /* Nothing to do */ + return; + + /* Grab the first TD and add it to the QH */ + td = begin; + qh->element = virt_to_bus(td) | UHCI_PTR_DEPTH; + + /* Go through the rest of the TD's, link them together */ + prevtd = td; + td = td->next; + while (td) { + prevtd->link = virt_to_bus(td) | UHCI_PTR_DEPTH; + + prevtd = td; + td = td->next; + } + + prevtd->link = UHCI_PTR_TERM; +} + +static struct uhci_td *uhci_td_alloc(struct uhci_device *dev) +{ + struct uhci_td *td; + + td = kmem_cache_alloc(uhci_td_cachep, in_interrupt() ? SLAB_ATOMIC : SLAB_KERNEL); + if (!td) + return NULL; + + td->link = UHCI_PTR_TERM; + td->buffer = 0; + + td->frameptr = NULL; + td->nexttd = td->prevtd = NULL; + td->next = NULL; + td->dev = dev; + INIT_LIST_HEAD(&td->irq_list); + INIT_LIST_HEAD(&td->list); + + uhci_inc_dev_use(dev); + + return td; +} + +static void uhci_td_free(struct uhci_td *td) +{ + kmem_cache_free(uhci_td_cachep, td); + + if (td->dev) + uhci_dec_dev_use(td->dev); +} + +static void uhci_schedule_delete_td(struct uhci *uhci, struct uhci_td *td) +{ + unsigned long flags; + + spin_lock_irqsave(&uhci->freelist_lock, flags); + list_add(&td->list, &uhci->td_free_list); + if (td->dev) { + uhci_dec_dev_use(td->dev); + td->dev = NULL; + } + spin_unlock_irqrestore(&uhci->freelist_lock, flags); +} + +static struct uhci_qh *uhci_qh_alloc(struct uhci_device *dev) +{ + struct uhci_qh *qh; + + qh = kmem_cache_alloc(uhci_qh_cachep, in_interrupt() ? SLAB_ATOMIC : SLAB_KERNEL); + if (!qh) + return NULL; + + qh->element = UHCI_PTR_TERM; + qh->link = UHCI_PTR_TERM; + + qh->dev = dev; + qh->prevqh = qh->nextqh = NULL; + + INIT_LIST_HEAD(&qh->list); + + uhci_inc_dev_use(dev); + + return qh; +} + +static void uhci_qh_free(struct uhci_qh *qh) +{ + kmem_cache_free(uhci_qh_cachep, qh); + + if (qh->dev) + uhci_dec_dev_use(qh->dev); +} + +static void uhci_schedule_delete_qh(struct uhci *uhci, struct uhci_qh *qh) +{ + unsigned long flags; + + spin_lock_irqsave(&uhci->freelist_lock, flags); + list_add(&qh->list, &uhci->qh_free_list); + if (qh->dev) { + uhci_dec_dev_use(qh->dev); + qh->dev = NULL; + } + spin_unlock_irqrestore(&uhci->freelist_lock, flags); +} + +static void uhci_insert_qh(struct uhci *uhci, struct uhci_qh *skelqh, struct uhci_qh *qh) +{ + unsigned long flags; + + spin_lock_irqsave(&uhci->framelist_lock, flags); + + /* Fix the linked list pointers */ + qh->nextqh = skelqh->nextqh; + qh->prevqh = skelqh; + if (skelqh->nextqh) + skelqh->nextqh->prevqh = qh; + skelqh->nextqh = qh; + + qh->link = skelqh->link; + skelqh->link = virt_to_bus(qh) | UHCI_PTR_QH; + + spin_unlock_irqrestore(&uhci->framelist_lock, flags); +} + +static void uhci_remove_qh(struct uhci *uhci, struct uhci_qh *qh) +{ + unsigned long flags; + + spin_lock_irqsave(&uhci->framelist_lock, flags); + if (qh->prevqh) { + qh->prevqh->nextqh = qh->nextqh; + qh->prevqh->link = qh->link; + } + if (qh->nextqh) + qh->nextqh->prevqh = qh->prevqh; + qh->prevqh = qh->nextqh = NULL; + spin_unlock_irqrestore(&uhci->framelist_lock, flags); +} + +static void inline uhci_fill_td(struct uhci_td *td, __u32 status, + __u32 info, __u32 buffer) +{ + td->status = status; + td->info = info; + td->buffer = buffer; +} + +static void uhci_add_td_to_urb(urb_t *urb, struct uhci_td *td) +{ + struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; + + td->urb = urb; + + if (urbp->end) + urbp->end->next = td; + + urbp->end = td; + + if (!urbp->begin) + urbp->begin = td; +} + +/* + * Map status to standard result codes + * + * <status> is (td->status & 0xFE0000) [a.k.a. uhci_status_bits(td->status)] + * <dir_out> is True for output TDs and False for input TDs. + */ +static int uhci_map_status(int status, int dir_out) +{ + if (!status) + return 0; + if (status & TD_CTRL_BITSTUFF) /* Bitstuff error */ + return -EPROTO; + if (status & TD_CTRL_CRCTIMEO) { /* CRC/Timeout */ + if (dir_out) + return -ETIMEDOUT; + else + return -EILSEQ; + } + if (status & TD_CTRL_NAK) /* NAK */ + return -ETIMEDOUT; + if (status & TD_CTRL_BABBLE) /* Babble */ + return -EPIPE; + if (status & TD_CTRL_DBUFERR) /* Buffer error */ + return -ENOSR; + if (status & TD_CTRL_STALLED) /* Stalled */ + return -EPIPE; + if (status & TD_CTRL_ACTIVE) /* Active */ + return 0; + + return -EINVAL; +} + +/* + * Control transfers + */ +static int uhci_submit_control(urb_t *urb) +{ + struct uhci_td *td; + struct uhci_qh *qh; + unsigned long destination, status; + struct uhci_device *dev = usb_to_uhci(urb->dev); + struct uhci *uhci = dev->uhci; + int maxsze = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)); + int len = urb->transfer_buffer_length; + unsigned char *data = urb->transfer_buffer; + struct urb_priv *urbp; + + /* The "pipe" thing contains the destination in bits 8--18 */ + destination = (urb->pipe & PIPE_DEVEP_MASK) | USB_PID_SETUP; + + /* 3 errors */ + status = (urb->pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE | (3 << 27); + + urbp = kmalloc(sizeof(*urbp), in_interrupt() ? GFP_ATOMIC : GFP_KERNEL); + if (!urbp) + return -ENOMEM; + + urbp->begin = urbp->end = NULL; + + urb->hcpriv = urbp; + + /* + * Build the TD for the control request + */ + td = uhci_td_alloc(dev); + if (!td) + return -ENOMEM; + + uhci_add_td_to_urb(urb, td); + uhci_fill_td(td, status, destination | (7 << 21), + virt_to_bus(urb->setup_packet)); + + /* + * If direction is "send", change the frame from SETUP (0x2D) + * to OUT (0xE1). Else change it from SETUP to IN (0x69). + */ + destination ^= (USB_PID_SETUP ^ usb_packetid(urb->pipe)); + + if (!(urb->transfer_flags & USB_DISABLE_SPD)) + status |= TD_CTRL_SPD; + + /* + * Build the DATA TD's + */ + td = uhci_td_alloc(dev); + if (!td) { + /* FIXME: Free the TD's */ + return -ENOMEM; + } + + while (len > 0) { + int pktsze = len; + + if (pktsze > maxsze) + pktsze = maxsze; + + /* Alternate Data0/1 (start with Data1) */ + destination ^= 1 << TD_TOKEN_TOGGLE; + + uhci_add_td_to_urb(urb, td); + uhci_fill_td(td, status, destination | ((pktsze - 1) << 21), + virt_to_bus(data)); + + data += pktsze; + len -= pktsze; + + td = uhci_td_alloc(dev); + if (!td) + /* FIXME: Free all of the previously allocated td's */ + return -ENOMEM; + } + + /* + * Build the final TD for control status + * + * It's IN if the pipe is an output pipe or we're not expecting + * data back. + */ + destination &= ~TD_PID; + if (usb_pipeout(urb->pipe) || !urb->transfer_buffer_length) + destination |= USB_PID_IN; + else + destination |= USB_PID_OUT; + + destination |= 1 << TD_TOKEN_TOGGLE; /* End in Data1 */ + + status &= ~TD_CTRL_SPD; + + uhci_add_td_to_urb(urb, td); + uhci_fill_td(td, status | TD_CTRL_IOC, + destination | (UHCI_NULL_DATA_SIZE << 21), 0); + + uhci_add_irq_list(uhci, td); + + qh = uhci_qh_alloc(dev); + if (!qh) { + /* FIXME: Free all of the TD's */ + return -ENOMEM; + } + uhci_insert_tds_in_qh(qh, urbp->begin); + + uhci_insert_qh(uhci, &uhci->skel_control_qh, qh); + urbp->qh = qh; + + uhci_add_urb_list(uhci, urb); + + usb_inc_dev_use(urb->dev); + + return -EINPROGRESS; +} + +static int uhci_unlink_control(urb_t *urb) +{ + struct urb_priv *urbp = urb->hcpriv; + struct uhci_td *td; + struct uhci_device *dev = usb_to_uhci(urb->dev); + struct uhci *uhci = dev->uhci; + + if (!urbp) + return -EINVAL; + + uhci_remove_qh(uhci, urbp->qh); + uhci_schedule_delete_qh(uhci, urbp->qh); + + /* Go through the rest of the TD's, deleting them, then scheduling */ + /* their deletion */ + td = urbp->begin; + while (td) { + struct uhci_td *next = td->next; + + if (td->status & TD_CTRL_IOC) + uhci_remove_irq_list(uhci, td); + + uhci_schedule_delete_td(uhci, td); + + td = next; + } + + kfree(urbp); + urb->hcpriv = NULL; + + uhci_remove_urb_list(uhci, urb); + + return 0; +} + +static int uhci_result_control(urb_t *urb) +{ + struct urb_priv *urbp = urb->hcpriv; + struct uhci_td *td; + unsigned int status; + int ret; + + td = urbp->begin; + if (!td) /* Nothing to do */ + return -EINVAL; + + /* The first TD is the SETUP phase, check the status, but skip */ + /* the count */ + status = uhci_status_bits(td->status); + if (status & TD_CTRL_ACTIVE) + return -EINPROGRESS; + + if (status) + goto td_error; + + urb->actual_length = 0; + + /* The rest of the TD's (but the last) are data */ + td = td->next; + while (td && td->next) { + status = uhci_status_bits(td->status); + if (status & TD_CTRL_ACTIVE) + return -EINPROGRESS; + + urb->actual_length += uhci_actual_length(td->status); + + /* If SPD is set then we received a short packet */ + /* There will be no status phase at the end */ + if (td->status & TD_CTRL_SPD && (uhci_actual_length(td->status) < uhci_expected_length(td->info))) + goto td_success; + + if (status) + goto td_error; + + td = td->next; + } + + /* Control status phase */ + status = uhci_status_bits(td->status); + + /* APC BackUPS Pro kludge */ + /* It tries to send all of the descriptor instead of */ + /* the amount we requested */ + if (td->status & TD_CTRL_IOC && + status & TD_CTRL_ACTIVE && + status & TD_CTRL_NAK) + goto td_success; + + if (status & TD_CTRL_ACTIVE) + return -EINPROGRESS; + + if (status) + goto td_error; + +td_success: + uhci_unlink_control(urb); + + return 0; + +td_error: + /* Some debugging code */ + if (debug) { + dbg("uhci_result_control() failed with status %x", + status); + + /* Print the chain for debugging purposes */ + uhci_show_queue(urbp->qh); + } + + if (status & TD_CTRL_STALLED) { + /* endpoint has stalled - mark it halted */ + usb_endpoint_halt(urb->dev, uhci_endpoint(td->info), + uhci_packetout(td->info)); + uhci_unlink_control(urb); + + return -EPIPE; + } + + ret = uhci_map_status(status, uhci_packetout(td->info)); + + uhci_unlink_control(urb); + + return ret; +} + +/* + * Interrupt transfers + */ +static int uhci_submit_interrupt(urb_t *urb) +{ + struct uhci_td *td; + unsigned long destination, status; + struct uhci_device *dev = usb_to_uhci(urb->dev); + struct uhci *uhci = dev->uhci; + struct urb_priv *urbp; + + if (urb->transfer_buffer_length > usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe))) + return -EINVAL; + + /* The "pipe" thing contains the destination in bits 8--18 */ + destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe); + + status = (urb->pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE | TD_CTRL_SPD | + TD_CTRL_IOC; + + urbp = kmalloc(sizeof(*urbp), in_interrupt() ? GFP_ATOMIC : GFP_KERNEL); + if (!urbp) + return -ENOMEM; + + urbp->begin = urbp->end = NULL; + + urb->hcpriv = urbp; + + td = uhci_td_alloc(dev); + if (!td) + return -ENOMEM; + + destination |= (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE); + destination |= ((urb->transfer_buffer_length - 1) << 21); + + uhci_add_td_to_urb(urb, td); + uhci_fill_td(td, status, destination, + virt_to_bus(urb->transfer_buffer)); + + uhci_add_irq_list(uhci, td); + + uhci_insert_td(uhci, &uhci->skeltd[__interval_to_skel(urb->interval)], td); + + uhci_add_urb_list(uhci, urb); + + usb_inc_dev_use(urb->dev); + + return -EINPROGRESS; +} + +static int uhci_unlink_interrupt(urb_t *urb) +{ + struct urb_priv *urbp = urb->hcpriv; + struct uhci_td *td; + struct uhci_device *dev = usb_to_uhci(urb->dev); + struct uhci *uhci = dev->uhci; + + if (!urbp) + return -EINVAL; + + td = urbp->begin; + uhci_remove_td(uhci, td); + if (td->status & TD_CTRL_IOC) + uhci_remove_irq_list(uhci, td); + uhci_schedule_delete_td(uhci, td); + + kfree(urbp); + urb->hcpriv = NULL; + + uhci_remove_urb_list(uhci, urb); + + return 0; +} + +static int uhci_result_interrupt(urb_t *urb) +{ + struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; + struct uhci_td *td; + int status; + + if (!urbp) + return -EINVAL; + + td = urbp->begin; + if (!td) + return -EINVAL; + + status = uhci_status_bits(td->status); + if (status & TD_CTRL_ACTIVE) + return -EINPROGRESS; + + if (status) + return uhci_map_status(status, uhci_packetout(td->info)); + + urb->actual_length += uhci_actual_length(td->status); + + return 0; +} + +static void uhci_reset_interrupt(urb_t *urb) +{ + struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; + struct uhci_td *td; + + if (urb->interval) { + td = urbp->begin; + + usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)); + td->status = (td->status & 0x2F000000) | TD_CTRL_ACTIVE | TD_CTRL_IOC; + td->info &= ~(1 << TD_TOKEN_TOGGLE); + td->info |= (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE); + + urb->status = -EINPROGRESS; + } else + uhci_unlink_interrupt(urb); +} + +/* + * Bulk transfers + */ +static int uhci_submit_bulk(urb_t *urb) +{ + struct uhci_td *td; + struct uhci_qh *qh; + unsigned long destination, status; + struct uhci_device *dev = usb_to_uhci(urb->dev); + struct uhci *uhci = dev->uhci; + int maxsze = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)); + int len = urb->transfer_buffer_length; + unsigned char *data = urb->transfer_buffer; + struct urb_priv *urbp; + + if (len < 0) + return -EINVAL; + + /* The "pipe" thing contains the destination in bits 8--18 */ + destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe); + + /* 3 errors */ + status = (urb->pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE | (3 << 27); + if (!(urb->transfer_flags & USB_DISABLE_SPD)) + status |= TD_CTRL_SPD; + + urbp = kmalloc(sizeof(*urbp), in_interrupt() ? GFP_ATOMIC : GFP_KERNEL); + if (!urbp) + return -ENOMEM; + + urbp->begin = urbp->end = NULL; + + urb->hcpriv = urbp; + + /* + * Build the DATA TD's + */ + while (len > 0) { + int pktsze = len; + + if (pktsze > maxsze) + pktsze = maxsze; + + td = uhci_td_alloc(dev); + if (!td) { + /* FIXME: Free the TD's */ + return -ENOMEM; + } + + uhci_add_td_to_urb(urb, td); + uhci_fill_td(td, status, destination | ((pktsze - 1) << 21) | + (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), + usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE), + virt_to_bus(data)); + + data += pktsze; + len -= maxsze; + + if (len <= 0) { + td->status |= TD_CTRL_IOC; + uhci_add_irq_list(uhci, td); + } + + usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), + usb_pipeout(urb->pipe)); + } + + qh = uhci_qh_alloc(dev); + if (!qh) { + /* FIXME: Free all of the TD's */ + return -ENOMEM; + } + uhci_insert_tds_in_qh(qh, urbp->begin); + + uhci_insert_qh(dev->uhci, &dev->uhci->skel_bulk_qh, qh); + urbp->qh = qh; + + uhci_add_urb_list(uhci, urb); + + usb_inc_dev_use(urb->dev); + + return -EINPROGRESS; +} + +/* We can use the control unlink since they're identical */ +#define uhci_unlink_bulk uhci_unlink_control + +static int uhci_result_bulk(urb_t *urb) +{ + struct urb_priv *urbp = urb->hcpriv; + struct uhci_td *td; + unsigned int status; + + urb->actual_length = 0; + + /* The rest of the TD's (but the last) are data */ + for (td = urbp->begin; td; td = td->next) { + status = uhci_status_bits(td->status); + if (status & TD_CTRL_ACTIVE) + return -EINPROGRESS; + + urb->actual_length += uhci_actual_length(td->status); + + /* If SPD is set then we received a short packet */ + if (td->status & TD_CTRL_SPD && (uhci_actual_length(td->status) < uhci_expected_length(td->info))) { + usb_settoggle(urb->dev, uhci_endpoint(td->info), + uhci_packetout(td->info), + uhci_toggle(td->info) ^ 1); + + goto td_success; + } + + if (status) + goto td_error; + } + +td_success: + uhci_unlink_bulk(urb); + + return 0; + +td_error: + /* Some debugging code */ + if (debug) { + dbg("uhci_result_bulk() failed with status %x", + status); + + /* Print the chain for debugging purposes */ + uhci_show_queue(urbp->qh); + } + + if (status & TD_CTRL_STALLED) { + /* endpoint has stalled - mark it halted */ + usb_endpoint_halt(urb->dev, uhci_endpoint(td->info), + uhci_packetout(td->info)); + return -EPIPE; + } + + return uhci_map_status(status, uhci_packetout(td->info)); +} + +/* + * Isochronous transfers + */ +static int isochronous_find_limits(urb_t *urb, unsigned int *start, unsigned int *end) +{ + urb_t *u, *last_urb = NULL; + struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv; + struct list_head *tmp, *head = &uhci->urb_list; + unsigned long flags; + + spin_lock_irqsave(&uhci->urblist_lock, flags); + tmp = head->next; + while (tmp != head) { + u = list_entry(tmp, urb_t, urb_list); + + /* look for pending URB's with identical pipe handle */ + if ((urb->pipe == u->pipe) && (urb->dev == u->dev) && + (u->status == -EINPROGRESS) && (u != urb)) { + if (!last_urb) + *start = u->start_frame; + last_urb = u; + } + tmp = tmp->next; + } + spin_unlock_irqrestore(&uhci->urblist_lock, flags); + + if (last_urb) { + *end = (last_urb->start_frame + last_urb->number_of_packets) & 1023; + return 0; + } else + return -1; // no previous urb found + +} + +static int isochronous_find_start(urb_t *urb) +{ + int limits; + unsigned int start = 0, end = 0; + + if (urb->number_of_packets > 900) /* 900? Why? */ + return -EFBIG; + + limits = isochronous_find_limits(urb, &start, &end); + + if (urb->transfer_flags & USB_ISO_ASAP) { + if (limits) { + int curframe; + + curframe = uhci_get_current_frame_number(urb->dev) % UHCI_NUMFRAMES; + urb->start_frame = (curframe + 10) % UHCI_NUMFRAMES; + } else + urb->start_frame = end; + } else { + urb->start_frame %= UHCI_NUMFRAMES; + /* FIXME: Sanity check */ + } + + return 0; +} + +static int uhci_submit_isochronous(urb_t *urb) +{ + struct uhci_td *td; + struct uhci_device *dev = usb_to_uhci(urb->dev); + struct uhci *uhci = dev->uhci; + struct urb_priv *urbp; + int i, ret, framenum; + int status, destination; + + status = TD_CTRL_ACTIVE | TD_CTRL_IOS; + destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe); + + ret = isochronous_find_start(urb); + if (ret) + return ret; + + urbp = kmalloc(sizeof(*urbp), in_interrupt() ? GFP_ATOMIC : GFP_KERNEL); + if (!urbp) + return -ENOMEM; + + urbp->begin = urbp->end = NULL; + + urb->hcpriv = urbp; + + framenum = urb->start_frame; + for (i = 0; i < urb->number_of_packets; i++, framenum++) { + if (!urb->iso_frame_desc[i].length) + continue; + + td = uhci_td_alloc(dev); + if (!td) { + /* FIXME: Free the TD's */ + return -ENOMEM; + } + + uhci_add_td_to_urb(urb, td); + uhci_fill_td(td, status, destination | ((urb->iso_frame_desc[i].length - 1) << 21), + virt_to_bus(urb->transfer_buffer + urb->iso_frame_desc[i].offset)); + + if (i + 1 >= urb->number_of_packets) { + td->status |= TD_CTRL_IOC; + uhci_add_irq_list(uhci, td); + } + + uhci_insert_td_frame_list(uhci, td, framenum); + } + + uhci_add_urb_list(uhci, urb); + + usb_inc_dev_use(urb->dev); + + return -EINPROGRESS; +} + +static int uhci_unlink_isochronous(urb_t *urb) +{ + struct urb_priv *urbp = urb->hcpriv; + struct uhci_td *td; + struct uhci_device *dev = usb_to_uhci(urb->dev); + struct uhci *uhci = dev->uhci; + + if (!urbp) + return -EINVAL; + + /* Go through the rest of the TD's, deleting them, then scheduling */ + /* their deletion */ + td = urbp->begin; + while (td) { + struct uhci_td *next = td->next; + + uhci_remove_td(uhci, td); + + if (td->status & TD_CTRL_IOC) + uhci_remove_irq_list(uhci, td); + uhci_schedule_delete_td(uhci, td); + + td = next; + } + + kfree(urbp); + urb->hcpriv = NULL; + + uhci_remove_urb_list(uhci, urb); + + return 0; +} + +static int uhci_result_isochronous(urb_t *urb) +{ + struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; + struct uhci_td *td; + int status; + int i, ret = 0; + + td = urbp->end; + if (!td) /* Nothing to do */ + return -EINVAL; + + status = uhci_status_bits(td->status); + if (status & TD_CTRL_ACTIVE) + return -EINPROGRESS; + + /* Assume no errors, we'll overwrite this if not */ + urb->status = 0; + + urb->actual_length = 0; + for (i = 0, td = urbp->begin; td; i++, td = td->next) { + int actlength; + + actlength = uhci_actual_length(td->status); + urb->iso_frame_desc[i].actual_length = actlength; + urb->actual_length += actlength; + + status = uhci_map_status(uhci_status_bits(td->status), usb_pipeout(urb->pipe)); + urb->iso_frame_desc[i].status = status; + if (status != 0) { + urb->error_count++; + ret = status; + } + } + + uhci_unlink_isochronous(urb); + + return status; +} + +static int uhci_submit_urb(urb_t *urb) +{ + int ret = -EINVAL; + struct uhci *uhci; + + if (!urb) + return -EINVAL; + + if (!urb->dev || !urb->dev->bus) + return -ENODEV; + + uhci = (struct uhci *)urb->dev->bus->hcpriv; + + if (usb_pipedevice(urb->pipe) == uhci->rh.devnum) + return rh_submit_urb(urb); /* Virtual root hub */ + + switch (usb_pipetype(urb->pipe)) { + case PIPE_CONTROL: + ret = uhci_submit_control(urb); + break; + case PIPE_INTERRUPT: + ret = uhci_submit_interrupt(urb); + break; + case PIPE_BULK: + ret = uhci_submit_bulk(urb); + break; + case PIPE_ISOCHRONOUS: + ret = uhci_submit_isochronous(urb); + break; + } + + urb->status = ret; + if (ret == -EINPROGRESS) { + usb_inc_dev_use(urb->dev); + + return 0; + } + + return ret; +} + +/* + * Return the result of a transfer + */ +static void uhci_transfer_result(urb_t *urb) +{ + urb_t *turb; + int proceed = 0, is_ring = 0; + int ret = -EINVAL; + + switch (usb_pipetype(urb->pipe)) { + case PIPE_CONTROL: + ret = uhci_result_control(urb); + break; + case PIPE_INTERRUPT: + /* Interrupts are an exception */ + urb->status = uhci_result_interrupt(urb); + if (urb->status != -EINPROGRESS) { + urb->complete(urb); + uhci_reset_interrupt(urb); + } + return; + case PIPE_BULK: + ret = uhci_result_bulk(urb); + break; + case PIPE_ISOCHRONOUS: + ret = uhci_result_isochronous(urb); + break; + } + + urb->status = ret; + if (urb->status == -EINPROGRESS) + return; + + if (urb->next) { + turb = urb->next; + do { + if (turb->status != -EINPROGRESS) { + proceed = 1; + break; + } + + turb = turb->next; + } while (turb && turb != urb && turb != urb->next); + + if (turb == urb || turb == urb->next) + is_ring = 1; + } + + if (urb->complete && (!proceed || (urb->transfer_flags & USB_URB_EARLY_COMPLETE))) { + urb->complete(urb); + if (!proceed && is_ring) + uhci_submit_urb(urb); + } + + if (proceed && urb->next) { + turb = urb->next; + do { + if (turb->status != -EINPROGRESS && + uhci_submit_urb(turb) != 0) + + turb = turb->next; + } while (turb && turb != urb->next); + + if (urb->complete && !(urb->transfer_flags & USB_URB_EARLY_COMPLETE)) + urb->complete(urb); + } + + usb_dec_dev_use(urb->dev); +} + +static int uhci_unlink_urb(urb_t *urb) +{ + struct uhci *uhci; + int ret = 0; + + if (!urb) + return -EINVAL; + + uhci = (struct uhci *)urb->dev->bus->hcpriv; + + if (usb_pipedevice(urb->pipe) == uhci->rh.devnum) + return rh_unlink_urb(urb); + + if (urb->status == -EINPROGRESS) { + switch (usb_pipetype(urb->pipe)) { + case PIPE_CONTROL: + ret = uhci_unlink_control(urb); + break; + case PIPE_INTERRUPT: + ret = uhci_unlink_interrupt(urb); + break; + case PIPE_BULK: + ret = uhci_unlink_bulk(urb); + break; + case PIPE_ISOCHRONOUS: + ret = uhci_unlink_isochronous(urb); + break; + } + + if (urb->complete) + urb->complete(urb); + + if (in_interrupt()) { /* wait at least 1 frame */ + int errorcount = 10; + + if (errorcount--) + dbg("uhci_unlink_urb called from interrupt for urb %p", urb); + udelay(1000); + } else + schedule_timeout(1+1*HZ/1000); + + usb_dec_dev_use(urb->dev); + } + + urb->status = -ENOENT; + + return ret; +} + +/* + * uhci_get_current_frame_number() + * + * returns the current frame number for a USB bus/controller. + */ +static int uhci_get_current_frame_number(struct usb_device *usb_dev) +{ + struct uhci_device *dev = (struct uhci_device *)usb_dev->hcpriv; + + return inw(dev->uhci->io_addr + USBFRNUM); +} + +struct usb_operations uhci_device_operations = { + uhci_alloc_dev, + uhci_free_dev, + uhci_get_current_frame_number, + uhci_submit_urb, + uhci_unlink_urb +}; + +/* ------------------------------------------------------------------- + Virtual Root Hub + ------------------------------------------------------------------- */ + +static __u8 root_hub_dev_des[] = +{ + 0x12, /* __u8 bLength; */ + 0x01, /* __u8 bDescriptorType; Device */ + 0x00, /* __u16 bcdUSB; v1.0 */ + 0x01, + 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */ + 0x00, /* __u8 bDeviceSubClass; */ + 0x00, /* __u8 bDeviceProtocol; */ + 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */ + 0x00, /* __u16 idVendor; */ + 0x00, + 0x00, /* __u16 idProduct; */ + 0x00, + 0x00, /* __u16 bcdDevice; */ + 0x00, + 0x00, /* __u8 iManufacturer; */ + 0x00, /* __u8 iProduct; */ + 0x00, /* __u8 iSerialNumber; */ + 0x01 /* __u8 bNumConfigurations; */ +}; + + +/* Configuration descriptor */ +static __u8 root_hub_config_des[] = +{ + 0x09, /* __u8 bLength; */ + 0x02, /* __u8 bDescriptorType; Configuration */ + 0x19, /* __u16 wTotalLength; */ + 0x00, + 0x01, /* __u8 bNumInterfaces; */ + 0x01, /* __u8 bConfigurationValue; */ + 0x00, /* __u8 iConfiguration; */ + 0x40, /* __u8 bmAttributes; + Bit 7: Bus-powered, 6: Self-powered, + Bit 5 Remote-wakeup, 4..0: resvd */ + 0x00, /* __u8 MaxPower; */ + + /* interface */ + 0x09, /* __u8 if_bLength; */ + 0x04, /* __u8 if_bDescriptorType; Interface */ + 0x00, /* __u8 if_bInterfaceNumber; */ + 0x00, /* __u8 if_bAlternateSetting; */ + 0x01, /* __u8 if_bNumEndpoints; */ + 0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */ + 0x00, /* __u8 if_bInterfaceSubClass; */ + 0x00, /* __u8 if_bInterfaceProtocol; */ + 0x00, /* __u8 if_iInterface; */ + + /* endpoint */ + 0x07, /* __u8 ep_bLength; */ + 0x05, /* __u8 ep_bDescriptorType; Endpoint */ + 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */ + 0x03, /* __u8 ep_bmAttributes; Interrupt */ + 0x08, /* __u16 ep_wMaxPacketSize; 8 Bytes */ + 0x00, + 0xff /* __u8 ep_bInterval; 255 ms */ +}; + +static __u8 root_hub_hub_des[] = +{ + 0x09, /* __u8 bLength; */ + 0x29, /* __u8 bDescriptorType; Hub-descriptor */ + 0x02, /* __u8 bNbrPorts; */ + 0x00, /* __u16 wHubCharacteristics; */ + 0x00, + 0x01, /* __u8 bPwrOn2pwrGood; 2ms */ + 0x00, /* __u8 bHubContrCurrent; 0 mA */ + 0x00, /* __u8 DeviceRemovable; *** 7 Ports max *** */ + 0xff /* __u8 PortPwrCtrlMask; *** 7 ports max *** */ +}; + +/*-------------------------------------------------------------------------*/ +/* prepare Interrupt pipe transaction data; HUB INTERRUPT ENDPOINT */ +static int rh_send_irq(urb_t *urb) +{ + int i, len = 1; + struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv; + unsigned int io_addr = uhci->io_addr; + __u16 data = 0; + + for (i = 0; i < uhci->rh.numports; i++) { + data |= ((inw(io_addr + USBPORTSC1 + i * 2) & 0xa) > 0 ? (1 << (i + 1)) : 0); + len = (i + 1) / 8 + 1; + } + + *(__u16 *) urb->transfer_buffer = cpu_to_le16(data); + urb->actual_length = len; + urb->status = USB_ST_NOERROR; + + if ((data > 0) && (uhci->rh.send != 0)) { +#ifdef DEBUG /* JE */ +static int foo=5; +if (foo--) +#endif + dbg("root-hub INT complete: port1: %x port2: %x data: %x", + inw(io_addr + USBPORTSC1), inw(io_addr + USBPORTSC2), data); + urb->complete(urb); + } + + return USB_ST_NOERROR; +} + +/*-------------------------------------------------------------------------*/ +/* Virtual Root Hub INTs are polled by this timer every "interval" ms */ +static int rh_init_int_timer(urb_t *urb); + +static void rh_int_timer_do(unsigned long ptr) +{ + int len; + urb_t *urb = (urb_t *)ptr; + struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv; + + if (uhci->rh.send) { + len = rh_send_irq(urb); + if (len > 0) { + urb->actual_length = len; + if (urb->complete) + urb->complete(urb); + } + } + + rh_init_int_timer(urb); +} + +/*-------------------------------------------------------------------------*/ +/* Root Hub INTs are polled by this timer */ +static int rh_init_int_timer(urb_t *urb) +{ + struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv; + + uhci->rh.interval = urb->interval; + init_timer(&uhci->rh.rh_int_timer); + uhci->rh.rh_int_timer.function = rh_int_timer_do; + uhci->rh.rh_int_timer.data = (unsigned long)urb; + uhci->rh.rh_int_timer.expires = jiffies + (HZ * (urb->interval < 30 ? 30 : urb->interval)) / 1000; + add_timer(&uhci->rh.rh_int_timer); + + return 0; +} + +/*-------------------------------------------------------------------------*/ +#define OK(x) len = (x); break + +#define CLR_RH_PORTSTAT(x) \ + status = inw(io_addr + USBPORTSC1 + 2 * (wIndex-1)); \ + status = (status & 0xfff5) & ~(x); \ + outw(status, io_addr + USBPORTSC1 + 2 * (wIndex-1)) + +#define SET_RH_PORTSTAT(x) \ + status = inw(io_addr + USBPORTSC1 + 2 * (wIndex-1)); \ + status = (status & 0xfff5) | (x); \ + outw(status, io_addr + USBPORTSC1 + 2 * (wIndex-1)) + + +/*-------------------------------------------------------------------------*/ +/************************* + ** Root Hub Control Pipe + *************************/ + +static int rh_submit_urb(urb_t *urb) +{ + struct usb_device *usb_dev = urb->dev; + struct uhci *uhci = (struct uhci *)usb_dev->bus->hcpriv; + unsigned int pipe = urb->pipe; + devrequest *cmd = (devrequest *)urb->setup_packet; + void *data = urb->transfer_buffer; + int leni = urb->transfer_buffer_length; + int len = 0; + int status = 0; + int stat = USB_ST_NOERROR; + int i; + unsigned int io_addr = uhci->io_addr; + __u16 cstatus; + __u16 bmRType_bReq; + __u16 wValue; + __u16 wIndex; + __u16 wLength; + + if (usb_pipetype(pipe) == PIPE_INTERRUPT) { + uhci->rh.urb = urb; + uhci->rh.send = 1; + uhci->rh.interval = urb->interval; + rh_init_int_timer(urb); + + return USB_ST_NOERROR; + } + + bmRType_bReq = cmd->requesttype | cmd->request << 8; + wValue = le16_to_cpu(cmd->value); + wIndex = le16_to_cpu(cmd->index); + wLength = le16_to_cpu(cmd->length); + + for (i = 0; i < 8; i++) + uhci->rh.c_p_r[i] = 0; + + switch (bmRType_bReq) { + /* Request Destination: + without flags: Device, + RH_INTERFACE: interface, + RH_ENDPOINT: endpoint, + RH_CLASS means HUB here, + RH_OTHER | RH_CLASS almost ever means HUB_PORT here + */ + + case RH_GET_STATUS: + *(__u16 *)data = cpu_to_le16(1); + OK(2); + case RH_GET_STATUS | RH_INTERFACE: + *(__u16 *)data = cpu_to_le16(0); + OK(2); + case RH_GET_STATUS | RH_ENDPOINT: + *(__u16 *)data = cpu_to_le16(0); + OK(2); + case RH_GET_STATUS | RH_CLASS: + *(__u32 *)data = cpu_to_le32(0); + OK(4); /* hub power */ + case RH_GET_STATUS | RH_OTHER | RH_CLASS: + status = inw(io_addr + USBPORTSC1 + 2 * (wIndex - 1)); + cstatus = ((status & USBPORTSC_CSC) >> (1 - 0)) | + ((status & USBPORTSC_PEC) >> (3 - 1)) | + (uhci->rh.c_p_r[wIndex - 1] << (0 + 4)); + status = (status & USBPORTSC_CCS) | + ((status & USBPORTSC_PE) >> (2 - 1)) | + ((status & USBPORTSC_SUSP) >> (12 - 2)) | + ((status & USBPORTSC_PR) >> (9 - 4)) | + (1 << 8) | /* power on */ + ((status & USBPORTSC_LSDA) << (-8 + 9)); + + *(__u16 *)data = cpu_to_le16(status); + *(__u16 *)(data + 2) = cpu_to_le16(cstatus); + OK(4); + case RH_CLEAR_FEATURE | RH_ENDPOINT: + switch (wValue) { + case RH_ENDPOINT_STALL: + OK(0); + } + break; + case RH_CLEAR_FEATURE | RH_CLASS: + switch (wValue) { + case RH_C_HUB_OVER_CURRENT: + OK(0); /* hub power over current */ + } + break; + case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS: + switch (wValue) { + case RH_PORT_ENABLE: + CLR_RH_PORTSTAT(USBPORTSC_PE); + OK(0); + case RH_PORT_SUSPEND: + CLR_RH_PORTSTAT(USBPORTSC_SUSP); + OK(0); + case RH_PORT_POWER: + OK(0); /* port power */ + case RH_C_PORT_CONNECTION: + SET_RH_PORTSTAT(USBPORTSC_CSC); + OK(0); + case RH_C_PORT_ENABLE: + SET_RH_PORTSTAT(USBPORTSC_PEC); + OK(0); + case RH_C_PORT_SUSPEND: + /*** WR_RH_PORTSTAT(RH_PS_PSSC); */ + OK(0); + case RH_C_PORT_OVER_CURRENT: + OK(0); /* port power over current */ + case RH_C_PORT_RESET: + uhci->rh.c_p_r[wIndex - 1] = 0; + OK(0); + } + break; + case RH_SET_FEATURE | RH_OTHER | RH_CLASS: + switch (wValue) { + case RH_PORT_SUSPEND: + SET_RH_PORTSTAT(USBPORTSC_SUSP); + OK(0); + case RH_PORT_RESET: + SET_RH_PORTSTAT(USBPORTSC_PR); + wait_ms(10); + uhci->rh.c_p_r[wIndex - 1] = 1; + CLR_RH_PORTSTAT(USBPORTSC_PR); + udelay(10); + SET_RH_PORTSTAT(USBPORTSC_PE); + wait_ms(10); + SET_RH_PORTSTAT(0xa); + OK(0); + case RH_PORT_POWER: + OK(0); /* port power ** */ + case RH_PORT_ENABLE: + SET_RH_PORTSTAT (USBPORTSC_PE); + OK(0); + } + break; + case RH_SET_ADDRESS: + uhci->rh.devnum = wValue; + OK(0); + case RH_GET_DESCRIPTOR: + switch ((wValue & 0xff00) >> 8) { + case 0x01: /* device descriptor */ + len = min(leni, min(sizeof(root_hub_dev_des), wLength)); + memcpy(data, root_hub_dev_des, len); + OK(len); + case 0x02: /* configuration descriptor */ + len = min(leni, min(sizeof(root_hub_config_des), wLength)); + memcpy (data, root_hub_config_des, len); + OK(len); + case 0x03: /* string descriptors */ + stat = -EPIPE; + } + break; + case RH_GET_DESCRIPTOR | RH_CLASS: + root_hub_hub_des[2] = uhci->rh.numports; + len = min(leni, min(sizeof(root_hub_hub_des), wLength)); + memcpy(data, root_hub_hub_des, len); + OK(len); + case RH_GET_CONFIGURATION: + *(__u8 *)data = 0x01; + OK(1); + case RH_SET_CONFIGURATION: + OK(0); + default: + stat = -EPIPE; + } + + urb->actual_length = len; + urb->status = stat; + if (urb->complete) + urb->complete(urb); + + return USB_ST_NOERROR; +} +/*-------------------------------------------------------------------------*/ + +static int rh_unlink_urb(urb_t *urb) +{ + struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv; + + uhci->rh.send = 0; + del_timer(&uhci->rh.rh_int_timer); + + return 0; +} +/*-------------------------------------------------------------------*/ + +/* + * This is just incredibly fragile. The timings must be just + * right, and they aren't really documented very well. + * + * Note the short delay between disabling reset and enabling + * the port.. + */ +static void uhci_reset_port(unsigned int port) +{ + unsigned short status; + + status = inw(port); + outw(status | USBPORTSC_PR, port); /* reset port */ + wait_ms(10); + outw(status & ~USBPORTSC_PR, port); + udelay(50); + + status = inw(port); + outw(status | USBPORTSC_PE, port); /* enable port */ + wait_ms(10); + + status = inw(port); + if (!(status & USBPORTSC_PE)) { + outw(status | USBPORTSC_PE, port); /* one more try at enabling port */ + wait_ms(50); + } + +} + +void uhci_free_pending(struct uhci *uhci) +{ + struct list_head *tmp, *head; + + /* Free all of the pending QH's and TD's */ + head = &uhci->td_free_list; + tmp = head->next; + while (tmp != head) { + struct uhci_td *td = list_entry(tmp, struct uhci_td, list); + + tmp = tmp->next; + + list_del(&td->list); + INIT_LIST_HEAD(&td->list); + + uhci_td_free(td); + } + + head = &uhci->qh_free_list; + tmp = head->next; + while (tmp != head) { + struct uhci_qh *qh = list_entry(tmp, struct uhci_qh, list); + + tmp = tmp->next; + + list_del(&qh->list); + INIT_LIST_HEAD(&qh->list); + + uhci_qh_free(qh); + } +} + +static void uhci_interrupt(int irq, void *__uhci, struct pt_regs *regs) +{ + struct uhci *uhci = __uhci; + unsigned int io_addr = uhci->io_addr; + unsigned short status; + unsigned long flags; + struct list_head *tmp, *head; + urb_t *urb; + + /* + * Read the interrupt status, and write it back to clear the + * interrupt cause + */ + status = inw(io_addr + USBSTS); + if (!status) /* shared interrupt, not mine */ + return; + outw(status, io_addr + USBSTS); + + if (status & ~(USBSTS_USBINT | USBSTS_ERROR)) { + if (status & USBSTS_RD) + printk(KERN_INFO "uhci: resume detected, not implemented\n"); + if (status & USBSTS_HSE) + printk(KERN_ERR "uhci: host system error, PCI problems?\n"); + if (status & USBSTS_HCPE) + printk(KERN_ERR "uhci: host controller process error. something bad happened\n"); + if (status & USBSTS_HCH) { + printk(KERN_ERR "uhci: host controller halted. very bad\n"); + /* FIXME: Reset the controller, fix the offending TD */ + } + } + + /* Free all of the pending QH's and TD's */ + spin_lock(&uhci->freelist_lock); + uhci_free_pending(uhci); + spin_unlock(&uhci->freelist_lock); + + /* Walk the list of pending TD's to see which ones completed.. */ + nested_lock(&uhci->irqlist_lock, flags); + head = &uhci->interrupt_list; + tmp = head->next; + while (tmp != head) { + struct uhci_td *td = list_entry(tmp, struct uhci_td, irq_list); + + urb = td->urb; + + tmp = tmp->next; + + /* Checks the status and does all of the magic necessary */ + uhci_transfer_result(urb); + } + nested_unlock(&uhci->irqlist_lock, flags); +} + +static void reset_hc(struct uhci *uhci) +{ + unsigned int io_addr = uhci->io_addr; + + /* Global reset for 50ms */ + outw(USBCMD_GRESET, io_addr + USBCMD); + wait_ms(50); + outw(0, io_addr + USBCMD); + wait_ms(10); +} + +static void start_hc(struct uhci *uhci) +{ + unsigned int io_addr = uhci->io_addr; + int timeout = 1000; + + /* + * Reset the HC - this will force us to get a + * new notification of any already connected + * ports due to the virtual disconnect that it + * implies. + */ + outw(USBCMD_HCRESET, io_addr + USBCMD); + while (inw(io_addr + USBCMD) & USBCMD_HCRESET) { + if (!--timeout) { + printk(KERN_ERR "uhci: USBCMD_HCRESET timed out!\n"); + break; + } + } + + /* Turn on all interrupts */ + outw(USBINTR_TIMEOUT | USBINTR_RESUME | USBINTR_IOC | USBINTR_SP, + io_addr + USBINTR); + + /* Start at frame 0 */ + outw(0, io_addr + USBFRNUM); + outl(virt_to_bus(uhci->fl), io_addr + USBFLBASEADD); + + /* Run and mark it configured with a 64-byte max packet */ + outw(USBCMD_RS | USBCMD_CF | USBCMD_MAXP, io_addr + USBCMD); +} + +/* + * Allocate a frame list, and then setup the skeleton + * + * The hardware doesn't really know any difference + * in the queues, but the order does matter for the + * protocols higher up. The order is: + * + * - any isochronous events handled before any + * of the queues. We don't do that here, because + * we'll create the actual TD entries on demand. + * - The first queue is the "interrupt queue". + * - The second queue is the "control queue". + * - The third queue is "bulk data". + */ +static struct uhci *alloc_uhci(unsigned int io_addr, unsigned int io_size) +{ + int i, port; + struct uhci *uhci; + struct usb_bus *bus; + + uhci = kmalloc(sizeof(*uhci), GFP_KERNEL); + if (!uhci) + return NULL; + + memset(uhci, 0, sizeof(*uhci)); + + uhci->irq = -1; + uhci->io_addr = io_addr; + uhci->io_size = io_size; + + INIT_LIST_HEAD(&uhci->interrupt_list); + INIT_LIST_HEAD(&uhci->urb_list); + INIT_LIST_HEAD(&uhci->td_free_list); + INIT_LIST_HEAD(&uhci->qh_free_list); + + spin_lock_init(&uhci->urblist_lock); + spin_lock_init(&uhci->framelist_lock); + spin_lock_init(&uhci->freelist_lock); + nested_init(&uhci->irqlist_lock); + + /* We need exactly one page (per UHCI specs), how convenient */ + /* We assume that one page is atleast 4k (1024 frames * 4 bytes) */ + uhci->fl = (void *)__get_free_page(GFP_KERNEL); + if (!uhci->fl) + goto au_free_uhci; + + bus = usb_alloc_bus(&uhci_device_operations); + if (!bus) + goto au_free_fl; + + uhci->bus = bus; + bus->hcpriv = uhci; + + /* Initialize the root hub */ + + /* UHCI specs says devices must have 2 ports, but goes on to say */ + /* they may have more but give no way to determine how many they */ + /* have. However, according to the UHCI spec, Bit 7 is always set */ + /* to 1. So we try to use this to our advantage */ + for (port = 0; port < (io_size - 0x10) / 2; port++) { + unsigned int portstatus; + + portstatus = inw(io_addr + 0x10 + (port * 2)); + if (!(portstatus & 0x0080)) + break; + } + if (debug) + info("detected %d ports", port); + + /* This is experimental so anything less than 2 or greater than 8 is */ + /* something weird and we'll ignore it */ + if (port < 2 || port > 8) { + info("port count misdetected? forcing to 2 ports"); + port = 2; + } + + uhci->rh.numports = port; + + /* + * 9 Interrupt queues; link int2 to int1, int4 to int2, etc + * then link int1 to control and control to bulk + */ + for (i = 1; i < 9; i++) { + struct uhci_td *td = &uhci->skeltd[i]; + + uhci_fill_td(td, 0, (UHCI_NULL_DATA_SIZE << 21) | (0x7f << 8) | USB_PID_IN, 0); + td->link = virt_to_bus(&uhci->skeltd[i - 1]); + } + + + uhci_fill_td(&uhci->skel_int1_td, 0, (UHCI_NULL_DATA_SIZE << 21) | (0x7f << 8) | USB_PID_IN, 0); + uhci->skel_int1_td.link = virt_to_bus(&uhci->skel_control_qh) | UHCI_PTR_QH; + + uhci->skel_control_qh.link = virt_to_bus(&uhci->skel_bulk_qh) | UHCI_PTR_QH; + uhci->skel_control_qh.element = UHCI_PTR_TERM; + + uhci->skel_bulk_qh.link = UHCI_PTR_TERM; + uhci->skel_bulk_qh.element = UHCI_PTR_TERM; + + /* + * Fill the frame list: make all entries point to + * the proper interrupt queue. + * + * This is probably silly, but it's a simple way to + * scatter the interrupt queues in a way that gives + * us a reasonable dynamic range for irq latencies. + */ + for (i = 0; i < 1024; i++) { + struct uhci_td *irq = &uhci->skel_int2_td; + + if (i & 1) { + irq++; + if (i & 2) { + irq++; + if (i & 4) { + irq++; + if (i & 8) { + irq++; + if (i & 16) { + irq++; + if (i & 32) { + irq++; + if (i & 64) + irq++; + } + } + } + } + } + } + + /* Only place we don't use the frame list routines */ + uhci->fl->frame[i] = virt_to_bus(irq); + } + + return uhci; + +/* + * error exits: + */ +au_free_fl: + free_page((unsigned long)uhci->fl); +au_free_uhci: + kfree(uhci); + + return NULL; +} + +/* + * De-allocate all resources.. + */ +static void release_uhci(struct uhci *uhci) +{ + if (uhci->irq >= 0) { + free_irq(uhci->irq, uhci); + uhci->irq = -1; + } + + if (uhci->fl) { + free_page((unsigned long)uhci->fl); + uhci->fl = NULL; + } + + usb_free_bus(uhci->bus); + kfree(uhci); +} + +int uhci_start_root_hub(struct uhci *uhci) +{ + struct usb_device *usb_dev; + + usb_dev = usb_alloc_dev(NULL, uhci->bus); + if (!usb_dev) + return -1; + + usb_to_uhci(usb_dev)->uhci = uhci; + + uhci->bus->root_hub = usb_dev; + usb_connect(usb_dev); + + if (usb_new_device(usb_dev) != 0) { + usb_free_dev(usb_dev); + + return -1; + } + + return 0; +} + +/* + * If we've successfully found a UHCI, now is the time to increment the + * module usage count, and return success.. + */ +static int setup_uhci(int irq, unsigned int io_addr, unsigned int io_size) +{ + int retval; + struct uhci *uhci; + + uhci = alloc_uhci(io_addr, io_size); + if (!uhci) + return -ENOMEM; + + INIT_LIST_HEAD(&uhci->uhci_list); + list_add(&uhci->uhci_list, &uhci_list); + + request_region(uhci->io_addr, io_size, "usb-uhci"); + + reset_hc(uhci); + + usb_register_bus(uhci->bus); + start_hc(uhci); + + retval = -EBUSY; + if (request_irq(irq, uhci_interrupt, SA_SHIRQ, "usb-uhci", uhci) == 0) { + uhci->irq = irq; + + if (!uhci_start_root_hub(uhci)) + return 0; + } + + /* Couldn't allocate IRQ if we got here */ + list_del(&uhci->uhci_list); + INIT_LIST_HEAD(&uhci->uhci_list); + + reset_hc(uhci); + release_region(uhci->io_addr, uhci->io_size); + release_uhci(uhci); + + return retval; +} + +static int found_uhci(struct pci_dev *dev) +{ + int i; + + /* Search for the IO base address.. */ + for (i = 0; i < 6; i++) { + unsigned int io_addr = dev->resource[i].start; + unsigned int io_size = + dev->resource[i].end - dev->resource[i].start + 1; + + /* IO address? */ + if (!(dev->resource[i].flags & 1)) + continue; + + /* Is it already in use? */ + if (check_region(io_addr, io_size)) + break; + + /* disable legacy emulation */ + pci_write_config_word(dev, USBLEGSUP, USBLEGSUP_DEFAULT); + + pci_enable_device(dev); + + if (!dev->irq) { + err("found UHCI device with no IRQ assigned. check BIOS settings!"); + continue; + } + + return setup_uhci(dev->irq, io_addr, io_size); + } + + return -1; +} + +#ifdef CONFIG_APM +static int handle_apm_event(apm_event_t event) +{ + static int down = 0; + + switch (event) { + case APM_SYS_SUSPEND: + case APM_USER_SUSPEND: + if (down) { + dbg("received extra suspend event"); + break; + } + down = 1; + break; + case APM_NORMAL_RESUME: + case APM_CRITICAL_RESUME: + if (!down) { + dbg("received bogus resume event"); + break; + } + down = 0; + break; + } + return 0; +} +#endif + +int uhci_init(void) +{ + int retval; + struct pci_dev *dev; + u8 type; + + retval = -ENOMEM; + + /* We throw all of the TD's and QH's into a kmem cache */ + /* TD's and QH's need to be 16 byte aligned and SLAB_HWCACHE_ALIGN */ + /* does this for us */ + uhci_td_cachep = kmem_cache_create("uhci_td", + sizeof(struct uhci_td), 0, + SLAB_HWCACHE_ALIGN, NULL, NULL); + + if (!uhci_td_cachep) + goto td_failed; + + uhci_qh_cachep = kmem_cache_create("uhci_qh", + sizeof(struct uhci_qh), 0, + SLAB_HWCACHE_ALIGN, NULL, NULL); + + if (!uhci_qh_cachep) + goto qh_failed; + + retval = -ENODEV; + dev = NULL; + for (;;) { + dev = pci_find_class(PCI_CLASS_SERIAL_USB << 8, dev); + if (!dev) + break; + + /* Is it the UHCI programming interface? */ + pci_read_config_byte(dev, PCI_CLASS_PROG, &type); + if (type != 0) + continue; + + /* Ok set it up */ + retval = found_uhci(dev); + } + + /* We only want to return an error code if ther was an error */ + /* and we didn't find a UHCI controller */ + if (retval && uhci_list.next == &uhci_list) + goto init_failed; + +#ifdef CONFIG_APM + apm_register_callback(&handle_apm_event); +#endif + + return 0; + +init_failed: + if (kmem_cache_destroy(uhci_qh_cachep)) + printk(KERN_INFO "uhci: not all QH's were freed\n"); + +qh_failed: + if (kmem_cache_destroy(uhci_td_cachep)) + printk(KERN_INFO "uhci: not all TD's were freed\n"); + +td_failed: + return retval; +} + +void uhci_cleanup(void) +{ + struct list_head *next, *tmp, *head = &uhci_list; + int i; + unsigned long flags; + + tmp = head->next; + while (tmp != head) { + struct uhci *uhci = list_entry(tmp, struct uhci, uhci_list); + + next = tmp->next; + + list_del(&uhci->uhci_list); + INIT_LIST_HEAD(&uhci->uhci_list); + + if (uhci->bus->root_hub) + usb_disconnect(&uhci->bus->root_hub); + + usb_deregister_bus(uhci->bus); + + reset_hc(uhci); + release_region(uhci->io_addr, uhci->io_size); + + /* Free any outstanding TD's and QH's */ + spin_lock_irqsave(&uhci->freelist_lock, flags); + uhci_free_pending(uhci); + spin_unlock_irqrestore(&uhci->freelist_lock, flags); + + release_uhci(uhci); + + tmp = next; + } + + if (kmem_cache_destroy(uhci_qh_cachep)) + printk(KERN_INFO "uhci: not all QH's were freed\n"); + + if (kmem_cache_destroy(uhci_td_cachep)) + printk(KERN_INFO "uhci: not all TD's were freed\n"); +} + +#ifdef MODULE +int init_module(void) +{ + return uhci_init(); +} + +void cleanup_module(void) +{ +#ifdef CONFIG_APM + apm_unregister_callback(&handle_apm_event); +#endif + uhci_cleanup(); +} +#endif //MODULE + diff --git a/drivers/usb/uhci.h b/drivers/usb/uhci.h new file mode 100644 index 000000000..65c597861 --- /dev/null +++ b/drivers/usb/uhci.h @@ -0,0 +1,425 @@ +#ifndef __LINUX_UHCI_H +#define __LINUX_UHCI_H + +#include <linux/list.h> + +#include "usb.h" + +/* + * This nested spinlock code is courtesy of Davide Libenzi <dlibenzi@maticad.it> + */ +struct s_nested_lock { + spinlock_t lock; + void *uniq; + short int count; +}; + +#define nested_init(snl) \ + spin_lock_init(&(snl)->lock); \ + (snl)->uniq = NULL; \ + (snl)->count = 0; + +#define nested_lock(snl, flags) \ + if ((snl)->uniq == current) { \ + (snl)->count++; \ + flags = 0; /* No warnings */ \ + } else { \ + spin_lock_irqsave(&(snl)->lock, flags); \ + (snl)->count++; \ + (snl)->uniq = current; \ + } + +#define nested_unlock(snl, flags) \ + if (!--(snl)->count) { \ + (snl)->uniq = NULL; \ + spin_unlock_irqrestore(&(snl)->lock, flags); \ + } + +/* + * Universal Host Controller Interface data structures and defines + */ + +/* Command register */ +#define USBCMD 0 +#define USBCMD_RS 0x0001 /* Run/Stop */ +#define USBCMD_HCRESET 0x0002 /* Host reset */ +#define USBCMD_GRESET 0x0004 /* Global reset */ +#define USBCMD_EGSM 0x0008 /* Global Suspend Mode */ +#define USBCMD_FGR 0x0010 /* Force Global Resume */ +#define USBCMD_SWDBG 0x0020 /* SW Debug mode */ +#define USBCMD_CF 0x0040 /* Config Flag (sw only) */ +#define USBCMD_MAXP 0x0080 /* Max Packet (0 = 32, 1 = 64) */ + +/* Status register */ +#define USBSTS 2 +#define USBSTS_USBINT 0x0001 /* Interrupt due to IOC */ +#define USBSTS_ERROR 0x0002 /* Interrupt due to error */ +#define USBSTS_RD 0x0004 /* Resume Detect */ +#define USBSTS_HSE 0x0008 /* Host System Error - basically PCI problems */ +#define USBSTS_HCPE 0x0010 /* Host Controller Process Error - the scripts were buggy */ +#define USBSTS_HCH 0x0020 /* HC Halted */ + +/* Interrupt enable register */ +#define USBINTR 4 +#define USBINTR_TIMEOUT 0x0001 /* Timeout/CRC error enable */ +#define USBINTR_RESUME 0x0002 /* Resume interrupt enable */ +#define USBINTR_IOC 0x0004 /* Interrupt On Complete enable */ +#define USBINTR_SP 0x0008 /* Short packet interrupt enable */ + +#define USBFRNUM 6 +#define USBFLBASEADD 8 +#define USBSOF 12 + +/* USB port status and control registers */ +#define USBPORTSC1 16 +#define USBPORTSC2 18 +#define USBPORTSC_CCS 0x0001 /* Current Connect Status ("device present") */ +#define USBPORTSC_CSC 0x0002 /* Connect Status Change */ +#define USBPORTSC_PE 0x0004 /* Port Enable */ +#define USBPORTSC_PEC 0x0008 /* Port Enable Change */ +#define USBPORTSC_LS 0x0030 /* Line Status */ +#define USBPORTSC_RD 0x0040 /* Resume Detect */ +#define USBPORTSC_LSDA 0x0100 /* Low Speed Device Attached */ +#define USBPORTSC_PR 0x0200 /* Port Reset */ +#define USBPORTSC_SUSP 0x1000 /* Suspend */ + +/* Legacy support register */ +#define USBLEGSUP 0xc0 +#define USBLEGSUP_DEFAULT 0x2000 /* only PIRQ enable set */ + +#define UHCI_NULL_DATA_SIZE 0x7FF /* for UHCI controller TD */ + +#define UHCI_PTR_BITS 0x000F +#define UHCI_PTR_TERM 0x0001 +#define UHCI_PTR_QH 0x0002 +#define UHCI_PTR_DEPTH 0x0004 + +#define UHCI_NUMFRAMES 1024 /* in the frame list [array] */ +#define UHCI_MAX_SOF_NUMBER 2047 /* in an SOF packet */ +#define CAN_SCHEDULE_FRAMES 1000 /* how far future frames can be scheduled */ + +struct uhci_td; + +struct uhci_qh { + /* Hardware fields */ + __u32 link; /* Next queue */ + __u32 element; /* Queue element pointer */ + + /* Software fields */ + struct uhci_qh *prevqh, *nextqh; /* Previous and next TD in queue */ + + struct uhci_device *dev; /* The owning device */ + + struct list_head list; +} __attribute__((aligned(16))); + +struct uhci_framelist { + __u32 frame[UHCI_NUMFRAMES]; +} __attribute__((aligned(4096))); + +/* + * for TD <status>: + */ +#define TD_CTRL_SPD (1 << 29) /* Short Packet Detect */ +#define TD_CTRL_C_ERR_MASK (3 << 27) /* Error Counter bits */ +#define TD_CTRL_LS (1 << 26) /* Low Speed Device */ +#define TD_CTRL_IOS (1 << 25) /* Isochronous Select */ +#define TD_CTRL_IOC (1 << 24) /* Interrupt on Complete */ +#define TD_CTRL_ACTIVE (1 << 23) /* TD Active */ +#define TD_CTRL_STALLED (1 << 22) /* TD Stalled */ +#define TD_CTRL_DBUFERR (1 << 21) /* Data Buffer Error */ +#define TD_CTRL_BABBLE (1 << 20) /* Babble Detected */ +#define TD_CTRL_NAK (1 << 19) /* NAK Received */ +#define TD_CTRL_CRCTIMEO (1 << 18) /* CRC/Time Out Error */ +#define TD_CTRL_BITSTUFF (1 << 17) /* Bit Stuff Error */ +#define TD_CTRL_ACTLEN_MASK 0x7FF /* actual length, encoded as n - 1 */ + +#define TD_CTRL_ANY_ERROR (TD_CTRL_STALLED | TD_CTRL_DBUFERR | \ + TD_CTRL_BABBLE | TD_CTRL_CRCTIME | TD_CTRL_BITSTUFF) + +#define uhci_status_bits(ctrl_sts) (ctrl_sts & 0xFE0000) +#define uhci_actual_length(ctrl_sts) ((ctrl_sts + 1) & TD_CTRL_ACTLEN_MASK) /* 1-based */ + +#define uhci_ptr_to_virt(x) bus_to_virt(x & ~UHCI_PTR_BITS) + +/* + * for TD <info>: (a.k.a. Token) + */ +#define TD_TOKEN_TOGGLE 19 +#define TD_PID 0xFF + +#define uhci_maxlen(token) ((token) >> 21) +#define uhci_expected_length(info) (((info >> 21) + 1) & TD_CTRL_ACTLEN_MASK) /* 1-based */ +#define uhci_toggle(token) (((token) >> TD_TOKEN_TOGGLE) & 1) +#define uhci_endpoint(token) (((token) >> 15) & 0xf) +#define uhci_devaddr(token) (((token) >> 8) & 0x7f) +#define uhci_devep(token) (((token) >> 8) & 0x7ff) +#define uhci_packetid(token) ((token) & 0xff) +#define uhci_packetout(token) (uhci_packetid(token) != USB_PID_IN) +#define uhci_packetin(token) (uhci_packetid(token) == USB_PID_IN) + +/* + * The documentation says "4 words for hardware, 4 words for software". + * + * That's silly, the hardware doesn't care. The hardware only cares that + * the hardware words are 16-byte aligned, and we can have any amount of + * sw space after the TD entry as far as I can tell. + * + * But let's just go with the documentation, at least for 32-bit machines. + * On 64-bit machines we probably want to take advantage of the fact that + * hw doesn't really care about the size of the sw-only area. + * + * Alas, not anymore, we have more than 4 words for software, woops + */ +struct uhci_td { + /* Hardware fields */ + __u32 link; + __u32 status; + __u32 info; + __u32 buffer; + + /* Software fields */ + unsigned int *frameptr; /* Frame list pointer */ + struct uhci_td *prevtd, *nexttd; /* Previous and next TD in queue */ + + struct uhci_device *dev; + struct urb *urb; /* URB this TD belongs to */ + struct uhci_td *next; /* List of chained TD's for an URB */ + + struct list_head irq_list; /* Active interrupt list.. */ + struct list_head list; +} __attribute__((aligned(16))); + +/* + * Note the alignment requirements of the entries + * + * Each UHCI device has pre-allocated QH and TD entries. + * You can use more than the pre-allocated ones, but I + * don't see you usually needing to. + */ +struct uhci; + +struct uhci_device { + struct usb_device *usb; + + atomic_t refcnt; + + struct uhci *uhci; /* HC this device is connected to */ +}; + +#define uhci_to_usb(uhci) ((uhci)->usb) +#define usb_to_uhci(usb) ((struct uhci_device *)(usb)->hcpriv) + +/* + * There are various standard queues. We set up several different + * queues for each of the three basic queue types: interrupt, + * control, and bulk. + * + * - There are various different interrupt latencies: ranging from + * every other USB frame (2 ms apart) to every 256 USB frames (ie + * 256 ms apart). Make your choice according to how obnoxious you + * want to be on the wire, vs how critical latency is for you. + * - The control list is done every frame. + * - There are 4 bulk lists, so that up to four devices can have a + * bulk list of their own and when run concurrently all four lists + * will be be serviced. + * + * This is a bit misleading, there are various interrupt latencies, but they + * vary a bit, interrupt2 isn't exactly 2ms, it can vary up to 4ms since the + * other queues can "override" it. interrupt4 can vary up to 8ms, etc. Minor + * problem + * + * In the case of the root hub, these QH's are just head's of qh's. Don't + * be scared, it kinda makes sense. Look at this wonderful picture care of + * Linus: + * + * generic- -> dev1- -> generic- -> dev1- -> control- -> bulk- -> ... + * iso-QH iso-QH irq-QH irq-QH QH QH + * | | | | | | + * End dev1-iso-TD1 End dev1-irq-TD1 ... ... + * | + * dev1-iso-TD2 + * | + * .... + * + * This may vary a bit (the UHCI docs don't explicitly say you can put iso + * transfers in QH's and all of their pictures don't have that either) but + * other than that, that is what we're doing now + * + * And now we don't put Iso transfers in QH's, so we don't waste one on it + * --jerdfelt + * + * To keep with Linus' nomenclature, this is called the QH skeleton. These + * labels (below) are only signficant to the root hub's QH's + */ + +#define UHCI_NUM_SKELTD 9 +#define skel_int1_td skeltd[0] +#define skel_int2_td skeltd[1] +#define skel_int4_td skeltd[2] +#define skel_int8_td skeltd[3] +#define skel_int16_td skeltd[4] +#define skel_int32_td skeltd[5] +#define skel_int64_td skeltd[6] +#define skel_int128_td skeltd[7] +#define skel_int256_td skeltd[8] + +#define UHCI_NUM_SKELQH 2 +#define skel_control_qh skelqh[0] +#define skel_bulk_qh skelqh[1] + +/* + * Search tree for determining where <interval> fits in the + * skelqh[] skeleton. + * + * An interrupt request should be placed into the slowest skelqh[] + * which meets the interval/period/frequency requirement. + * An interrupt request is allowed to be faster than <interval> but not slower. + * + * For a given <interval>, this function returns the appropriate/matching + * skelqh[] index value. + * + * NOTE: For UHCI, we don't really need int256_qh since the maximum interval + * is 255 ms. However, we do need an int1_qh since 1 is a valid interval + * and we should meet that frequency when requested to do so. + * This will require some change(s) to the UHCI skeleton. + */ +static inline int __interval_to_skel(int interval) +{ + if (interval < 16) { + if (interval < 4) { + if (interval < 2) + return 0; /* int1 for 0-1 ms */ + return 1; /* int2 for 2-3 ms */ + } + if (interval < 8) + return 2; /* int4 for 4-7 ms */ + return 3; /* int8 for 8-15 ms */ + } + if (interval < 64) { + if (interval < 32) + return 4; /* int16 for 16-31 ms */ + return 5; /* int32 for 32-63 ms */ + } + if (interval < 128) + return 6; /* int64 for 64-127 ms */ + return 7; /* int128 for 128-255 ms (Max.) */ +} + +struct virt_root_hub { + int devnum; /* Address of Root Hub endpoint */ + void *urb; + void *int_addr; + int send; + int interval; + int numports; + int c_p_r[8]; + struct timer_list rh_int_timer; +}; + +/* + * This describes the full uhci information. + * + * Note how the "proper" USB information is just + * a subset of what the full implementation needs. + */ +struct uhci { + int irq; + unsigned int io_addr; + unsigned int io_size; + + struct list_head uhci_list; + + struct usb_bus *bus; + + struct uhci_td skeltd[UHCI_NUM_SKELTD]; /* Skeleton TD's */ + struct uhci_qh skelqh[UHCI_NUM_SKELQH]; /* Skeleton QH's */ + + struct uhci_framelist *fl; /* Frame list */ + + struct s_nested_lock irqlist_lock; + struct list_head interrupt_list; /* List of interrupt-active TD's for this uhci */ + + spinlock_t urblist_lock; + struct list_head urb_list; + + spinlock_t framelist_lock; + + spinlock_t freelist_lock; + struct list_head td_free_list; + struct list_head qh_free_list; + + struct virt_root_hub rh; /* private data of the virtual root hub */ +}; + +struct urb_priv { + struct uhci_qh *qh; /* QH for this URB */ + struct uhci_td *begin; + struct uhci_td *end; +}; + +/* ------------------------------------------------------------------------- + Virtual Root HUB + ------------------------------------------------------------------------- */ +/* destination of request */ +#define RH_INTERFACE 0x01 +#define RH_ENDPOINT 0x02 +#define RH_OTHER 0x03 + +#define RH_CLASS 0x20 +#define RH_VENDOR 0x40 + +/* Requests: bRequest << 8 | bmRequestType */ +#define RH_GET_STATUS 0x0080 +#define RH_CLEAR_FEATURE 0x0100 +#define RH_SET_FEATURE 0x0300 +#define RH_SET_ADDRESS 0x0500 +#define RH_GET_DESCRIPTOR 0x0680 +#define RH_SET_DESCRIPTOR 0x0700 +#define RH_GET_CONFIGURATION 0x0880 +#define RH_SET_CONFIGURATION 0x0900 +#define RH_GET_STATE 0x0280 +#define RH_GET_INTERFACE 0x0A80 +#define RH_SET_INTERFACE 0x0B00 +#define RH_SYNC_FRAME 0x0C80 +/* Our Vendor Specific Request */ +#define RH_SET_EP 0x2000 + +/* Hub port features */ +#define RH_PORT_CONNECTION 0x00 +#define RH_PORT_ENABLE 0x01 +#define RH_PORT_SUSPEND 0x02 +#define RH_PORT_OVER_CURRENT 0x03 +#define RH_PORT_RESET 0x04 +#define RH_PORT_POWER 0x08 +#define RH_PORT_LOW_SPEED 0x09 +#define RH_C_PORT_CONNECTION 0x10 +#define RH_C_PORT_ENABLE 0x11 +#define RH_C_PORT_SUSPEND 0x12 +#define RH_C_PORT_OVER_CURRENT 0x13 +#define RH_C_PORT_RESET 0x14 + +/* Hub features */ +#define RH_C_HUB_LOCAL_POWER 0x00 +#define RH_C_HUB_OVER_CURRENT 0x01 +#define RH_DEVICE_REMOTE_WAKEUP 0x00 +#define RH_ENDPOINT_STALL 0x01 + +/* Our Vendor Specific feature */ +#define RH_REMOVE_EP 0x00 + +#define RH_ACK 0x01 +#define RH_REQ_ERR -1 +#define RH_NACK 0x00 + +/* needed for the debugging code */ +struct uhci_td *uhci_link_to_td(unsigned int element); + +/* Debugging code */ +void uhci_show_td(struct uhci_td *td); +void uhci_show_status(struct uhci *uhci); +void uhci_show_queue(struct uhci_qh *qh); +void uhci_show_queues(struct uhci *uhci); + +#endif + diff --git a/drivers/usb/usb-core.c b/drivers/usb/usb-core.c index 68927a086..9e7557e8f 100644 --- a/drivers/usb/usb-core.c +++ b/drivers/usb/usb-core.c @@ -132,6 +132,9 @@ int usb_init(void) #ifdef CONFIG_USB_UHCI uhci_init(); #endif +#ifdef CONFIG_USB_UHCI_ALT + uhci_init(); +#endif #ifdef CONFIG_USB_OHCI ohci_hcd_init(); #endif diff --git a/drivers/usb/usb-serial.c b/drivers/usb/usb-serial.c index 9f37fcc9e..f30057d9c 100644 --- a/drivers/usb/usb-serial.c +++ b/drivers/usb/usb-serial.c @@ -996,7 +996,7 @@ static int whiteheat_writememory (struct usb_serial *serial, int address, unsign // dbg("whiteheat_writememory %x, %d", address, length); if (!transfer_buffer) { - err("whiteheat_writememory: kmalloc(%d) failed.\n", length); + err("whiteheat_writememory: kmalloc(%d) failed.", length); return -ENOMEM; } memcpy (transfer_buffer, data, length); @@ -1078,7 +1078,7 @@ static int whiteheat_startup (struct usb_serial *serial) response = whiteheat_writememory (serial, record->address, (unsigned char *)record->data, record->data_size, 0xa0); if (response < 0) { - err("whiteheat_writememory failed for second firmware step (%d %04X %p %d)\n", + err("whiteheat_writememory failed for second firmware step (%d %04X %p %d)", response, record->address, record->data, record->data_size); break; } @@ -1127,7 +1127,7 @@ static void visor_serial_close(struct tty_struct *tty, struct file * filp) dbg("visor_serial_close port %d", port); if (!transfer_buffer) { - err("visor_serial_close: kmalloc(%d) failed.\n", 0x12); + err("visor_serial_close: kmalloc(%d) failed.", 0x12); } else { /* send a shutdown message to the device */ usb_control_msg (serial->dev, usb_rcvctrlpipe(serial->dev, 0), VISOR_CLOSE_NOTIFICATION, @@ -1175,7 +1175,7 @@ static int visor_startup (struct usb_serial *serial) unsigned char *transfer_buffer = kmalloc (256, GFP_KERNEL); if (!transfer_buffer) { - err("visor_startup: kmalloc(%d) failed.\n", 256); + err("visor_startup: kmalloc(%d) failed.", 256); return -ENOMEM; } diff --git a/drivers/usb/usb-uhci-debug.h b/drivers/usb/usb-uhci-debug.h new file mode 100644 index 000000000..73d16937a --- /dev/null +++ b/drivers/usb/usb-uhci-debug.h @@ -0,0 +1,195 @@ +#ifdef DEBUG + +static void uhci_show_qh (puhci_desc_t qh) +{ + if (qh->type != QH_TYPE) { + dbg("qh has not QH_TYPE"); + return; + } + dbg("uhci_show_qh %p (%08lX):", qh, virt_to_bus (qh)); + + if (qh->hw.qh.head & UHCI_PTR_TERM) + dbg("Head Terminate"); + else { + if (qh->hw.qh.head & UHCI_PTR_QH) + dbg("Head points to QH"); + else + dbg("Head points to TD"); + + dbg("head: %08X", qh->hw.qh.head & ~UHCI_PTR_BITS); + } + if (qh->hw.qh.element & UHCI_PTR_TERM) + dbg("Element Terminate"); + else { + + if (qh->hw.qh.element & UHCI_PTR_QH) + dbg("Element points to QH"); + else + dbg("Element points to TD"); + dbg("element: %08X", qh->hw.qh.element & ~UHCI_PTR_BITS); + } +} +#endif + +static void uhci_show_td (puhci_desc_t td) +{ + char *spid; + warn("uhci_show_td %p (%08lX) ", td, virt_to_bus (td)); + + switch (td->hw.td.info & 0xff) { + case USB_PID_SETUP: + spid = "SETUP"; + break; + case USB_PID_OUT: + spid = " OUT "; + break; + case USB_PID_IN: + spid = " IN "; + break; + default: + spid = " ? "; + break; + } + + warn("MaxLen=%02x DT%d EndPt=%x Dev=%x, PID=%x(%s) (buf=%08x)", + td->hw.td.info >> 21, + ((td->hw.td.info >> 19) & 1), + (td->hw.td.info >> 15) & 15, + (td->hw.td.info >> 8) & 127, + (td->hw.td.info & 0xff), + spid, + td->hw.td.buffer); + + warn("Len=%02x e%d %s%s%s%s%s%s%s%s%s%s", + td->hw.td.status & 0x7ff, + ((td->hw.td.status >> 27) & 3), + (td->hw.td.status & TD_CTRL_SPD) ? "SPD " : "", + (td->hw.td.status & TD_CTRL_LS) ? "LS " : "", + (td->hw.td.status & TD_CTRL_IOC) ? "IOC " : "", + (td->hw.td.status & TD_CTRL_ACTIVE) ? "Active " : "", + (td->hw.td.status & TD_CTRL_STALLED) ? "Stalled " : "", + (td->hw.td.status & TD_CTRL_DBUFERR) ? "DataBufErr " : "", + (td->hw.td.status & TD_CTRL_BABBLE) ? "Babble " : "", + (td->hw.td.status & TD_CTRL_NAK) ? "NAK " : "", + (td->hw.td.status & TD_CTRL_CRCTIMEO) ? "CRC/Timeo " : "", + (td->hw.td.status & TD_CTRL_BITSTUFF) ? "BitStuff " : "" + ); +#if 1 + if (td->hw.td.link & UHCI_PTR_TERM) + warn("Link Terminate"); + else { + if (td->hw.td.link & UHCI_PTR_QH) + warn("%s, link points to QH @ %08x", + (td->hw.td.link & UHCI_PTR_DEPTH ? "Depth first" : " Breadth first"), + td->hw.td.link & ~UHCI_PTR_BITS); + else + warn("%s, link points to TD @ %08x", + (td->hw.td.link & UHCI_PTR_DEPTH ? "Depth first" : " Breadth first"), + td->hw.td.link & ~UHCI_PTR_BITS); + } +#endif +} +#ifdef DEBUG +static void uhci_show_td_queue (puhci_desc_t td) +{ + dbg("uhci_show_td_queue %p (%08lX):", td, virt_to_bus (td)); + while (1) { + uhci_show_td (td); + if (td->hw.td.link & UHCI_PTR_TERM) + break; + //if(!(td->hw.td.link&UHCI_PTR_DEPTH)) + // break; + if (td != bus_to_virt (td->hw.td.link & ~UHCI_PTR_BITS)) + td = bus_to_virt (td->hw.td.link & ~UHCI_PTR_BITS); + else { + dbg("td points to itself!"); + break; + } +// schedule(); + } +} + +static void uhci_show_queue (puhci_desc_t qh) +{ + dbg("uhci_show_queue %p:", qh); + while (1) { + uhci_show_qh (qh); + + if (qh->hw.qh.element & UHCI_PTR_QH) + dbg("Warning: qh->element points to qh!"); + else if (!(qh->hw.qh.element & UHCI_PTR_TERM)) + uhci_show_td_queue (bus_to_virt (qh->hw.qh.element & ~UHCI_PTR_BITS)); + + if (qh->hw.qh.head & UHCI_PTR_TERM) + break; + + if (qh != bus_to_virt (qh->hw.qh.head & ~UHCI_PTR_BITS)) + qh = bus_to_virt (qh->hw.qh.head & ~UHCI_PTR_BITS); + else { + dbg("qh points to itself!"); + break; + } + } +} + +static void uhci_show_sc (int port, unsigned short status) +{ + dbg(" stat%d = %04x %s%s%s%s%s%s%s%s", + port, + status, + (status & USBPORTSC_SUSP) ? "PortSuspend " : "", + (status & USBPORTSC_PR) ? "PortReset " : "", + (status & USBPORTSC_LSDA) ? "LowSpeed " : "", + (status & USBPORTSC_RD) ? "ResumeDetect " : "", + (status & USBPORTSC_PEC) ? "EnableChange " : "", + (status & USBPORTSC_PE) ? "PortEnabled " : "", + (status & USBPORTSC_CSC) ? "ConnectChange " : "", + (status & USBPORTSC_CCS) ? "PortConnected " : ""); +} + +void uhci_show_status (puhci_t s) +{ + unsigned int io_addr = s->io_addr; + unsigned short usbcmd, usbstat, usbint, usbfrnum; + unsigned int flbaseadd; + unsigned char sof; + unsigned short portsc1, portsc2; + + usbcmd = inw (io_addr + 0); + usbstat = inw (io_addr + 2); + usbint = inw (io_addr + 4); + usbfrnum = inw (io_addr + 6); + flbaseadd = inl (io_addr + 8); + sof = inb (io_addr + 12); + portsc1 = inw (io_addr + 16); + portsc2 = inw (io_addr + 18); + + dbg(" usbcmd = %04x %s%s%s%s%s%s%s%s", + usbcmd, + (usbcmd & USBCMD_MAXP) ? "Maxp64 " : "Maxp32 ", + (usbcmd & USBCMD_CF) ? "CF " : "", + (usbcmd & USBCMD_SWDBG) ? "SWDBG " : "", + (usbcmd & USBCMD_FGR) ? "FGR " : "", + (usbcmd & USBCMD_EGSM) ? "EGSM " : "", + (usbcmd & USBCMD_GRESET) ? "GRESET " : "", + (usbcmd & USBCMD_HCRESET) ? "HCRESET " : "", + (usbcmd & USBCMD_RS) ? "RS " : ""); + + dbg(" usbstat = %04x %s%s%s%s%s%s", + usbstat, + (usbstat & USBSTS_HCH) ? "HCHalted " : "", + (usbstat & USBSTS_HCPE) ? "HostControllerProcessError " : "", + (usbstat & USBSTS_HSE) ? "HostSystemError " : "", + (usbstat & USBSTS_RD) ? "ResumeDetect " : "", + (usbstat & USBSTS_ERROR) ? "USBError " : "", + (usbstat & USBSTS_USBINT) ? "USBINT " : ""); + + dbg(" usbint = %04x", usbint); + dbg(" usbfrnum = (%d)%03x", (usbfrnum >> 10) & 1, + 0xfff & (4 * (unsigned int) usbfrnum)); + dbg(" flbaseadd = %08x", flbaseadd); + dbg(" sof = %02x", sof); + uhci_show_sc (1, portsc1); + uhci_show_sc (2, portsc2); +} +#endif diff --git a/drivers/usb/usb-uhci.c b/drivers/usb/usb-uhci.c index 3fe469699..a85dfab5a 100644 --- a/drivers/usb/usb-uhci.c +++ b/drivers/usb/usb-uhci.c @@ -43,7 +43,7 @@ #include "usb.h" #include "usb-uhci.h" -#include "uhci-debug.h" +#include "usb-uhci-debug.h" #ifdef CONFIG_APM #include <linux/apm_bios.h> diff --git a/drivers/usb/usb.c b/drivers/usb/usb.c index 213f69036..32d6e519e 100644 --- a/drivers/usb/usb.c +++ b/drivers/usb/usb.c @@ -1595,17 +1595,34 @@ int usb_string(struct usb_device *dev, int index, char *buf, size_t size) tbuf = kmalloc(256, GFP_KERNEL); if (!tbuf) return -ENOMEM; + + /* get langid for strings if it's not yet known */ + if (!dev->have_langid) { + err = usb_get_string(dev, 0, 0, tbuf, 4); + if (err < 0) { + err("error getting string descriptor 0 (error=%d)", err); + goto errout; + } else if (tbuf[0] < 4) { + err("string descriptor 0 too short"); + err = -EINVAL; + goto errout; + } else { + dev->have_langid = -1; + dev->string_langid = tbuf[2] | (tbuf[3]<< 8); + /* always use the first langid listed */ + info("USB device number %d default language ID 0x%x", + dev->devnum, dev->string_langid); + } + } + /* - * is this two step process necessary? can't we just - * ask for a maximum length string and then take the length - * that was returned? + * Just ask for a maximum length string and then take the length + * that was returned. */ - err = usb_get_string(dev, dev->string_langid, index, tbuf, 4); - if (err < 0) - goto errout; - err = usb_get_string(dev, dev->string_langid, index, tbuf, tbuf[0]); + err = usb_get_string(dev, dev->string_langid, index, tbuf, 255); if (err < 0) goto errout; + info("actual string desc. length = %d", err); size--; /* leave room for trailing NULL char in output buffer */ for (idx = 0, u = 2; u < err; u += 2) { @@ -1633,7 +1650,6 @@ int usb_string(struct usb_device *dev, int index, char *buf, size_t size) */ int usb_new_device(struct usb_device *dev) { - unsigned char *buf; int addr, err; int tmp; @@ -1709,21 +1725,6 @@ int usb_new_device(struct usb_device *dev) err("failed to set default configuration"); return -1; } - /* get langid for strings */ - buf = kmalloc(256, GFP_KERNEL); - if (!buf) { - err("out of memory\n"); - } else { - err = usb_get_string(dev, 0, 0, buf, 4); - if (err < 0) { - err("error getting string descriptor 0 (error=%d)\n", err); - } else if (buf[0] < 4) { - err("string descriptpr 0 too short\n"); - } else - dev->string_langid = buf[2] | (buf[3]<< 8); - kfree(buf); - info("USB device number %d default language ID 0x%x", dev->devnum, dev->string_langid); - } if (dev->descriptor.iManufacturer) usb_show_string(dev, "Manufacturer", dev->descriptor.iManufacturer); diff --git a/drivers/usb/usb.h b/drivers/usb/usb.h index ec2754b1f..a7b027bf2 100644 --- a/drivers/usb/usb.h +++ b/drivers/usb/usb.h @@ -509,6 +509,7 @@ struct usb_device { struct usb_device_descriptor descriptor;/* Descriptor */ struct usb_config_descriptor *config; /* All of the configs */ + int have_langid; /* whether string_langid is valid yet */ int string_langid; /* language ID for strings */ void *hcpriv; /* Host Controller private data */ diff --git a/drivers/video/dn_accel.h b/drivers/video/dn_accel.h new file mode 100644 index 000000000..b39be932d --- /dev/null +++ b/drivers/video/dn_accel.h @@ -0,0 +1,9 @@ +#ifndef _DN_ACCEL_H_ +#define _DN_ACCEL_H_ + +#include <linux/fb.h> + +void dn_bitblt(struct display *p,int x_src,int y_src, int x_dest, int y_dest, + int x_count, int y_count); + +#endif diff --git a/drivers/video/dn_cfb4.c b/drivers/video/dn_cfb4.c new file mode 100644 index 000000000..453eb52d6 --- /dev/null +++ b/drivers/video/dn_cfb4.c @@ -0,0 +1,546 @@ +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/tty.h> +#include <linux/malloc.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <asm/setup.h> +#include <asm/segment.h> +#include <asm/system.h> +#include <asm/irq.h> +#include <asm/amigahw.h> +#include <asm/amigaints.h> +#include <asm/apollohw.h> +#include <linux/fb.h> +#include <linux/module.h> +#include <video/fbcon.h> +#include <video/fbcon-mfb.h> +#include "dn_accel.h" + +/* apollo video HW definitions */ + +/* + * Control Registers. IOBASE + $x + * + * Note: these are the Memory/IO BASE definitions for a mono card set to the + * alternate address + * + * Control 3A and 3B serve identical functions except that 3A + * deals with control 1 and 3b deals with Color LUT reg. + */ + +#define AP_IOBASE 0x3d0 /* Base address of 1 plane board. */ +#define AP_STATUS isaIO2mem(AP_IOBASE+0) /* Status register. Read */ +#define AP_WRITE_ENABLE isaIO2mem(AP_IOBASE+0) /* Write Enable Register Write */ +#define AP_DEVICE_ID isaIO2mem(AP_IOBASE+1) /* Device ID Register. Read */ +#define AP_ROP_1 isaIO2mem(AP_IOBASE+2) /* Raster Operation reg. Write Word */ +#define AP_DIAG_MEM_REQ isaIO2mem(AP_IOBASE+4) /* Diagnostic Memory Request. Write Word */ +#define AP_CONTROL_0 isaIO2mem(AP_IOBASE+8) /* Control Register 0. Read/Write */ +#define AP_CONTROL_1 isaIO2mem(AP_IOBASE+0xa) /* Control Register 1. Read/Write */ +#define AP_CONTROL_2 isaIO2mem(AP_IOBASE+0xc) /* Control Register 2. Read/Write */ +#define AP_CONTROL_3A isaIO2mem(AP_IOBASE+0xe) /* Control Register 3a. Read/Write */ +#define AP_LUT_RED isaIO2mem(AP_IOBASE+9) /* Red Lookup Table register. Write */ +#define AP_LUT_GREEN isaIO2mem(AP_IOBASE+0xb) /* Green Lookup Table register. Write */ +#define AP_LUT_BLUE isaIO2mem(AP_IOBASE+0xd) /* Blue Lookup Table register. Write */ +#define AP_AD_CHANNEL isaIO2mem(AP_IOBASE+0xf) /* A/D Result/Channel register. Read/Write */ + + +#define FRAME_BUFFER_START 0x0A0000 +#define FRAME_BUFFER_LEN 0x20000 + +/* CREG 0 */ +#define VECTOR_MODE 0x40 /* 010x.xxxx */ +#define DBLT_MODE 0x80 /* 100x.xxxx */ +#define NORMAL_MODE 0xE0 /* 111x.xxxx */ +#define SHIFT_BITS 0x1F /* xxx1.1111 */ + /* other bits are Shift value */ + +/* CREG 1 */ +#define AD_BLT 0x80 /* 1xxx.xxxx */ + +#define ROP_EN 0x10 /* xxx1.xxxx */ +#define DST_EQ_SRC 0x00 /* xxx0.xxxx */ +#define nRESET_SYNC 0x08 /* xxxx.1xxx */ +#define SYNC_ENAB 0x02 /* xxxx.xx1x */ + +#define BLANK_DISP 0x00 /* xxxx.xxx0 */ +#define ENAB_DISP 0x01 /* xxxx.xxx1 */ + +#define NORM_CREG1 (nRESET_SYNC | SYNC_ENAB | ENAB_DISP) /* no reset sync */ + +/* CREG 2B */ + +/* + * Following 3 defines are common to 1, 4 and 8 plane. + */ + +#define S_DATA_1s 0x00 /* 00xx.xxxx */ /* set source to all 1's -- vector drawing */ +#define S_DATA_PIX 0x40 /* 01xx.xxxx */ /* takes source from ls-bits and replicates over 16 bits */ +#define S_DATA_PLN 0xC0 /* 11xx.xxxx */ /* normal, each data access =16-bits in + one plane of image mem */ + +/* CREG 3A/CREG 3B */ +# define RESET_CREG 0x80 /* 1000.0000 */ + +/* ROP REG - all one nibble */ +/* ********* NOTE : this is used r0,r1,r2,r3 *********** */ +#define ROP(r2,r3,r0,r1) ( (U_SHORT)((r0)|((r1)<<4)|((r2)<<8)|((r3)<<12)) ) +#define DEST_ZERO 0x0 +#define SRC_AND_DEST 0x1 +#define SRC_AND_nDEST 0x2 +#define SRC 0x3 +#define nSRC_AND_DEST 0x4 +#define DEST 0x5 +#define SRC_XOR_DEST 0x6 +#define SRC_OR_DEST 0x7 +#define SRC_NOR_DEST 0x8 +#define SRC_XNOR_DEST 0x9 +#define nDEST 0xA +#define SRC_OR_nDEST 0xB +#define nSRC 0xC +#define nSRC_OR_DEST 0xD +#define SRC_NAND_DEST 0xE +#define DEST_ONE 0xF + +#define SWAP(A) ((A>>8) | ((A&0xff) <<8)) + + +void dn_video_setup(char *options, int *ints); + +/* frame buffer operations */ + +static int dn_fb_open(struct fb_info *info,int user); +static int dn_fb_release(struct fb_info *info,int user); +static int dn_fb_get_fix(struct fb_fix_screeninfo *fix, int con, + struct fb_info *info); +static int dn_fb_get_var(struct fb_var_screeninfo *var, int con, + struct fb_info *info); +static int dn_fb_set_var(struct fb_var_screeninfo *var, int isactive, + struct fb_info *info); +static int dn_fb_get_cmap(struct fb_cmap *cmap,int kspc,int con, + struct fb_info *info); +static int dn_fb_set_cmap(struct fb_cmap *cmap,int kspc,int con, + struct fb_info *info); +static int dn_fb_pan_display(struct fb_var_screeninfo *var, int con, + struct fb_info *info); +static int dn_fb_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg, int con, struct fb_info *info); + +static int dnfbcon_switch(int con,struct fb_info *info); +static int dnfbcon_updatevar(int con,struct fb_info *info); +static void dnfbcon_blank(int blank,struct fb_info *info); + +static void dn_fb_set_disp(int con,struct fb_info *info); + +static struct display disp[MAX_NR_CONSOLES]; +static struct fb_info fb_info; +static struct fb_ops dn_fb_ops = { + dn_fb_open,dn_fb_release, dn_fb_get_fix, dn_fb_get_var, dn_fb_set_var, + dn_fb_get_cmap, dn_fb_set_cmap, dn_fb_pan_display, dn_fb_ioctl +}; + +static int currcon=0; + +#define NUM_TOTAL_MODES 1 +struct fb_var_screeninfo dn_fb_predefined[] = { + + { 0, }, + +}; + +static char dn_fb_name[]="Apollo "; + +/* accel stuff */ +#define USE_DN_ACCEL + +static struct display_switch dispsw_apollofb; + +static int dn_fb_open(struct fb_info *info,int user) +{ + /* + * Nothing, only a usage count for the moment + */ + + MOD_INC_USE_COUNT; + return(0); +} + +static int dn_fb_release(struct fb_info *info,int user) +{ + MOD_DEC_USE_COUNT; + return(0); +} + +static int dn_fb_get_fix(struct fb_fix_screeninfo *fix, int con, + struct fb_info *info) { + + strcpy(fix->id,"Apollo Color4"); + fix->smem_start=(FRAME_BUFFER_START+IO_BASE); + fix->smem_len=FRAME_BUFFER_LEN; + fix->type=FB_TYPE_PACKED_PIXELS; + fix->type_aux=0; + fix->visual=FB_VISUAL_MONO10; + fix->xpanstep=0; + fix->ypanstep=0; + fix->ywrapstep=0; + fix->line_length=128; + + return 0; + +} + +static int dn_fb_get_var(struct fb_var_screeninfo *var, int con, + struct fb_info *info) { + + var->xres=1024; + var->yres=800; + var->xres_virtual=1024; + var->yres_virtual=1024; + var->xoffset=0; + var->yoffset=0; + var->bits_per_pixel=1; + var->grayscale=0; + var->nonstd=0; + var->activate=0; + var->height=-1; + var->width=-1; + var->pixclock=0; + var->left_margin=0; + var->right_margin=0; + var->hsync_len=0; + var->vsync_len=0; + var->sync=0; + var->vmode=FB_VMODE_NONINTERLACED; + + return 0; + +} + +static int dn_fb_set_var(struct fb_var_screeninfo *var, int con, + struct fb_info *info) { + + printk("fb_set_var\n"); + if(var->xres!=1024) + return -EINVAL; + if(var->yres!=800) + return -EINVAL; + if(var->xres_virtual!=1024) + return -EINVAL; + if(var->yres_virtual!=1024) + return -EINVAL; + if(var->xoffset!=0) + return -EINVAL; + if(var->yoffset!=0) + return -EINVAL; + if(var->bits_per_pixel!=1) + return -EINVAL; + if(var->grayscale!=0) + return -EINVAL; + if(var->nonstd!=0) + return -EINVAL; + if(var->activate!=0) + return -EINVAL; + if(var->pixclock!=0) + return -EINVAL; + if(var->left_margin!=0) + return -EINVAL; + if(var->right_margin!=0) + return -EINVAL; + if(var->hsync_len!=0) + return -EINVAL; + if(var->vsync_len!=0) + return -EINVAL; + if(var->sync!=0) + return -EINVAL; + if(var->vmode!=FB_VMODE_NONINTERLACED) + return -EINVAL; + + return 0; + +} + +static int dn_fb_get_cmap(struct fb_cmap *cmap,int kspc,int con, + struct fb_info *info) { + + printk("get cmap not supported\n"); + + return -EINVAL; +} + +static int dn_fb_set_cmap(struct fb_cmap *cmap,int kspc,int con, + struct fb_info *info) { + + printk("set cmap not supported\n"); + + return -EINVAL; + +} + +static int dn_fb_pan_display(struct fb_var_screeninfo *var, int con, + struct fb_info *info) { + + printk("panning not supported\n"); + + return -EINVAL; + +} + +static int dn_fb_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg, int con, struct fb_info *info) { + + printk("no IOCTLs as of yet.\n"); + + return -EINVAL; + +} + +static void dn_fb_set_disp(int con, struct fb_info *info) { + + struct fb_fix_screeninfo fix; + struct display *display; + + + dn_fb_get_fix(&fix,con, info); + + if (con>=0) + display=&fb_display[con]; + else + display=&disp[0]; + + if(con==-1) + con=0; + + display->screen_base = (u_char *)fix.smem_start; +printk("screenbase: %lx\n",fix.smem_start); + display->visual = fix.visual; + display->type = fix.type; + display->type_aux = fix.type_aux; + display->ypanstep = fix.ypanstep; + display->ywrapstep = fix.ywrapstep; + display->can_soft_blank = 1; + display->inverse = 0; + display->line_length = fix.line_length; + display->scrollmode = SCROLL_YREDRAW; +#ifdef FBCON_HAS_MFB + display->dispsw = &fbcon_mfb; +#else + display->dispsw=&fbcon_dummy; +#endif + +} + +unsigned long dnfb_init(unsigned long mem_start) { + + int err; + +printk("dn_fb_init\n"); + + fb_info.changevar=NULL; + strcpy(&fb_info.modename[0],dn_fb_name); + fb_info.fontname[0]=0; + fb_info.disp=disp; + fb_info.switch_con=&dnfbcon_switch; + fb_info.updatevar=&dnfbcon_updatevar; + fb_info.blank=&dnfbcon_blank; + fb_info.node = -1; + fb_info.fbops = &dn_fb_ops; + fb_info.flags = FBINFO_FLAG_DEFAULT; + + dn_fb_get_var(&disp[0].var,0, &fb_info); + dn_fb_set_disp(-1, &fb_info); + +printk("dn_fb_init: register\n"); + err=register_framebuffer(&fb_info); + if(err < 0) { + panic("unable to register apollo frame buffer\n"); + } + + /* now we have registered we can safely setup the hardware */ + + outb(RESET_CREG, AP_CONTROL_3A); + outb(NORMAL_MODE, AP_CONTROL_0); + outb((AD_BLT | DST_EQ_SRC | NORM_CREG1), AP_CONTROL_1); + outb(S_DATA_PLN, AP_CONTROL_2); + outw(SWAP(0x3), AP_ROP_1); + + printk("apollo frame buffer alive and kicking !\n"); + + + return mem_start; + +} + + +static int dnfbcon_switch(int con, struct fb_info *info) { + + currcon=con; + + return 0; + +} + +static int dnfbcon_updatevar(int con, struct fb_info *info) { + + return 0; + +} + +static void dnfbcon_blank(int blank, struct fb_info *info) { + + if(blank) { + outb(0x0, AP_CONTROL_3A); + } + else { + outb(0x1, AP_CONTROL_3A); + } + + return ; + +} + +void dn_video_setup(char *options, int *ints) { + + return; + +} + +void dn_bitblt(struct display *p,int x_src,int y_src, int x_dest, int y_dest, + int x_count, int y_count) { + + int incr,y_delta,pre_read=0,x_end,x_word_count; + ushort *src,dummy; + uint start_mask,end_mask,dest; + short i,j; + + incr=(y_dest<=y_src) ? 1 : -1 ; + + src=(ushort *)(p->screen_base+ y_src*p->next_line+(x_src >> 4)); + dest=y_dest*(p->next_line >> 1)+(x_dest >> 4); + + if(incr>0) { + y_delta=(p->next_line*8)-x_src-x_count; + x_end=x_dest+x_count-1; + x_word_count=(x_end>>4) - (x_dest >> 4) + 1; + start_mask=0xffff0000 >> (x_dest & 0xf); + end_mask=0x7ffff >> (x_end & 0xf); + outb((((x_dest & 0xf) - (x_src &0xf)) % 16)|(0x4 << 5),AP_CONTROL_0); + if((x_dest & 0xf) < (x_src & 0xf)) + pre_read=1; + } + else { + y_delta=-((p->next_line*8)-x_src-x_count); + x_end=x_dest-x_count+1; + x_word_count=(x_dest>>4) - (x_end >> 4) + 1; + start_mask=0x7ffff >> (x_dest & 0xf); + end_mask=0xffff0000 >> (x_end & 0xf); + outb(((-((x_src & 0xf) - (x_dest &0xf))) % 16)|(0x4 << 5),AP_CONTROL_0); + if((x_dest & 0xf) > (x_src & 0xf)) + pre_read=1; + } + + for(i=0;i<y_count;i++) { + + if(pre_read) { + dummy=*src; + src+=incr; + } + + if(x_word_count) { + outb(start_mask,AP_WRITE_ENABLE); + *src=dest; + src+=incr; + dest+=incr; + outb(0,AP_WRITE_ENABLE); + + for(j=1;j<(x_word_count-1);j++) { + *src=dest; + src+=incr; + dest+=incr; + } + + outb(start_mask,AP_WRITE_ENABLE); + *src=dest; + dest+=incr; + src+=incr; + } + else { + outb(start_mask | end_mask, AP_WRITE_ENABLE); + *src=dest; + dest+=incr; + src+=incr; + } + src+=(y_delta/16); + dest+=(y_delta/16); + } + outb(NORMAL_MODE,AP_CONTROL_0); +} + +static void bmove_apollofb(struct display *p, int sy, int sx, int dy, int dx, + int height, int width) +{ + + int fontheight,fontwidth; + + fontheight=fontheight(p); + fontwidth=fontwidth(p); + +#ifdef USE_DN_ACCEL + dn_bitblt(p,sx,sy*fontheight,dx,dy*fontheight,width*fontwidth, + height*fontheight); +#else + u_char *src, *dest; + u_int rows; + + if (sx == 0 && dx == 0 && width == p->next_line) { + src = p->screen_base+sy*fontheight*width; + dest = p->screen_base+dy*fontheight*width; + mymemmove(dest, src, height*fontheight*width); + } else if (dy <= sy) { + src = p->screen_base+sy*fontheight*p->next_line+sx; + dest = p->screen_base+dy*fontheight*p->next_line+dx; + for (rows = height*fontheight; rows--;) { + mymemmove(dest, src, width); + src += p->next_line; + dest += p->next_line; + } + } else { + src = p->screen_base+((sy+height)*fontheight-1)*p->next_line+sx; + dest = p->screen_base+((dy+height)*fontheight-1)*p->next_line+dx; + for (rows = height*fontheight; rows--;) { + mymemmove(dest, src, width); + src -= p->next_line; + dest -= p->next_line; + } + } +#endif +} + +static void clear_apollofb(struct vc_data *conp, struct display *p, int sy, int sx, + int height, int width) +{ + fbcon_mfb_clear(conp,p,sy,sx,height,width); +} + +static void putc_apollofb(struct vc_data *conp, struct display *p, int c, int yy, + int xx) +{ + fbcon_mfb_putc(conp,p,c,yy,xx); +} + +static void putcs_apollofb(struct vc_data *conp, struct display *p, const unsigned short *s, + int count, int yy, int xx) +{ + fbcon_mfb_putcs(conp,p,s,count,yy,xx); +} + +static void rev_char_apollofb(struct display *p, int xx, int yy) +{ + fbcon_mfb_revc(p,xx,yy); +} + +static struct display_switch dispsw_apollofb = { + fbcon_mfb_setup, bmove_apollofb, clear_apollofb, + putc_apollofb, putcs_apollofb, rev_char_apollofb +}; diff --git a/drivers/video/dn_cfb8.c b/drivers/video/dn_cfb8.c new file mode 100644 index 000000000..fe650f6e3 --- /dev/null +++ b/drivers/video/dn_cfb8.c @@ -0,0 +1,594 @@ +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/tty.h> +#include <linux/malloc.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <asm/setup.h> +#include <asm/segment.h> +#include <asm/system.h> +#include <asm/irq.h> +#include <asm/amigahw.h> +#include <asm/amigaints.h> +#include <asm/apollohw.h> +#include <linux/fb.h> +#include <linux/module.h> +#include "dn_accel.h" +#include "fbcon.h" +#include "fbcon-mfb.h" + +/* apollo video HW definitions */ + +/* + * Control Registers. IOBASE + $x + * + * Note: these are the Memory/IO BASE definitions for a mono card set to the + * alternate address + * + * Control 3A and 3B serve identical functions except that 3A + * deals with control 1 and 3b deals with Color LUT reg. + */ + +#define AP_IOBASE 0x3d0 /* Base address of 1 plane board. */ +#define AP_STATUS isaIO2mem(AP_IOBASE+0) /* Status register. Read */ +#define AP_WRITE_ENABLE isaIO2mem(AP_IOBASE+0) /* Write Enable Register Write */ +#define AP_DEVICE_ID isaIO2mem(AP_IOBASE+1) /* Device ID Register. Read */ +#define AP_ROP_0 isaIO2mem(AP_IOBASE+2) /* Raster Operation reg. Write Word */ +#define AP_ROP_1 isaIO2mem(AP_IOBASE+4) /* Raster Operation reg. Write Word */ +#define AP_DIAG_MEM_REQ isaIO2mem(AP_IOBASE+6) /* Diagnostic Memory Request. Write Word */ +#define AP_CONTROL_0 isaIO2mem(AP_IOBASE+8) /* Control Register 0. Read/Write */ +#define AP_LUT_DATA isaIO2mem(AP_IOBASE+9) /* Control Register 0. Read/Write */ +#define AP_CONTROL_1 isaIO2mem(AP_IOBASE+0xa) /* Control Register 1. Read/Write */ +#define AP_LUT_CONTROL isaIO2mem(AP_IOBASE+0xb) /* Control Register 1. Read/Write */ +#define AP_CONTROL_2A isaIO2mem(AP_IOBASE+0xc) /* Control Register 2. Read/Write */ +#define AP_CONTROL_2B isaIO2mem(AP_IOBASE+0xd) /* Control Register 2. Read/Write */ +#define AP_CONTROL_3A isaIO2mem(AP_IOBASE+0xe) /* Control Register 3a. Read/Write */ +#define AP_CONTROL_3B isaIO2mem(AP_IOBASE+0xf) /* Control Register 3a. Read/Write */ + + +#define FRAME_BUFFER_START 0x0A0000 +#define FRAME_BUFFER_LEN 0x20000 + +/* CREG 0 */ +#define VECTOR_MODE 0x40 /* 010x.xxxx */ +#define DBLT_MODE 0x80 /* 100x.xxxx */ +#define NORMAL_MODE 0xE0 /* 111x.xxxx */ +#define SHIFT_BITS 0x1F /* xxx1.1111 */ + /* other bits are Shift value */ + +/* CREG 1 */ +#define AD_BLT 0x80 /* 1xxx.xxxx */ + +#define ROP_EN 0x10 /* xxx1.xxxx */ +#define DST_EQ_SRC 0x00 /* xxx0.xxxx */ +#define nRESET_SYNC 0x08 /* xxxx.1xxx */ +#define SYNC_ENAB 0x02 /* xxxx.xx1x */ + +#define BLANK_DISP 0x00 /* xxxx.xxx0 */ +#define ENAB_DISP 0x01 /* xxxx.xxx1 */ + +#define NORM_CREG1 (nRESET_SYNC | SYNC_ENAB | ENAB_DISP) /* no reset sync */ + +/* CREG 2B */ + +/* + * Following 3 defines are common to 1, 4 and 8 plane. + */ + +#define S_DATA_1s 0x00 /* 00xx.xxxx */ /* set source to all 1's -- vector drawing */ +#define S_DATA_PIX 0x40 /* 01xx.xxxx */ /* takes source from ls-bits and replicates over 16 bits */ +#define S_DATA_PLN 0xC0 /* 11xx.xxxx */ /* normal, each data access =16-bits in + one plane of image mem */ + +/* CREG 3A/CREG 3B */ +# define RESET_CREG 0x80 /* 1000.0000 */ + +/* ROP REG - all one nibble */ +/* ********* NOTE : this is used r0,r1,r2,r3 *********** */ +#define ROP(r2,r3,r0,r1) ( (U_SHORT)((r0)|((r1)<<4)|((r2)<<8)|((r3)<<12)) ) +#define DEST_ZERO 0x0 +#define SRC_AND_DEST 0x1 +#define SRC_AND_nDEST 0x2 +#define SRC 0x3 +#define nSRC_AND_DEST 0x4 +#define DEST 0x5 +#define SRC_XOR_DEST 0x6 +#define SRC_OR_DEST 0x7 +#define SRC_NOR_DEST 0x8 +#define SRC_XNOR_DEST 0x9 +#define nDEST 0xA +#define SRC_OR_nDEST 0xB +#define nSRC 0xC +#define nSRC_OR_DEST 0xD +#define SRC_NAND_DEST 0xE +#define DEST_ONE 0xF + +#define SWAP(A) ((A>>8) | ((A&0xff) <<8)) + + +void dn_video_setup(char *options, int *ints); + +/* frame buffer operations */ + +static int dn_fb_open(struct fb_info *info); +static int dn_fb_release(struct fb_info *info); +static int dn_fb_get_fix(struct fb_fix_screeninfo *fix, int con, + struct fb_info *info); +static int dn_fb_get_var(struct fb_var_screeninfo *var, int con, + struct fb_info *info); +static int dn_fb_set_var(struct fb_var_screeninfo *var, int isactive, + struct fb_info *info); +static int dn_fb_get_cmap(struct fb_cmap *cmap,int kspc,int con, + struct fb_info *info); +static int dn_fb_set_cmap(struct fb_cmap *cmap,int kspc,int con, + struct fb_info *info); +static int dn_fb_pan_display(struct fb_var_screeninfo *var, int con, + struct fb_info *info); +static int dn_fb_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg, int con, struct fb_info *info); + +static int dnfbcon_switch(int con,struct fb_info *info); +static int dnfbcon_updatevar(int con,struct fb_info *info); +static void dnfbcon_blank(int blank,struct fb_info *info); + +static void dn_fb_set_disp(int con,struct fb_info *info); + +static struct display disp[MAX_NR_CONSOLES]; +static struct fb_info fb_info; +static struct fb_ops dn_fb_ops = { + dn_fb_open,dn_fb_release, dn_fb_get_fix, dn_fb_get_var, dn_fb_set_var, + dn_fb_get_cmap, dn_fb_set_cmap, dn_fb_pan_display, dn_fb_ioctl +}; + +static int currcon=0; + +#define NUM_TOTAL_MODES 1 +struct fb_var_screeninfo dn_fb_predefined[] = { + + { 0, }, + +}; + +static char dn_fb_name[]="Apollo "; + +/* accel stuff */ +#define USE_DN_ACCEL + +static struct display_switch dispsw_apollofb; + +static int dn_fb_open(struct fb_info *info) +{ + /* + * Nothing, only a usage count for the moment + */ + + MOD_INC_USE_COUNT; + return(0); +} + +static int dn_fb_release(struct fb_info *info) +{ + MOD_DEC_USE_COUNT; + return(0); +} + +static int dn_fb_get_fix(struct fb_fix_screeninfo *fix, int con, + struct fb_info *info) { + + strcpy(fix->id,"Apollo Color8"); + fix->smem_start=(char*)(FRAME_BUFFER_START+IO_BASE); + fix->smem_len=FRAME_BUFFER_LEN; + fix->type=FB_TYPE_PACKED_PIXELS; + fix->type_aux=0; + fix->visual=FB_VISUAL_MONO10; + fix->xpanstep=0; + fix->ypanstep=0; + fix->ywrapstep=0; + fix->line_length=128; + + return 0; + +} + +static int dn_fb_get_var(struct fb_var_screeninfo *var, int con, + struct fb_info *info) { + + var->xres=1024; + var->yres=800; + var->xres_virtual=1024; + var->yres_virtual=1024; + var->xoffset=0; + var->yoffset=0; + var->bits_per_pixel=1; + var->grayscale=0; + var->nonstd=0; + var->activate=0; + var->height=-1; + var->width=-1; + var->pixclock=0; + var->left_margin=0; + var->right_margin=0; + var->hsync_len=0; + var->vsync_len=0; + var->sync=0; + var->vmode=FB_VMODE_NONINTERLACED; + + return 0; + +} + +static int dn_fb_set_var(struct fb_var_screeninfo *var, int con, + struct fb_info *info) { + + printk("fb_set_var\n"); + if(var->xres!=1024) + return -EINVAL; + if(var->yres!=800) + return -EINVAL; + if(var->xres_virtual!=1024) + return -EINVAL; + if(var->yres_virtual!=1024) + return -EINVAL; + if(var->xoffset!=0) + return -EINVAL; + if(var->yoffset!=0) + return -EINVAL; + if(var->bits_per_pixel!=1) + return -EINVAL; + if(var->grayscale!=0) + return -EINVAL; + if(var->nonstd!=0) + return -EINVAL; + if(var->activate!=0) + return -EINVAL; + if(var->pixclock!=0) + return -EINVAL; + if(var->left_margin!=0) + return -EINVAL; + if(var->right_margin!=0) + return -EINVAL; + if(var->hsync_len!=0) + return -EINVAL; + if(var->vsync_len!=0) + return -EINVAL; + if(var->sync!=0) + return -EINVAL; + if(var->vmode!=FB_VMODE_NONINTERLACED) + return -EINVAL; + + return 0; + +} + +static int dn_fb_get_cmap(struct fb_cmap *cmap,int kspc,int con, + struct fb_info *info) { + + printk("get cmap not supported\n"); + + return -EINVAL; +} + +static int dn_fb_set_cmap(struct fb_cmap *cmap,int kspc,int con, + struct fb_info *info) { + + printk("set cmap not supported\n"); + + return -EINVAL; + +} + +static int dn_fb_pan_display(struct fb_var_screeninfo *var, int con, + struct fb_info *info) { + + printk("panning not supported\n"); + + return -EINVAL; + +} + +static int dn_fb_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg, int con, struct fb_info *info) { + + printk("no IOCTLs as of yet.\n"); + + return -EINVAL; + +} + +static void dn_fb_set_disp(int con, struct fb_info *info) { + + struct fb_fix_screeninfo fix; + + dn_fb_get_fix(&fix,con, info); + if(con==-1) + con=0; + + disp[con].screen_base = (u_char *)fix.smem_start; +printk("screenbase: %p\n",fix.smem_start); + disp[con].visual = fix.visual; + disp[con].type = fix.type; + disp[con].type_aux = fix.type_aux; + disp[con].ypanstep = fix.ypanstep; + disp[con].ywrapstep = fix.ywrapstep; + disp[con].can_soft_blank = 1; + disp[con].inverse = 0; + disp[con].line_length = fix.line_length; + disp[con].dispsw = &dispsw_apollofb; +} + +unsigned long dn_fb_init(unsigned long mem_start) { + + int err; + +printk("dn_fb_init\n"); + + fb_info.changevar=NULL; + strcpy(&fb_info.modename[0],dn_fb_name); + fb_info.fontname[0]=0; + fb_info.disp=disp; + fb_info.switch_con=&dnfbcon_switch; + fb_info.updatevar=&dnfbcon_updatevar; + fb_info.blank=&dnfbcon_blank; + fb_info.node = -1; + fb_info.fbops = &dn_fb_ops; + +printk("dn_fb_init: register\n"); + err=register_framebuffer(&fb_info); + if(err < 0) { + panic("unable to register apollo frame buffer\n"); + } + + /* now we have registered we can safely setup the hardware */ + + outb(RESET_CREG, AP_CONTROL_3A); + outb(RESET_CREG, AP_CONTROL_3B); + outw(0x0, AP_WRITE_ENABLE); + outb(NORMAL_MODE, AP_CONTROL_0); + outb((AD_BLT | DST_EQ_SRC | NORM_CREG1), AP_CONTROL_1); + outb(0, AP_CONTROL_2A); + outb(S_DATA_PLN, AP_CONTROL_2B); + outw(SWAP(0x3), AP_ROP_1); + + printk("apollo frame buffer alive and kicking !\n"); + + dn_fb_get_var(&disp[0].var,0, &fb_info); + + dn_fb_set_disp(-1, &fb_info); + + return mem_start; + +} + + +static int dnfbcon_switch(int con, struct fb_info *info) { + + currcon=con; + + return 0; + +} + +static int dnfbcon_updatevar(int con, struct fb_info *info) { + + return 0; + +} + +static void dnfbcon_blank(int blank, struct fb_info *info) { + + if(blank) { + outb(0x0, AP_CONTROL_3A); + } + else { + outb(0x1, AP_CONTROL_3A); + } + + return ; + +} + +void dn_video_setup(char *options, int *ints) { + + return; + +} + +void dn_bitblt(struct display *p,int x_src,int y_src, int x_dest, int y_dest, + int x_count, int y_count) { + + int incr,y_delta,pre_read=0,x_end,x_word_count; + ushort *src,dummy; + uint start_mask,end_mask,dest; + short i,j; + + incr=(y_dest<=y_src) ? 1 : -1 ; + + src=(ushort *)(p->screen_base+ y_src*p->next_line+(x_src >> 4)); + dest=y_dest*(p->next_line >> 1)+(x_dest >> 4); + + if(incr>0) { + y_delta=(p->next_line*8)-x_src-x_count; + x_end=x_dest+x_count-1; + x_word_count=(x_end>>4) - (x_dest >> 4) + 1; + start_mask=0xffff0000 >> (x_dest & 0xf); + end_mask=0x7ffff >> (x_end & 0xf); + outb((((x_dest & 0xf) - (x_src &0xf)) % 16)|(0x4 << 5),AP_CONTROL_0); + if((x_dest & 0xf) < (x_src & 0xf)) + pre_read=1; + } + else { + y_delta=-((p->next_line*8)-x_src-x_count); + x_end=x_dest-x_count+1; + x_word_count=(x_dest>>4) - (x_end >> 4) + 1; + start_mask=0x7ffff >> (x_dest & 0xf); + end_mask=0xffff0000 >> (x_end & 0xf); + outb(((-((x_src & 0xf) - (x_dest &0xf))) % 16)|(0x4 << 5),AP_CONTROL_0); + if((x_dest & 0xf) > (x_src & 0xf)) + pre_read=1; + } + + for(i=0;i<y_count;i++) { + + if(pre_read) { + dummy=*src; + src+=incr; + } + + if(x_word_count) { + outb(start_mask,AP_WRITE_ENABLE); + *src=dest; + src+=incr; + dest+=incr; + outb(0,AP_WRITE_ENABLE); + + for(j=1;j<(x_word_count-1);j++) { + *src=dest; + src+=incr; + dest+=incr; + } + + outb(start_mask,AP_WRITE_ENABLE); + *src=dest; + dest+=incr; + src+=incr; + } + else { + outb(start_mask | end_mask, AP_WRITE_ENABLE); + *src=dest; + dest+=incr; + src+=incr; + } + src+=(y_delta/16); + dest+=(y_delta/16); + } + outb(NORMAL_MODE,AP_CONTROL_0); +} + +static void bmove_apollofb(struct display *p, int sy, int sx, int dy, int dx, + int height, int width) +{ + +#ifdef USE_DN_ACCEL + dn_bitblt(p,sx,sy*p->fontheight,dx,dy*p->fontheight,width*p->fontwidth, + height*p->fontheight); +#else + u_char *src, *dest; + u_int rows; + + if (sx == 0 && dx == 0 && width == p->next_line) { + src = p->screen_base+sy*p->fontheight*width; + dest = p->screen_base+dy*p->fontheight*width; + mymemmove(dest, src, height*p->fontheight*width); + } else if (dy <= sy) { + src = p->screen_base+sy*p->fontheight*p->next_line+sx; + dest = p->screen_base+dy*p->fontheight*p->next_line+dx; + for (rows = height*p->fontheight; rows--;) { + mymemmove(dest, src, width); + src += p->next_line; + dest += p->next_line; + } + } else { + src = p->screen_base+((sy+height)*p->fontheight-1)*p->next_line+sx; + dest = p->screen_base+((dy+height)*p->fontheight-1)*p->next_line+dx; + for (rows = height*p->fontheight; rows--;) { + mymemmove(dest, src, width); + src -= p->next_line; + dest -= p->next_line; + } + } +#endif +} + +static void clear_apollofb(struct vc_data *conp, struct display *p, int sy, int sx, + int height, int width) +{ + u_char *dest; + u_int rows; + + dest = p->screen_base+sy*p->fontheight*p->next_line+sx; + + if (sx == 0 && width == p->next_line) + if (attr_reverse(p,conp)) + mymemset(dest, height*p->fontheight*width); + else + mymemclear(dest, height*p->fontheight*width); + else + for (rows = height*p->fontheight; rows--; dest += p->next_line) + if (attr_reverse(p,conp)) + mymemset(dest, width); + else + mymemclear_small(dest, width); +} + +static void putc_apollofb(struct vc_data *conp, struct display *p, int c, int yy, + int xx) +{ + u_char *dest, *cdat; + u_int rows, bold, revs, underl; + u_char d; + + c &= 0xff; + + dest = p->screen_base+yy*p->fontheight*p->next_line+xx; + cdat = p->fontdata+c*p->fontheight; + bold = attr_bold(p,conp); + revs = attr_reverse(p,conp); + underl = attr_underline(p,conp); + + for (rows = p->fontheight; rows--; dest += p->next_line) { + d = *cdat++; + if (underl && !rows) + d = 0xff; + else if (bold) + d |= d>>1; + if (revs) + d = ~d; + *dest = d; + } +} + +static void putcs_apollofb(struct vc_data *conp, struct display *p, const char *s, + int count, int yy, int xx) +{ + u_char *dest, *dest0, *cdat; + u_int rows, bold, revs, underl; + u_char c, d; + + dest0 = p->screen_base+yy*p->fontheight*p->next_line+xx; + bold = attr_bold(p,conp); + revs = attr_reverse(p,conp); + underl = attr_underline(p,conp); + + while (count--) { + c = *s++; + dest = dest0++; + cdat = p->fontdata+c*p->fontheight; + for (rows = p->fontheight; rows--; dest += p->next_line) { + d = *cdat++; + if (underl && !rows) + d = 0xff; + else if (bold) + d |= d>>1; + if (revs) + d = ~d; + *dest = d; + } + } +} + +static void rev_char_apollofb(struct display *p, int xx, int yy) +{ + u_char *dest; + u_int rows; + + dest = p->screen_base+yy*p->fontheight*p->next_line+xx; + for (rows = p->fontheight; rows--; dest += p->next_line) + *dest = ~*dest; +} + +static struct display_switch dispsw_apollofb = { + fbcon_mfb_setup, bmove_apollofb, clear_apollofb, + putc_apollofb, putcs_apollofb, rev_char_apollofb +}; diff --git a/drivers/video/dnfb.c b/drivers/video/dnfb.c index 3730695dd..30860ae2b 100644 --- a/drivers/video/dnfb.c +++ b/drivers/video/dnfb.c @@ -10,14 +10,15 @@ #include <asm/segment.h> #include <asm/system.h> #include <asm/irq.h> +#include <asm/amigahw.h> +#include <asm/amigaints.h> #include <asm/apollohw.h> #include <linux/fb.h> #include <linux/module.h> - +#include "dn_accel.h" #include <video/fbcon.h> #include <video/fbcon-mfb.h> - /* apollo video HW definitions */ /* @@ -30,16 +31,16 @@ * deals with control 1 and 3b deals with Color LUT reg. */ -#define AP_IOBASE 0x5d80 /* Base address of 1 plane board. */ -#define AP_STATUS 0x5d80 /* Status register. Read */ -#define AP_WRITE_ENABLE 0x5d80 /* Write Enable Register Write */ -#define AP_DEVICE_ID 0x5d81 /* Device ID Register. Read */ -#define AP_ROP_1 0x5d82 /* Raster Operation reg. Write Word */ -#define AP_DIAG_MEM_REQ 0x5d84 /* Diagnostic Memory Request. Write Word */ -#define AP_CONTROL_0 0x5d88 /* Control Register 0. Read/Write */ -#define AP_CONTROL_1 0x5d8a /* Control Register 1. Read/Write */ -#define AP_CONTROL_3A 0x5d8e /* Control Register 3a. Read/Write */ -#define AP_CONTROL_2 0x5d8c /* Control Register 2. Read/Write */ +#define AP_IOBASE 0x3b0 /* Base address of 1 plane board. */ +#define AP_STATUS isaIO2mem(AP_IOBASE+0) /* Status register. Read */ +#define AP_WRITE_ENABLE isaIO2mem(AP_IOBASE+0) /* Write Enable Register Write */ +#define AP_DEVICE_ID isaIO2mem(AP_IOBASE+1) /* Device ID Register. Read */ +#define AP_ROP_1 isaIO2mem(AP_IOBASE+2) /* Raster Operation reg. Write Word */ +#define AP_DIAG_MEM_REQ isaIO2mem(AP_IOBASE+4) /* Diagnostic Memory Request. Write Word */ +#define AP_CONTROL_0 isaIO2mem(AP_IOBASE+8) /* Control Register 0. Read/Write */ +#define AP_CONTROL_1 isaIO2mem(AP_IOBASE+0xa) /* Control Register 1. Read/Write */ +#define AP_CONTROL_3A isaIO2mem(AP_IOBASE+0xe) /* Control Register 3a. Read/Write */ +#define AP_CONTROL_2 isaIO2mem(AP_IOBASE+0xc) /* Control Register 2. Read/Write */ #define FRAME_BUFFER_START 0x0FA0000 @@ -112,44 +113,57 @@ #endif +void dn_video_setup(char *options, int *ints); + /* frame buffer operations */ -static int dnfb_open(struct fb_info *info, int user); -static int dnfb_release(struct fb_info *info, int user); -static int dnfb_get_fix(struct fb_fix_screeninfo *fix, int con, - struct fb_info *info); -static int dnfb_get_var(struct fb_var_screeninfo *var, int con, - struct fb_info *info); -static int dnfb_set_var(struct fb_var_screeninfo *var, int con, - struct fb_info *info); -static int dnfb_get_cmap(struct fb_cmap *cmap,int kspc,int con, +static int dn_fb_open(struct fb_info *info,int user); +static int dn_fb_release(struct fb_info *info,int user); +static int dn_fb_get_fix(struct fb_fix_screeninfo *fix, int con, struct fb_info *info); -static int dnfb_set_cmap(struct fb_cmap *cmap,int kspc,int con, +static int dn_fb_get_var(struct fb_var_screeninfo *var, int con, struct fb_info *info); -static int dnfb_pan_display(struct fb_var_screeninfo *var, int con, - struct fb_info *info); -static int dnfb_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg, int con, - struct fb_info *info); +static int dn_fb_set_var(struct fb_var_screeninfo *var, int isactive, + struct fb_info *info); +static int dn_fb_get_cmap(struct fb_cmap *cmap,int kspc,int con, + struct fb_info *info); +static int dn_fb_set_cmap(struct fb_cmap *cmap,int kspc,int con, + struct fb_info *info); +static int dn_fb_pan_display(struct fb_var_screeninfo *var, int con, + struct fb_info *info); +static int dn_fb_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg, int con, struct fb_info *info); -static int dnfbcon_switch(int con, struct fb_info *info); -static int dnfbcon_updatevar(int con, struct fb_info *info); -static void dnfbcon_blank(int blank, struct fb_info *info); +static int dnfbcon_switch(int con,struct fb_info *info); +static int dnfbcon_updatevar(int con,struct fb_info *info); +static void dnfbcon_blank(int blank,struct fb_info *info); -static void dnfb_set_disp(int con, struct fb_info *info); +static void dn_fb_set_disp(int con,struct fb_info *info); static struct display disp[MAX_NR_CONSOLES]; static struct fb_info fb_info; -static struct fb_ops dnfb_ops = { - dnfb_open,dnfb_release, dnfb_get_fix, dnfb_get_var, dnfb_set_var, - dnfb_get_cmap, dnfb_set_cmap, dnfb_pan_display, dnfb_ioctl +static struct fb_ops dn_fb_ops = { + dn_fb_open,dn_fb_release, dn_fb_get_fix, dn_fb_get_var, dn_fb_set_var, + dn_fb_get_cmap, dn_fb_set_cmap, dn_fb_pan_display, dn_fb_ioctl }; static int currcon=0; -static char dnfb_name[]="Apollo"; +#define NUM_TOTAL_MODES 1 +struct fb_var_screeninfo dn_fb_predefined[] = { + + { 0, }, + +}; + +static char dn_fb_name[]="Apollo "; + +/* accel stuff */ +#define USE_DN_ACCEL -static int dnfb_open(struct fb_info *info, int user) +static struct display_switch dispsw_apollofb; + +static int dn_fb_open(struct fb_info *info,int user) { /* * Nothing, only a usage count for the moment @@ -159,18 +173,17 @@ static int dnfb_open(struct fb_info *info, int user) return(0); } -static int dnfb_release(struct fb_info *info, int user) +static int dn_fb_release(struct fb_info *info,int user) { MOD_DEC_USE_COUNT; return(0); } -static int dnfb_get_fix(struct fb_fix_screeninfo *fix, int con, - struct fb_info *info) -{ - memset(fix, 0, sizeof(struct fb_fix_screeninfo)); +static int dn_fb_get_fix(struct fb_fix_screeninfo *fix, int con, + struct fb_info *info) { + strcpy(fix->id,"Apollo Mono"); - fix->smem_start=FRAME_BUFFER_START+IO_BASE; + fix->smem_start=(FRAME_BUFFER_START+IO_BASE); fix->smem_len=FRAME_BUFFER_LEN; fix->type=FB_TYPE_PACKED_PIXELS; fix->type_aux=0; @@ -184,10 +197,9 @@ static int dnfb_get_fix(struct fb_fix_screeninfo *fix, int con, } -static int dnfb_get_var(struct fb_var_screeninfo *var, int con, - struct fb_info *info) -{ - memset(var, 0, sizeof(struct fb_var_screeninfo)); +static int dn_fb_get_var(struct fb_var_screeninfo *var, int con, + struct fb_info *info) { + var->xres=1280; var->yres=1024; var->xres_virtual=2048; @@ -212,9 +224,10 @@ static int dnfb_get_var(struct fb_var_screeninfo *var, int con, } -static int dnfb_set_var(struct fb_var_screeninfo *var, int con, - struct fb_info *info) -{ +static int dn_fb_set_var(struct fb_var_screeninfo *var, int con, + struct fb_info *info) { + + printk("fb_set_var\n"); if(var->xres!=1280) return -EINVAL; if(var->yres!=1024) @@ -254,119 +267,284 @@ static int dnfb_set_var(struct fb_var_screeninfo *var, int con, } -static int dnfb_get_cmap(struct fb_cmap *cmap, int kspc, int con, - struct fb_info *info) -{ +static int dn_fb_get_cmap(struct fb_cmap *cmap,int kspc,int con, + struct fb_info *info) { + printk("get cmap not supported\n"); return -EINVAL; } -static int dnfb_set_cmap(struct fb_cmap *cmap, int kspc, int con, - struct fb_info *info) -{ +static int dn_fb_set_cmap(struct fb_cmap *cmap,int kspc,int con, + struct fb_info *info) { + printk("set cmap not supported\n"); return -EINVAL; } -static int dnfb_pan_display(struct fb_var_screeninfo *var, int con, - struct fb_info *info) -{ +static int dn_fb_pan_display(struct fb_var_screeninfo *var, int con, + struct fb_info *info) { + printk("panning not supported\n"); return -EINVAL; } -static int dnfb_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg, int con, - struct fb_info *info) -{ +static int dn_fb_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg, int con, struct fb_info *info) { + + printk("no IOCTLs as of yet.\n"); + return -EINVAL; + } -static void dnfb_set_disp(int con, struct fb_info *info) -{ +static void dn_fb_set_disp(int con, struct fb_info *info) { + struct fb_fix_screeninfo fix; + struct display *display; + + dn_fb_get_fix(&fix,con, info); + + if (con>=0) + display=&fb_display[con]; + else + display=&disp[0]; - dnfb_get_fix(&fix, con, info); if(con==-1) con=0; - disp[con].screen_base = fix.smem_start; - disp[con].visual = fix.visual; - disp[con].type = fix.type; - disp[con].type_aux = fix.type_aux; - disp[con].ypanstep = fix.ypanstep; - disp[con].ywrapstep = fix.ywrapstep; - disp[con].can_soft_blank = 1; - disp[con].inverse = 0; - disp[con].line_length = fix.line_length; + display->screen_base = (u_char *)fix.smem_start; + display->visual = fix.visual; + display->type = fix.type; + display->type_aux = fix.type_aux; + display->ypanstep = fix.ypanstep; + display->ywrapstep = fix.ywrapstep; + display->can_soft_blank = 1; + display->inverse = 0; + display->line_length = fix.line_length; #ifdef FBCON_HAS_MFB - disp[con].dispsw = &fbcon_mfb; + display->dispsw = &fbcon_mfb; #else - disp[con].dispsw = &fbcon_dummy; + display->dispsw=&fbcon_dummy; #endif + } -int dnfb_init(void) -{ +unsigned long dnfb_init(unsigned long mem_start) { + + int err; + + fb_info.changevar=NULL; - strcpy(&fb_info.modename[0],dnfb_name); + strcpy(&fb_info.modename[0],dn_fb_name); fb_info.fontname[0]=0; fb_info.disp=disp; fb_info.switch_con=&dnfbcon_switch; fb_info.updatevar=&dnfbcon_updatevar; fb_info.blank=&dnfbcon_blank; fb_info.node = -1; - fb_info.fbops = &dnfb_ops; - fb_info.flags = FBINFO_FLAG_DEFAULT; + fb_info.fbops = &dn_fb_ops; - outb(RESET_CREG, AP_CONTROL_3A); - outw(0x0, AP_WRITE_ENABLE); - outb(NORMAL_MODE,AP_CONTROL_0); - outb((AD_BLT | DST_EQ_SRC | NORM_CREG1), AP_CONTROL_1); - outb(S_DATA_PLN, AP_CONTROL_2); - outw(SWAP(0x3),AP_ROP_1); - - dnfb_get_var(&disp[0].var, 0, &fb_info); - dnfb_set_disp(-1, &fb_info); - - if (register_framebuffer(&fb_info) < 0) { - printk(KERN_ERR "unable to register apollo frame buffer\n"); - return -EINVAL; + dn_fb_get_var(&disp[0].var,0, &fb_info); + + dn_fb_set_disp(-1, &fb_info); + + err=register_framebuffer(&fb_info); + if(err < 0) { + panic("unable to register apollo frame buffer\n"); } - printk("fb%d: apollo frame buffer alive and kicking !\n", - GET_FB_IDX(fb_info.node)); - return 0; + /* now we have registered we can safely setup the hardware */ + + outb(RESET_CREG, AP_CONTROL_3A); + outw(0x0, AP_WRITE_ENABLE); + outb(NORMAL_MODE, AP_CONTROL_0); + outb((AD_BLT | DST_EQ_SRC | NORM_CREG1), AP_CONTROL_1); + outb(S_DATA_PLN, AP_CONTROL_2); + outw(SWAP(0x3), AP_ROP_1); + + printk("apollo frame buffer alive and kicking !\n"); + + + return mem_start; + } -static int dnfbcon_switch(int con, struct fb_info *info) -{ +static int dnfbcon_switch(int con, struct fb_info *info) { + currcon=con; return 0; } -static int dnfbcon_updatevar(int con, struct fb_info *info) -{ +static int dnfbcon_updatevar(int con, struct fb_info *info) { + return 0; + } -static void dnfbcon_blank(int blank, struct fb_info *info) -{ +static void dnfbcon_blank(int blank, struct fb_info *info) { + if(blank) { - outb(0, AP_CONTROL_3A); - outb((AD_BLT | DST_EQ_SRC | NORM_CREG1) & ~ENAB_DISP, - AP_CONTROL_1); + outb(0x0, AP_CONTROL_3A); + } + else { + outb(0x1, AP_CONTROL_3A); + } + + return ; + +} + +void dn_video_setup(char *options, int *ints) { + + return; + +} + +void dn_bitblt(struct display *p,int x_src,int y_src, int x_dest, int y_dest, + int x_count, int y_count) { + + int incr,y_delta,pre_read=0,x_end,x_word_count; + ushort *src,dummy; + uint start_mask,end_mask,dest; + short i,j; + + incr=(y_dest<=y_src) ? 1 : -1 ; + + src=(ushort *)(p->screen_base+ y_src*p->next_line+(x_src >> 4)); + dest=y_dest*(p->next_line >> 1)+(x_dest >> 4); + + if(incr>0) { + y_delta=(p->next_line*8)-x_src-x_count; + x_end=x_dest+x_count-1; + x_word_count=(x_end>>4) - (x_dest >> 4) + 1; + start_mask=0xffff0000 >> (x_dest & 0xf); + end_mask=0x7ffff >> (x_end & 0xf); + outb((((x_dest & 0xf) - (x_src &0xf)) % 16)|(0x4 << 5),AP_CONTROL_0); + if((x_dest & 0xf) < (x_src & 0xf)) + pre_read=1; } else { - outb(1, AP_CONTROL_3A); - outb((AD_BLT | DST_EQ_SRC | NORM_CREG1), AP_CONTROL_1); + y_delta=-((p->next_line*8)-x_src-x_count); + x_end=x_dest-x_count+1; + x_word_count=(x_dest>>4) - (x_end >> 4) + 1; + start_mask=0x7ffff >> (x_dest & 0xf); + end_mask=0xffff0000 >> (x_end & 0xf); + outb(((-((x_src & 0xf) - (x_dest &0xf))) % 16)|(0x4 << 5),AP_CONTROL_0); + if((x_dest & 0xf) > (x_src & 0xf)) + pre_read=1; + } + + for(i=0;i<y_count;i++) { + + outb(0xc | (dest >> 16), AP_CONTROL_3A); + + if(pre_read) { + dummy=*src; + src+=incr; + } + + if(x_word_count) { + outb(start_mask,AP_WRITE_ENABLE); + *src=dest; + src+=incr; + dest+=incr; + outb(0,AP_WRITE_ENABLE); + + for(j=1;j<(x_word_count-1);j++) { + *src=dest; + src+=incr; + dest+=incr; + } + + outb(start_mask,AP_WRITE_ENABLE); + *src=dest; + dest+=incr; + src+=incr; + } + else { + outb(start_mask | end_mask, AP_WRITE_ENABLE); + *src=dest; + dest+=incr; + src+=incr; + } + src+=(y_delta/16); + dest+=(y_delta/16); + } + outb(NORMAL_MODE,AP_CONTROL_0); +} + +static void bmove_apollofb(struct display *p, int sy, int sx, int dy, int dx, + int height, int width) +{ + + int fontheight,fontwidth; + + fontheight=fontheight(p); + fontwidth=fontwidth(p); + +#ifdef USE_DN_ACCEL + dn_bitblt(p,sx,sy*fontheight,dx,dy*fontheight,width*fontwidth, + height*fontheight); +#else + u_char *src, *dest; + u_int rows; + + if (sx == 0 && dx == 0 && width == p->next_line) { + src = p->screen_base+sy*fontheight*width; + dest = p->screen_base+dy*fontheight*width; + mymemmove(dest, src, height*fontheight*width); + } else if (dy <= sy) { + src = p->screen_base+sy*fontheight*next_line+sx; + dest = p->screen_base+dy*fontheight*next_line+dx; + for (rows = height*fontheight; rows--;) { + mymemmove(dest, src, width); + src += p->next_line; + dest += p->next_line; } + } else { + src = p->screen_base+((sy+height)*fontheight-1)*p->next_line+sx; + dest = p->screen_base+((dy+height)*fontheight-1)*p->next_line+dx; + for (rows = height*fontheight; rows--;) { + mymemmove(dest, src, width); + src -= p->next_line; + dest -= p->next_line; + } + } +#endif +} + +static void clear_apollofb(struct vc_data *conp, struct display *p, int sy, int sx, + int height, int width) +{ + fbcon_mfb_clear(conp,p,sy,sx,height,width); } + +static void putc_apollofb(struct vc_data *conp, struct display *p, int c, int yy, + int xx) +{ + fbcon_mfb_putc(conp,p,c,yy,xx); +} + +static void putcs_apollofb(struct vc_data *conp, struct display *p, const char *s, + int count, int yy, int xx) +{ + fbcon_mfb_putcs(conp,p,s,count,yy,xx); +} + +static void rev_char_apollofb(struct display *p, int xx, int yy) +{ + fbcon_mfb_revc(p,xx,yy); +} + +static struct display_switch dispsw_apollofb = { + fbcon_mfb_setup, bmove_apollofb, clear_apollofb, + putc_apollofb, putcs_apollofb, rev_char_apollofb +}; |