Mario Scheithauer | eda66c3 | 2022-04-26 13:50:52 +0200 | [diff] [blame] | 1 | /* SPDX-License-Identifier: GPL-2.0-only */ |
| 2 | |
| 3 | #include <console/console.h> |
| 4 | #include <device/pci.h> |
| 5 | #include <device/pci_ids.h> |
Mario Scheithauer | dccdace | 2022-04-27 11:24:05 +0200 | [diff] [blame] | 6 | #include <soc/soc_chip.h> |
Mario Scheithauer | eda66c3 | 2022-04-26 13:50:52 +0200 | [diff] [blame] | 7 | #include <soc/tsn_gbe.h> |
Mario Scheithauer | dccdace | 2022-04-27 11:24:05 +0200 | [diff] [blame] | 8 | #include <timer.h> |
Mario Scheithauer | eda66c3 | 2022-04-26 13:50:52 +0200 | [diff] [blame] | 9 | |
Mario Scheithauer | d691c21 | 2022-04-26 14:16:59 +0200 | [diff] [blame] | 10 | static void program_mac_address(struct device *dev, void *base) |
| 11 | { |
| 12 | enum cb_err status; |
| 13 | uint8_t mac[MAC_ADDR_LEN]; |
| 14 | |
| 15 | /* Check first whether there is a valid MAC address available */ |
| 16 | status = mainboard_get_mac_address(dev, mac); |
| 17 | if (status != CB_SUCCESS) { |
| 18 | printk(BIOS_INFO, "TSN GbE: No valid MAC address found\n"); |
| 19 | return; |
| 20 | } |
| 21 | |
| 22 | printk(BIOS_DEBUG, "TSN GbE: Programming MAC Address...\n"); |
| 23 | |
| 24 | /* Write the upper 16 bits of the first 6-byte MAC address */ |
Angel Pons | ecff521 | 2022-05-16 15:56:36 +0200 | [diff] [blame] | 25 | clrsetbits32(base + TSN_MAC_ADD0_HIGH, 0xffff, ((mac[5] << 8) | mac[4])); |
Mario Scheithauer | d691c21 | 2022-04-26 14:16:59 +0200 | [diff] [blame] | 26 | /* Write the lower 32 bits of the first 6-byte MAC address */ |
Angel Pons | ecff521 | 2022-05-16 15:56:36 +0200 | [diff] [blame] | 27 | clrsetbits32(base + TSN_MAC_ADD0_LOW, 0xffffffff, |
Mario Scheithauer | d691c21 | 2022-04-26 14:16:59 +0200 | [diff] [blame] | 28 | (mac[3] << 24) | (mac[2] << 16) | (mac[1] << 8) | mac[0]); |
| 29 | } |
| 30 | |
Mario Scheithauer | dccdace | 2022-04-27 11:24:05 +0200 | [diff] [blame] | 31 | enum cb_err phy_gmii_ready(void *base) |
| 32 | { |
| 33 | struct stopwatch sw; |
| 34 | |
| 35 | stopwatch_init_msecs_expire(&sw, TSN_GMII_TIMEOUT_MS); |
| 36 | do { |
| 37 | if (!(read32((base + TSN_MAC_MDIO_ADR)) & TSN_MAC_GMII_BUSY)) |
| 38 | return CB_SUCCESS; |
| 39 | |
| 40 | } while (!stopwatch_expired(&sw)); |
| 41 | |
| 42 | printk(BIOS_ERR, "%s Timeout after %ld msec\n", __func__, |
| 43 | stopwatch_duration_msecs(&sw)); |
| 44 | return CB_ERR; |
| 45 | } |
| 46 | |
| 47 | uint16_t tsn_mdio_read(void *base, uint8_t phy_adr, uint8_t reg_adr) |
| 48 | { |
| 49 | uint16_t data = 0; |
| 50 | enum cb_err status; |
| 51 | |
| 52 | clrsetbits32(base + TSN_MAC_MDIO_ADR, TSN_MAC_MDIO_ADR_MASK, |
| 53 | TSN_MAC_PHYAD(phy_adr) | TSN_MAC_REGAD(reg_adr) |
| 54 | | TSN_MAC_CLK_TRAIL_4 | TSN_MAC_CSR_CLK_DIV_62 |
| 55 | | TSN_MAC_OP_CMD_READ | TSN_MAC_GMII_BUSY); |
| 56 | |
| 57 | /* Wait for MDIO frame transfer to complete before reading MDIO DATA register */ |
| 58 | status = phy_gmii_ready(base); |
| 59 | if (status == CB_ERR) { |
| 60 | printk(BIOS_ERR, "%s TSN GMII busy. PHY Adr: 0x%x, Reg 0x%x\n", |
| 61 | __func__, phy_adr, reg_adr); |
| 62 | } else { |
| 63 | data = read16(base + TSN_MAC_MDIO_DATA); |
| 64 | printk(BIOS_DEBUG, "%s PHY Adr: 0x%x, Reg: 0x%x , Data: 0x%x\n", |
| 65 | __func__, phy_adr, reg_adr, data); |
| 66 | } |
| 67 | return data; |
| 68 | } |
| 69 | |
| 70 | void tsn_mdio_write(void *base, uint8_t phy_adr, uint8_t reg_adr, uint16_t data) |
| 71 | { |
| 72 | enum cb_err status; |
| 73 | |
| 74 | setbits16(base + TSN_MAC_MDIO_DATA, data); |
| 75 | clrsetbits32(base + TSN_MAC_MDIO_ADR, TSN_MAC_MDIO_ADR_MASK, |
| 76 | TSN_MAC_PHYAD(phy_adr) | TSN_MAC_REGAD(reg_adr) |
| 77 | | TSN_MAC_CLK_TRAIL_4 | TSN_MAC_CSR_CLK_DIV_62 |
| 78 | | TSN_MAC_OP_CMD_WRITE | TSN_MAC_GMII_BUSY); |
| 79 | |
| 80 | /* Wait for MDIO frame transfer to complete before do next */ |
| 81 | status = phy_gmii_ready(base); |
| 82 | if (status == CB_ERR) |
| 83 | printk(BIOS_ERR, "%s TSN GMII busy. PHY Adr: 0x%x, Reg 0x%x\n", |
| 84 | __func__, phy_adr, reg_adr); |
| 85 | else |
| 86 | printk(BIOS_DEBUG, "%s PHY Adr: 0x%x, Reg: 0x%x , Data: 0x%x\n", |
| 87 | __func__, phy_adr, reg_adr, data); |
| 88 | } |
| 89 | |
| 90 | static void tsn_set_phy2mac_irq_polarity(void *base, enum tsn_phy_irq_polarity pol) |
| 91 | { |
| 92 | uint16_t gcr_reg; |
| 93 | |
| 94 | if (pol == RISING_EDGE) { |
| 95 | /* Read TSN adhoc PHY sublayer register - global configuration register */ |
| 96 | gcr_reg = tsn_mdio_read(base, TSN_MAC_MDIO_ADHOC_ADR, TSN_MAC_MDIO_GCR); |
| 97 | gcr_reg |= TSN_MAC_PHY2MAC_INTR_POL; |
| 98 | tsn_mdio_write(base, TSN_MAC_MDIO_ADHOC_ADR, TSN_MAC_MDIO_GCR, gcr_reg); |
| 99 | } |
| 100 | } |
| 101 | |
Mario Scheithauer | d691c21 | 2022-04-26 14:16:59 +0200 | [diff] [blame] | 102 | static void gbe_tsn_init(struct device *dev) |
| 103 | { |
| 104 | /* Get the base address of the I/O registers in memory space */ |
| 105 | struct resource *gbe_tsn_res = find_resource(dev, PCI_BASE_ADDRESS_0); |
Angel Pons | ecff521 | 2022-05-16 15:56:36 +0200 | [diff] [blame] | 106 | void *io_mem_base = (void *)(uintptr_t)gbe_tsn_res->base; |
Mario Scheithauer | dccdace | 2022-04-27 11:24:05 +0200 | [diff] [blame] | 107 | config_t *config = config_of(dev); |
Mario Scheithauer | d691c21 | 2022-04-26 14:16:59 +0200 | [diff] [blame] | 108 | |
| 109 | /* Program MAC address */ |
Angel Pons | ecff521 | 2022-05-16 15:56:36 +0200 | [diff] [blame] | 110 | program_mac_address(dev, io_mem_base); |
Mario Scheithauer | dccdace | 2022-04-27 11:24:05 +0200 | [diff] [blame] | 111 | |
| 112 | /* Set PHY-to-MAC IRQ polarity according to devicetree */ |
| 113 | switch (dev->path.pci.devfn) { |
| 114 | case PCH_DEVFN_GBE: |
| 115 | tsn_set_phy2mac_irq_polarity(io_mem_base, config->pch_tsn_phy_irq_edge); |
| 116 | break; |
| 117 | case PCH_DEVFN_PSEGBE0: |
| 118 | tsn_set_phy2mac_irq_polarity(io_mem_base, config->pse_tsn_phy_irq_edge[0]); |
| 119 | break; |
| 120 | case PCH_DEVFN_PSEGBE1: |
| 121 | tsn_set_phy2mac_irq_polarity(io_mem_base, config->pse_tsn_phy_irq_edge[1]); |
| 122 | break; |
| 123 | } |
Mario Scheithauer | d691c21 | 2022-04-26 14:16:59 +0200 | [diff] [blame] | 124 | } |
| 125 | |
Mario Scheithauer | eda66c3 | 2022-04-26 13:50:52 +0200 | [diff] [blame] | 126 | static struct device_operations gbe_tsn_ops = { |
| 127 | .read_resources = pci_dev_read_resources, |
| 128 | .set_resources = pci_dev_set_resources, |
| 129 | .enable_resources = pci_dev_enable_resources, |
Mario Scheithauer | d691c21 | 2022-04-26 14:16:59 +0200 | [diff] [blame] | 130 | .init = gbe_tsn_init, |
Mario Scheithauer | eda66c3 | 2022-04-26 13:50:52 +0200 | [diff] [blame] | 131 | }; |
| 132 | |
Mario Scheithauer | 5b757b5 | 2022-05-19 12:06:33 +0200 | [diff] [blame] | 133 | static const unsigned short gbe_tsn_device_ids[] = { |
| 134 | PCI_DID_INTEL_EHL_GBE_HOST, |
| 135 | PCI_DID_INTEL_EHL_GBE_PSE_0, |
| 136 | PCI_DID_INTEL_EHL_GBE_PSE_1, |
| 137 | 0 |
| 138 | }; |
Mario Scheithauer | eda66c3 | 2022-04-26 13:50:52 +0200 | [diff] [blame] | 139 | |
| 140 | static const struct pci_driver gbe_tsn_driver __pci_driver = { |
| 141 | .ops = &gbe_tsn_ops, |
| 142 | .vendor = PCI_VID_INTEL, |
| 143 | .devices = gbe_tsn_device_ids, |
| 144 | }; |