aboutsummaryrefslogtreecommitdiff
path: root/sys/i386/isa/pcic.c
blob: f5b31c9fe071864564e220d0cb8835c6b66790ef (plain) (blame)
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
/*-
 * TODO:
 * [1] integrate into current if_ed.c
 * [2] parse tuples to find out where to map the shared memory buffer,
 *     and what to write into the configuration register
 * [3] move pcic-specific code into a separate module.
 *
 * Device driver for IBM PCMCIA Credit Card Adapter for Ethernet,
 * if_ze.c
 *
 * Based on the Device driver for National Semiconductor DS8390 ethernet
 * adapters by David Greenman.  Modifications for PCMCIA by Keith Moore.
 * Adapted for FreeBSD 1.1.5 by Jordan Hubbard.
 *
 * Currently supports only the IBM Credit Card Adapter for Ethernet, but
 * could probably work with other PCMCIA cards also, if it were modified
 * to get the locations of the PCMCIA configuration option register (COR)
 * by parsing the configuration tuples, rather than by hard-coding in
 * the value expected by IBM's card.
 *
 * Sources for data on the PCMCIA/IBM CCAE specific portions of the driver:
 *
 * [1] _Local Area Network Credit Card Adapters Technical Reference_,
 *     IBM Corp., SC30-3585-00, part # 33G9243.
 * [2] "pre-alpha" PCMCIA support code for Linux by Barry Jaspan.
 * [3] Intel 82536SL PC Card Interface Controller Data Sheet, Intel
 *     Order Number 290423-002
 * [4] National Semiconductor DP83902A ST-NIC (tm) Serial Network
 *     Interface Controller for Twisted Pair data sheet.
 *
 *
 * Copyright (C) 1993, David Greenman. This software may be used, modified,
 *   copied, distributed, and sold, in both source and binary form provided
 *   that the above copyright and these terms are retained. Under no
 *   circumstances is the author responsible for the proper functioning
 *   of this software, nor does the author assume any responsibility
 *   for damages incurred with its use.
 */
#include <sys/param.h>
#if defined(__FreeBSD__)
#include <sys/systm.h>
#include <sys/kernel.h>
#include <machine/clock.h>
#endif
#include <i386/isa/isa.h>
#include <i386/isa/isa_device.h>
#include <i386/isa/icu.h>
#include <i386/isa/pcic.h>

void
pcic_print_regs (int slot)
{
    int i, j;

    for (i = 0; i < 0x40; i += 16) {
	for (j = 0; j < 16; ++j)
	    printf ("%02x ", pcic_getb (slot, i + j));
	printf ("\n");
    }
}

/*
 * map a portion of the card's memory space into system memory
 * space.
 *
 * slot = # of the slot the card is plugged into
 * window = which pcic memory map registers to use (0..4)
 * sys_addr = base system PHYSICAL memory address where we want it.  must
 *	      be on an appropriate boundary (lower 12 bits are zero).
 * card_addr = the base address of the card's memory to correspond
 *             to sys_addr
 * length = length of the segment to map (may be rounded up as necessary)
 * type = which card memory space to map (attribute or shared)
 * width = 1 for byte-wide mapping; 2 for word (16-bit) mapping.
 */

void
pcic_map_memory (int slot, int window, unsigned long sys_addr,
		 unsigned long card_addr, unsigned long length,
		 enum memtype type, int width)
{
    unsigned short offset;
    unsigned short mem_start_addr;
    unsigned short mem_stop_addr;

    sys_addr >>= 12;
    card_addr >>= 12;
    length >>= 12;
    /*
     * compute an offset for the chip such that
     * (sys_addr + offset) = card_addr
     * but the arithmetic is done modulo 2^14
     */
    offset = (card_addr - sys_addr) & 0x3FFF;
    /*
     * now OR in the bit for "attribute memory" if necessary
     */
    if (type == ATTRIBUTE) {
	offset |= (PCIC_REG << 8);
	/* REG == "region active" pin on card */
    }
    /*
     * okay, set up the chip memory mapping registers, and turn
     * on the enable bit for this window.
     * if we are doing 16-bit wide accesses (width == 2),
     * turn on the appropriate bit.
     *
     * XXX for now, we set all of the wait state bits to zero.
     * Not really sure how they should be set.
     */
    mem_start_addr = sys_addr & 0xFFF;
    if (width == 2)
	mem_start_addr |= (PCIC_DATA16 << 8);
    mem_stop_addr = (sys_addr + length) & 0xFFF;

    pcic_putw (slot, MEM_START_ADDR(window), mem_start_addr);
    pcic_putw (slot, MEM_STOP_ADDR(window), mem_stop_addr);
    pcic_putw (slot, MEM_OFFSET(window), offset);
    /*
     * Assert the bit (PCIC_MEMCS16) that says to decode all of
     * the address lines.
     */
    pcic_putb (slot, PCIC_ADDRWINE,
	       pcic_getb (slot, PCIC_ADDRWINE) |
	       MEM_ENABLE_BIT(window) | PCIC_MEMCS16);
}

void
pcic_unmap_memory (int slot, int window)
{
    /*
     * seems like we need to turn off the enable bit first, after which
     * we can clear the registers out just to be sure.
     */
    pcic_putb (slot, PCIC_ADDRWINE,
	       pcic_getb (slot, PCIC_ADDRWINE) & ~MEM_ENABLE_BIT(window));
    pcic_putw (slot, MEM_START_ADDR(window), 0);
    pcic_putw (slot, MEM_STOP_ADDR(window), 0);
    pcic_putw (slot, MEM_OFFSET(window), 0);
}

/*
 * map a range of addresses into system i/o space
 * (no translation of i/o addresses is possible)
 *
 * 'width' is:
 * + 0 to tell the PCIC to generate the ISA IOCS16* signal from
 *   the PCMCIA IOIS16* signal.
 * + 1 to select 8-bit width
 * + 2 to select 16-bit width
 */

void
pcic_map_io (int slot, int window, unsigned short base, unsigned short length,
	     unsigned short width)
{
    unsigned char x;

    pcic_putw (slot, IO_START_ADDR(window), base);
    pcic_putw (slot, IO_STOP_ADDR(window), base+length-1);
    /*
     * select the bits that determine whether
     * an i/o operation is 8 or 16 bits wide
     */
    x = pcic_getb (slot, PCIC_IOCTL);
    switch (width) {
    case 0:			/* PCMCIA card decides */
	if (window)
	    x = (x & 0xf0) | PCIC_IO1_CS16;
	else
	    x = (x & 0x0f) | PCIC_IO0_CS16;
	break;
    case 1:			/* 8 bits wide */
	break;
    case 2:			/* 16 bits wide */
	if (window)
	    x = (x & 0xf0) | PCIC_IO1_16BIT;
	else
	    x = (x & 0x0f) | PCIC_IO0_16BIT;
	break;
    }
    pcic_putb (slot, PCIC_IOCTL, x);
    pcic_putb (slot, PCIC_ADDRWINE,
	       pcic_getb (slot, PCIC_ADDRWINE) | IO_ENABLE_BIT(window));
}

#ifdef TEST
void
pcic_unmap_io (int slot, int window)
{
    pcic_putb (slot, PCIC_ADDRWINE,
	       pcic_getb (slot, PCIC_ADDRWINE) & ~IO_ENABLE_BIT(window));
    pcic_putw (slot, IO_START_ADDR(window), 0);
    pcic_putw (slot, IO_STOP_ADDR(window), 0);
}
#endif /* TEST */

/*
 * tell the PCIC which irq we want to use.  only the following are legal:
 * 3, 4, 5, 7, 9, 10, 11, 12, 14, 15
 *
 * NB: 'irq' is an interrupt NUMBER, not a MASK as in struct isa_device.
 */

void
pcic_map_irq (int slot, int irq)
{
    if (irq < 3 || irq == 6 || irq == 8 || irq == 13 || irq > 15) {
	printf ("zp: pcic_map_irq (slot %d): illegal irq %d\n", slot, irq);
	return;
    }
    pcic_putb (slot, PCIC_INT_GEN,
	       pcic_getb (slot, PCIC_INT_GEN) | (irq & 0x0F));
}

void
pcic_power_on (int slot)
{
    pcic_putb (slot, PCIC_STATUS,
	       pcic_getb (slot, PCIC_STATUS) | PCIC_POW);
    DELAY (100000);
    pcic_putb (slot, PCIC_POWER,
	       pcic_getb (slot, PCIC_POWER) | PCIC_DISRST | PCIC_PCPWRE);
    DELAY (100000);
    pcic_putb (slot, PCIC_POWER,
	       pcic_getb (slot, PCIC_POWER) | PCIC_OUTENA);
}

void
pcic_power_off (int slot)
{
    pcic_putb (slot, PCIC_POWER,
	       pcic_getb (slot, PCIC_POWER) & ~(PCIC_OUTENA|PCIC_PCPWRE));
}

void
pcic_reset (int slot)
{
    /* assert RESET (by clearing a bit!), wait a bit, and de-assert it */
    pcic_putb (slot, PCIC_INT_GEN,
	       pcic_getb (slot, PCIC_INT_GEN) & ~PCIC_CARDRESET);
    DELAY (100000);
    pcic_putb (slot, PCIC_INT_GEN,
	       pcic_getb (slot, PCIC_INT_GEN) | PCIC_CARDRESET);
}