pcix.c revision 1.3 1 /* $NetBSD: pcix.c,v 1.3 2026/06/19 21:13:52 rkujawa Exp $ */
2
3 /*
4 * Copyright (c) 2012, 2014, 2024, 2026 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Radoslaw Kujawa.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31 /*
32 * IBM PLB-PCIX host bridge, as found on the 440GP/440SPe/460EX
33 *
34 * XXX The bridge addresses are currently straight from 460EX include
35 */
36
37 #include <sys/cdefs.h>
38 __KERNEL_RCSID(0, "$NetBSD: pcix.c,v 1.3 2026/06/19 21:13:52 rkujawa Exp $");
39
40 #ifdef _KERNEL_OPT
41 #include "opt_pci.h"
42 #endif
43
44 #include <sys/param.h>
45 #include <sys/systm.h>
46 #include <sys/device.h>
47 #include <sys/extent.h>
48 #include <sys/bus.h>
49
50 #include <dev/pci/pcivar.h>
51 #include <dev/pci/pcireg.h>
52 #include <dev/pci/pciconf.h>
53
54 #include <powerpc/ibm4xx/amcc460ex.h>
55 #include <powerpc/ibm4xx/cpu.h>
56 #include <powerpc/ibm4xx/spr.h>
57 #include <powerpc/ibm4xx/dev/plbvar.h>
58 #include <powerpc/ibm4xx/pci_machdep.h>
59 #include <powerpc/pci_machdep.h>
60
61 /* Internal register offsets (relative to the internal register base) */
62 #define PCIX0_POM0LAL 0x68
63 #define PCIX0_POM0LAH 0x6c
64 #define PCIX0_POM0SA 0x70
65 #define PCIX0_POM0PCIAL 0x74
66 #define PCIX0_POM0PCIAH 0x78
67 #define PCIX0_POM1LAL 0x7c
68 #define PCIX0_POM1LAH 0x80
69 #define PCIX0_POM1SA 0x84
70 #define PCIX0_POM1PCIAL 0x88
71 #define PCIX0_POM1PCIAH 0x8c
72 #define PCIX0_POM2SA 0x90
73 #define PCIX0_PIM0SAL 0x98
74 #define PCIX0_PIM0LAL 0x9c
75 #define PCIX0_PIM0LAH 0xa0
76 #define PCIX0_PIM1SA 0xa4
77 #define PCIX0_PIM1LAL 0xa8
78 #define PCIX0_PIM1LAH 0xac
79 #define PCIX0_PIM2SAL 0xb0
80 #define PCIX0_PIM0SAH 0xf8
81
82 #define PIM_SA_ENABLE 0x1
83 #define PIM_SA_PREFETCH 0x2 /* inbound read prefetch (bit 1) */
84
85 #define PCIC_CFGADDR 0
86 #define PCIC_CFGDATA 4
87
88 static int pcix_match(device_t, cfdata_t, void *);
89 static void pcix_attach(device_t, device_t, void *);
90 static int pcix_print(void *, const char *);
91
92 CFATTACH_DECL_NEW(pcix, 0, pcix_match, pcix_attach, NULL, NULL);
93
94 static pcireg_t pcix_conf_read(void *, pcitag_t, int);
95 static void pcix_conf_write(void *, pcitag_t, int, pcireg_t);
96 static void pcix_attach_hook(device_t, device_t,
97 struct pcibus_attach_args *);
98 static int pcix_conf_hook(void *, int, int, int, pcireg_t);
99
100 /* CFGADDR/CFGDATA pair */
101 static struct powerpc_bus_space pcix_cfg_tag = {
102 _BUS_SPACE_LITTLE_ENDIAN | _BUS_SPACE_MEM_TYPE,
103 0x00000000, /* offset */
104 AMCC460EX_PCIX0_CFG_PLBA, /* extent base */
105 AMCC460EX_PCIX0_CFG_PLBA + 8, /* extent limit */
106 };
107
108 /* Internal (window) registers */
109 static struct powerpc_bus_space pcix_reg_tag = {
110 _BUS_SPACE_LITTLE_ENDIAN | _BUS_SPACE_MEM_TYPE,
111 0x00000000,
112 AMCC460EX_PCIX0_REGS_PLBA,
113 AMCC460EX_PCIX0_REGS_PLBA + 0x100,
114 };
115
116 /* PCI I/O: bus 0x0000-0xffff -> PLB I/O window */
117 static struct powerpc_bus_space pcix_io_tag = {
118 _BUS_SPACE_LITTLE_ENDIAN | _BUS_SPACE_IO_TYPE,
119 AMCC460EX_PCIX0_IO_PLBA, /* offset */
120 0x00000000, /* extent base */
121 0x00010000, /* extent limit */
122 };
123
124 /*
125 * PCI memory: bus addr = low 32 bits of PLB addr.
126 */
127 static struct powerpc_bus_space pcix_mem_tag = {
128 _BUS_SPACE_LITTLE_ENDIAN | _BUS_SPACE_MEM_TYPE,
129 0x00000000,
130 AMCC460EX_PCIX0_MEM_BASE,
131 AMCC460EX_PCIX0_PMEM_BASE + AMCC460EX_PCIX0_PMEM_SIZE,
132 };
133
134 static bus_space_handle_t pcix_cfg_ioh;
135 static bus_space_handle_t pcix_reg_ioh;
136
137 static struct genppc_pci_chipset pcix_chipset = {
138 .pc_conf_v = NULL,
139 .pc_attach_hook = pcix_attach_hook,
140 .pc_bus_maxdevs = ibm4xx_pci_bus_maxdevs,
141 .pc_make_tag = ibm4xx_pci_make_tag,
142 .pc_conf_read = pcix_conf_read,
143 .pc_conf_write = pcix_conf_write,
144
145 .pc_intr_v = &pcix_chipset,
146 .pc_intr_map = genppc_pci_intr_map,
147 .pc_intr_string = genppc_pci_intr_string,
148 .pc_intr_evcnt = genppc_pci_intr_evcnt,
149 .pc_intr_establish = genppc_pci_intr_establish,
150 .pc_intr_disestablish = genppc_pci_intr_disestablish,
151 .pc_intr_setattr = ibm4xx_pci_intr_setattr,
152 .pc_intr_type = genppc_pci_intr_type,
153 .pc_intr_alloc = genppc_pci_intr_alloc,
154 .pc_intr_release = genppc_pci_intr_release,
155 .pc_intx_alloc = genppc_pci_intx_alloc,
156
157 .pc_msi_v = &pcix_chipset,
158 GENPPC_PCI_MSI_INITIALIZER,
159
160 .pc_msix_v = &pcix_chipset,
161 GENPPC_PCI_MSIX_INITIALIZER,
162
163 .pc_conf_interrupt = ibm4xx_pci_conf_interrupt,
164 .pc_decompose_tag = ibm4xx_pci_decompose_tag,
165 .pc_conf_hook = pcix_conf_hook,
166 };
167
168 static void
169 pcix_attach_hook(device_t parent, device_t self,
170 struct pcibus_attach_args *pba)
171 {
172 }
173
174 static int
175 pcix_conf_hook(void *v, int bus, int dev, int func, pcireg_t id)
176 {
177
178 return PCI_CONF_DEFAULT;
179 }
180
181 /*
182 * Translate the tag
183 */
184 static uint32_t
185 pcix_cfg_addr(pcitag_t tag, int reg)
186 {
187 uint32_t addr = (tag & 0x00ffff00) | (reg & 0xfc);
188
189 if (tag & 0x00ff0000) /* bus number != 0 -> Type 1 */
190 addr |= 1;
191 return addr;
192 }
193
194 static pcireg_t
195 pcix_conf_read(void *v, pcitag_t tag, int reg)
196 {
197 pcireg_t data;
198
199 if ((unsigned int)reg >= PCI_CONF_SIZE)
200 return (pcireg_t) -1;
201
202 bus_space_write_4(&pcix_cfg_tag, pcix_cfg_ioh, PCIC_CFGADDR,
203 pcix_cfg_addr(tag, reg));
204 data = bus_space_read_4(&pcix_cfg_tag, pcix_cfg_ioh, PCIC_CFGDATA);
205 bus_space_write_4(&pcix_cfg_tag, pcix_cfg_ioh, PCIC_CFGADDR, 0);
206 return data;
207 }
208
209 static void
210 pcix_conf_write(void *v, pcitag_t tag, int reg, pcireg_t data)
211 {
212
213 if ((unsigned int)reg >= PCI_CONF_SIZE)
214 return;
215
216 bus_space_write_4(&pcix_cfg_tag, pcix_cfg_ioh, PCIC_CFGADDR,
217 pcix_cfg_addr(tag, reg));
218 bus_space_write_4(&pcix_cfg_tag, pcix_cfg_ioh, PCIC_CFGDATA, data);
219 bus_space_write_4(&pcix_cfg_tag, pcix_cfg_ioh, PCIC_CFGADDR, 0);
220 }
221
222 #define PCIX_REG_WRITE(reg, val) \
223 bus_space_write_4(&pcix_reg_tag, pcix_reg_ioh, (reg), (val))
224
225 static void
226 pcix_setup_windows(void)
227 {
228
229 /* POM0: PLB -> PCI memory, 128MB */
230 PCIX_REG_WRITE(PCIX0_POM0SA, 0); /* disable while */
231 PCIX_REG_WRITE(PCIX0_POM0LAL, AMCC460EX_PCIX0_MEM_BASE);
232 PCIX_REG_WRITE(PCIX0_POM0LAH, AMCC460EX_PCIX0_MEM_PLBA_H);
233 PCIX_REG_WRITE(PCIX0_POM0PCIAL, AMCC460EX_PCIX0_MEM_BASE);
234 PCIX_REG_WRITE(PCIX0_POM0PCIAH, 0);
235 PCIX_REG_WRITE(PCIX0_POM0SA, ~(0x08000000 - 1) | 1);
236
237 /* POM1: PLB -> PCI prefetchable memory, identity mapped, 256MB */
238 PCIX_REG_WRITE(PCIX0_POM1SA, 0); /* disable while */
239 PCIX_REG_WRITE(PCIX0_POM1LAL, AMCC460EX_PCIX0_PMEM_BASE);
240 PCIX_REG_WRITE(PCIX0_POM1LAH, AMCC460EX_PCIX0_PMEM_PLBA_H);
241 PCIX_REG_WRITE(PCIX0_POM1PCIAL, AMCC460EX_PCIX0_PMEM_BASE);
242 PCIX_REG_WRITE(PCIX0_POM1PCIAH, 0);
243 PCIX_REG_WRITE(PCIX0_POM1SA, ~(AMCC460EX_PCIX0_PMEM_SIZE - 1) | 1);
244
245 /* POM2 unused */
246 PCIX_REG_WRITE(PCIX0_POM2SA, 0);
247
248 /* PIM0: PCI 0 -> PLB 0, 2GB (covers all of RAM) */
249 PCIX_REG_WRITE(PCIX0_PIM0SAL, 0); /* disable while */
250 PCIX_REG_WRITE(PCIX0_PIM0LAL, 0);
251 PCIX_REG_WRITE(PCIX0_PIM0LAH, 0);
252 PCIX_REG_WRITE(PCIX0_PIM0SAH, 0xffffffff);
253 /*
254 * Enable inbound read prefetch
255 */
256 PCIX_REG_WRITE(PCIX0_PIM0SAL,
257 ~(0x80000000U - 1) | PIM_SA_ENABLE | PIM_SA_PREFETCH);
258
259 /* PIM1/PIM2 unused */
260 PCIX_REG_WRITE(PCIX0_PIM1SA, 0);
261 PCIX_REG_WRITE(PCIX0_PIM2SAL, 0);
262 }
263
264 static int
265 pcix_match(device_t parent, cfdata_t cf, void *aux)
266 {
267 struct plb_attach_args *paa = aux;
268
269 if (strcmp(paa->plb_name, cf->cf_name) != 0)
270 return 0;
271
272 return 1;
273 }
274
275 static void
276 pcix_attach(device_t parent, device_t self, void *aux)
277 {
278 struct plb_attach_args *paa = aux;
279 struct pcibus_attach_args pba;
280 pci_chipset_tag_t pc = &pcix_chipset;
281
282 aprint_normal(": PLB-PCIX host bridge\n");
283
284 if (bus_space_init(&pcix_cfg_tag, "pcixcfg", NULL, 0) ||
285 bus_space_map(&pcix_cfg_tag, AMCC460EX_PCIX0_CFG_PLBA, 8, 0,
286 &pcix_cfg_ioh))
287 panic("pcix_attach: can't map config registers");
288 if (bus_space_init(&pcix_reg_tag, "pcixreg", NULL, 0) ||
289 bus_space_map(&pcix_reg_tag, AMCC460EX_PCIX0_REGS_PLBA, 0x100, 0,
290 &pcix_reg_ioh))
291 panic("pcix_attach: can't map bridge registers");
292 if (bus_space_init(&pcix_io_tag, "pcixio", NULL, 0))
293 panic("pcix_attach: can't init IO tag");
294 if (bus_space_init(&pcix_mem_tag, "pcixmem", NULL, 0))
295 panic("pcix_attach: can't init MEM tag");
296
297 pcix_setup_windows();
298
299 #ifdef PCI_NETBSD_CONFIGURE
300 struct pciconf_resources *pcires = pciconf_resource_init();
301
302 pciconf_resource_add(pcires, PCICONF_RESOURCE_IO,
303 0x1000, 0x10000 - 0x1000);
304 pciconf_resource_add(pcires, PCICONF_RESOURCE_MEM,
305 AMCC460EX_PCIX0_MEM_BASE, AMCC460EX_PCIX0_MEM_SIZE);
306 pciconf_resource_add(pcires, PCICONF_RESOURCE_PREFETCHABLE_MEM,
307 AMCC460EX_PCIX0_PMEM_BASE, AMCC460EX_PCIX0_PMEM_SIZE);
308
309 pci_configure_bus(pc, pcires, 0, 32);
310 pciconf_resource_fini(pcires);
311 #endif
312
313 pba.pba_iot = &pcix_io_tag;
314 pba.pba_memt = &pcix_mem_tag;
315 pba.pba_dmat = paa->plb_dmat;
316 pba.pba_dmat64 = NULL;
317 pba.pba_pc = pc;
318 pba.pba_bus = 0;
319 pba.pba_bridgetag = NULL;
320 pba.pba_flags = PCI_FLAGS_MEM_OKAY | PCI_FLAGS_IO_OKAY;
321 config_found(self, &pba, pcix_print, CFARGS_NONE);
322 }
323
324 static int
325 pcix_print(void *aux, const char *p)
326 {
327
328 if (p == NULL)
329 return UNCONF;
330 return QUIET;
331 }
332