summaryrefslogtreecommitdiffstats
path: root/src/soc/intel/elkhartlake/tsn_gbe.c
blob: ea2ef0e5c7c12cb17dcb41bc72904b3962bedfc6 (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
/* SPDX-License-Identifier: GPL-2.0-only */

#include <console/console.h>
#include <device/pci.h>
#include <device/pci_ids.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]);
}

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

	stopwatch_init_msecs_expire(&sw, TSN_GMII_TIMEOUT_MS);
	do {
		if (!(read32((base + TSN_MAC_MDIO_ADR)) & TSN_MAC_GMII_BUSY))
			return CB_SUCCESS;

	} while (!stopwatch_expired(&sw));

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

uint16_t tsn_mdio_read(void *base, uint8_t phy_adr, uint8_t reg_adr)
{
	uint16_t data = 0;
	enum cb_err status;

	clrsetbits32(base + TSN_MAC_MDIO_ADR, TSN_MAC_MDIO_ADR_MASK,
			TSN_MAC_PHYAD(phy_adr) | TSN_MAC_REGAD(reg_adr)
			| TSN_MAC_CLK_TRAIL_4 | TSN_MAC_CSR_CLK_DIV_62
			| TSN_MAC_OP_CMD_READ | TSN_MAC_GMII_BUSY);

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

void tsn_mdio_write(void *base, uint8_t phy_adr, uint8_t reg_adr, uint16_t data)
{
	enum cb_err status;

	setbits16(base + TSN_MAC_MDIO_DATA, data);
	clrsetbits32(base + TSN_MAC_MDIO_ADR, TSN_MAC_MDIO_ADR_MASK,
			TSN_MAC_PHYAD(phy_adr) | TSN_MAC_REGAD(reg_adr)
			| TSN_MAC_CLK_TRAIL_4 | TSN_MAC_CSR_CLK_DIV_62
			| TSN_MAC_OP_CMD_WRITE | TSN_MAC_GMII_BUSY);

	/* Wait for MDIO frame transfer to complete before do next */
	status = phy_gmii_ready(base);
	if (status == CB_ERR)
		printk(BIOS_ERR, "%s TSN GMII busy. PHY Adr: 0x%x, Reg 0x%x\n",
				__func__, phy_adr, reg_adr);
	else
		printk(BIOS_DEBUG, "%s PHY Adr: 0x%x, Reg: 0x%x , Data: 0x%x\n",
				__func__, phy_adr, reg_adr, data);
}

static void tsn_set_phy2mac_irq_polarity(void *base, enum tsn_phy_irq_polarity pol)
{
	uint16_t gcr_reg;

	if (pol == RISING_EDGE) {
		/* Read TSN adhoc PHY sublayer register - global configuration register */
		gcr_reg = tsn_mdio_read(base, TSN_MAC_MDIO_ADHOC_ADR, TSN_MAC_MDIO_GCR);
		gcr_reg |= TSN_MAC_PHY2MAC_INTR_POL;
		tsn_mdio_write(base, TSN_MAC_MDIO_ADHOC_ADR, TSN_MAC_MDIO_GCR, gcr_reg);
	}
}

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(io_mem_base, config->pch_tsn_phy_irq_edge);
		break;
	case PCH_DEVFN_PSEGBE0:
		tsn_set_phy2mac_irq_polarity(io_mem_base, config->pse_tsn_phy_irq_edge[0]);
		break;
	case PCH_DEVFN_PSEGBE1:
		tsn_set_phy2mac_irq_polarity(io_mem_base, config->pse_tsn_phy_irq_edge[1]);
		break;
	}
}

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,
	.init             = gbe_tsn_init,
};

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,
};