| /* SPDX-License-Identifier: GPL-2.0-only */ |
| |
| #include <console/console.h> |
| #include <delay.h> |
| #include <device/mdio.h> |
| #include <device/pci.h> |
| #include <device/pci_ids.h> |
| #include <device/pci_ops.h> |
| #include <intelblocks/lpss.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]); |
| } |
| |
| |
| static void tsn_set_phy2mac_irq_polarity(struct device *dev, enum tsn_phy_irq_polarity pol) |
| { |
| uint16_t gcr_reg; |
| const struct mdio_bus_operations *mdio_ops; |
| |
| mdio_ops = dev_get_mdio_ops(dev); |
| if (!mdio_ops) |
| return; |
| |
| if (pol == RISING_EDGE) { |
| /* Read TSN adhoc PHY sublayer register - global configuration register */ |
| gcr_reg = mdio_ops->read(dev, TSN_MAC_MDIO_ADHOC_ADR, TSN_MAC_MDIO_GCR); |
| gcr_reg |= TSN_MAC_PHY2MAC_INTR_POL; |
| mdio_ops->write(dev, TSN_MAC_MDIO_ADHOC_ADR, TSN_MAC_MDIO_GCR, gcr_reg); |
| } |
| } |
| |
| static void gbe_tsn_enable(struct device *dev) |
| { |
| /* Ensure controller is in D0 state. */ |
| lpss_set_power_state(PCI_DEV(0, PCI_SLOT(dev->path.pci.devfn), |
| PCI_FUNC(dev->path.pci.devfn)), STATE_D0); |
| } |
| |
| 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(dev, config->pch_tsn_phy_irq_edge); |
| break; |
| case PCH_DEVFN_PSEGBE0: |
| tsn_set_phy2mac_irq_polarity(dev, config->pse_tsn_phy_irq_edge[0]); |
| break; |
| case PCH_DEVFN_PSEGBE1: |
| tsn_set_phy2mac_irq_polarity(dev, config->pse_tsn_phy_irq_edge[1]); |
| break; |
| } |
| } |
| |
| static enum cb_err phy_gmii_ready(void *base) |
| { |
| struct stopwatch sw; |
| |
| stopwatch_init_msecs_expire(&sw, GMII_TIMEOUT_MS); |
| do { |
| if (!(read32((base + MAC_MDIO_ADR)) & MAC_GMII_BUSY)) |
| return CB_SUCCESS; |
| mdelay(1); |
| } while (!stopwatch_expired(&sw)); |
| |
| printk(BIOS_ERR, "%s Timeout after %lld msec\n", __func__, |
| stopwatch_duration_msecs(&sw)); |
| return CB_ERR; |
| } |
| |
| static uint16_t tsn_mdio_read(struct device *dev, uint8_t phy_adr, uint8_t reg_adr) |
| { |
| uint16_t data = 0; |
| struct resource *gbe_tsn_res = find_resource(dev, PCI_BASE_ADDRESS_0); |
| void *mmio_base = res2mmio(gbe_tsn_res, 0, 0); |
| |
| if (!mmio_base) |
| return data; |
| |
| clrsetbits32(mmio_base + MAC_MDIO_ADR, MAC_MDIO_ADR_MASK, |
| MAC_PHYAD(phy_adr) | MAC_REGAD(reg_adr) |
| | MAC_CLK_TRAIL_4 | MAC_CSR_CLK_DIV_102 |
| | MAC_OP_CMD_READ | MAC_GMII_BUSY); |
| |
| /* Wait for MDIO frame transfer to complete before reading MDIO DATA register. */ |
| if (phy_gmii_ready(mmio_base) != CB_SUCCESS) { |
| printk(BIOS_ERR, "%s TSN GMII busy. PHY Adr: 0x%x, Reg 0x%x\n", |
| __func__, phy_adr, reg_adr); |
| } else { |
| data = read16(mmio_base + MAC_MDIO_DATA); |
| printk(BIOS_SPEW, "%s PHY Adr: 0x%x, Reg: 0x%x , Data: 0x%x\n", |
| __func__, phy_adr, reg_adr, data); |
| } |
| return data; |
| } |
| |
| static void tsn_mdio_write(struct device *dev, uint8_t phy_adr, uint8_t reg_adr, uint16_t data) |
| { |
| struct resource *gbe_tsn_res = find_resource(dev, PCI_BASE_ADDRESS_0); |
| void *mmio_base = res2mmio(gbe_tsn_res, 0, 0); |
| |
| if (!mmio_base) |
| return; |
| |
| write16(mmio_base + MAC_MDIO_DATA, data); |
| clrsetbits32(mmio_base + MAC_MDIO_ADR, MAC_MDIO_ADR_MASK, |
| MAC_PHYAD(phy_adr) | MAC_REGAD(reg_adr) |
| | MAC_CLK_TRAIL_4 | MAC_CSR_CLK_DIV_102 |
| | MAC_OP_CMD_WRITE | MAC_GMII_BUSY); |
| |
| /* Wait for MDIO frame transfer to complete before exit. */ |
| if (phy_gmii_ready(mmio_base) != CB_SUCCESS) |
| printk(BIOS_ERR, "%s TSN GMII busy. PHY Adr: 0x%x, Reg 0x%x\n", |
| __func__, phy_adr, reg_adr); |
| else |
| printk(BIOS_SPEW, "%s PHY Adr: 0x%x, Reg: 0x%x , Data: 0x%x\n", |
| __func__, phy_adr, reg_adr, data); |
| } |
| |
| static struct mdio_bus_operations mdio_ops = { |
| .read = tsn_mdio_read, |
| .write = tsn_mdio_write, |
| }; |
| |
| 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, |
| .scan_bus = scan_generic_bus, |
| .enable = gbe_tsn_enable, |
| .init = gbe_tsn_init, |
| .ops_mdio = &mdio_ops, |
| }; |
| |
| 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, |
| }; |