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
|
/* $NetBSD: cy_pci.c,v 1.26 2018/12/09 11:14:02 jdolecek Exp $ */
/*
* cy_pci.c
*
* Driver for Cyclades Cyclom-8/16/32 multiport serial cards
* (currently not tested with Cyclom-32 cards)
*
* Timo Rossi, 1996
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: cy_pci.c,v 1.26 2018/12/09 11:14:02 jdolecek Exp $");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/bus.h>
#include <sys/intr.h>
#include <dev/pci/pcivar.h>
#include <dev/pci/pcireg.h>
#include <dev/pci/pcidevs.h>
#include <dev/ic/cd1400reg.h>
#include <dev/ic/cyreg.h>
#include <dev/ic/cyvar.h>
struct cy_pci_softc {
struct cy_softc sc_cy; /* real cy softc */
bus_space_tag_t sc_iot; /* PLX runtime i/o tag */
bus_space_handle_t sc_ioh; /* PLX runtime i/o handle */
};
static const struct cy_pci_product {
pci_product_id_t cp_product; /* product ID */
pcireg_t cp_memtype; /* memory type */
} cy_pci_products[] = {
{ PCI_PRODUCT_CYCLADES_CYCLOMY_1,
PCI_MAPREG_TYPE_MEM|PCI_MAPREG_MEM_TYPE_32BIT_1M },
{ PCI_PRODUCT_CYCLADES_CYCLOM4Y_1,
PCI_MAPREG_TYPE_MEM|PCI_MAPREG_MEM_TYPE_32BIT_1M },
{ PCI_PRODUCT_CYCLADES_CYCLOM8Y_1,
PCI_MAPREG_TYPE_MEM|PCI_MAPREG_MEM_TYPE_32BIT_1M },
{ PCI_PRODUCT_CYCLADES_CYCLOMY_2,
PCI_MAPREG_TYPE_MEM|PCI_MAPREG_MEM_TYPE_32BIT },
{ PCI_PRODUCT_CYCLADES_CYCLOM4Y_2,
PCI_MAPREG_TYPE_MEM|PCI_MAPREG_MEM_TYPE_32BIT },
{ PCI_PRODUCT_CYCLADES_CYCLOM8Y_2,
PCI_MAPREG_TYPE_MEM|PCI_MAPREG_MEM_TYPE_32BIT },
{ 0,
0 },
};
static const int cy_pci_nproducts =
sizeof(cy_pci_products) / sizeof(cy_pci_products[0]);
static const struct cy_pci_product *
cy_pci_lookup(const struct pci_attach_args *pa)
{
const struct cy_pci_product *cp;
int i;
if (PCI_VENDOR(pa->pa_id) != PCI_VENDOR_CYCLADES)
return (NULL);
for (i = 0; i < cy_pci_nproducts; i++) {
cp = &cy_pci_products[i];
if (PCI_PRODUCT(pa->pa_id) == cp->cp_product)
return (cp);
}
return (NULL);
}
static int
cy_pci_match(device_t parent, cfdata_t match, void *aux)
{
struct pci_attach_args *pa = aux;
return (cy_pci_lookup(pa) != NULL);
}
static void
cy_pci_attach(device_t parent, device_t self, void *aux)
{
struct cy_pci_softc *psc = device_private(self);
struct cy_softc *sc = &psc->sc_cy;
struct pci_attach_args *pa = aux;
pci_intr_handle_t ih;
const struct cy_pci_product *cp;
const char *intrstr;
int plx_ver;
char intrbuf[PCI_INTRSTR_LEN];
sc->sc_dev = self;
aprint_naive(": Multi-port serial controller\n");
sc->sc_bustype = CY_BUSTYPE_PCI;
cp = cy_pci_lookup(pa);
if (cp == NULL)
panic("cy_pci_attach: impossible");
aprint_normal(": Cyclades-Y multiport serial\n");
if (pci_mapreg_map(pa, 0x14, PCI_MAPREG_TYPE_IO, 0,
&psc->sc_iot, &psc->sc_ioh, NULL, NULL) != 0) {
aprint_error_dev(sc->sc_dev, "unable to map PLX registers\n");
return;
}
if (pci_mapreg_map(pa, 0x18, cp->cp_memtype, 0,
&sc->sc_memt, &sc->sc_bsh, NULL, NULL) != 0) {
aprint_error_dev(sc->sc_dev,
"unable to map device registers\n");
return;
}
if (cy_find(sc) == 0) {
aprint_error_dev(sc->sc_dev, "unable to find CD1400s\n");
return;
}
/*
* XXX Like the Cyclades-Z, we should really check the EEPROM to
* determine the "poll or interrupt" setting. For now, we always
* map the interrupt and enable it in the PLX.
*/
/* Map and establish the interrupt. */
if (pci_intr_map(pa, &ih) != 0) {
aprint_error_dev(sc->sc_dev, "unable to map interrupt\n");
return;
}
intrstr = pci_intr_string(pa->pa_pc, ih, intrbuf, sizeof(intrbuf));
sc->sc_ih = pci_intr_establish_xname(pa->pa_pc, ih, IPL_TTY, cy_intr,
sc, device_xname(self));
if (sc->sc_ih == NULL) {
aprint_error_dev(sc->sc_dev, "unable to establish interrupt");
if (intrstr != NULL)
aprint_error(" at %s", intrstr);
aprint_error("\n");
return;
}
aprint_normal_dev(sc->sc_dev, "interrupting at %s\n", intrstr);
cy_attach(sc);
plx_ver = bus_space_read_1(sc->sc_memt, sc->sc_bsh, CY_PLX_VER) & 0x0f;
/* Enable PCI card interrupts */
switch (plx_ver) {
case CY_PLX_9050:
bus_space_write_2(psc->sc_iot, psc->sc_ioh, CY_PCI_INTENA_9050,
bus_space_read_2(psc->sc_iot, psc->sc_ioh,
CY_PCI_INTENA_9050) | 0x40);
break;
case CY_PLX_9060:
case CY_PLX_9080:
default:
bus_space_write_2(psc->sc_iot, psc->sc_ioh, CY_PCI_INTENA,
bus_space_read_2(psc->sc_iot, psc->sc_ioh,
CY_PCI_INTENA) | 0x900);
}
}
CFATTACH_DECL_NEW(cy_pci, sizeof(struct cy_pci_softc),
cy_pci_match, cy_pci_attach, NULL, NULL);
|