pcix.c revision 1.2 1 /* $NetBSD: pcix.c,v 1.2 2026/06/17 15:08:54 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.2 2026/06/17 15:08:54 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 PCIC_CFGADDR 0
83 #define PCIC_CFGDATA 4
84
85 static int pcix_match(device_t, cfdata_t, void *);
86 static void pcix_attach(device_t, device_t, void *);
87 static int pcix_print(void *, const char *);
88
89 CFATTACH_DECL_NEW(pcix, 0, pcix_match, pcix_attach, NULL, NULL);
90
91 static pcireg_t pcix_conf_read(void *, pcitag_t, int);
92 static void pcix_conf_write(void *, pcitag_t, int, pcireg_t);
93 static void pcix_attach_hook(device_t, device_t,
94 struct pcibus_attach_args *);
95 static int pcix_conf_hook(void *, int, int, int, pcireg_t);
96
97 /* CFGADDR/CFGDATA pair */
98 static struct powerpc_bus_space pcix_cfg_tag = {
99 _BUS_SPACE_LITTLE_ENDIAN | _BUS_SPACE_MEM_TYPE,
100 0x00000000, /* offset */
101 AMCC460EX_PCIX0_CFG_PLBA, /* extent base */
102 AMCC460EX_PCIX0_CFG_PLBA + 8, /* extent limit */
103 };
104
105 /* Internal (window) registers */
106 static struct powerpc_bus_space pcix_reg_tag = {
107 _BUS_SPACE_LITTLE_ENDIAN | _BUS_SPACE_MEM_TYPE,
108 0x00000000,
109 AMCC460EX_PCIX0_REGS_PLBA,
110 AMCC460EX_PCIX0_REGS_PLBA + 0x100,
111 };
112
113 /* PCI I/O: bus 0x0000-0xffff -> PLB I/O window */
114 static struct powerpc_bus_space pcix_io_tag = {
115 _BUS_SPACE_LITTLE_ENDIAN | _BUS_SPACE_IO_TYPE,
116 AMCC460EX_PCIX0_IO_PLBA, /* offset */
117 0x00000000, /* extent base */
118 0x00010000, /* extent limit */
119 };
120
121 /*
122 * PCI memory: bus addr = low 32 bits of PLB addr.
123 */
124 static struct powerpc_bus_space pcix_mem_tag = {
125 _BUS_SPACE_LITTLE_ENDIAN | _BUS_SPACE_MEM_TYPE,
126 0x00000000,
127 AMCC460EX_PCIX0_MEM_BASE,
128 AMCC460EX_PCIX0_PMEM_BASE + AMCC460EX_PCIX0_PMEM_SIZE,
129 };
130
131 static bus_space_handle_t pcix_cfg_ioh;
132 static bus_space_handle_t pcix_reg_ioh;
133
134 static struct genppc_pci_chipset pcix_chipset = {
135 .pc_conf_v = NULL,
136 .pc_attach_hook = pcix_attach_hook,
137 .pc_bus_maxdevs = ibm4xx_pci_bus_maxdevs,
138 .pc_make_tag = ibm4xx_pci_make_tag,
139 .pc_conf_read = pcix_conf_read,
140 .pc_conf_write = pcix_conf_write,
141
142 .pc_intr_v = &pcix_chipset,
143 .pc_intr_map = genppc_pci_intr_map,
144 .pc_intr_string = genppc_pci_intr_string,
145 .pc_intr_evcnt = genppc_pci_intr_evcnt,
146 .pc_intr_establish = genppc_pci_intr_establish,
147 .pc_intr_disestablish = genppc_pci_intr_disestablish,
148 .pc_intr_setattr = ibm4xx_pci_intr_setattr,
149 .pc_intr_type = genppc_pci_intr_type,
150 .pc_intr_alloc = genppc_pci_intr_alloc,
151 .pc_intr_release = genppc_pci_intr_release,
152 .pc_intx_alloc = genppc_pci_intx_alloc,
153
154 .pc_msi_v = &pcix_chipset,
155 GENPPC_PCI_MSI_INITIALIZER,
156
157 .pc_msix_v = &pcix_chipset,
158 GENPPC_PCI_MSIX_INITIALIZER,
159
160 .pc_conf_interrupt = ibm4xx_pci_conf_interrupt,
161 .pc_decompose_tag = ibm4xx_pci_decompose_tag,
162 .pc_conf_hook = pcix_conf_hook,
163 };
164
165 static void
166 pcix_attach_hook(device_t parent, device_t self,
167 struct pcibus_attach_args *pba)
168 {
169 }
170
171 static int
172 pcix_conf_hook(void *v, int bus, int dev, int func, pcireg_t id)
173 {
174
175 return PCI_CONF_DEFAULT;
176 }
177
178 /*
179 * Translate the tag
180 */
181 static uint32_t
182 pcix_cfg_addr(pcitag_t tag, int reg)
183 {
184 uint32_t addr = (tag & 0x00ffff00) | (reg & 0xfc);
185
186 if (tag & 0x00ff0000) /* bus number != 0 -> Type 1 */
187 addr |= 1;
188 return addr;
189 }
190
191 static pcireg_t
192 pcix_conf_read(void *v, pcitag_t tag, int reg)
193 {
194 pcireg_t data;
195
196 if ((unsigned int)reg >= PCI_CONF_SIZE)
197 return (pcireg_t) -1;
198
199 bus_space_write_4(&pcix_cfg_tag, pcix_cfg_ioh, PCIC_CFGADDR,
200 pcix_cfg_addr(tag, reg));
201 data = bus_space_read_4(&pcix_cfg_tag, pcix_cfg_ioh, PCIC_CFGDATA);
202 bus_space_write_4(&pcix_cfg_tag, pcix_cfg_ioh, PCIC_CFGADDR, 0);
203 return data;
204 }
205
206 static void
207 pcix_conf_write(void *v, pcitag_t tag, int reg, pcireg_t data)
208 {
209
210 if ((unsigned int)reg >= PCI_CONF_SIZE)
211 return;
212
213 bus_space_write_4(&pcix_cfg_tag, pcix_cfg_ioh, PCIC_CFGADDR,
214 pcix_cfg_addr(tag, reg));
215 bus_space_write_4(&pcix_cfg_tag, pcix_cfg_ioh, PCIC_CFGDATA, data);
216 bus_space_write_4(&pcix_cfg_tag, pcix_cfg_ioh, PCIC_CFGADDR, 0);
217 }
218
219 #define PCIX_REG_WRITE(reg, val) \
220 bus_space_write_4(&pcix_reg_tag, pcix_reg_ioh, (reg), (val))
221
222 static void
223 pcix_setup_windows(void)
224 {
225
226 /* POM0: PLB -> PCI memory, 128MB */
227 PCIX_REG_WRITE(PCIX0_POM0SA, 0); /* disable while */
228 PCIX_REG_WRITE(PCIX0_POM0LAL, AMCC460EX_PCIX0_MEM_BASE);
229 PCIX_REG_WRITE(PCIX0_POM0LAH, AMCC460EX_PCIX0_MEM_PLBA_H);
230 PCIX_REG_WRITE(PCIX0_POM0PCIAL, AMCC460EX_PCIX0_MEM_BASE);
231 PCIX_REG_WRITE(PCIX0_POM0PCIAH, 0);
232 PCIX_REG_WRITE(PCIX0_POM0SA, ~(0x08000000 - 1) | 1);
233
234 /* POM1: PLB -> PCI prefetchable memory, identity mapped, 256MB */
235 PCIX_REG_WRITE(PCIX0_POM1SA, 0); /* disable while */
236 PCIX_REG_WRITE(PCIX0_POM1LAL, AMCC460EX_PCIX0_PMEM_BASE);
237 PCIX_REG_WRITE(PCIX0_POM1LAH, AMCC460EX_PCIX0_PMEM_PLBA_H);
238 PCIX_REG_WRITE(PCIX0_POM1PCIAL, AMCC460EX_PCIX0_PMEM_BASE);
239 PCIX_REG_WRITE(PCIX0_POM1PCIAH, 0);
240 PCIX_REG_WRITE(PCIX0_POM1SA, ~(AMCC460EX_PCIX0_PMEM_SIZE - 1) | 1);
241
242 /* POM2 unused */
243 PCIX_REG_WRITE(PCIX0_POM2SA, 0);
244
245 /* PIM0: PCI 0 -> PLB 0, 2GB (covers all of RAM) */
246 PCIX_REG_WRITE(PCIX0_PIM0SAL, 0); /* disable while */
247 PCIX_REG_WRITE(PCIX0_PIM0LAL, 0);
248 PCIX_REG_WRITE(PCIX0_PIM0LAH, 0);
249 PCIX_REG_WRITE(PCIX0_PIM0SAH, 0xffffffff);
250 PCIX_REG_WRITE(PCIX0_PIM0SAL, ~(0x80000000U - 1) | 1);
251
252 /* PIM1/PIM2 unused */
253 PCIX_REG_WRITE(PCIX0_PIM1SA, 0);
254 PCIX_REG_WRITE(PCIX0_PIM2SAL, 0);
255 }
256
257 static int
258 pcix_match(device_t parent, cfdata_t cf, void *aux)
259 {
260 struct plb_attach_args *paa = aux;
261
262 if (strcmp(paa->plb_name, cf->cf_name) != 0)
263 return 0;
264
265 return 1;
266 }
267
268 static void
269 pcix_attach(device_t parent, device_t self, void *aux)
270 {
271 struct plb_attach_args *paa = aux;
272 struct pcibus_attach_args pba;
273 pci_chipset_tag_t pc = &pcix_chipset;
274
275 aprint_normal(": PLB-PCIX host bridge\n");
276
277 if (bus_space_init(&pcix_cfg_tag, "pcixcfg", NULL, 0) ||
278 bus_space_map(&pcix_cfg_tag, AMCC460EX_PCIX0_CFG_PLBA, 8, 0,
279 &pcix_cfg_ioh))
280 panic("pcix_attach: can't map config registers");
281 if (bus_space_init(&pcix_reg_tag, "pcixreg", NULL, 0) ||
282 bus_space_map(&pcix_reg_tag, AMCC460EX_PCIX0_REGS_PLBA, 0x100, 0,
283 &pcix_reg_ioh))
284 panic("pcix_attach: can't map bridge registers");
285 if (bus_space_init(&pcix_io_tag, "pcixio", NULL, 0))
286 panic("pcix_attach: can't init IO tag");
287 if (bus_space_init(&pcix_mem_tag, "pcixmem", NULL, 0))
288 panic("pcix_attach: can't init MEM tag");
289
290 pcix_setup_windows();
291
292 #ifdef PCI_NETBSD_CONFIGURE
293 struct pciconf_resources *pcires = pciconf_resource_init();
294
295 pciconf_resource_add(pcires, PCICONF_RESOURCE_IO,
296 0x1000, 0x10000 - 0x1000);
297 pciconf_resource_add(pcires, PCICONF_RESOURCE_MEM,
298 AMCC460EX_PCIX0_MEM_BASE, AMCC460EX_PCIX0_MEM_SIZE);
299 pciconf_resource_add(pcires, PCICONF_RESOURCE_PREFETCHABLE_MEM,
300 AMCC460EX_PCIX0_PMEM_BASE, AMCC460EX_PCIX0_PMEM_SIZE);
301
302 pci_configure_bus(pc, pcires, 0, 32);
303 pciconf_resource_fini(pcires);
304 #endif
305
306 pba.pba_iot = &pcix_io_tag;
307 pba.pba_memt = &pcix_mem_tag;
308 pba.pba_dmat = paa->plb_dmat;
309 pba.pba_dmat64 = NULL;
310 pba.pba_pc = pc;
311 pba.pba_bus = 0;
312 pba.pba_bridgetag = NULL;
313 pba.pba_flags = PCI_FLAGS_MEM_OKAY | PCI_FLAGS_IO_OKAY;
314 config_found(self, &pba, pcix_print, CFARGS_NONE);
315 }
316
317 static int
318 pcix_print(void *aux, const char *p)
319 {
320
321 if (p == NULL)
322 return UNCONF;
323 return QUIET;
324 }
325