1eed8b80fSAndrew Turner /*-
2eed8b80fSAndrew Turner * SPDX-License-Identifier: ISC
3eed8b80fSAndrew Turner *
4eed8b80fSAndrew Turner * Copyright (c) 2020 Dr Robert Harvey Crowston <crowston@protonmail.com>
5eed8b80fSAndrew Turner *
6eed8b80fSAndrew Turner * Permission to use, copy, modify, and distribute this software for any
7eed8b80fSAndrew Turner * purpose with or without fee is hereby granted, provided that the above
8eed8b80fSAndrew Turner * copyright notice and this permission notice appear in all copies.
9eed8b80fSAndrew Turner *
10eed8b80fSAndrew Turner * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11eed8b80fSAndrew Turner * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12eed8b80fSAndrew Turner * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13eed8b80fSAndrew Turner * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14eed8b80fSAndrew Turner * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15eed8b80fSAndrew Turner * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16eed8b80fSAndrew Turner * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17eed8b80fSAndrew Turner *
18eed8b80fSAndrew Turner *
19eed8b80fSAndrew Turner */
20eed8b80fSAndrew Turner
21eed8b80fSAndrew Turner /*
22eed8b80fSAndrew Turner * BCM2838-compatible PCI-express controller.
23eed8b80fSAndrew Turner *
24eed8b80fSAndrew Turner * Broadcom likes to give the same chip lots of different names. The name of
25eed8b80fSAndrew Turner * this driver is taken from the Raspberry Pi 4 Broadcom 2838 chip.
26eed8b80fSAndrew Turner */
27eed8b80fSAndrew Turner
28eed8b80fSAndrew Turner #include <sys/param.h>
29eed8b80fSAndrew Turner #include <sys/systm.h>
30eed8b80fSAndrew Turner #include <sys/endian.h>
31eed8b80fSAndrew Turner #include <sys/kernel.h>
32eed8b80fSAndrew Turner #include <sys/module.h>
33eed8b80fSAndrew Turner #include <sys/bus.h>
34eed8b80fSAndrew Turner #include <sys/proc.h>
35eed8b80fSAndrew Turner #include <sys/rman.h>
36eed8b80fSAndrew Turner #include <sys/intr.h>
37eed8b80fSAndrew Turner #include <sys/mutex.h>
38eed8b80fSAndrew Turner
39eed8b80fSAndrew Turner #include <dev/ofw/openfirm.h>
40eed8b80fSAndrew Turner #include <dev/ofw/ofw_bus.h>
41eed8b80fSAndrew Turner #include <dev/ofw/ofw_bus_subr.h>
42eed8b80fSAndrew Turner
43eed8b80fSAndrew Turner #include <dev/pci/pci_host_generic.h>
44eed8b80fSAndrew Turner #include <dev/pci/pci_host_generic_fdt.h>
45eed8b80fSAndrew Turner #include <dev/pci/pcivar.h>
46eed8b80fSAndrew Turner #include <dev/pci/pcireg.h>
47eed8b80fSAndrew Turner #include <dev/pci/pcib_private.h>
48eed8b80fSAndrew Turner
49eed8b80fSAndrew Turner #include <machine/bus.h>
50eed8b80fSAndrew Turner #include <machine/intr.h>
51eed8b80fSAndrew Turner
52eed8b80fSAndrew Turner #include "pcib_if.h"
53eed8b80fSAndrew Turner #include "msi_if.h"
54eed8b80fSAndrew Turner
55eed8b80fSAndrew Turner #define PCI_ID_VAL3 0x43c
56eed8b80fSAndrew Turner #define CLASS_SHIFT 0x10
57eed8b80fSAndrew Turner #define SUBCLASS_SHIFT 0x8
58eed8b80fSAndrew Turner
59eed8b80fSAndrew Turner #define REG_CONTROLLER_HW_REV 0x406c
60eed8b80fSAndrew Turner #define REG_BRIDGE_CTRL 0x9210
61eed8b80fSAndrew Turner #define BRIDGE_DISABLE_FLAG 0x1
62eed8b80fSAndrew Turner #define BRIDGE_RESET_FLAG 0x2
6310e0c34bSHP van Braam #define REG_PCIE_HARD_DEBUG 0x4204
64ac89220bSMike Karels #define REG_DMA_CONFIG 0x4008
65ac89220bSMike Karels #define REG_DMA_WINDOW_LOW 0x4034
66ac89220bSMike Karels #define REG_DMA_WINDOW_HIGH 0x4038
67ac89220bSMike Karels #define REG_DMA_WINDOW_1 0x403c
68eed8b80fSAndrew Turner #define REG_BRIDGE_GISB_WINDOW 0x402c
69eed8b80fSAndrew Turner #define REG_BRIDGE_STATE 0x4068
70eed8b80fSAndrew Turner #define REG_BRIDGE_LINK_STATE 0x00bc
71ac89220bSMike Karels #define REG_BUS_WINDOW_LOW 0x400c
72ac89220bSMike Karels #define REG_BUS_WINDOW_HIGH 0x4010
73ac89220bSMike Karels #define REG_CPU_WINDOW_LOW 0x4070
74ac89220bSMike Karels #define REG_CPU_WINDOW_START_HIGH 0x4080
75ac89220bSMike Karels #define REG_CPU_WINDOW_END_HIGH 0x4084
76eed8b80fSAndrew Turner
77eed8b80fSAndrew Turner #define REG_MSI_ADDR_LOW 0x4044
78eed8b80fSAndrew Turner #define REG_MSI_ADDR_HIGH 0x4048
79eed8b80fSAndrew Turner #define REG_MSI_CONFIG 0x404c
80eed8b80fSAndrew Turner #define REG_MSI_CLR 0x4508
81eed8b80fSAndrew Turner #define REG_MSI_MASK_CLR 0x4514
82eed8b80fSAndrew Turner #define REG_MSI_RAISED 0x4500
83eed8b80fSAndrew Turner #define REG_MSI_EOI 0x4060
84eed8b80fSAndrew Turner #define NUM_MSI 32
85eed8b80fSAndrew Turner
86eed8b80fSAndrew Turner #define REG_EP_CONFIG_CHOICE 0x9000
87eed8b80fSAndrew Turner #define REG_EP_CONFIG_DATA 0x8000
88eed8b80fSAndrew Turner
8910e0c34bSHP van Braam #define L1SS_ENABLE 0x00200000
9010e0c34bSHP van Braam #define CLKREQ_ENABLE 0x2
9110e0c34bSHP van Braam
92eed8b80fSAndrew Turner /*
93ac89220bSMike Karels * The system memory controller can address up to 16 GiB of physical memory
94ac89220bSMike Karels * (although at time of writing the largest memory size available for purchase
95ac89220bSMike Karels * is 8 GiB). However, the system DMA controller is capable of accessing only a
96ac89220bSMike Karels * limited portion of the address space. Worse, the PCI-e controller has further
97ac89220bSMike Karels * constraints for DMA, and those limitations are not wholly clear to the
98ac89220bSMike Karels * author. NetBSD and Linux allow DMA on the lower 3 GiB of the physical memory,
99ac89220bSMike Karels * but experimentation shows DMA performed above 960 MiB results in data
100ac89220bSMike Karels * corruption with this driver. The limit of 960 MiB is taken from OpenBSD, but
101ac89220bSMike Karels * apparently that value was chosen for satisfying a constraint of an unrelated
102ac89220bSMike Karels * peripheral.
103ac89220bSMike Karels *
104ac89220bSMike Karels * Whatever the true maximum address, 960 MiB works.
105eed8b80fSAndrew Turner */
106ac89220bSMike Karels #define DMA_HIGH_LIMIT 0x3c000000
107ac89220bSMike Karels #define MAX_MEMORY_LOG2 0x21
108ac89220bSMike Karels #define REG_VALUE_DMA_WINDOW_LOW (MAX_MEMORY_LOG2 - 0xf)
109ac89220bSMike Karels #define REG_VALUE_DMA_WINDOW_HIGH 0x0
110ac89220bSMike Karels #define DMA_WINDOW_ENABLE 0x3000
111ac89220bSMike Karels #define REG_VALUE_DMA_WINDOW_CONFIG \
112ac89220bSMike Karels (((MAX_MEMORY_LOG2 - 0xf) << 0x1b) | DMA_WINDOW_ENABLE)
113ac89220bSMike Karels
114eed8b80fSAndrew Turner #define REG_VALUE_MSI_CONFIG 0xffe06540
115eed8b80fSAndrew Turner
116eed8b80fSAndrew Turner struct bcm_pcib_irqsrc {
117eed8b80fSAndrew Turner struct intr_irqsrc isrc;
118eed8b80fSAndrew Turner u_int irq;
119eed8b80fSAndrew Turner bool allocated;
120eed8b80fSAndrew Turner };
121eed8b80fSAndrew Turner
122eed8b80fSAndrew Turner struct bcm_pcib_softc {
123eed8b80fSAndrew Turner struct generic_pcie_fdt_softc base;
124eed8b80fSAndrew Turner device_t dev;
125ac89220bSMike Karels bus_dma_tag_t dmat;
126eed8b80fSAndrew Turner struct mtx config_mtx;
127eed8b80fSAndrew Turner struct mtx msi_mtx;
128eed8b80fSAndrew Turner struct resource *msi_irq_res;
129eed8b80fSAndrew Turner void *msi_intr_cookie;
130eed8b80fSAndrew Turner struct bcm_pcib_irqsrc *msi_isrcs;
131eed8b80fSAndrew Turner pci_addr_t msi_addr;
132eed8b80fSAndrew Turner };
133eed8b80fSAndrew Turner
134eed8b80fSAndrew Turner static struct ofw_compat_data compat_data[] = {
135eed8b80fSAndrew Turner {"brcm,bcm2711-pcie", 1},
136eed8b80fSAndrew Turner {"brcm,bcm7211-pcie", 1},
137eed8b80fSAndrew Turner {"brcm,bcm7445-pcie", 1},
138eed8b80fSAndrew Turner {NULL, 0}
139eed8b80fSAndrew Turner };
140eed8b80fSAndrew Turner
141eed8b80fSAndrew Turner static int
bcm_pcib_probe(device_t dev)142eed8b80fSAndrew Turner bcm_pcib_probe(device_t dev)
143eed8b80fSAndrew Turner {
144eed8b80fSAndrew Turner
145eed8b80fSAndrew Turner if (!ofw_bus_status_okay(dev))
146eed8b80fSAndrew Turner return (ENXIO);
147eed8b80fSAndrew Turner
148eed8b80fSAndrew Turner if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
149eed8b80fSAndrew Turner return (ENXIO);
150eed8b80fSAndrew Turner
151eed8b80fSAndrew Turner device_set_desc(dev,
152eed8b80fSAndrew Turner "BCM2838-compatible PCI-express controller");
153eed8b80fSAndrew Turner return (BUS_PROBE_DEFAULT);
154eed8b80fSAndrew Turner }
155eed8b80fSAndrew Turner
156ac89220bSMike Karels static bus_dma_tag_t
bcm_pcib_get_dma_tag(device_t dev,device_t child)157ac89220bSMike Karels bcm_pcib_get_dma_tag(device_t dev, device_t child)
158ac89220bSMike Karels {
159ac89220bSMike Karels struct bcm_pcib_softc *sc;
160ac89220bSMike Karels
161ac89220bSMike Karels sc = device_get_softc(dev);
162ac89220bSMike Karels return (sc->dmat);
163ac89220bSMike Karels }
164ac89220bSMike Karels
165eed8b80fSAndrew Turner static void
bcm_pcib_set_reg(struct bcm_pcib_softc * sc,uint32_t reg,uint32_t val)166eed8b80fSAndrew Turner bcm_pcib_set_reg(struct bcm_pcib_softc *sc, uint32_t reg, uint32_t val)
167eed8b80fSAndrew Turner {
168eed8b80fSAndrew Turner
1698bc94f25SAndrew Turner bus_write_4(sc->base.base.res, reg, htole32(val));
170eed8b80fSAndrew Turner }
171eed8b80fSAndrew Turner
172eed8b80fSAndrew Turner static uint32_t
bcm_pcib_read_reg(struct bcm_pcib_softc * sc,uint32_t reg)173eed8b80fSAndrew Turner bcm_pcib_read_reg(struct bcm_pcib_softc *sc, uint32_t reg)
174eed8b80fSAndrew Turner {
175eed8b80fSAndrew Turner
1768bc94f25SAndrew Turner return (le32toh(bus_read_4(sc->base.base.res, reg)));
177eed8b80fSAndrew Turner }
178eed8b80fSAndrew Turner
179eed8b80fSAndrew Turner static void
bcm_pcib_reset_controller(struct bcm_pcib_softc * sc)180eed8b80fSAndrew Turner bcm_pcib_reset_controller(struct bcm_pcib_softc *sc)
181eed8b80fSAndrew Turner {
182eed8b80fSAndrew Turner uint32_t val;
183eed8b80fSAndrew Turner
184eed8b80fSAndrew Turner val = bcm_pcib_read_reg(sc, REG_BRIDGE_CTRL);
185eed8b80fSAndrew Turner val = val | BRIDGE_RESET_FLAG | BRIDGE_DISABLE_FLAG;
186eed8b80fSAndrew Turner bcm_pcib_set_reg(sc, REG_BRIDGE_CTRL, val);
187eed8b80fSAndrew Turner
188eed8b80fSAndrew Turner DELAY(100);
189eed8b80fSAndrew Turner
190eed8b80fSAndrew Turner val = bcm_pcib_read_reg(sc, REG_BRIDGE_CTRL);
191eed8b80fSAndrew Turner val = val & ~BRIDGE_RESET_FLAG;
192eed8b80fSAndrew Turner bcm_pcib_set_reg(sc, REG_BRIDGE_CTRL, val);
193eed8b80fSAndrew Turner
194eed8b80fSAndrew Turner DELAY(100);
195eed8b80fSAndrew Turner
19610e0c34bSHP van Braam bcm_pcib_set_reg(sc, REG_PCIE_HARD_DEBUG, 0);
197eed8b80fSAndrew Turner
198eed8b80fSAndrew Turner DELAY(100);
199eed8b80fSAndrew Turner }
200eed8b80fSAndrew Turner
201eed8b80fSAndrew Turner static void
bcm_pcib_enable_controller(struct bcm_pcib_softc * sc)202eed8b80fSAndrew Turner bcm_pcib_enable_controller(struct bcm_pcib_softc *sc)
203eed8b80fSAndrew Turner {
204eed8b80fSAndrew Turner uint32_t val;
205eed8b80fSAndrew Turner
206eed8b80fSAndrew Turner val = bcm_pcib_read_reg(sc, REG_BRIDGE_CTRL);
207eed8b80fSAndrew Turner val = val & ~BRIDGE_DISABLE_FLAG;
208eed8b80fSAndrew Turner bcm_pcib_set_reg(sc, REG_BRIDGE_CTRL, val);
209eed8b80fSAndrew Turner
210eed8b80fSAndrew Turner DELAY(100);
211eed8b80fSAndrew Turner }
212eed8b80fSAndrew Turner
213eed8b80fSAndrew Turner static int
bcm_pcib_check_ranges(device_t dev)214eed8b80fSAndrew Turner bcm_pcib_check_ranges(device_t dev)
215eed8b80fSAndrew Turner {
216eed8b80fSAndrew Turner struct bcm_pcib_softc *sc;
217eed8b80fSAndrew Turner struct pcie_range *ranges;
218eed8b80fSAndrew Turner int error = 0, i;
219eed8b80fSAndrew Turner
220eed8b80fSAndrew Turner sc = device_get_softc(dev);
221eed8b80fSAndrew Turner ranges = &sc->base.base.ranges[0];
222eed8b80fSAndrew Turner
223eed8b80fSAndrew Turner /* The first range needs to be non-zero. */
224eed8b80fSAndrew Turner if (ranges[0].size == 0) {
225eed8b80fSAndrew Turner device_printf(dev, "error: first outbound memory range "
226eed8b80fSAndrew Turner "(pci addr: 0x%jx, cpu addr: 0x%jx) has zero size.\n",
227eed8b80fSAndrew Turner ranges[0].pci_base, ranges[0].phys_base);
228eed8b80fSAndrew Turner error = ENXIO;
229eed8b80fSAndrew Turner }
230eed8b80fSAndrew Turner
231eed8b80fSAndrew Turner /*
232eed8b80fSAndrew Turner * The controller can actually handle three distinct ranges, but we
233eed8b80fSAndrew Turner * only implement support for one.
234eed8b80fSAndrew Turner */
235eed8b80fSAndrew Turner for (i = 1; (bootverbose || error) && i < MAX_RANGES_TUPLES; ++i) {
236eed8b80fSAndrew Turner if (ranges[i].size > 0)
237eed8b80fSAndrew Turner device_printf(dev,
238eed8b80fSAndrew Turner "note: outbound memory range %d (pci addr: 0x%jx, "
239eed8b80fSAndrew Turner "cpu addr: 0x%jx, size: 0x%jx) will be ignored.\n",
240eed8b80fSAndrew Turner i, ranges[i].pci_base, ranges[i].phys_base,
241eed8b80fSAndrew Turner ranges[i].size);
242eed8b80fSAndrew Turner }
243eed8b80fSAndrew Turner
244eed8b80fSAndrew Turner return (error);
245eed8b80fSAndrew Turner }
246eed8b80fSAndrew Turner
247eed8b80fSAndrew Turner static const char *
bcm_pcib_link_state_string(uint32_t mode)248eed8b80fSAndrew Turner bcm_pcib_link_state_string(uint32_t mode)
249eed8b80fSAndrew Turner {
250eed8b80fSAndrew Turner
251eed8b80fSAndrew Turner switch(mode & PCIEM_LINK_STA_SPEED) {
252eed8b80fSAndrew Turner case 0:
253eed8b80fSAndrew Turner return ("not up");
254eed8b80fSAndrew Turner case 1:
255eed8b80fSAndrew Turner return ("2.5 GT/s");
256eed8b80fSAndrew Turner case 2:
257eed8b80fSAndrew Turner return ("5.0 GT/s");
258eed8b80fSAndrew Turner case 4:
259eed8b80fSAndrew Turner return ("8.0 GT/s");
260eed8b80fSAndrew Turner default:
261eed8b80fSAndrew Turner return ("unknown");
262eed8b80fSAndrew Turner }
263eed8b80fSAndrew Turner }
264eed8b80fSAndrew Turner
265eed8b80fSAndrew Turner static bus_addr_t
bcm_get_offset_and_prepare_config(struct bcm_pcib_softc * sc,u_int bus,u_int slot,u_int func,u_int reg)266eed8b80fSAndrew Turner bcm_get_offset_and_prepare_config(struct bcm_pcib_softc *sc, u_int bus,
267eed8b80fSAndrew Turner u_int slot, u_int func, u_int reg)
268eed8b80fSAndrew Turner {
269eed8b80fSAndrew Turner /*
270eed8b80fSAndrew Turner * Config for an end point is only available through a narrow window for
271eed8b80fSAndrew Turner * one end point at a time. We first tell the controller which end point
272eed8b80fSAndrew Turner * we want, then access it through the window.
273eed8b80fSAndrew Turner */
274eed8b80fSAndrew Turner uint32_t func_index;
275eed8b80fSAndrew Turner
276eed8b80fSAndrew Turner if (bus == 0 && slot == 0 && func == 0)
277eed8b80fSAndrew Turner /*
278eed8b80fSAndrew Turner * Special case for root device; its config is always available
279eed8b80fSAndrew Turner * through the zero-offset.
280eed8b80fSAndrew Turner */
281eed8b80fSAndrew Turner return (reg);
282eed8b80fSAndrew Turner
283eed8b80fSAndrew Turner /* Tell the controller to show us the config in question. */
284eed8b80fSAndrew Turner func_index = PCIE_ADDR_OFFSET(bus, slot, func, 0);
285eed8b80fSAndrew Turner bcm_pcib_set_reg(sc, REG_EP_CONFIG_CHOICE, func_index);
286eed8b80fSAndrew Turner
287eed8b80fSAndrew Turner return (REG_EP_CONFIG_DATA + reg);
288eed8b80fSAndrew Turner }
289eed8b80fSAndrew Turner
290eed8b80fSAndrew Turner static bool
bcm_pcib_is_valid_quad(struct bcm_pcib_softc * sc,u_int bus,u_int slot,u_int func,u_int reg)291eed8b80fSAndrew Turner bcm_pcib_is_valid_quad(struct bcm_pcib_softc *sc, u_int bus, u_int slot,
292eed8b80fSAndrew Turner u_int func, u_int reg)
293eed8b80fSAndrew Turner {
294eed8b80fSAndrew Turner
295eed8b80fSAndrew Turner if ((bus < sc->base.base.bus_start) || (bus > sc->base.base.bus_end))
296eed8b80fSAndrew Turner return (false);
297eed8b80fSAndrew Turner if ((slot > PCI_SLOTMAX) || (func > PCI_FUNCMAX) || (reg > PCIE_REGMAX))
298eed8b80fSAndrew Turner return (false);
299eed8b80fSAndrew Turner
300eed8b80fSAndrew Turner if (bus == 0 && slot == 0 && func == 0)
301eed8b80fSAndrew Turner return (true);
302eed8b80fSAndrew Turner if (bus == 0)
303eed8b80fSAndrew Turner /*
304eed8b80fSAndrew Turner * Probing other slots and funcs on bus 0 will lock up the
305eed8b80fSAndrew Turner * memory controller.
306eed8b80fSAndrew Turner */
307eed8b80fSAndrew Turner return (false);
308eed8b80fSAndrew Turner
309eed8b80fSAndrew Turner return (true);
310eed8b80fSAndrew Turner }
311eed8b80fSAndrew Turner
312eed8b80fSAndrew Turner static uint32_t
bcm_pcib_read_config(device_t dev,u_int bus,u_int slot,u_int func,u_int reg,int bytes)313eed8b80fSAndrew Turner bcm_pcib_read_config(device_t dev, u_int bus, u_int slot, u_int func, u_int reg,
314eed8b80fSAndrew Turner int bytes)
315eed8b80fSAndrew Turner {
316eed8b80fSAndrew Turner struct bcm_pcib_softc *sc;
317eed8b80fSAndrew Turner bus_addr_t offset;
318eed8b80fSAndrew Turner uint32_t data;
319eed8b80fSAndrew Turner
320eed8b80fSAndrew Turner sc = device_get_softc(dev);
321eed8b80fSAndrew Turner if (!bcm_pcib_is_valid_quad(sc, bus, slot, func, reg))
322eed8b80fSAndrew Turner return (~0U);
323eed8b80fSAndrew Turner
324eed8b80fSAndrew Turner mtx_lock(&sc->config_mtx);
325eed8b80fSAndrew Turner offset = bcm_get_offset_and_prepare_config(sc, bus, slot, func, reg);
326eed8b80fSAndrew Turner
327eed8b80fSAndrew Turner switch (bytes) {
328eed8b80fSAndrew Turner case 1:
3298bc94f25SAndrew Turner data = bus_read_1(sc->base.base.res, offset);
330eed8b80fSAndrew Turner break;
331eed8b80fSAndrew Turner case 2:
3328bc94f25SAndrew Turner data = le16toh(bus_read_2(sc->base.base.res, offset));
333eed8b80fSAndrew Turner break;
334eed8b80fSAndrew Turner case 4:
3358bc94f25SAndrew Turner data = le32toh(bus_read_4(sc->base.base.res, offset));
336eed8b80fSAndrew Turner break;
337eed8b80fSAndrew Turner default:
338eed8b80fSAndrew Turner data = ~0U;
339eed8b80fSAndrew Turner break;
340eed8b80fSAndrew Turner }
341eed8b80fSAndrew Turner
342eed8b80fSAndrew Turner mtx_unlock(&sc->config_mtx);
343eed8b80fSAndrew Turner return (data);
344eed8b80fSAndrew Turner }
345eed8b80fSAndrew Turner
346eed8b80fSAndrew Turner static void
bcm_pcib_write_config(device_t dev,u_int bus,u_int slot,u_int func,u_int reg,uint32_t val,int bytes)347eed8b80fSAndrew Turner bcm_pcib_write_config(device_t dev, u_int bus, u_int slot,
348eed8b80fSAndrew Turner u_int func, u_int reg, uint32_t val, int bytes)
349eed8b80fSAndrew Turner {
350eed8b80fSAndrew Turner struct bcm_pcib_softc *sc;
351eed8b80fSAndrew Turner uint32_t offset;
352eed8b80fSAndrew Turner
353eed8b80fSAndrew Turner sc = device_get_softc(dev);
354eed8b80fSAndrew Turner if (!bcm_pcib_is_valid_quad(sc, bus, slot, func, reg))
355eed8b80fSAndrew Turner return;
356eed8b80fSAndrew Turner
357eed8b80fSAndrew Turner mtx_lock(&sc->config_mtx);
358eed8b80fSAndrew Turner offset = bcm_get_offset_and_prepare_config(sc, bus, slot, func, reg);
359eed8b80fSAndrew Turner
360eed8b80fSAndrew Turner switch (bytes) {
361eed8b80fSAndrew Turner case 1:
3628bc94f25SAndrew Turner bus_write_1(sc->base.base.res, offset, val);
363eed8b80fSAndrew Turner break;
364eed8b80fSAndrew Turner case 2:
3658bc94f25SAndrew Turner bus_write_2(sc->base.base.res, offset, htole16(val));
366eed8b80fSAndrew Turner break;
367eed8b80fSAndrew Turner case 4:
3688bc94f25SAndrew Turner bus_write_4(sc->base.base.res, offset, htole32(val));
369eed8b80fSAndrew Turner break;
370eed8b80fSAndrew Turner default:
371eed8b80fSAndrew Turner break;
372eed8b80fSAndrew Turner }
373eed8b80fSAndrew Turner
374eed8b80fSAndrew Turner mtx_unlock(&sc->config_mtx);
375eed8b80fSAndrew Turner }
376eed8b80fSAndrew Turner
377eed8b80fSAndrew Turner static void
bcm_pcib_msi_intr_process(struct bcm_pcib_softc * sc,uint32_t interrupt_bitmap,struct trapframe * tf)378eed8b80fSAndrew Turner bcm_pcib_msi_intr_process(struct bcm_pcib_softc *sc, uint32_t interrupt_bitmap,
379eed8b80fSAndrew Turner struct trapframe *tf)
380eed8b80fSAndrew Turner {
381eed8b80fSAndrew Turner struct bcm_pcib_irqsrc *irqsrc;
382eed8b80fSAndrew Turner uint32_t bit, irq;
383eed8b80fSAndrew Turner
384eed8b80fSAndrew Turner while ((bit = ffs(interrupt_bitmap))) {
385eed8b80fSAndrew Turner irq = bit - 1;
386eed8b80fSAndrew Turner
387eed8b80fSAndrew Turner /* Acknowledge interrupt. */
388eed8b80fSAndrew Turner bcm_pcib_set_reg(sc, REG_MSI_CLR, 1 << irq);
389eed8b80fSAndrew Turner
390eed8b80fSAndrew Turner /* Send EOI. */
391eed8b80fSAndrew Turner bcm_pcib_set_reg(sc, REG_MSI_EOI, 1);
392eed8b80fSAndrew Turner
393eed8b80fSAndrew Turner /* Despatch to handler. */
394eed8b80fSAndrew Turner irqsrc = &sc->msi_isrcs[irq];
395eed8b80fSAndrew Turner if (intr_isrc_dispatch(&irqsrc->isrc, tf))
396eed8b80fSAndrew Turner device_printf(sc->dev,
397eed8b80fSAndrew Turner "note: unexpected interrupt (%d) triggered.\n",
398eed8b80fSAndrew Turner irq);
399eed8b80fSAndrew Turner
400eed8b80fSAndrew Turner /* Done with this interrupt. */
401eed8b80fSAndrew Turner interrupt_bitmap = interrupt_bitmap & ~(1 << irq);
402eed8b80fSAndrew Turner }
403eed8b80fSAndrew Turner }
404eed8b80fSAndrew Turner
405eed8b80fSAndrew Turner static int
bcm_pcib_msi_intr(void * arg)406eed8b80fSAndrew Turner bcm_pcib_msi_intr(void *arg)
407eed8b80fSAndrew Turner {
408eed8b80fSAndrew Turner struct bcm_pcib_softc *sc;
409eed8b80fSAndrew Turner struct trapframe *tf;
410eed8b80fSAndrew Turner uint32_t interrupt_bitmap;
411eed8b80fSAndrew Turner
412eed8b80fSAndrew Turner sc = (struct bcm_pcib_softc *) arg;
413eed8b80fSAndrew Turner tf = curthread->td_intr_frame;
414eed8b80fSAndrew Turner
415eed8b80fSAndrew Turner while ((interrupt_bitmap = bcm_pcib_read_reg(sc, REG_MSI_RAISED)))
416eed8b80fSAndrew Turner bcm_pcib_msi_intr_process(sc, interrupt_bitmap, tf);
417eed8b80fSAndrew Turner
418eed8b80fSAndrew Turner return (FILTER_HANDLED);
419eed8b80fSAndrew Turner }
420eed8b80fSAndrew Turner
421eed8b80fSAndrew Turner static int
bcm_pcib_alloc_msi(device_t dev,device_t child,int count,int maxcount,device_t * pic,struct intr_irqsrc ** srcs)422eed8b80fSAndrew Turner bcm_pcib_alloc_msi(device_t dev, device_t child, int count, int maxcount,
423eed8b80fSAndrew Turner device_t *pic, struct intr_irqsrc **srcs)
424eed8b80fSAndrew Turner {
425eed8b80fSAndrew Turner struct bcm_pcib_softc *sc;
426eed8b80fSAndrew Turner int first_int, i;
427eed8b80fSAndrew Turner
428eed8b80fSAndrew Turner sc = device_get_softc(dev);
429eed8b80fSAndrew Turner mtx_lock(&sc->msi_mtx);
430eed8b80fSAndrew Turner
431eed8b80fSAndrew Turner /* Find a continguous region of free message-signalled interrupts. */
432eed8b80fSAndrew Turner for (first_int = 0; first_int + count < NUM_MSI; ) {
433eed8b80fSAndrew Turner for (i = first_int; i < first_int + count; ++i) {
434eed8b80fSAndrew Turner if (sc->msi_isrcs[i].allocated)
435eed8b80fSAndrew Turner goto next;
436eed8b80fSAndrew Turner }
437eed8b80fSAndrew Turner goto found;
438eed8b80fSAndrew Turner next:
439eed8b80fSAndrew Turner first_int = i + 1;
440eed8b80fSAndrew Turner }
441eed8b80fSAndrew Turner
442eed8b80fSAndrew Turner /* No appropriate region available. */
443eed8b80fSAndrew Turner mtx_unlock(&sc->msi_mtx);
444eed8b80fSAndrew Turner device_printf(dev, "warning: failed to allocate %d MSI messages.\n",
445eed8b80fSAndrew Turner count);
446eed8b80fSAndrew Turner return (ENXIO);
447eed8b80fSAndrew Turner
448eed8b80fSAndrew Turner found:
449eed8b80fSAndrew Turner /* Mark the messages as in use. */
450eed8b80fSAndrew Turner for (i = 0; i < count; ++i) {
451eed8b80fSAndrew Turner sc->msi_isrcs[i + first_int].allocated = true;
452eed8b80fSAndrew Turner srcs[i] = &(sc->msi_isrcs[i + first_int].isrc);
453eed8b80fSAndrew Turner }
454eed8b80fSAndrew Turner
455eed8b80fSAndrew Turner mtx_unlock(&sc->msi_mtx);
456eed8b80fSAndrew Turner *pic = device_get_parent(dev);
457eed8b80fSAndrew Turner
458eed8b80fSAndrew Turner return (0);
459eed8b80fSAndrew Turner }
460eed8b80fSAndrew Turner
461eed8b80fSAndrew Turner static int
bcm_pcib_map_msi(device_t dev,device_t child,struct intr_irqsrc * isrc,uint64_t * addr,uint32_t * data)462eed8b80fSAndrew Turner bcm_pcib_map_msi(device_t dev, device_t child, struct intr_irqsrc *isrc,
463eed8b80fSAndrew Turner uint64_t *addr, uint32_t *data)
464eed8b80fSAndrew Turner {
465eed8b80fSAndrew Turner struct bcm_pcib_softc *sc;
466eed8b80fSAndrew Turner struct bcm_pcib_irqsrc *msi_msg;
467eed8b80fSAndrew Turner
468eed8b80fSAndrew Turner sc = device_get_softc(dev);
469eed8b80fSAndrew Turner msi_msg = (struct bcm_pcib_irqsrc *) isrc;
470eed8b80fSAndrew Turner
471eed8b80fSAndrew Turner *addr = sc->msi_addr;
472eed8b80fSAndrew Turner *data = (REG_VALUE_MSI_CONFIG & 0xffff) | msi_msg->irq;
473eed8b80fSAndrew Turner return (0);
474eed8b80fSAndrew Turner }
475eed8b80fSAndrew Turner
476eed8b80fSAndrew Turner static int
bcm_pcib_release_msi(device_t dev,device_t child,int count,struct intr_irqsrc ** isrc)477eed8b80fSAndrew Turner bcm_pcib_release_msi(device_t dev, device_t child, int count,
478eed8b80fSAndrew Turner struct intr_irqsrc **isrc)
479eed8b80fSAndrew Turner {
480eed8b80fSAndrew Turner struct bcm_pcib_softc *sc;
481eed8b80fSAndrew Turner struct bcm_pcib_irqsrc *msi_isrc;
482eed8b80fSAndrew Turner int i;
483eed8b80fSAndrew Turner
484eed8b80fSAndrew Turner sc = device_get_softc(dev);
485eed8b80fSAndrew Turner mtx_lock(&sc->msi_mtx);
486eed8b80fSAndrew Turner
487eed8b80fSAndrew Turner for (i = 0; i < count; i++) {
488eed8b80fSAndrew Turner msi_isrc = (struct bcm_pcib_irqsrc *) isrc[i];
489eed8b80fSAndrew Turner msi_isrc->allocated = false;
490eed8b80fSAndrew Turner }
491eed8b80fSAndrew Turner
492eed8b80fSAndrew Turner mtx_unlock(&sc->msi_mtx);
493eed8b80fSAndrew Turner return (0);
494eed8b80fSAndrew Turner }
495eed8b80fSAndrew Turner
496eed8b80fSAndrew Turner static int
bcm_pcib_msi_attach(device_t dev)497eed8b80fSAndrew Turner bcm_pcib_msi_attach(device_t dev)
498eed8b80fSAndrew Turner {
499eed8b80fSAndrew Turner struct bcm_pcib_softc *sc;
500eed8b80fSAndrew Turner phandle_t node, xref;
501eed8b80fSAndrew Turner char const *bcm_name;
502b25d7f74SAndrew Turner int error, i, rid;
503eed8b80fSAndrew Turner
504eed8b80fSAndrew Turner sc = device_get_softc(dev);
505eed8b80fSAndrew Turner sc->msi_addr = 0xffffffffc;
506eed8b80fSAndrew Turner
507eed8b80fSAndrew Turner /* Clear any pending interrupts. */
508eed8b80fSAndrew Turner bcm_pcib_set_reg(sc, REG_MSI_CLR, 0xffffffff);
509eed8b80fSAndrew Turner
510eed8b80fSAndrew Turner rid = 1;
511eed8b80fSAndrew Turner sc->msi_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
512eed8b80fSAndrew Turner RF_ACTIVE);
513eed8b80fSAndrew Turner if (sc->msi_irq_res == NULL) {
514eed8b80fSAndrew Turner device_printf(dev, "could not allocate MSI irq resource.\n");
515eed8b80fSAndrew Turner return (ENXIO);
516eed8b80fSAndrew Turner }
517eed8b80fSAndrew Turner
518eed8b80fSAndrew Turner sc->msi_isrcs = malloc(sizeof(*sc->msi_isrcs) * NUM_MSI, M_DEVBUF,
519eed8b80fSAndrew Turner M_WAITOK | M_ZERO);
520eed8b80fSAndrew Turner
521b25d7f74SAndrew Turner error = bus_setup_intr(dev, sc->msi_irq_res, INTR_TYPE_BIO |
522eed8b80fSAndrew Turner INTR_MPSAFE, bcm_pcib_msi_intr, NULL, sc, &sc->msi_intr_cookie);
523c3147104SAndrew Turner if (error != 0) {
524eed8b80fSAndrew Turner device_printf(dev, "error: failed to setup MSI handler.\n");
5255749fdc5SAndrew Turner return (error);
526eed8b80fSAndrew Turner }
527eed8b80fSAndrew Turner
528eed8b80fSAndrew Turner bcm_name = device_get_nameunit(dev);
529eed8b80fSAndrew Turner for (i = 0; i < NUM_MSI; i++) {
530eed8b80fSAndrew Turner sc->msi_isrcs[i].irq = i;
531eed8b80fSAndrew Turner error = intr_isrc_register(&sc->msi_isrcs[i].isrc, dev, 0,
532eed8b80fSAndrew Turner "%s,%u", bcm_name, i);
533c3147104SAndrew Turner if (error != 0) {
534eed8b80fSAndrew Turner device_printf(dev,
535eed8b80fSAndrew Turner "error: failed to register interrupt %d.\n", i);
5365749fdc5SAndrew Turner return (error);
537eed8b80fSAndrew Turner }
538eed8b80fSAndrew Turner }
539eed8b80fSAndrew Turner
540eed8b80fSAndrew Turner node = ofw_bus_get_node(dev);
541eed8b80fSAndrew Turner xref = OF_xref_from_node(node);
542eed8b80fSAndrew Turner OF_device_register_xref(xref, dev);
543eed8b80fSAndrew Turner
544eed8b80fSAndrew Turner error = intr_msi_register(dev, xref);
545c3147104SAndrew Turner if (error != 0)
5465749fdc5SAndrew Turner return (error);
547eed8b80fSAndrew Turner
548eed8b80fSAndrew Turner mtx_init(&sc->msi_mtx, "bcm_pcib: msi_mtx", NULL, MTX_DEF);
549eed8b80fSAndrew Turner
550eed8b80fSAndrew Turner bcm_pcib_set_reg(sc, REG_MSI_MASK_CLR, 0xffffffff);
551eed8b80fSAndrew Turner bcm_pcib_set_reg(sc, REG_MSI_ADDR_LOW, (sc->msi_addr & 0xffffffff) | 1);
552eed8b80fSAndrew Turner bcm_pcib_set_reg(sc, REG_MSI_ADDR_HIGH, (sc->msi_addr >> 32));
553eed8b80fSAndrew Turner bcm_pcib_set_reg(sc, REG_MSI_CONFIG, REG_VALUE_MSI_CONFIG);
554eed8b80fSAndrew Turner
555eed8b80fSAndrew Turner return (0);
556eed8b80fSAndrew Turner }
557eed8b80fSAndrew Turner
558eed8b80fSAndrew Turner static void
bcm_pcib_relocate_bridge_window(device_t dev)559eed8b80fSAndrew Turner bcm_pcib_relocate_bridge_window(device_t dev)
560eed8b80fSAndrew Turner {
561eed8b80fSAndrew Turner /*
562eed8b80fSAndrew Turner * In principle an out-of-bounds bridge window could be automatically
563eed8b80fSAndrew Turner * adjusted at resource-activation time to lie within the bus address
564eed8b80fSAndrew Turner * space by pcib_grow_window(), but that is not possible because the
565eed8b80fSAndrew Turner * out-of-bounds resource allocation fails at allocation time. Instead,
566eed8b80fSAndrew Turner * we will just fix up the window on the controller here, before it is
567eed8b80fSAndrew Turner * re-discovered by pcib_probe_windows().
568eed8b80fSAndrew Turner */
569eed8b80fSAndrew Turner
570eed8b80fSAndrew Turner struct bcm_pcib_softc *sc;
571eed8b80fSAndrew Turner pci_addr_t base, size, new_base, new_limit;
572eed8b80fSAndrew Turner uint16_t val;
573eed8b80fSAndrew Turner
574eed8b80fSAndrew Turner sc = device_get_softc(dev);
575eed8b80fSAndrew Turner
576eed8b80fSAndrew Turner val = bcm_pcib_read_config(dev, 0, 0, 0, PCIR_MEMBASE_1, 2);
577eed8b80fSAndrew Turner base = PCI_PPBMEMBASE(0, val);
578eed8b80fSAndrew Turner
579eed8b80fSAndrew Turner val = bcm_pcib_read_config(dev, 0, 0, 0, PCIR_MEMLIMIT_1, 2);
580eed8b80fSAndrew Turner size = PCI_PPBMEMLIMIT(0, val) - base;
581eed8b80fSAndrew Turner
582eed8b80fSAndrew Turner new_base = sc->base.base.ranges[0].pci_base;
583eed8b80fSAndrew Turner val = (uint16_t) (new_base >> 16);
584eed8b80fSAndrew Turner bcm_pcib_write_config(dev, 0, 0, 0, PCIR_MEMBASE_1, val, 2);
585eed8b80fSAndrew Turner
586eed8b80fSAndrew Turner new_limit = new_base + size;
587eed8b80fSAndrew Turner val = (uint16_t) (new_limit >> 16);
588eed8b80fSAndrew Turner bcm_pcib_write_config(dev, 0, 0, 0, PCIR_MEMLIMIT_1, val, 2);
589eed8b80fSAndrew Turner }
590eed8b80fSAndrew Turner
591eed8b80fSAndrew Turner static uint32_t
encode_cpu_window_low(pci_addr_t phys_base,bus_size_t size)592eed8b80fSAndrew Turner encode_cpu_window_low(pci_addr_t phys_base, bus_size_t size)
593eed8b80fSAndrew Turner {
594eed8b80fSAndrew Turner
595eed8b80fSAndrew Turner return (((phys_base >> 0x10) & 0xfff0) |
596eed8b80fSAndrew Turner ((phys_base + size - 1) & 0xfff00000));
597eed8b80fSAndrew Turner }
598eed8b80fSAndrew Turner
599eed8b80fSAndrew Turner static uint32_t
encode_cpu_window_start_high(pci_addr_t phys_base)600eed8b80fSAndrew Turner encode_cpu_window_start_high(pci_addr_t phys_base)
601eed8b80fSAndrew Turner {
602eed8b80fSAndrew Turner
603eed8b80fSAndrew Turner return ((phys_base >> 0x20) & 0xff);
604eed8b80fSAndrew Turner }
605eed8b80fSAndrew Turner
606eed8b80fSAndrew Turner static uint32_t
encode_cpu_window_end_high(pci_addr_t phys_base,bus_size_t size)607eed8b80fSAndrew Turner encode_cpu_window_end_high(pci_addr_t phys_base, bus_size_t size)
608eed8b80fSAndrew Turner {
609eed8b80fSAndrew Turner
610eed8b80fSAndrew Turner return (((phys_base + size - 1) >> 0x20) & 0xff);
611eed8b80fSAndrew Turner }
612eed8b80fSAndrew Turner
613eed8b80fSAndrew Turner static int
bcm_pcib_attach(device_t dev)614eed8b80fSAndrew Turner bcm_pcib_attach(device_t dev)
615eed8b80fSAndrew Turner {
616eed8b80fSAndrew Turner struct bcm_pcib_softc *sc;
617eed8b80fSAndrew Turner pci_addr_t phys_base, pci_base;
618eed8b80fSAndrew Turner bus_size_t size;
61910e0c34bSHP van Braam uint32_t hardware_rev, bridge_state, link_state, tmp;
620eed8b80fSAndrew Turner int error, tries;
621eed8b80fSAndrew Turner
622eed8b80fSAndrew Turner sc = device_get_softc(dev);
623eed8b80fSAndrew Turner sc->dev = dev;
624eed8b80fSAndrew Turner
625ac89220bSMike Karels /*
626ac89220bSMike Karels * This tag will be used in preference to the one created in
627ac89220bSMike Karels * pci_host_generic.c.
628ac89220bSMike Karels */
629ac89220bSMike Karels error = bus_dma_tag_create(bus_get_dma_tag(dev), /* parent */
630ac89220bSMike Karels 1, 0, /* alignment, bounds */
631ac89220bSMike Karels DMA_HIGH_LIMIT, /* lowaddr */
632ac89220bSMike Karels BUS_SPACE_MAXADDR, /* highaddr */
633ac89220bSMike Karels NULL, NULL, /* filter, filterarg */
634ac89220bSMike Karels DMA_HIGH_LIMIT, /* maxsize */
635ac89220bSMike Karels BUS_SPACE_UNRESTRICTED, /* nsegments */
636ac89220bSMike Karels DMA_HIGH_LIMIT, /* maxsegsize */
637ac89220bSMike Karels 0, /* flags */
638ac89220bSMike Karels NULL, NULL, /* lockfunc, lockarg */
639ac89220bSMike Karels &sc->dmat);
640c3147104SAndrew Turner if (error != 0)
641ac89220bSMike Karels return (error);
642ac89220bSMike Karels
643eed8b80fSAndrew Turner error = pci_host_generic_setup_fdt(dev);
644c3147104SAndrew Turner if (error != 0)
645eed8b80fSAndrew Turner return (error);
646eed8b80fSAndrew Turner
647eed8b80fSAndrew Turner error = bcm_pcib_check_ranges(dev);
648c3147104SAndrew Turner if (error != 0)
649eed8b80fSAndrew Turner return (error);
650eed8b80fSAndrew Turner
651eed8b80fSAndrew Turner mtx_init(&sc->config_mtx, "bcm_pcib: config_mtx", NULL, MTX_DEF);
652eed8b80fSAndrew Turner
653eed8b80fSAndrew Turner bcm_pcib_reset_controller(sc);
654eed8b80fSAndrew Turner
655eed8b80fSAndrew Turner hardware_rev = bcm_pcib_read_reg(sc, REG_CONTROLLER_HW_REV) & 0xffff;
656eed8b80fSAndrew Turner device_printf(dev, "hardware identifies as revision 0x%x.\n",
657eed8b80fSAndrew Turner hardware_rev);
658eed8b80fSAndrew Turner
659eed8b80fSAndrew Turner /*
660eed8b80fSAndrew Turner * Set PCI->CPU memory window. This encodes the inbound window showing
661ac89220bSMike Karels * the system memory to the controller.
662eed8b80fSAndrew Turner */
663ac89220bSMike Karels bcm_pcib_set_reg(sc, REG_DMA_WINDOW_LOW, REG_VALUE_DMA_WINDOW_LOW);
664ac89220bSMike Karels bcm_pcib_set_reg(sc, REG_DMA_WINDOW_HIGH, REG_VALUE_DMA_WINDOW_HIGH);
665ac89220bSMike Karels bcm_pcib_set_reg(sc, REG_DMA_CONFIG, REG_VALUE_DMA_WINDOW_CONFIG);
666ac89220bSMike Karels
667eed8b80fSAndrew Turner bcm_pcib_set_reg(sc, REG_BRIDGE_GISB_WINDOW, 0);
668ac89220bSMike Karels bcm_pcib_set_reg(sc, REG_DMA_WINDOW_1, 0);
669eed8b80fSAndrew Turner
670eed8b80fSAndrew Turner bcm_pcib_enable_controller(sc);
671eed8b80fSAndrew Turner
672eed8b80fSAndrew Turner /* Wait for controller to start. */
673eed8b80fSAndrew Turner for(tries = 0; ; ++tries) {
674eed8b80fSAndrew Turner bridge_state = bcm_pcib_read_reg(sc, REG_BRIDGE_STATE);
675eed8b80fSAndrew Turner
676eed8b80fSAndrew Turner if ((bridge_state & 0x30) == 0x30)
677eed8b80fSAndrew Turner /* Controller ready. */
678eed8b80fSAndrew Turner break;
679eed8b80fSAndrew Turner
680eed8b80fSAndrew Turner if (tries > 100) {
681eed8b80fSAndrew Turner device_printf(dev,
682eed8b80fSAndrew Turner "error: controller failed to start.\n");
683eed8b80fSAndrew Turner return (ENXIO);
684eed8b80fSAndrew Turner }
685eed8b80fSAndrew Turner
686eed8b80fSAndrew Turner DELAY(1000);
687eed8b80fSAndrew Turner }
688eed8b80fSAndrew Turner
689eed8b80fSAndrew Turner link_state = bcm_pcib_read_reg(sc, REG_BRIDGE_LINK_STATE) >> 0x10;
690eed8b80fSAndrew Turner if (!link_state) {
691eed8b80fSAndrew Turner device_printf(dev, "error: controller started but link is not "
692eed8b80fSAndrew Turner "up.\n");
693eed8b80fSAndrew Turner return (ENXIO);
694eed8b80fSAndrew Turner }
695eed8b80fSAndrew Turner if (bootverbose)
696eed8b80fSAndrew Turner device_printf(dev, "note: reported link speed is %s.\n",
697eed8b80fSAndrew Turner bcm_pcib_link_state_string(link_state));
698eed8b80fSAndrew Turner
699eed8b80fSAndrew Turner /*
700eed8b80fSAndrew Turner * Set the CPU->PCI memory window. The map in this direction is not 1:1.
701eed8b80fSAndrew Turner * Addresses seen by the CPU need to be adjusted to make sense to the
702eed8b80fSAndrew Turner * controller as they pass through the window.
703eed8b80fSAndrew Turner */
704eed8b80fSAndrew Turner pci_base = sc->base.base.ranges[0].pci_base;
705eed8b80fSAndrew Turner phys_base = sc->base.base.ranges[0].phys_base;
706eed8b80fSAndrew Turner size = sc->base.base.ranges[0].size;
707eed8b80fSAndrew Turner
708ac89220bSMike Karels bcm_pcib_set_reg(sc, REG_BUS_WINDOW_LOW, pci_base & 0xffffffff);
709ac89220bSMike Karels bcm_pcib_set_reg(sc, REG_BUS_WINDOW_HIGH, pci_base >> 32);
710eed8b80fSAndrew Turner
711ac89220bSMike Karels bcm_pcib_set_reg(sc, REG_CPU_WINDOW_LOW,
712eed8b80fSAndrew Turner encode_cpu_window_low(phys_base, size));
713ac89220bSMike Karels bcm_pcib_set_reg(sc, REG_CPU_WINDOW_START_HIGH,
714eed8b80fSAndrew Turner encode_cpu_window_start_high(phys_base));
715ac89220bSMike Karels bcm_pcib_set_reg(sc, REG_CPU_WINDOW_END_HIGH,
716eed8b80fSAndrew Turner encode_cpu_window_end_high(phys_base, size));
717eed8b80fSAndrew Turner
718eed8b80fSAndrew Turner /*
719eed8b80fSAndrew Turner * The controller starts up declaring itself an endpoint; readvertise it
720eed8b80fSAndrew Turner * as a bridge.
721eed8b80fSAndrew Turner */
722eed8b80fSAndrew Turner bcm_pcib_set_reg(sc, PCI_ID_VAL3,
723eed8b80fSAndrew Turner PCIC_BRIDGE << CLASS_SHIFT | PCIS_BRIDGE_PCI << SUBCLASS_SHIFT);
724eed8b80fSAndrew Turner
72510e0c34bSHP van Braam tmp = bcm_pcib_read_reg(sc, REG_PCIE_HARD_DEBUG);
72610e0c34bSHP van Braam tmp |= CLKREQ_ENABLE;
72710e0c34bSHP van Braam
72810e0c34bSHP van Braam if (ofw_bus_has_prop(dev, "brcm,enable-l1ss")) {
72910e0c34bSHP van Braam if (bootverbose)
73010e0c34bSHP van Braam device_printf(dev, "note: enabling L1SS due to OF "
73110e0c34bSHP van Braam "property brcm,enable-l1ss\n");
73210e0c34bSHP van Braam
73310e0c34bSHP van Braam tmp |= L1SS_ENABLE;
73410e0c34bSHP van Braam }
73510e0c34bSHP van Braam
73610e0c34bSHP van Braam bcm_pcib_set_reg(sc, REG_PCIE_HARD_DEBUG, tmp);
737eed8b80fSAndrew Turner DELAY(100);
738eed8b80fSAndrew Turner
739eed8b80fSAndrew Turner bcm_pcib_relocate_bridge_window(dev);
740eed8b80fSAndrew Turner
741eed8b80fSAndrew Turner /* Configure interrupts. */
742eed8b80fSAndrew Turner error = bcm_pcib_msi_attach(dev);
743c3147104SAndrew Turner if (error != 0)
744eed8b80fSAndrew Turner return (error);
745eed8b80fSAndrew Turner
746eed8b80fSAndrew Turner /* Done. */
7475b56413dSWarner Losh device_add_child(dev, "pci", DEVICE_UNIT_ANY);
748*18250ec6SJohn Baldwin bus_attach_children(dev);
749*18250ec6SJohn Baldwin return (0);
750eed8b80fSAndrew Turner }
751eed8b80fSAndrew Turner
752eed8b80fSAndrew Turner /*
753eed8b80fSAndrew Turner * Device method table.
754eed8b80fSAndrew Turner */
755eed8b80fSAndrew Turner static device_method_t bcm_pcib_methods[] = {
756ac89220bSMike Karels /* Bus interface. */
757ac89220bSMike Karels DEVMETHOD(bus_get_dma_tag, bcm_pcib_get_dma_tag),
758ac89220bSMike Karels
759eed8b80fSAndrew Turner /* Device interface. */
760eed8b80fSAndrew Turner DEVMETHOD(device_probe, bcm_pcib_probe),
761eed8b80fSAndrew Turner DEVMETHOD(device_attach, bcm_pcib_attach),
762eed8b80fSAndrew Turner
763eed8b80fSAndrew Turner /* PCIB interface. */
764eed8b80fSAndrew Turner DEVMETHOD(pcib_read_config, bcm_pcib_read_config),
765eed8b80fSAndrew Turner DEVMETHOD(pcib_write_config, bcm_pcib_write_config),
766eed8b80fSAndrew Turner
767eed8b80fSAndrew Turner /* MSI interface. */
768eed8b80fSAndrew Turner DEVMETHOD(msi_alloc_msi, bcm_pcib_alloc_msi),
769eed8b80fSAndrew Turner DEVMETHOD(msi_release_msi, bcm_pcib_release_msi),
770eed8b80fSAndrew Turner DEVMETHOD(msi_map_msi, bcm_pcib_map_msi),
771eed8b80fSAndrew Turner
772eed8b80fSAndrew Turner DEVMETHOD_END
773eed8b80fSAndrew Turner };
774eed8b80fSAndrew Turner
775eed8b80fSAndrew Turner DEFINE_CLASS_1(pcib, bcm_pcib_driver, bcm_pcib_methods,
776eed8b80fSAndrew Turner sizeof(struct bcm_pcib_softc), generic_pcie_fdt_driver);
777eed8b80fSAndrew Turner
77882d4dc06SJohn Baldwin DRIVER_MODULE(bcm_pcib, simplebus, bcm_pcib_driver, 0, 0);
779ac89220bSMike Karels
780