summaryrefslogtreecommitdiffstats
path: root/drivers/net/ethernet/realtek/atp.h
blob: 0edc642c2c2f4c1d9a90d94d1db07c1a63c0a1bd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
/* Linux header file for the ATP pocket ethernet adapter. */
/* v1.09 8/9/2000 becker@scyld.com. */

#include <linux/if_ether.h>
#include <linux/types.h>

/* The header prepended to received packets. */
struct rx_header {
    ushort pad;			/* Pad. */
    ushort rx_count;
    ushort rx_status;		/* Unknown bit assignments :-<.  */
    ushort cur_addr;		/* Apparently the current buffer address(?) */
};

#define PAR_DATA	0
#define PAR_STATUS	1
#define PAR_CONTROL 2

enum chip_type { RTL8002, RTL8012 };

#define Ctrl_LNibRead	0x08	/* LP_PSELECP */
#define Ctrl_HNibRead	0
#define Ctrl_LNibWrite	0x08	/* LP_PSELECP */
#define Ctrl_HNibWrite	0
#define Ctrl_SelData	0x04	/* LP_PINITP */
#define Ctrl_IRQEN	0x10	/* LP_PINTEN */

#define EOW	0xE0
#define EOC	0xE0
#define WrAddr	0x40	/* Set address of EPLC read, write register. */
#define RdAddr	0xC0
#define HNib	0x10

enum page0_regs
{
    /* The first six registers hold the ethernet physical station address. */
    PAR0 = 0, PAR1 = 1, PAR2 = 2, PAR3 = 3, PAR4 = 4, PAR5 = 5,
    TxCNT0 = 6, TxCNT1 = 7,		/* The transmit byte count. */
    TxSTAT = 8, RxSTAT = 9,		/* Tx and Rx status. */
    ISR = 10, IMR = 11,			/* Interrupt status and mask. */
    CMR1 = 12,				/* Command register 1. */
    CMR2 = 13,				/* Command register 2. */
    MODSEL = 14,			/* Mode select register. */
    MAR = 14,				/* Memory address register (?). */
    CMR2_h = 0x1d, };

enum eepage_regs
{ PROM_CMD = 6, PROM_DATA = 7 };	/* Note that PROM_CMD is in the "high" bits. */


#define ISR_TxOK	0x01
#define ISR_RxOK	0x04
#define ISR_TxErr	0x02
#define ISRh_RxErr	0x11	/* ISR, high nibble */

#define CMR1h_MUX	0x08	/* Select printer multiplexor on 8012. */
#define CMR1h_RESET	0x04	/* Reset. */
#define CMR1h_RxENABLE	0x02	/* Rx unit enable.  */
#define CMR1h_TxENABLE	0x01	/* Tx unit enable.  */
#define CMR1h_TxRxOFF	0x00
#define CMR1_ReXmit	0x08	/* Trigger a retransmit. */
#define CMR1_Xmit	0x04	/* Trigger a transmit. */
#define	CMR1_IRQ	0x02	/* Interrupt active. */
#define	CMR1_BufEnb	0x01	/* Enable the buffer(?). */
#define	CMR1_NextPkt	0x01	/* Enable the buffer(?). */

#define CMR2_NULL	8
#define CMR2_IRQOUT	9
#define CMR2_RAMTEST	10
#define CMR2_EEPROM	12	/* Set to page 1, for reading the EEPROM. */

#define CMR2h_OFF	0	/* No accept mode. */
#define CMR2h_Physical	1	/* Accept a physical address match only. */
#define CMR2h_Normal	2	/* Accept physical and broadcast address. */
#define CMR2h_PROMISC	3	/* Promiscuous mode. */

/* An inline function used below: it differs from inb() by explicitly return an unsigned
   char, saving a truncation. */
static inline unsigned char inbyte(unsigned short port)
{
    unsigned char _v;
    __asm__ __volatile__ ("inb %w1,%b0" :"=a" (_v):"d" (port));
    return _v;
}

/* Read register OFFSET.
   This command should always be terminated with read_end(). */
static inline unsigned char read_nibble(short port, unsigned char offset)
{
    unsigned char retval;
    outb(EOC+offset, port + PAR_DATA);
    outb(RdAddr+offset, port + PAR_DATA);
    inbyte(port + PAR_STATUS);		/* Settling time delay */
    retval = inbyte(port + PAR_STATUS);
    outb(EOC+offset, port + PAR_DATA);

    return retval;
}

/* Functions for bulk data read.  The interrupt line is always disabled. */
/* Get a byte using read mode 0, reading data from the control lines. */
static inline unsigned char read_byte_mode0(short ioaddr)
{
    unsigned char low_nib;

    outb(Ctrl_LNibRead, ioaddr + PAR_CONTROL);
    inbyte(ioaddr + PAR_STATUS);
    low_nib = (inbyte(ioaddr + PAR_STATUS) >> 3) & 0x0f;
    outb(Ctrl_HNibRead, ioaddr + PAR_CONTROL);
    inbyte(ioaddr + PAR_STATUS);	/* Settling time delay -- needed!  */
    inbyte(ioaddr + PAR_STATUS);	/* Settling time delay -- needed!  */
    return low_nib | ((inbyte(ioaddr + PAR_STATUS) << 1) & 0xf0);
}

/* The same as read_byte_mode0(), but does multiple inb()s for stability. */
static inline unsigned char read_byte_mode2(short ioaddr)
{
    unsigned char low_nib;

    outb(Ctrl_LNibRead, ioaddr + PAR_CONTROL);
    inbyte(ioaddr + PAR_STATUS);
    low_nib = (inbyte(ioaddr + PAR_STATUS) >> 3) & 0x0f;
    outb(Ctrl_HNibRead, ioaddr + PAR_CONTROL);
    inbyte(ioaddr + PAR_STATUS);	/* Settling time delay -- needed!  */
    return low_nib | ((inbyte(ioaddr + PAR_STATUS) << 1) & 0xf0);
}

/* Read a byte through the data register. */
static inline unsigned char read_byte_mode4(short ioaddr)
{
    unsigned char low_nib;

    outb(RdAddr | MAR, ioaddr + PAR_DATA);
    low_nib = (inbyte(ioaddr + PAR_STATUS) >> 3) & 0x0f;
    outb(RdAddr | HNib | MAR, ioaddr + PAR_DATA);
    return low_nib | ((inbyte(ioaddr + PAR_STATUS) << 1) & 0xf0);
}

/* Read a byte through the data register, double reading to allow settling. */
static inline unsigned char read_byte_mode6(short ioaddr)
{
    unsigned char low_nib;

    outb(RdAddr | MAR, ioaddr + PAR_DATA);
    inbyte(ioaddr + PAR_STATUS);
    low_nib = (inbyte(ioaddr + PAR_STATUS) >> 3) & 0x0f;
    outb(RdAddr | HNib | MAR, ioaddr + PAR_DATA);
    inbyte(ioaddr + PAR_STATUS);
    return low_nib | ((inbyte(ioaddr + PAR_STATUS) << 1) & 0xf0);
}

static inline void
write_reg(short port, unsigned char reg, unsigned char value)
{
    unsigned char outval;
    outb(EOC | reg, port + PAR_DATA);
    outval = WrAddr | reg;
    outb(outval, port + PAR_DATA);
    outb(outval, port + PAR_DATA);	/* Double write for PS/2. */

    outval &= 0xf0;
    outval |= value;
    outb(outval, port + PAR_DATA);
    outval &= 0x1f;
    outb(outval, port + PAR_DATA);
    outb(outval, port + PAR_DATA);

    outb(EOC | outval, port + PAR_DATA);
}

static inline void
write_reg_high(short port, unsigned char reg, unsigned char value)
{
    unsigned char outval = EOC | HNib | reg;

    outb(outval, port + PAR_DATA);
    outval &= WrAddr | HNib | 0x0f;
    outb(outval, port + PAR_DATA);
    outb(outval, port + PAR_DATA);	/* Double write for PS/2. */

    outval = WrAddr | HNib | value;
    outb(outval, port + PAR_DATA);
    outval &= HNib | 0x0f;		/* HNib | value */
    outb(outval, port + PAR_DATA);
    outb(outval, port + PAR_DATA);

    outb(EOC | HNib | outval, port + PAR_DATA);
}

/* Write a byte out using nibble mode.  The low nibble is written first. */
static inline void
write_reg_byte(short port, unsigned char reg, unsigned char value)
{
    unsigned char outval;
    outb(EOC | reg, port + PAR_DATA); 	/* Reset the address register. */
    outval = WrAddr | reg;
    outb(outval, port + PAR_DATA);
    outb(outval, port + PAR_DATA);	/* Double write for PS/2. */

    outb((outval & 0xf0) | (value & 0x0f), port + PAR_DATA);
    outb(value & 0x0f, port + PAR_DATA);
    value >>= 4;
    outb(value, port + PAR_DATA);
    outb(0x10 | value, port + PAR_DATA);
    outb(0x10 | value, port + PAR_DATA);

    outb(EOC  | value, port + PAR_DATA); 	/* Reset the address register. */
}

/*
 * Bulk data writes to the packet buffer.  The interrupt line remains enabled.
 * The first, faster method uses only the dataport (data modes 0, 2 & 4).
 * The second (backup) method uses data and control regs (modes 1, 3 & 5).
 * It should only be needed when there is skew between the individual data
 * lines.
 */
static inline void write_byte_mode0(short ioaddr, unsigned char value)
{
    outb(value & 0x0f, ioaddr + PAR_DATA);
    outb((value>>4) | 0x10, ioaddr + PAR_DATA);
}

static inline void write_byte_mode1(short ioaddr, unsigned char value)
{
    outb(value & 0x0f, ioaddr + PAR_DATA);
    outb(Ctrl_IRQEN | Ctrl_LNibWrite, ioaddr + PAR_CONTROL);
    outb((value>>4) | 0x10, ioaddr + PAR_DATA);
    outb(Ctrl_IRQEN | Ctrl_HNibWrite, ioaddr + PAR_CONTROL);
}

/* Write 16bit VALUE to the packet buffer: the same as above just doubled. */
static inline void write_word_mode0(short ioaddr, unsigned short value)
{
    outb(value & 0x0f, ioaddr + PAR_DATA);
    value >>= 4;
    outb((value & 0x0f) | 0x10, ioaddr + PAR_DATA);
    value >>= 4;
    outb(value & 0x0f, ioaddr + PAR_DATA);
    value >>= 4;
    outb((value & 0x0f) | 0x10, ioaddr + PAR_DATA);
}

/*  EEPROM_Ctrl bits. */
#define EE_SHIFT_CLK	0x04	/* EEPROM shift clock. */
#define EE_CS		0x02	/* EEPROM chip select. */
#define EE_CLK_HIGH	0x12
#define EE_CLK_LOW	0x16
#define EE_DATA_WRITE	0x01	/* EEPROM chip data in. */
#define EE_DATA_READ	0x08	/* EEPROM chip data out. */

/* Delay between EEPROM clock transitions. */
#define eeprom_delay(ticks) \
do { int _i = 40; while (--_i > 0) { __SLOW_DOWN_IO; }} while (0)

/* The EEPROM commands include the alway-set leading bit. */
#define EE_WRITE_CMD(offset)	(((5 << 6) + (offset)) << 17)
#define EE_READ(offset) 	(((6 << 6) + (offset)) << 17)
#define EE_ERASE(offset)	(((7 << 6) + (offset)) << 17)
#define EE_CMD_SIZE	27	/* The command+address+data size. */
OpenPOWER on IntegriCloud