// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2020, Intel Corporation
*/
#include <linux/clk-provider.h>
#include <linux/pci.h>
#include <linux/dmi.h>
#include "dwmac-intel.h"
#include "dwmac4.h"
#include "stmmac.h"
#include "stmmac_ptp.h"
struct intel_priv_data {
int mdio_adhoc_addr; /* mdio address for serdes & etc */
unsigned long crossts_adj;
bool is_pse;
};
/* This struct is used to associate PCI Function of MAC controller on a board,
* discovered via DMI, with the address of PHY connected to the MAC. The
* negative value of the address means that MAC controller is not connected
* with PHY.
*/
struct stmmac_pci_func_data {
unsigned int func;
int phy_addr;
};
struct stmmac_pci_dmi_data {
const struct stmmac_pci_func_data *func;
size_t nfuncs;
};
struct stmmac_pci_info {
int (*setup)(struct pci_dev *pdev, struct plat_stmmacenet_data *plat);
};
static int stmmac_pci_find_phy_addr(struct pci_dev *pdev,
const struct dmi_system_id *dmi_list)
{
const struct stmmac_pci_func_data *func_data;
const struct stmmac_pci_dmi_data *dmi_data;
const struct dmi_system_id *dmi_id;
int func = PCI_FUNC(pdev->devfn);
size_t n;
dmi_id = dmi_first_match(dmi_list);
if (!dmi_id)
return -ENODEV;
dmi_data = dmi_id->driver_data;
func_data = dmi_data->func;
for (n = 0; n < dmi_data->nfuncs; n++, func_data++)
if (func_data->func == func)
return func_data->phy_addr;
return -ENODEV;
}
static int serdes_status_poll(struct stmmac_priv *priv, int phyaddr,
int phyreg, u32 mask, u32 val)
{
unsigned int retries = 10;
int val_rd;
do {
val_rd = mdiobus_read(priv->mii, phyaddr, phyreg);
if ((val_rd & mask) == (val & mask))
return 0;
udelay(POLL_DELAY_US);
} while (--retries);
return -ETIMEDOUT;
}
static int intel_serdes_powerup(struct net_device *ndev, void *priv_data)
{
struct intel_priv_data *intel_priv = priv_data;
struct stmmac_priv *priv = netdev_priv(ndev);
int serdes_phy_addr = 0;
u32 data = 0;
if (!intel_priv->mdio_adhoc_addr)
return 0;
serdes_phy_addr = intel_priv->mdio_adhoc_addr;
/* Set the serdes rate and the PCLK rate */
data = mdiobus_read(priv->mii, serdes_phy_addr,
SERDES_GCR0);
data &= ~SERDES_RATE_MASK;
data &= ~SERDES_PCLK_MASK;
if (priv->plat->max_speed == 2500)
data |= SERDES_RATE_PCIE_GEN2 << SERDES_RATE_PCIE_SHIFT |
SERDES_PCLK_37p5MHZ << SERDES_PCLK_SHIFT;
else
data |= SERDES_RATE_PCIE_GEN1 << SERDES_RATE_PCIE_SHIFT |
SERDES_PCLK_70MHZ << SERDES_PCLK_SHIFT;
mdiobus_write(priv->mii, serdes_phy_addr, SERDES_GCR0, data);
/* assert clk_req */
data = mdiobus_read(priv->mii, serdes_phy_addr, SERDES_GCR0);
data |= SERDES_PLL_CLK;
mdiobus_write(priv->mii, serdes_phy_addr, SERDES_GCR0, data);
/* check for clk_ack assertion */
data = serdes_status_poll(priv, serdes_phy_addr,
SERDES_GSR0,
SERDES_PLL_CLK,
SERDES_PLL_CLK);
if (data) {
dev_err(priv->device, "Serdes PLL clk request timeout\n");
return data;
}
/* assert lane reset */
data = mdiobus_read