diff --git a/src/soc/intel/elkhartlake/chip.h b/src/soc/intel/elkhartlake/chip.h index 618d00f2e8..68810f4a24 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 e9f6c8d1fe..b1d04cccd2 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 a90c4af25e..50e9e16999 100644 --- a/src/soc/intel/elkhartlake/tsn_gbe.c +++ b/src/soc/intel/elkhartlake/tsn_gbe.c @@ -3,7 +3,9 @@ #include #include #include +#include #include +#include 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 = {