aboutsummaryrefslogtreecommitdiff
path: root/core/pci-quirk.c
blob: 5c8b091ea320b8531428566c0ba0fd78f37a51ea (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
/*
 * Deal with PCI device quirks
 *
 * Copyright 2017-2018 IBM Corp.
 */

#define pr_fmt(fmt)  "PCI-QUIRK: " fmt

#include <skiboot.h>
#include <pci.h>
#include <pci-cfg.h>
#include <pci-quirk.h>
#include <platform.h>
#include <ast.h>

static int64_t cfg_block_filter(void *dev __unused,
				struct pci_cfg_reg_filter *pcrf __unused,
				uint32_t offset __unused, uint32_t len,
				uint32_t *data, bool write)
{
	if (write)
		return OPAL_SUCCESS;

	switch (len) {
	case 4:
		*data = 0x0;
		return OPAL_SUCCESS;
	case 2:
		*((uint16_t *)data) = 0x0;
		return OPAL_SUCCESS;
	case 1:
		*((uint8_t *)data) = 0x0;
		return OPAL_SUCCESS;
	}

	return OPAL_PARAMETER; /* should never happen */
}

/* blocks config accesses to registers in the range: [start, end] */
#define BLOCK_CFG_RANGE(pd, start, end) \
	pci_add_cfg_reg_filter(pd, start, end - start + 1, \
		PCI_REG_FLAG_WRITE | PCI_REG_FLAG_READ, \
		cfg_block_filter);

static void quirk_microsemi_gen4_sw(struct phb *phb, struct pci_device *pd)
{
	uint8_t data;
	bool frozen;
	int offset;
	int start;

	pci_check_clear_freeze(phb);

	/*
	 * Reading from 0xff should trigger a UR on the affected switches.
	 * If we don't get a freeze then we don't need the workaround
	 */
	pci_cfg_read8(phb, pd->bdfn, 0xff, &data);
	frozen = pci_check_clear_freeze(phb);
	if (!frozen)
		return;

	for (start = -1, offset = 0; offset < 4096; offset++) {
		pci_cfg_read8(phb, pd->bdfn, offset, &data);
		frozen = pci_check_clear_freeze(phb);

		if (start < 0 && frozen) { /* new UR range */
			start = offset;
		} else if (start >= 0 && !frozen) { /* end of range */
			BLOCK_CFG_RANGE(pd, start, offset - 1);
			PCINOTICE(phb, pd->bdfn, "Applied UR workaround to [%03x..%03x]\n", start, offset - 1);

			start = -1;
		}
	}

	/* range lasted until the end of config space */
	if (start >= 0) {
		BLOCK_CFG_RANGE(pd, start, 0xfff);
		PCINOTICE(phb, pd->bdfn, "Applied UR workaround to [%03x..fff]\n", start);
	}
}

static void quirk_astbmc_vga(struct phb *phb __unused,
			     struct pci_device *pd)
{
	struct dt_node *np = pd->dn;
	uint32_t revision, mcr_configuration, mcr_scu_mpll, mcr_scu_strap;

	if (ast_sio_is_enabled()) {
		revision = ast_ahb_readl(SCU_REVISION_ID);
		mcr_configuration = ast_ahb_readl(MCR_CONFIGURATION);
		mcr_scu_mpll = ast_ahb_readl(MCR_SCU_MPLL);
		mcr_scu_strap = ast_ahb_readl(MCR_SCU_STRAP);
	} else {
		/* Previously we would warn, now SIO disabled by design */
		prlog(PR_INFO, "Assumed platform default parameters for %s\n",
		      __func__);
		revision = bmc_platform->hw->scu_revision_id;
		mcr_configuration = bmc_platform->hw->mcr_configuration;
		mcr_scu_mpll = bmc_platform->hw->mcr_scu_mpll;
		mcr_scu_strap = bmc_platform->hw->mcr_scu_strap;
	}

	dt_add_property_cells(np, "aspeed,scu-revision-id", revision);
	dt_add_property_cells(np, "aspeed,mcr-configuration", mcr_configuration);
	dt_add_property_cells(np, "aspeed,mcr-scu-mpll", mcr_scu_mpll);
	dt_add_property_cells(np, "aspeed,mcr-scu-strap", mcr_scu_strap);
}

/* Quirks are: {fixup function, vendor ID, (device ID or PCI_ANY_ID)} */
static const struct pci_quirk quirk_table[] = {
	/* ASPEED 2400 VGA device */
	{ 0x1a03, 0x2000, &quirk_astbmc_vga },
	{ 0x11f8, 0x4052, &quirk_microsemi_gen4_sw },
	{ 0, 0, NULL }
};

static void __pci_handle_quirk(struct phb *phb, struct pci_device *pd,
			       const struct pci_quirk *quirks)
{
	while (quirks->vendor_id) {
		if (quirks->vendor_id == PCI_VENDOR_ID(pd->vdid) &&
		    (quirks->device_id == PCI_ANY_ID ||
		     quirks->device_id == PCI_DEVICE_ID(pd->vdid)))
			quirks->fixup(phb, pd);
		quirks++;
	}
}

void pci_handle_quirk(struct phb *phb, struct pci_device *pd)
{
	__pci_handle_quirk(phb, pd, quirk_table);
}