vlpci.c revision 1.3 1 /* $NetBSD: vlpci.c,v 1.3 2017/02/19 14:34:40 jakllsch Exp $ */
2
3 /*
4 * Copyright (c) 2017 Jonathan A. Kollasch
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
20 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
23 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
24 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
25 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
26 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: vlpci.c,v 1.3 2017/02/19 14:34:40 jakllsch Exp $");
31
32 #include "opt_pci.h"
33 #include "pci.h"
34
35 #include <sys/param.h>
36 #include <sys/bus.h>
37 #include <sys/device.h>
38 #include <sys/extent.h>
39 #include <sys/mutex.h>
40
41 #include <dev/isa/isavar.h>
42
43 #include <dev/ofw/openfirm.h>
44
45 #include <dev/pci/pcivar.h>
46 #include <dev/pci/pciconf.h>
47 #include <arm/pci_machdep.h>
48
49 static int vlpci_match(device_t, struct cfdata *, void *);
50 static void vlpci_attach(device_t, device_t, void *);
51
52 static void vlpci_pc_attach_hook(device_t, device_t,
53 struct pcibus_attach_args *);
54 static int vlpci_pc_bus_maxdevs(void *, int);
55 static pcitag_t vlpci_pc_make_tag(void *, int, int, int);
56 static void vlpci_pc_decompose_tag(void *, pcitag_t, int *, int *, int *);
57 static pcireg_t vlpci_pc_conf_read(void *, pcitag_t, int);
58 static void vlpci_pc_conf_write(void *, pcitag_t, int, pcireg_t);
59 #ifdef __HAVE_PCI_CONF_HOOK
60 static int vlpci_pc_conf_hook(void *, int, int, int, pcireg_t);
61 #endif
62 static void vlpci_pc_conf_interrupt(void *, int, int, int, int, int *);
63
64 struct vlpci_softc {
65 device_t sc_dev;
66 kmutex_t sc_lock;
67 bus_space_handle_t sc_conf_ioh;
68 bus_space_handle_t sc_reg_ioh;
69 struct arm32_pci_chipset sc_pc;
70 };
71
72 CFATTACH_DECL_NEW(vlpci, sizeof(struct vlpci_softc),
73 vlpci_match, vlpci_attach, NULL, NULL);
74
75 static const char * const compat_strings[] = { "via,vt82c505", NULL };
76
77 static void
78 regwrite_1(struct vlpci_softc * const sc, uint8_t off, uint8_t val)
79 {
80 mutex_spin_enter(&sc->sc_lock);
81 bus_space_write_1(&isa_io_bs_tag, sc->sc_reg_ioh, 0, off);
82 bus_space_write_1(&isa_io_bs_tag, sc->sc_reg_ioh, 1, val);
83 mutex_spin_exit(&sc->sc_lock);
84 }
85
86 static int
87 vlpci_match(device_t parent, struct cfdata *match, void *aux)
88 {
89 struct ofbus_attach_args * const oba = aux;
90
91 if (of_compatible(oba->oba_phandle, compat_strings) < 0)
92 return 0;
93
94 return 2; /* beat generic ofbus */
95 }
96
97 static void
98 vlpci_attach(device_t parent, device_t self, void *aux)
99 {
100 struct vlpci_softc * const sc = device_private(self);
101 pci_chipset_tag_t const pc = &sc->sc_pc;
102 struct pcibus_attach_args pba;
103
104 aprint_normal("\n");
105
106 sc->sc_dev = self;
107 mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_HIGH);
108 memset(&pba, 0, sizeof(pba));
109
110 if (bus_space_map(&isa_io_bs_tag, 0xa8, 0x2,
111 0, &sc->sc_reg_ioh) != 0) {
112 aprint_error_dev(self, "failed to map 0xa8-9\n");
113 return;
114 }
115 if (bus_space_map(&isa_io_bs_tag, 0xcf8, 0x8,
116 0, &sc->sc_conf_ioh) != 0) {
117 aprint_error_dev(self, "failed to map 0xcf8-f\n");
118 return;
119 }
120
121 /* Enable VLB/PCI bridge */
122 regwrite_1(sc, 0x96, 0x18); /* Undocumented by VIA */
123 regwrite_1(sc, 0x93, 0xd0);
124
125 pc->pc_conf_v = sc;
126 pc->pc_attach_hook = vlpci_pc_attach_hook;
127 pc->pc_bus_maxdevs = vlpci_pc_bus_maxdevs;
128 pc->pc_make_tag = vlpci_pc_make_tag;
129 pc->pc_decompose_tag = vlpci_pc_decompose_tag;
130 pc->pc_conf_read = vlpci_pc_conf_read;
131 pc->pc_conf_write = vlpci_pc_conf_write;
132 #ifdef __HAVE_PCI_CONF_HOOK
133 pc->pc_conf_hook = vlpci_pc_conf_hook;
134 #endif
135 pc->pc_conf_interrupt = vlpci_pc_conf_interrupt;
136
137 pc->pc_intr_v = sc;
138
139 pba.pba_flags = PCI_FLAGS_IO_OKAY; /* XXX test this, implement more */
140 pba.pba_pc = &sc->sc_pc;
141 pba.pba_bus = 0;
142
143 config_found_ia(self, "pcibus", &pba, pcibusprint);
144 }
145
146 static void
147 vlpci_pc_attach_hook(device_t parent, device_t self,
148 struct pcibus_attach_args *pba)
149 {
150 }
151
152 static int
153 vlpci_pc_bus_maxdevs(void *v, int busno)
154 {
155 return busno == 0 ? 32 : 0;
156 }
157
158 static pcitag_t
159 vlpci_pc_make_tag(void *v, int b, int d, int f)
160 {
161 return (b << 16) | (d << 11) | (f << 8);
162 }
163
164 static void
165 vlpci_pc_decompose_tag(void *v, pcitag_t tag, int *bp, int *dp, int *fp)
166 {
167 if (bp)
168 *bp = (tag >> 16) & 0xff;
169 if (dp)
170 *dp = (tag >> 11) & 0x1f;
171 if (fp)
172 *fp = (tag >> 8) & 0x7;
173 }
174
175 static pcireg_t
176 vlpci_pc_conf_read(void *v, pcitag_t tag, int offset)
177 {
178 struct vlpci_softc * const sc = v;
179 pcireg_t ret;
180
181 KASSERT((offset & 3) == 0);
182
183 if (offset >= PCI_CONF_SIZE)
184 return 0xffffffff;
185
186 mutex_spin_enter(&sc->sc_lock);
187 bus_space_write_4(&isa_io_bs_tag, sc->sc_conf_ioh, 0,
188 0x80000000UL|tag|offset);
189 ret = bus_space_read_4(&isa_io_bs_tag, sc->sc_conf_ioh, 4);
190 mutex_spin_exit(&sc->sc_lock);
191
192 #if 0
193 device_printf(sc->sc_dev, "%s tag %x offset %x ret %x\n",
194 __func__, (unsigned int)tag, offset, ret);
195 #endif
196
197 return ret;
198 }
199
200 static void
201 vlpci_pc_conf_write(void *v, pcitag_t tag, int offset, pcireg_t val)
202 {
203 struct vlpci_softc * const sc = v;
204
205 KASSERT((offset & 3) == 0);
206
207 if (offset >= PCI_CONF_SIZE)
208 return;
209
210 #if 0
211 device_printf(sc->sc_dev, "%s tag %x offset %x val %x\n",
212 __func__, (unsigned int)tag, offset, val);
213 #endif
214
215 mutex_spin_enter(&sc->sc_lock);
216 bus_space_write_4(&isa_io_bs_tag, sc->sc_conf_ioh, 0,
217 0x80000000UL|tag|offset);
218 bus_space_write_4(&isa_io_bs_tag, sc->sc_conf_ioh, 4, val);
219 mutex_spin_exit(&sc->sc_lock);
220 }
221
222 #ifdef __HAVE_PCI_CONF_HOOK
223 static int
224 vlpci_pc_conf_hook(void *v, int b, int d, int f, pcireg_t id)
225 {
226 return PCI_CONF_DEFAULT & ~PCI_CONF_ENABLE_BM;
227 }
228 #endif
229
230 static void
231 vlpci_pc_conf_interrupt(void *v, int bus, int dev, int ipin, int swiz,
232 int *ilinep)
233 {
234 *ilinep = 0xff; /* XXX */
235 }
236