summaryrefslogtreecommitdiffstats
path: root/src/soc/intel/elkhartlake
diff options
context:
space:
mode:
authorMario Scheithauer <mario.scheithauer@siemens.com>2022-04-27 11:24:05 +0200
committerFelix Held <felix-coreboot@felixheld.de>2022-05-21 17:40:52 +0000
commitdccdaceb494fdb0466a1c46b68df3642280f587f (patch)
treef9fde441a368a3a17c40720865bcc5ba9f3c9e7d /src/soc/intel/elkhartlake
parent8698a6f5a9d656c165f594cb9cd532a80d8e5e99 (diff)
downloadcoreboot-dccdaceb494fdb0466a1c46b68df3642280f587f.tar.gz
coreboot-dccdaceb494fdb0466a1c46b68df3642280f587f.tar.bz2
coreboot-dccdaceb494fdb0466a1c46b68df3642280f587f.zip
soc/intel/ehl: Provide function to change PHY-to-MAC IRQ polarity
EHL MAC side expects a rising edge signal for an IRQ. Based on the mainboard wiring it could be necessary to change the interrupt polarity. This patch provides the functionality to invert a falling edge signal that comes from an external PHY. The inverting can be activated via devicetree parameter. Change-Id: Ia314014c7cacbeb72629c773c8c0bb5f002a3f54 Signed-off-by: Mario Scheithauer <mario.scheithauer@siemens.com> Reviewed-on: https://review.coreboot.org/c/coreboot/+/63888 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: Werner Zeh <werner.zeh@siemens.com>
Diffstat (limited to 'src/soc/intel/elkhartlake')
-rw-r--r--src/soc/intel/elkhartlake/chip.h8
-rw-r--r--src/soc/intel/elkhartlake/include/soc/tsn_gbe.h31
-rw-r--r--src/soc/intel/elkhartlake/tsn_gbe.c87
3 files changed, 123 insertions, 3 deletions
diff --git a/src/soc/intel/elkhartlake/chip.h b/src/soc/intel/elkhartlake/chip.h
index 618d00f2e884..68810f4a24bb 100644
--- a/src/soc/intel/elkhartlake/chip.h
+++ b/src/soc/intel/elkhartlake/chip.h
@@ -56,6 +56,12 @@ enum tsn_phy_type {
SGMII_plus = 3,
};
+/* TSN GBE PHY-to-MAC IRQ polarity: 0: falling edge, 1: rising edge */
+enum tsn_phy_irq_polarity {
+ FALLING_EDGE,
+ RISING_EDGE,
+};
+
/*
* PSE native pins and ownership assignment:-
* 0: Disable/pins are not owned by PSE/host
@@ -395,6 +401,8 @@ struct soc_intel_elkhartlake_config {
bool PseTsnGbeMultiVcEnable[MAX_PSE_TSN_PORTS];
/* PSE TSN Phy Interface Type */
enum tsn_phy_type PseTsnGbePhyType[MAX_PSE_TSN_PORTS];
+ enum tsn_phy_irq_polarity pch_tsn_phy_irq_edge;
+ enum tsn_phy_irq_polarity pse_tsn_phy_irq_edge[MAX_PSE_TSN_PORTS];
/* PSE related */
/*
diff --git a/src/soc/intel/elkhartlake/include/soc/tsn_gbe.h b/src/soc/intel/elkhartlake/include/soc/tsn_gbe.h
index e9f6c8d1fe7f..b1d04cccd223 100644
--- a/src/soc/intel/elkhartlake/include/soc/tsn_gbe.h
+++ b/src/soc/intel/elkhartlake/include/soc/tsn_gbe.h
@@ -3,13 +3,38 @@
#ifndef _SOC_ELKHARTLAKE_TSN_GBE_H_
#define _SOC_ELKHARTLAKE_TSN_GBE_H_
-#define MAC_ADDR_LEN 6
+#define MAC_ADDR_LEN 6
-#define TSN_MAC_ADD0_HIGH 0x300 /* MAC Address0 High register */
-#define TSN_MAC_ADD0_LOW 0x304 /* MAC Address0 Low register */
+#define TSN_MAC_ADD0_HIGH 0x300 /* MAC Address0 High register */
+#define TSN_MAC_ADD0_LOW 0x304 /* MAC Address0 Low register */
+
+#define TSN_GMII_TIMEOUT_MS 20
+
+#define TSN_MAC_MDIO_ADR 0x200 /* MAC MDIO Address register */
+#define TSN_MAC_MDIO_ADR_MASK 0x03FF7F0E
+#define TSN_MAC_PHYAD(pa) (pa << 21) /* Physical Layer Address */
+#define TSN_MAC_REGAD(rda) (rda << 16) /* Register/Device Address */
+#define TSN_MAC_CLK_TRAIL_4 (4 << 12) /* 4 Trailing Clocks */
+#define TSN_MAC_CSR_CLK_DIV_62 (1 << 8) /* 0001: CSR=100-150 MHz; CSR/62 */
+#define TSN_MAC_OP_CMD_WRITE (1 << 2) /* GMII Operation Command Write */
+#define TSN_MAC_OP_CMD_READ (3 << 2) /* GMII Operation Command Read */
+#define TSN_MAC_GMII_BUSY (1 << 0) /* GMII Busy bit */
+
+/* MDIO - Adhoc PHY Sublayer Register */
+#define TSN_MAC_MDIO_ADHOC_ADR 0x15
+/* Global Configuration Register */
+#define TSN_MAC_MDIO_GCR 0x0
+/* PHY to MAC Interrupt Polarity bit */
+#define TSN_MAC_PHY2MAC_INTR_POL (1 << 6)
+
+#define TSN_MAC_MDIO_DATA 0x204 /* MAC MDIO Data register */
/* We need one function we can call to get a MAC address to use. */
/* This function can be coded somewhere else but must exist. */
enum cb_err mainboard_get_mac_address(struct device *dev, uint8_t mac[MAC_ADDR_LEN]);
+enum cb_err phy_gmii_ready(void *base);
+uint16_t tsn_mdio_read(void *base, uint8_t phy_adr, uint8_t reg_adr);
+void tsn_mdio_write(void *base, uint8_t phy_adr, uint8_t reg_adr, uint16_t data);
+
#endif /* _SOC_ELKHARTLAKE_TSN_GBE_H_ */
diff --git a/src/soc/intel/elkhartlake/tsn_gbe.c b/src/soc/intel/elkhartlake/tsn_gbe.c
index a90c4af25eab..50e9e16999cd 100644
--- a/src/soc/intel/elkhartlake/tsn_gbe.c
+++ b/src/soc/intel/elkhartlake/tsn_gbe.c
@@ -3,7 +3,9 @@
#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)
{
@@ -26,14 +28,99 @@ static void program_mac_address(struct device *dev, void *base)
(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 %ld 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 = {