1296077Sadrian/*-
2296077Sadrian * Copyright (c) 2015 Landon Fuller <landon@landonf.org>
3296077Sadrian * All rights reserved.
4296077Sadrian *
5296077Sadrian * Redistribution and use in source and binary forms, with or without
6296077Sadrian * modification, are permitted provided that the following conditions
7296077Sadrian * are met:
8296077Sadrian * 1. Redistributions of source code must retain the above copyright
9296077Sadrian *    notice, this list of conditions and the following disclaimer,
10296077Sadrian *    without modification.
11296077Sadrian * 2. Redistributions in binary form must reproduce at minimum a disclaimer
12296077Sadrian *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
13296077Sadrian *    redistribution must be conditioned upon including a substantially
14296077Sadrian *    similar Disclaimer requirement for further binary redistribution.
15296077Sadrian *
16296077Sadrian * NO WARRANTY
17296077Sadrian * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18296077Sadrian * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19296077Sadrian * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
20296077Sadrian * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
21296077Sadrian * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
22296077Sadrian * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23296077Sadrian * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24296077Sadrian * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25296077Sadrian * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26296077Sadrian * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
27296077Sadrian * THE POSSIBILITY OF SUCH DAMAGES.
28296077Sadrian */
29296077Sadrian
30296077Sadrian#include <sys/cdefs.h>
31296077Sadrian__FBSDID("$FreeBSD: stable/11/sys/dev/bhnd/siba/siba_bhndb.c 331044 2018-03-16 02:44:18Z eadler $");
32296077Sadrian
33296077Sadrian#include <sys/param.h>
34296077Sadrian#include <sys/kernel.h>
35296077Sadrian#include <sys/bus.h>
36296077Sadrian#include <sys/module.h>
37300015Sadrian#include <sys/systm.h>
38296077Sadrian
39296077Sadrian#include <dev/bhnd/bhnd_ids.h>
40296077Sadrian#include <dev/bhnd/bhndb/bhndbvar.h>
41296077Sadrian#include <dev/bhnd/bhndb/bhndb_hwdata.h>
42296077Sadrian
43300015Sadrian#include "sibareg.h"
44296077Sadrian#include "sibavar.h"
45296077Sadrian
46296077Sadrian/*
47296077Sadrian * Supports attachment of siba(4) bus devices via a bhndb bridge.
48296077Sadrian */
49296077Sadrian
50298479Sadrian//
51298479Sadrian// TODO: PCI rev < 6 interrupt handling
52298479Sadrian//
53298479Sadrian// On early PCI cores (rev < 6) interrupt masking is handled via interconnect
54298479Sadrian// configuration registers (SBINTVEC), rather than the PCI_INT_MASK
55298479Sadrian// config register.
56298479Sadrian//
57298479Sadrian// On those devices, we should handle interrupts locally using SBINTVEC, rather
58298479Sadrian// than delegating to our parent bhndb device.
59298479Sadrian//
60298479Sadrian
61300015Sadrianstatic int	siba_bhndb_wars_hwup(struct siba_softc *sc);
62300015Sadrian
63300628Sadrian/* Bridge-specific core device quirks */
64300015Sadrianenum {
65300015Sadrian	/** When PCIe-bridged, the D11 core's initiator request
66300015Sadrian	 *  timeout must be disabled to prevent D11 from entering a
67300015Sadrian	 *  RESP_TIMEOUT error state. */
68300015Sadrian	SIBA_QUIRK_PCIE_D11_SB_TIMEOUT	= (1<<0)
69300015Sadrian};
70300015Sadrian
71300628Sadrianstatic struct bhnd_device_quirk bridge_quirks[] = {
72300628Sadrian	BHND_CHIP_QUIRK(4311, HWREV_EQ(2), SIBA_QUIRK_PCIE_D11_SB_TIMEOUT),
73300628Sadrian	BHND_CHIP_QUIRK(4312, HWREV_EQ(0), SIBA_QUIRK_PCIE_D11_SB_TIMEOUT),
74331044Seadler	BHND_DEVICE_QUIRK_END
75300015Sadrian};
76300015Sadrian
77300628Sadrianstatic struct bhnd_device bridge_devs[] = {
78301697Slandonf	BHND_DEVICE(BCM, PCI, NULL, bridge_quirks),
79331044Seadler	BHND_DEVICE_END
80300628Sadrian};
81300628Sadrian
82296077Sadrianstatic int
83296077Sadriansiba_bhndb_probe(device_t dev)
84296077Sadrian{
85296077Sadrian	const struct bhnd_chipid *cid;
86296077Sadrian
87296077Sadrian	/* Check bus type */
88296077Sadrian	cid = BHNDB_GET_CHIPID(device_get_parent(dev), dev);
89296077Sadrian	if (cid->chip_type != BHND_CHIPTYPE_SIBA)
90296077Sadrian		return (ENXIO);
91296077Sadrian
92296077Sadrian	/* Delegate to default probe implementation */
93296077Sadrian	return (siba_probe(dev));
94296077Sadrian}
95296077Sadrian
96296077Sadrianstatic int
97296077Sadriansiba_bhndb_attach(device_t dev)
98296077Sadrian{
99299235Sadrian	struct siba_softc		*sc;
100296077Sadrian	const struct bhnd_chipid	*chipid;
101296077Sadrian	int				 error;
102296077Sadrian
103299235Sadrian	sc = device_get_softc(dev);
104299235Sadrian
105296077Sadrian	/* Enumerate our children. */
106296077Sadrian	chipid = BHNDB_GET_CHIPID(device_get_parent(dev), dev);
107296077Sadrian	if ((error = siba_add_children(dev, chipid)))
108296077Sadrian		return (error);
109296077Sadrian
110296077Sadrian	/* Initialize full bridge configuration */
111296077Sadrian	error = BHNDB_INIT_FULL_CONFIG(device_get_parent(dev), dev,
112296077Sadrian	    bhndb_siba_priority_table);
113296077Sadrian	if (error)
114296077Sadrian		return (error);
115296077Sadrian
116299235Sadrian	/* Ask our parent bridge to find the corresponding bridge core */
117299235Sadrian	sc->hostb_dev = BHNDB_FIND_HOSTB_DEVICE(device_get_parent(dev), dev);
118299235Sadrian
119296077Sadrian	/* Call our superclass' implementation */
120300015Sadrian	if ((error = siba_attach(dev)))
121300015Sadrian		return (error);
122300015Sadrian
123300015Sadrian	/* Apply attach/resume work-arounds */
124300015Sadrian	if ((error = siba_bhndb_wars_hwup(sc)))
125300015Sadrian		return (error);
126300015Sadrian
127300015Sadrian	return (0);
128296077Sadrian}
129296077Sadrian
130300015Sadrianstatic int
131300015Sadriansiba_bhndb_resume(device_t dev)
132300015Sadrian{
133300015Sadrian	struct siba_softc	*sc;
134300015Sadrian	int			 error;
135300015Sadrian
136300015Sadrian	sc = device_get_softc(dev);
137300015Sadrian
138300015Sadrian	/* Apply attach/resume work-arounds */
139300015Sadrian	if ((error = siba_bhndb_wars_hwup(sc)))
140300015Sadrian		return (error);
141300015Sadrian
142300015Sadrian	/* Call our superclass' implementation */
143300015Sadrian	return (siba_resume(dev));
144300015Sadrian}
145300015Sadrian
146296077Sadrian/* Suspend all references to the device's cfg register blocks */
147296077Sadrianstatic void
148296077Sadriansiba_bhndb_suspend_cfgblocks(device_t dev, struct siba_devinfo *dinfo) {
149296077Sadrian	for (u_int i = 0; i < dinfo->core_id.num_cfg_blocks; i++) {
150296077Sadrian		if (dinfo->cfg[i] == NULL)
151296077Sadrian			continue;
152296077Sadrian
153296077Sadrian		BHNDB_SUSPEND_RESOURCE(device_get_parent(dev), dev,
154296077Sadrian		    SYS_RES_MEMORY, dinfo->cfg[i]->res);
155296077Sadrian	}
156296077Sadrian}
157296077Sadrian
158296077Sadrianstatic int
159296077Sadriansiba_bhndb_suspend_child(device_t dev, device_t child)
160296077Sadrian{
161296077Sadrian	struct siba_devinfo	*dinfo;
162296077Sadrian	int			 error;
163296077Sadrian
164296077Sadrian	if (device_get_parent(child) != dev)
165296077Sadrian		BUS_SUSPEND_CHILD(device_get_parent(dev), child);
166296077Sadrian
167296077Sadrian	dinfo = device_get_ivars(child);
168296077Sadrian
169296077Sadrian	/* Suspend the child */
170296077Sadrian	if ((error = bhnd_generic_br_suspend_child(dev, child)))
171296077Sadrian		return (error);
172296077Sadrian
173296077Sadrian	/* Suspend resource references to the child's config registers */
174296077Sadrian	siba_bhndb_suspend_cfgblocks(dev, dinfo);
175296077Sadrian
176296077Sadrian	return (0);
177296077Sadrian}
178296077Sadrian
179296077Sadrianstatic int
180296077Sadriansiba_bhndb_resume_child(device_t dev, device_t child)
181296077Sadrian{
182296077Sadrian	struct siba_devinfo	*dinfo;
183296077Sadrian	int			 error;
184296077Sadrian
185296077Sadrian	if (device_get_parent(child) != dev)
186296077Sadrian		BUS_SUSPEND_CHILD(device_get_parent(dev), child);
187296077Sadrian
188296077Sadrian	if (!device_is_suspended(child))
189296077Sadrian		return (EBUSY);
190296077Sadrian
191296077Sadrian	dinfo = device_get_ivars(child);
192296077Sadrian
193296077Sadrian	/* Resume all resource references to the child's config registers */
194296077Sadrian	for (u_int i = 0; i < dinfo->core_id.num_cfg_blocks; i++) {
195296077Sadrian		if (dinfo->cfg[i] == NULL)
196296077Sadrian			continue;
197296077Sadrian
198296077Sadrian		error = BHNDB_RESUME_RESOURCE(device_get_parent(dev), dev,
199296077Sadrian		    SYS_RES_MEMORY, dinfo->cfg[i]->res);
200296077Sadrian		if (error) {
201296077Sadrian			siba_bhndb_suspend_cfgblocks(dev, dinfo);
202296077Sadrian			return (error);
203296077Sadrian		}
204296077Sadrian	}
205296077Sadrian
206296077Sadrian	/* Resume the child */
207296077Sadrian	if ((error = bhnd_generic_br_resume_child(dev, child))) {
208296077Sadrian		siba_bhndb_suspend_cfgblocks(dev, dinfo);
209296077Sadrian		return (error);
210296077Sadrian	}
211296077Sadrian
212296077Sadrian	return (0);
213296077Sadrian}
214296077Sadrian
215300015Sadrian/* Work-around implementation for SIBA_QUIRK_PCIE_D11_SB_TIMEOUT */
216300015Sadrianstatic int
217300015Sadriansiba_bhndb_wars_pcie_clear_d11_timeout(struct siba_softc *sc)
218300015Sadrian{
219300015Sadrian	struct siba_devinfo	*dinfo;
220300015Sadrian	device_t		 d11;
221300015Sadrian	uint32_t		 imcfg;
222300015Sadrian
223300015Sadrian	/* Only applies when bridged by PCIe */
224300015Sadrian	if (bhnd_get_class(sc->hostb_dev) != BHND_DEVCLASS_PCIE)
225300015Sadrian		return (0);
226300015Sadrian
227300015Sadrian	/* Only applies if there's a D11 core */
228300628Sadrian	d11 = bhnd_match_child(sc->dev, &(struct bhnd_core_match) {
229300628Sadrian		BHND_MATCH_CORE(BHND_MFGID_BCM, BHND_COREID_D11),
230300628Sadrian		BHND_MATCH_CORE_UNIT(0)
231300015Sadrian	});
232300015Sadrian	if (d11 == NULL)
233300015Sadrian		return (0);
234300015Sadrian
235300015Sadrian	/* Clear initiator timeout in D11's CFG0 block */
236300015Sadrian	dinfo = device_get_ivars(d11);
237300015Sadrian	KASSERT(dinfo->cfg[0] != NULL, ("missing core config mapping"));
238300015Sadrian
239300015Sadrian	imcfg = bhnd_bus_read_4(dinfo->cfg[0], SIBA_CFG0_IMCONFIGLOW);
240300015Sadrian	imcfg &= ~SIBA_IMCL_RTO_MASK;
241300015Sadrian
242300015Sadrian	bhnd_bus_write_4(dinfo->cfg[0], SIBA_CFG0_IMCONFIGLOW, imcfg);
243300015Sadrian
244300015Sadrian	return (0);
245300015Sadrian}
246300015Sadrian
247300015Sadrian/**
248300015Sadrian * Apply any hardware workarounds that are required upon attach or resume
249300015Sadrian * of the bus.
250300015Sadrian */
251300015Sadrianstatic int
252300015Sadriansiba_bhndb_wars_hwup(struct siba_softc *sc)
253300015Sadrian{
254300015Sadrian	uint32_t		 quirks;
255300015Sadrian	int			 error;
256300015Sadrian
257300628Sadrian	quirks = bhnd_device_quirks(sc->hostb_dev, bridge_devs,
258300628Sadrian	    sizeof(bridge_devs[0]));
259300015Sadrian
260300015Sadrian	if (quirks & SIBA_QUIRK_PCIE_D11_SB_TIMEOUT) {
261300015Sadrian		if ((error = siba_bhndb_wars_pcie_clear_d11_timeout(sc)))
262300015Sadrian			return (error);
263300015Sadrian	}
264300015Sadrian
265300015Sadrian	return (0);
266300015Sadrian}
267300015Sadrian
268300015Sadrian
269296077Sadrianstatic device_method_t siba_bhndb_methods[] = {
270296077Sadrian	/* Device interface */
271296077Sadrian	DEVMETHOD(device_probe,			siba_bhndb_probe),
272296077Sadrian	DEVMETHOD(device_attach,		siba_bhndb_attach),
273300015Sadrian	DEVMETHOD(device_resume,		siba_bhndb_resume),
274296077Sadrian
275296077Sadrian	/* Bus interface */
276296077Sadrian	DEVMETHOD(bus_suspend_child,		siba_bhndb_suspend_child),
277296077Sadrian	DEVMETHOD(bus_resume_child,		siba_bhndb_resume_child),
278296077Sadrian
279296077Sadrian	DEVMETHOD_END
280296077Sadrian};
281296077Sadrian
282300445SadrianDEFINE_CLASS_2(bhnd, siba_bhndb_driver, siba_bhndb_methods,
283300445Sadrian    sizeof(struct siba_softc), bhnd_bhndb_driver, siba_driver);
284296077Sadrian
285296077SadrianDRIVER_MODULE(siba_bhndb, bhndb, siba_bhndb_driver, bhnd_devclass, NULL, NULL);
286296077Sadrian
287296077SadrianMODULE_VERSION(siba_bhndb, 1);
288296077SadrianMODULE_DEPEND(siba_bhndb, siba, 1, 1, 1);
289298943SadrianMODULE_DEPEND(siba_bhndb, bhnd, 1, 1, 1);
290298943SadrianMODULE_DEPEND(siba_bhndb, bhndb, 1, 1, 1);
291