aboutsummaryrefslogtreecommitdiff
path: root/src/soc/intel/elkhartlake/tsn_gbe.c
blob: 6092824b84ab8673a71f8954485a4141581b4d98 (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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
/* SPDX-License-Identifier: GPL-2.0-only */

#include <console/console.h>
#include <delay.h>
#include <device/mdio.h>
#include <device/pci.h>
#include <device/pci_ids.h>
#include <device/pci_ops.h>
#include <intelblocks/lpss.h>
#include <soc/soc_chip.h>
#include <soc/tsn_gbe.h>
#include <timer.h>

static void program_mac_address(struct device *dev, void *base)
{
	enum cb_err status;
	uint8_t mac[MAC_ADDR_LEN];

	/* Check first whether there is a valid MAC address available */
	status = mainboard_get_mac_address(dev, mac);
	if (status != CB_SUCCESS) {
		printk(BIOS_INFO, "TSN GbE: No valid MAC address found\n");
		return;
	}

	printk(BIOS_DEBUG, "TSN GbE: Programming MAC Address...\n");

	/* Write the upper 16 bits of the first 6-byte MAC address */
	clrsetbits32(base + TSN_MAC_ADD0_HIGH, 0xffff, ((mac[5] << 8) | mac[4]));
	/* Write the lower 32 bits of the first 6-byte MAC address */
	clrsetbits32(base + TSN_MAC_ADD0_LOW, 0xffffffff,
			(mac[3] << 24) | (mac[2] << 16) | (mac[1] << 8) | mac[0]);
}


static void tsn_set_phy2mac_irq_polarity(struct device *dev, enum tsn_phy_irq_polarity pol)
{
	uint16_t gcr_reg;
	const struct mdio_bus_operations *mdio_ops;

	mdio_ops = dev_get_mdio_ops(dev);
	if (!mdio_ops)
		return;

	if (pol == RISING_EDGE) {
		/* Read TSN adhoc PHY sublayer register - global configuration register */
		gcr_reg = mdio_ops->read(dev, TSN_MAC_MDIO_ADHOC_ADR, TSN_MAC_MDIO_GCR);
		gcr_reg |= TSN_MAC_PHY2MAC_INTR_POL;
		mdio_ops->write(dev, TSN_MAC_MDIO_ADHOC_ADR, TSN_MAC_MDIO_GCR, gcr_reg);
	}
}

static void gbe_tsn_enable(struct device *dev)
{
	/* Ensure controller is in D0 state. */
	lpss_set_power_state(PCI_DEV(0, PCI_SLOT(dev->path.pci.devfn),
			PCI_FUNC(dev->path.pci.devfn)), STATE_D0);
}

static void gbe_tsn_init(struct device *dev)
{
	/* Get the base address of the I/O registers in memory space */
	struct resource *gbe_tsn_res = find_resource(dev, PCI_BASE_ADDRESS_0);
	void *io_mem_base = (void *)(uintptr_t)gbe_tsn_res->base;
	config_t *config = config_of(dev);

	/* Program MAC address */
	program_mac_address(dev, io_mem_base);

	/* Set PHY-to-MAC IRQ polarity according to devicetree */
	switch (dev->path.pci.devfn) {
	case PCH_DEVFN_GBE:
		tsn_set_phy2mac_irq_polarity(dev, config->pch_tsn_phy_irq_edge);
		break;
	case PCH_DEVFN_PSEGBE0:
		tsn_set_phy2mac_irq_polarity(dev, config->pse_tsn_phy_irq_edge[0]);
		break;
	case PCH_DEVFN_PSEGBE1:
		tsn_set_phy2mac_irq_polarity(dev, config->pse_tsn_phy_irq_edge[1]);
		break;
	}
}

static enum cb_err phy_gmii_ready(void *base)
{
	struct stopwatch sw;

	stopwatch_init_msecs_expire(&sw, GMII_TIMEOUT_MS);
	do {
		if (!(read32((base + MAC_MDIO_ADR)) & MAC_GMII_BUSY))
			return CB_SUCCESS;
		mdelay(1);
	} while (!stopwatch_expired(&sw));

	printk(BIOS_ERR, "%s Timeout after %lld msec\n", __func__,
			stopwatch_duration_msecs(&sw));
	return CB_ERR;
}

static uint16_t tsn_mdio_read(struct device *dev, uint8_t phy_adr, uint8_t reg_adr)
{
	uint16_t data = 0;
	struct resource *gbe_tsn_res = find_resource(dev, PCI_BASE_ADDRESS_0);
	void *mmio_base = res2mmio(gbe_tsn_res, 0, 0);

	if (!mmio_base)
		return data;

	clrsetbits32(mmio_base + MAC_MDIO_ADR, MAC_MDIO_ADR_MASK,
			MAC_PHYAD(phy_adr) | MAC_REGAD(reg_adr)
			| MAC_CLK_TRAIL_4 | MAC_CSR_CLK_DIV_102
			| MAC_OP_CMD_READ | MAC_GMII_BUSY);

	/* Wait for MDIO frame transfer to complete before reading MDIO DATA register. */
	if (phy_gmii_ready(mmio_base) != CB_SUCCESS) {
		printk(BIOS_ERR, "%s TSN GMII busy. PHY Adr: 0x%x, Reg 0x%x\n",
				__func__, phy_adr, reg_adr);
	} else {
		data = read16(mmio_base + MAC_MDIO_DATA);
		printk(BIOS_SPEW, "%s PHY Adr: 0x%x, Reg: 0x%x , Data: 0x%x\n",
				__func__, phy_adr, reg_adr, data);
	}
	return data;
}

static void tsn_mdio_write(struct device *dev, uint8_t phy_adr, uint8_t reg_adr, uint16_t data)
{
	struct resource *gbe_tsn_res = find_resource(dev, PCI_BASE_ADDRESS_0);
	void *mmio_base = res2mmio(gbe_tsn_res, 0, 0);

	if (!mmio_base)
		return;

	write16(mmio_base + MAC_MDIO_DATA, data);
	clrsetbits32(mmio_base + MAC_MDIO_ADR, MAC_MDIO_ADR_MASK,
			MAC_PHYAD(phy_adr) | MAC_REGAD(reg_adr)
			| MAC_CLK_TRAIL_4 | MAC_CSR_CLK_DIV_102
			| MAC_OP_CMD_WRITE | MAC_GMII_BUSY);

	/* Wait for MDIO frame transfer to complete before exit. */
	if (phy_gmii_ready(mmio_base) != CB_SUCCESS)
		printk(BIOS_ERR, "%s TSN GMII busy. PHY Adr: 0x%x, Reg 0x%x\n",
				__func__, phy_adr, reg_adr);
	else
		printk(BIOS_SPEW, "%s PHY Adr: 0x%x, Reg: 0x%x , Data: 0x%x\n",
				__func__, phy_adr, reg_adr, data);
}

static struct mdio_bus_operations mdio_ops = {
	.read			= tsn_mdio_read,
	.write			= tsn_mdio_write,
};

static struct device_operations gbe_tsn_ops  = {
	.read_resources   = pci_dev_read_resources,
	.set_resources    = pci_dev_set_resources,
	.enable_resources = pci_dev_enable_resources,
	.scan_bus         = scan_generic_bus,
	.enable           = gbe_tsn_enable,
	.init             = gbe_tsn_init,
	.ops_mdio         = &mdio_ops,
};

static const unsigned short gbe_tsn_device_ids[] = {
	PCI_DID_INTEL_EHL_GBE_HOST,
	PCI_DID_INTEL_EHL_GBE_PSE_0,
	PCI_DID_INTEL_EHL_GBE_PSE_1,
	0
};

static const struct pci_driver gbe_tsn_driver __pci_driver = {
	.ops     = &gbe_tsn_ops,
	.vendor  = PCI_VID_INTEL,
	.devices = gbe_tsn_device_ids,
};