|
Added
Link Here
|
| 1 |
/* |
| 2 |
* Serial driver for non-intelligent PCI multiport adapters. |
| 3 |
* Implements the probe, attach and interrupt dispatcher, |
| 4 |
* and uses sio.c for the most part of work. |
| 5 |
* The PCI ports get unit numbers _after_ the ISA sio ports. |
| 6 |
* Option COM_MULTIPORT is not necessary. |
| 7 |
* |
| 8 |
* Copyright (C) 2000 Cronyx Engineering Ltd. |
| 9 |
* Author: Serge Vakulenko <vak@cronyx.ru> |
| 10 |
* |
| 11 |
* Supports: |
| 12 |
* Cronyx Omega-PCI adapter, by Serge Vakulenko <vak@cronyx.ru> |
| 13 |
* |
| 14 |
* This software is distributed with NO WARRANTIES, not even the implied |
| 15 |
* warranties for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| 16 |
* |
| 17 |
* Authors grant any other persons or organisations permission to use |
| 18 |
* or modify this software as long as this message is kept with the software, |
| 19 |
* all derivative works or modified versions. |
| 20 |
*/ |
| 21 |
#include <sys/param.h> |
| 22 |
#include <sys/systm.h> |
| 23 |
#include <sys/kernel.h> |
| 24 |
#include <sys/timepps.h> |
| 25 |
#include <sys/tty.h> |
| 26 |
#include <vm/vm.h> |
| 27 |
#include <vm/pmap.h> |
| 28 |
#include <machine/bus.h> |
| 29 |
#ifndef SMP |
| 30 |
#include <machine/lock.h> |
| 31 |
#endif |
| 32 |
#include <machine/resource.h> |
| 33 |
#include <sys/bus.h> |
| 34 |
#include <sys/rman.h> |
| 35 |
#include <pci/pcivar.h> |
| 36 |
#if __FreeBSD_version >= 400000 |
| 37 |
#include <isa/siovar.h> |
| 38 |
#include <isa/sioreg.h> |
| 39 |
#else |
| 40 |
#include <sys/malloc.h> |
| 41 |
#include <sys/interrupt.h> |
| 42 |
#include <i386/isa/siovar.h> |
| 43 |
#include <i386/isa/sioreg.h> |
| 44 |
#endif |
| 45 |
#include <isa/ic/ns16550.h> |
| 46 |
|
| 47 |
#include "sio.h" |
| 48 |
|
| 49 |
#define MAXCHAN 8 |
| 50 |
|
| 51 |
#ifndef __i386__ |
| 52 |
#define disable_intr() |
| 53 |
#define enable_intr() |
| 54 |
#endif |
| 55 |
|
| 56 |
#ifdef SMP |
| 57 |
#define disable_intr() COM_DISABLE_INTR() |
| 58 |
#define enable_intr() COM_ENABLE_INTR() |
| 59 |
#endif /* SMP */ |
| 60 |
|
| 61 |
typedef struct _sio_pci_t { |
| 62 |
int nchan; |
| 63 |
int fifo_size; |
| 64 |
int base_reg; |
| 65 |
int hw_rts_cts; |
| 66 |
Port_t iobase; |
| 67 |
Port_t iostep; |
| 68 |
struct resource *res; |
| 69 |
struct resource *irq; |
| 70 |
void *intrhand; |
| 71 |
struct com_s com [MAXCHAN]; |
| 72 |
} sio_pci_t; |
| 73 |
|
| 74 |
static void sio_pci_intr __P((void *arg)); |
| 75 |
|
| 76 |
static int sio_pci_numunits = NSIO; |
| 77 |
|
| 78 |
#if __FreeBSD_version >= 400000 |
| 79 |
/* |
| 80 |
* FreeBSD version 4.x |
| 81 |
*/ |
| 82 |
static int sio_pci_probe __P((device_t)); |
| 83 |
static int sio_pci_attach __P((device_t)); |
| 84 |
|
| 85 |
static devclass_t siopci_devclass; |
| 86 |
|
| 87 |
static device_method_t sio_pci_methods[] = { |
| 88 |
/* Device interface */ |
| 89 |
DEVMETHOD(device_probe, sio_pci_probe), |
| 90 |
DEVMETHOD(device_attach, sio_pci_attach), |
| 91 |
{ 0, 0 } |
| 92 |
}; |
| 93 |
|
| 94 |
static driver_t sio_pci_driver = { |
| 95 |
"siopci", |
| 96 |
sio_pci_methods, |
| 97 |
sizeof(sio_pci_t) |
| 98 |
}; |
| 99 |
DRIVER_MODULE(sio_pci, pci, sio_pci_driver, siopci_devclass, 0, 0); |
| 100 |
|
| 101 |
#else |
| 102 |
/* |
| 103 |
* FreeBSD version 3.x |
| 104 |
*/ |
| 105 |
static const char *sio_pci_probe __P((pcici_t, pcidi_t)); |
| 106 |
static void sio_pci_attach __P((pcici_t, int)); |
| 107 |
|
| 108 |
static u_long sio_pci_count; |
| 109 |
|
| 110 |
static struct pci_device sio_pci_device = { |
| 111 |
"siopci", |
| 112 |
sio_pci_probe, |
| 113 |
sio_pci_attach, |
| 114 |
&sio_pci_count, |
| 115 |
NULL |
| 116 |
}; |
| 117 |
DATA_SET(pcidevice_set, sio_pci_device); |
| 118 |
|
| 119 |
#endif /* __FreeBSD_version >= 400000 */ |
| 120 |
|
| 121 |
#if __FreeBSD_version >= 400000 |
| 122 |
static int |
| 123 |
sio_pci_probe(dev) |
| 124 |
device_t dev; |
| 125 |
{ |
| 126 |
u_long device_id = pci_get_vendor(dev) | pci_get_device(dev) << 16; |
| 127 |
#else |
| 128 |
static const char * |
| 129 |
sio_pci_probe(config_id, device_id) |
| 130 |
pcici_t config_id; |
| 131 |
pcidi_t device_id; |
| 132 |
{ |
| 133 |
#endif |
| 134 |
const char *desc = 0; |
| 135 |
|
| 136 |
if (device_id == 0xc00110b5) |
| 137 |
desc = "Cronyx-Omega-PCI Serial Adapter"; |
| 138 |
#ifdef notyet |
| 139 |
/* Add your device here. */ |
| 140 |
else if (device_id == 0xXXXXXXXX) |
| 141 |
desc = "XXX"; |
| 142 |
#endif |
| 143 |
|
| 144 |
#if __FreeBSD_version >= 400000 |
| 145 |
if (! desc) |
| 146 |
return ENXIO; |
| 147 |
device_set_desc(dev, desc); |
| 148 |
return 0; |
| 149 |
#else |
| 150 |
return desc; |
| 151 |
#endif |
| 152 |
} |
| 153 |
|
| 154 |
#if __FreeBSD_version >= 400000 |
| 155 |
static int |
| 156 |
sio_pci_attach(dev) |
| 157 |
device_t dev; |
| 158 |
{ |
| 159 |
u_long device_id = pci_get_vendor(dev) | |
| 160 |
pci_get_device(dev) << 16; |
| 161 |
sio_pci_t *d = device_get_softc(dev); |
| 162 |
int rid; |
| 163 |
#define PCI_READ_CONFIG(reg) pci_read_config(dev, reg, 4) |
| 164 |
#else |
| 165 |
static void |
| 166 |
sio_pci_attach(config_id, unit) |
| 167 |
pcici_t config_id; |
| 168 |
int unit; |
| 169 |
{ |
| 170 |
u_long device_id = config_id->vendor | config_id->device << 16; |
| 171 |
sio_pci_t *d = malloc(sizeof *d, M_DEVBUF, M_WAITOK); |
| 172 |
#define PCI_READ_CONFIG(reg) pci_conf_read(config_id, reg) |
| 173 |
#endif |
| 174 |
struct com_s *com; |
| 175 |
int s, err = 0; |
| 176 |
|
| 177 |
bzero((char*)d, sizeof(*d)); |
| 178 |
s = splimp(); |
| 179 |
|
| 180 |
/* Default values. */ |
| 181 |
d->nchan = 8; /* channels per adapter */ |
| 182 |
d->iostep = 8; /* addresses per channel */ |
| 183 |
d->base_reg = 0x18; /* pci register: base addr2 */ |
| 184 |
|
| 185 |
/* Device dependent values. */ |
| 186 |
if (device_id == 0xc00110b5) { |
| 187 |
d->fifo_size = 64; /* fifo size in bytes */ |
| 188 |
d->hw_rts_cts = 1; /* hardware rts/cts support */ |
| 189 |
} |
| 190 |
#ifdef notyet |
| 191 |
else if (device_id == 0xXXXXXXXX) { |
| 192 |
/* Add your device here. */ |
| 193 |
} |
| 194 |
#endif |
| 195 |
|
| 196 |
#if __FreeBSD_version >= 400000 |
| 197 |
/* Allocate i/o region. */ |
| 198 |
rid = d->base_reg; |
| 199 |
d->res = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 1, |
| 200 |
RF_ACTIVE); |
| 201 |
if (! d->res) { |
| 202 |
printf("sio%d: couldn't map ports/memory\n", sio_pci_numunits); |
| 203 |
err = ENXIO; |
| 204 |
goto fail; |
| 205 |
} |
| 206 |
|
| 207 |
/* Allocate interrupt. */ |
| 208 |
rid = 0; |
| 209 |
d->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, |
| 210 |
RF_SHAREABLE | RF_ACTIVE); |
| 211 |
if (! d->irq) { |
| 212 |
printf("sio%d: couldn't map interrupt\n", sio_pci_numunits); |
| 213 |
err = ENXIO; |
| 214 |
goto fail; |
| 215 |
} |
| 216 |
|
| 217 |
/* Register the interrupt handler. */ |
| 218 |
err = bus_setup_intr(dev, d->irq, INTR_TYPE_TTY | INTR_TYPE_FAST, |
| 219 |
sio_pci_intr, d, &d->intrhand); |
| 220 |
#else |
| 221 |
err = ! pci_map_int_right(config_id, sio_pci_intr, d, &tty_imask, |
| 222 |
INTR_FAST); |
| 223 |
#endif |
| 224 |
if (err) { |
| 225 |
printf("sio%d: couldn't set up irq\n", sio_pci_numunits); |
| 226 |
#if __FreeBSD_version >= 400000 |
| 227 |
fail: if (d->res) |
| 228 |
bus_release_resource(dev, SYS_RES_IOPORT, |
| 229 |
d->base_reg, d->res); |
| 230 |
if (d->irq) |
| 231 |
bus_release_resource(dev, SYS_RES_IRQ, 0, d->irq); |
| 232 |
#endif |
| 233 |
goto done; |
| 234 |
} |
| 235 |
|
| 236 |
/* Attach sio ports. */ |
| 237 |
d->iobase = PCI_READ_CONFIG (d->base_reg) & ~3; |
| 238 |
for (com=d->com; com<d->com+d->nchan; ++com) { |
| 239 |
com->iobase = d->iobase + (com - d->com) * d->iostep; |
| 240 |
|
| 241 |
/* Interrupt enable, do it _before_ sio_attach_unit. */ |
| 242 |
outb(com->iobase + com_mcr, MCR_IENABLE); |
| 243 |
|
| 244 |
if (sio_attach_unit(com, sio_pci_numunits++, com->iobase, |
| 245 |
d->fifo_size << 24, 0) != 0) |
| 246 |
printf("sio%d: cannot attach\n", sio_pci_numunits); |
| 247 |
|
| 248 |
/* Must do this _after_ sio_attach_unit. */ |
| 249 |
com->st16650a = d->hw_rts_cts; |
| 250 |
} |
| 251 |
done: |
| 252 |
splx (s); |
| 253 |
#if __FreeBSD_version >= 400000 |
| 254 |
return err; |
| 255 |
#endif |
| 256 |
} |
| 257 |
|
| 258 |
static void |
| 259 |
sio_pci_intr(arg) |
| 260 |
void *arg; |
| 261 |
{ |
| 262 |
sio_pci_t *d = arg; |
| 263 |
struct com_s *com; |
| 264 |
bool_t possibly_more_intrs; |
| 265 |
|
| 266 |
disable_intr(); |
| 267 |
do { |
| 268 |
possibly_more_intrs = FALSE; |
| 269 |
for (com=d->com; com<d->com+d->nchan; ++com) { |
| 270 |
if ((inb(com->int_id_port) & IIR_IMASK) != IIR_NOPEND) { |
| 271 |
siointr1(com); |
| 272 |
possibly_more_intrs = TRUE; |
| 273 |
} |
| 274 |
} |
| 275 |
} while (possibly_more_intrs); |
| 276 |
enable_intr(); |
| 277 |
} |