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> |
Mario Scheithauer | c16a7fc7 | 2022-11-02 16:00:27 +0100 | [diff] [blame] | 4 | #include <delay.h> |
| 5 | #include <device/mdio.h> |
Mario Scheithauer | eda66c3 | 2022-04-26 13:50:52 +0200 | [diff] [blame] | 6 | #include <device/pci.h> |
| 7 | #include <device/pci_ids.h> |
Mario Scheithauer | c16a7fc7 | 2022-11-02 16:00:27 +0100 | [diff] [blame] | 8 | #include <device/pci_ops.h> |
Mario Scheithauer | c8c64c1 | 2022-09-29 15:25:48 +0200 | [diff] [blame] | 9 | #include <intelblocks/lpss.h> |
Mario Scheithauer | dccdace | 2022-04-27 11:24:05 +0200 | [diff] [blame] | 10 | #include <soc/soc_chip.h> |
Mario Scheithauer | eda66c3 | 2022-04-26 13:50:52 +0200 | [diff] [blame] | 11 | #include <soc/tsn_gbe.h> |
Mario Scheithauer | dccdace | 2022-04-27 11:24:05 +0200 | [diff] [blame] | 12 | #include <timer.h> |
Mario Scheithauer | eda66c3 | 2022-04-26 13:50:52 +0200 | [diff] [blame] | 13 | |
Mario Scheithauer | d691c21 | 2022-04-26 14:16:59 +0200 | [diff] [blame] | 14 | static void program_mac_address(struct device *dev, void *base) |
| 15 | { |
| 16 | enum cb_err status; |
| 17 | uint8_t mac[MAC_ADDR_LEN]; |
| 18 | |
| 19 | /* Check first whether there is a valid MAC address available */ |
| 20 | status = mainboard_get_mac_address(dev, mac); |
| 21 | if (status != CB_SUCCESS) { |
| 22 | printk(BIOS_INFO, "TSN GbE: No valid MAC address found\n"); |
| 23 | return; |
| 24 | } |
| 25 | |
| 26 | printk(BIOS_DEBUG, "TSN GbE: Programming MAC Address...\n"); |
| 27 | |
| 28 | /* Write the upper 16 bits of the first 6-byte MAC address */ |
Angel Pons | ecff521 | 2022-05-16 15:56:36 +0200 | [diff] [blame] | 29 | clrsetbits32(base + TSN_MAC_ADD0_HIGH, 0xffff, ((mac[5] << 8) | mac[4])); |
Mario Scheithauer | d691c21 | 2022-04-26 14:16:59 +0200 | [diff] [blame] | 30 | /* Write the lower 32 bits of the first 6-byte MAC address */ |
Angel Pons | ecff521 | 2022-05-16 15:56:36 +0200 | [diff] [blame] | 31 | clrsetbits32(base + TSN_MAC_ADD0_LOW, 0xffffffff, |
Mario Scheithauer | d691c21 | 2022-04-26 14:16:59 +0200 | [diff] [blame] | 32 | (mac[3] << 24) | (mac[2] << 16) | (mac[1] << 8) | mac[0]); |
| 33 | } |
| 34 | |
Mario Scheithauer | dccdace | 2022-04-27 11:24:05 +0200 | [diff] [blame] | 35 | |
Mario Scheithauer | c16a7fc7 | 2022-11-02 16:00:27 +0100 | [diff] [blame] | 36 | static void tsn_set_phy2mac_irq_polarity(struct device *dev, enum tsn_phy_irq_polarity pol) |
Mario Scheithauer | dccdace | 2022-04-27 11:24:05 +0200 | [diff] [blame] | 37 | { |
| 38 | uint16_t gcr_reg; |
Mario Scheithauer | c16a7fc7 | 2022-11-02 16:00:27 +0100 | [diff] [blame] | 39 | const struct mdio_bus_operations *mdio_ops; |
| 40 | |
| 41 | mdio_ops = dev_get_mdio_ops(dev); |
| 42 | if (!mdio_ops) |
| 43 | return; |
Mario Scheithauer | dccdace | 2022-04-27 11:24:05 +0200 | [diff] [blame] | 44 | |
| 45 | if (pol == RISING_EDGE) { |
| 46 | /* Read TSN adhoc PHY sublayer register - global configuration register */ |
Mario Scheithauer | c16a7fc7 | 2022-11-02 16:00:27 +0100 | [diff] [blame] | 47 | gcr_reg = mdio_ops->read(dev, TSN_MAC_MDIO_ADHOC_ADR, TSN_MAC_MDIO_GCR); |
Mario Scheithauer | dccdace | 2022-04-27 11:24:05 +0200 | [diff] [blame] | 48 | gcr_reg |= TSN_MAC_PHY2MAC_INTR_POL; |
Mario Scheithauer | c16a7fc7 | 2022-11-02 16:00:27 +0100 | [diff] [blame] | 49 | mdio_ops->write(dev, TSN_MAC_MDIO_ADHOC_ADR, TSN_MAC_MDIO_GCR, gcr_reg); |
Mario Scheithauer | dccdace | 2022-04-27 11:24:05 +0200 | [diff] [blame] | 50 | } |
| 51 | } |
| 52 | |
Mario Scheithauer | c8c64c1 | 2022-09-29 15:25:48 +0200 | [diff] [blame] | 53 | static void gbe_tsn_enable(struct device *dev) |
| 54 | { |
| 55 | /* Ensure controller is in D0 state. */ |
| 56 | lpss_set_power_state(PCI_DEV(0, PCI_SLOT(dev->path.pci.devfn), |
| 57 | PCI_FUNC(dev->path.pci.devfn)), STATE_D0); |
| 58 | } |
| 59 | |
Mario Scheithauer | d691c21 | 2022-04-26 14:16:59 +0200 | [diff] [blame] | 60 | static void gbe_tsn_init(struct device *dev) |
| 61 | { |
| 62 | /* Get the base address of the I/O registers in memory space */ |
| 63 | struct resource *gbe_tsn_res = find_resource(dev, PCI_BASE_ADDRESS_0); |
Angel Pons | ecff521 | 2022-05-16 15:56:36 +0200 | [diff] [blame] | 64 | void *io_mem_base = (void *)(uintptr_t)gbe_tsn_res->base; |
Mario Scheithauer | dccdace | 2022-04-27 11:24:05 +0200 | [diff] [blame] | 65 | config_t *config = config_of(dev); |
Mario Scheithauer | d691c21 | 2022-04-26 14:16:59 +0200 | [diff] [blame] | 66 | |
| 67 | /* Program MAC address */ |
Angel Pons | ecff521 | 2022-05-16 15:56:36 +0200 | [diff] [blame] | 68 | program_mac_address(dev, io_mem_base); |
Mario Scheithauer | dccdace | 2022-04-27 11:24:05 +0200 | [diff] [blame] | 69 | |
| 70 | /* Set PHY-to-MAC IRQ polarity according to devicetree */ |
| 71 | switch (dev->path.pci.devfn) { |
| 72 | case PCH_DEVFN_GBE: |
Mario Scheithauer | c16a7fc7 | 2022-11-02 16:00:27 +0100 | [diff] [blame] | 73 | tsn_set_phy2mac_irq_polarity(dev, config->pch_tsn_phy_irq_edge); |
Mario Scheithauer | dccdace | 2022-04-27 11:24:05 +0200 | [diff] [blame] | 74 | break; |
| 75 | case PCH_DEVFN_PSEGBE0: |
Mario Scheithauer | c16a7fc7 | 2022-11-02 16:00:27 +0100 | [diff] [blame] | 76 | tsn_set_phy2mac_irq_polarity(dev, config->pse_tsn_phy_irq_edge[0]); |
Mario Scheithauer | dccdace | 2022-04-27 11:24:05 +0200 | [diff] [blame] | 77 | break; |
| 78 | case PCH_DEVFN_PSEGBE1: |
Mario Scheithauer | c16a7fc7 | 2022-11-02 16:00:27 +0100 | [diff] [blame] | 79 | tsn_set_phy2mac_irq_polarity(dev, config->pse_tsn_phy_irq_edge[1]); |
Mario Scheithauer | dccdace | 2022-04-27 11:24:05 +0200 | [diff] [blame] | 80 | break; |
| 81 | } |
Mario Scheithauer | d691c21 | 2022-04-26 14:16:59 +0200 | [diff] [blame] | 82 | } |
| 83 | |
Mario Scheithauer | c16a7fc7 | 2022-11-02 16:00:27 +0100 | [diff] [blame] | 84 | static enum cb_err phy_gmii_ready(void *base) |
| 85 | { |
| 86 | struct stopwatch sw; |
| 87 | |
| 88 | stopwatch_init_msecs_expire(&sw, GMII_TIMEOUT_MS); |
| 89 | do { |
| 90 | if (!(read32((base + MAC_MDIO_ADR)) & MAC_GMII_BUSY)) |
| 91 | return CB_SUCCESS; |
| 92 | mdelay(1); |
| 93 | } while (!stopwatch_expired(&sw)); |
| 94 | |
| 95 | printk(BIOS_ERR, "%s Timeout after %lld msec\n", __func__, |
| 96 | stopwatch_duration_msecs(&sw)); |
| 97 | return CB_ERR; |
| 98 | } |
| 99 | |
| 100 | static uint16_t tsn_mdio_read(struct device *dev, uint8_t phy_adr, uint8_t reg_adr) |
| 101 | { |
| 102 | uint16_t data = 0; |
| 103 | struct resource *gbe_tsn_res = find_resource(dev, PCI_BASE_ADDRESS_0); |
| 104 | void *mmio_base = res2mmio(gbe_tsn_res, 0, 0); |
| 105 | |
| 106 | if (!mmio_base) |
| 107 | return data; |
| 108 | |
| 109 | clrsetbits32(mmio_base + MAC_MDIO_ADR, MAC_MDIO_ADR_MASK, |
| 110 | MAC_PHYAD(phy_adr) | MAC_REGAD(reg_adr) |
| 111 | | MAC_CLK_TRAIL_4 | MAC_CSR_CLK_DIV_102 |
| 112 | | MAC_OP_CMD_READ | MAC_GMII_BUSY); |
| 113 | |
| 114 | /* Wait for MDIO frame transfer to complete before reading MDIO DATA register. */ |
| 115 | if (phy_gmii_ready(mmio_base) != CB_SUCCESS) { |
| 116 | printk(BIOS_ERR, "%s TSN GMII busy. PHY Adr: 0x%x, Reg 0x%x\n", |
| 117 | __func__, phy_adr, reg_adr); |
| 118 | } else { |
| 119 | data = read16(mmio_base + MAC_MDIO_DATA); |
| 120 | printk(BIOS_SPEW, "%s PHY Adr: 0x%x, Reg: 0x%x , Data: 0x%x\n", |
| 121 | __func__, phy_adr, reg_adr, data); |
| 122 | } |
| 123 | return data; |
| 124 | } |
| 125 | |
| 126 | static void tsn_mdio_write(struct device *dev, uint8_t phy_adr, uint8_t reg_adr, uint16_t data) |
| 127 | { |
| 128 | struct resource *gbe_tsn_res = find_resource(dev, PCI_BASE_ADDRESS_0); |
| 129 | void *mmio_base = res2mmio(gbe_tsn_res, 0, 0); |
| 130 | |
| 131 | if (!mmio_base) |
| 132 | return; |
| 133 | |
| 134 | write16(mmio_base + MAC_MDIO_DATA, data); |
| 135 | clrsetbits32(mmio_base + MAC_MDIO_ADR, MAC_MDIO_ADR_MASK, |
| 136 | MAC_PHYAD(phy_adr) | MAC_REGAD(reg_adr) |
| 137 | | MAC_CLK_TRAIL_4 | MAC_CSR_CLK_DIV_102 |
| 138 | | MAC_OP_CMD_WRITE | MAC_GMII_BUSY); |
| 139 | |
| 140 | /* Wait for MDIO frame transfer to complete before exit. */ |
| 141 | if (phy_gmii_ready(mmio_base) != CB_SUCCESS) |
| 142 | printk(BIOS_ERR, "%s TSN GMII busy. PHY Adr: 0x%x, Reg 0x%x\n", |
| 143 | __func__, phy_adr, reg_adr); |
| 144 | else |
| 145 | printk(BIOS_SPEW, "%s PHY Adr: 0x%x, Reg: 0x%x , Data: 0x%x\n", |
| 146 | __func__, phy_adr, reg_adr, data); |
| 147 | } |
| 148 | |
| 149 | static struct mdio_bus_operations mdio_ops = { |
| 150 | .read = tsn_mdio_read, |
| 151 | .write = tsn_mdio_write, |
| 152 | }; |
| 153 | |
Mario Scheithauer | eda66c3 | 2022-04-26 13:50:52 +0200 | [diff] [blame] | 154 | static struct device_operations gbe_tsn_ops = { |
| 155 | .read_resources = pci_dev_read_resources, |
| 156 | .set_resources = pci_dev_set_resources, |
| 157 | .enable_resources = pci_dev_enable_resources, |
Mario Scheithauer | bf89aae | 2022-11-08 13:23:49 +0100 | [diff] [blame] | 158 | .scan_bus = scan_generic_bus, |
Mario Scheithauer | c8c64c1 | 2022-09-29 15:25:48 +0200 | [diff] [blame] | 159 | .enable = gbe_tsn_enable, |
Mario Scheithauer | d691c21 | 2022-04-26 14:16:59 +0200 | [diff] [blame] | 160 | .init = gbe_tsn_init, |
Mario Scheithauer | c16a7fc7 | 2022-11-02 16:00:27 +0100 | [diff] [blame] | 161 | .ops_mdio = &mdio_ops, |
Mario Scheithauer | eda66c3 | 2022-04-26 13:50:52 +0200 | [diff] [blame] | 162 | }; |
| 163 | |
Mario Scheithauer | 5b757b5 | 2022-05-19 12:06:33 +0200 | [diff] [blame] | 164 | static const unsigned short gbe_tsn_device_ids[] = { |
| 165 | PCI_DID_INTEL_EHL_GBE_HOST, |
| 166 | PCI_DID_INTEL_EHL_GBE_PSE_0, |
| 167 | PCI_DID_INTEL_EHL_GBE_PSE_1, |
| 168 | 0 |
| 169 | }; |
Mario Scheithauer | eda66c3 | 2022-04-26 13:50:52 +0200 | [diff] [blame] | 170 | |
| 171 | static const struct pci_driver gbe_tsn_driver __pci_driver = { |
| 172 | .ops = &gbe_tsn_ops, |
| 173 | .vendor = PCI_VID_INTEL, |
| 174 | .devices = gbe_tsn_device_ids, |
| 175 | }; |