Files
Linux_Drivers/linux_5.10/drivers/net/ethernet/stmicro/stmmac/dwmac-thead.c
wangliang.wang 4f810186ab [linux] porting cvitek asic chips.
1. update cv182x/cv183x configuration file
	2. update cv181x/cv180x configuration file
	3. update clk driver for cvitek
	4. update dma driver for cvitek
	5. update soc driver for cvitek
	6. porting cvitek ion driver from kernel-4.19
	7. compatible with riscv

Change-Id: Icff9fafe0ebe7d6bab824bbadb952e08bdc66c19
2023-03-10 20:33:10 +08:00

607 lines
16 KiB
C

// SPDX-License-Identifier: GPL-2.0
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_net.h>
#include <linux/platform_device.h>
#include "stmmac_platform.h"
/* clock registers */
#define GMAC_CLK_CFG0 0x00
#define GMAC_CLK_CFG1 0x04
#define GMAC_CLK_CFG2 0x08
#define GMAC_CLK_CFG3 0x0C
#define GMAC_CLK_CFG4 0x10
#define GMAC_CLK_CFG5 0x14
#define GMAC_CLK_CFG6 0x18
/* phy interface */
#define DWMAC_PHYIF_MII_GMII 0
#define DWMAC_PHYIF_RGMII 1
#define DWMAC_PHYIF_RMII 4
/* register bit fields, bit[3]: reserved, bit[2:0]: phy interface */
#define DWMAC_PHYIF_MASK 0x7
#define DWMAC_PHYIF_BIT_WIDTH 4
/* TXCLK direction, 1:input, 0:output */
#define TXCLK_DIR_OUTPUT 0
#define TXCLK_DIR_INPUT 1
#define GMAC_CLK_PLLOUT_250M 250000000
#define GMAC_GMII_RGMII_RATE 125000000
#define GMAC_MII_RATE 25000000
/* clock divider for speed */
#define GMAC_CLKDIV_125M (GMAC_CLK_PLLOUT_250M / GMAC_GMII_RGMII_RATE)
#define GMAC_CLKDIV_25M (GMAC_CLK_PLLOUT_250M / GMAC_MII_RATE)
struct thead_dwmac_priv_data {
int id;
void __iomem *phy_if_reg;
void __iomem *txclk_dir_reg;
void __iomem *gmac_clk_reg;
phy_interface_t interface;
struct clk *gmac_pll_clk;
unsigned int gmac_pll_clk_freq;
};
/* set GMAC PHY interface, 0:MII/GMII, 1:RGMII, 4:RMII */
static void thead_dwmac_set_phy_if(struct platform_device *pdev,
void __iomem *phy_if_reg, int interface,
int devid)
{
struct device *dev = &pdev->dev;
unsigned int phyif = PHY_INTERFACE_MODE_MII;
volatile uint32_t reg;
if (phy_if_reg == NULL)
return;
switch (interface)
{
case PHY_INTERFACE_MODE_MII:
case PHY_INTERFACE_MODE_GMII:
phyif = DWMAC_PHYIF_MII_GMII;
break;
case PHY_INTERFACE_MODE_RGMII:
case PHY_INTERFACE_MODE_RGMII_TXID:
case PHY_INTERFACE_MODE_RGMII_RXID:
case PHY_INTERFACE_MODE_RGMII_ID:
phyif = DWMAC_PHYIF_RGMII;
break;
case PHY_INTERFACE_MODE_RMII:
phyif = DWMAC_PHYIF_RMII;
break;
default:
dev_err(dev, "phy interface %d not supported\n", interface);
return;
};
reg = readl(phy_if_reg);
reg &= ~(DWMAC_PHYIF_MASK << (DWMAC_PHYIF_BIT_WIDTH * devid));
reg |= (phyif & DWMAC_PHYIF_MASK) << (DWMAC_PHYIF_BIT_WIDTH * devid);
writel(reg, phy_if_reg);
}
/*
* set GMAC TXCLK direction
* MII : TXCLK is input
* GMII/RGMII : TXCLK is output
*/
static void thead_dwmac_set_txclk_dir(struct platform_device *pdev,
void __iomem *txclk_dir_reg, int interface)
{
struct device *dev = &pdev->dev;
unsigned int txclk_dir = TXCLK_DIR_INPUT;
if (txclk_dir_reg == NULL)
return;
switch (interface)
{
case PHY_INTERFACE_MODE_MII:
case PHY_INTERFACE_MODE_RMII:
txclk_dir = TXCLK_DIR_INPUT;
break;
case PHY_INTERFACE_MODE_GMII:
case PHY_INTERFACE_MODE_RGMII:
case PHY_INTERFACE_MODE_RGMII_TXID:
case PHY_INTERFACE_MODE_RGMII_RXID:
case PHY_INTERFACE_MODE_RGMII_ID:
txclk_dir = TXCLK_DIR_OUTPUT;
break;
default:
dev_err(dev, "phy interface %d not supported\n", interface);
return;
};
writel(txclk_dir, txclk_dir_reg);
}
static void thead_dwmac_set_clk_source(struct platform_device *pdev,
void __iomem *gmac_clk_reg, int interface)
{
struct device *dev = &pdev->dev;
volatile uint32_t reg;
if (gmac_clk_reg == NULL)
return;
reg = readl(gmac_clk_reg + GMAC_CLK_CFG0);
/* RX clock source */
reg |= BIT(7); /* gmac_rx_clk_sel: extern pin */
/* TX clock source */
if (interface == PHY_INTERFACE_MODE_MII) {
reg |= BIT(1); /* gmac_tx_clk_sel: extern pin */
reg &= ~BIT(2); /* gmac_tx_clk_gbit_sel: u_tx_clk_mux */
} else if (interface == PHY_INTERFACE_MODE_GMII) {
reg &= ~BIT(5); /* gmac_tx_clk_out_sel: GMAC PLL */
reg |= BIT(2); /* gmac_tx_clk_gbit_sel: GMAC PLL */
} else if (interface == PHY_INTERFACE_MODE_RGMII
|| interface == PHY_INTERFACE_MODE_RGMII_ID
|| interface == PHY_INTERFACE_MODE_RGMII_RXID
|| interface == PHY_INTERFACE_MODE_RGMII_TXID) {
reg &= ~BIT(5); /* gmac_tx_clk_out_sel: GMAC PLL */
reg |= BIT(2); /* gmac_tx_clk_gbit_sel: GMAC PLL */
} else {
dev_err(dev, "phy interface %d not supported\n", interface);
return;
}
writel(reg, gmac_clk_reg + GMAC_CLK_CFG0);
}
/* set clock source */
static void thead_dwmac_set_clock_delay(struct platform_device *pdev,
void __iomem *gmac_clk_reg, int interface)
{
unsigned int delay;
if (gmac_clk_reg == NULL)
return;
if (of_property_read_u32(pdev->dev.of_node, "rx-clk-delay",
&delay) == 0) {
/* RX clk delay */
writel(delay, gmac_clk_reg + GMAC_CLK_CFG1);
pr_info("RX clk delay: 0x%X\n", delay);
}
if (of_property_read_u32(pdev->dev.of_node, "tx-clk-delay",
&delay) == 0) {
/* TX clk delay */
writel(delay, gmac_clk_reg + GMAC_CLK_CFG2);
pr_info("TX clk delay: 0x%X\n", delay);
}
}
/* set gmac pll divider (u_pll_clk_div) to get 250MHz clock */
static void thead_dwmac_set_pll_250M(void __iomem *gmac_clk_reg, int interface,
unsigned int src_freq)
{
volatile unsigned int reg;
unsigned int div = 1;
if (gmac_clk_reg == NULL)
return;
if (interface == PHY_INTERFACE_MODE_MII) {
/* For MII, no internal PLL is used */
return;
} else if (interface == PHY_INTERFACE_MODE_GMII
|| interface == PHY_INTERFACE_MODE_RGMII
|| interface == PHY_INTERFACE_MODE_RGMII_ID
|| interface == PHY_INTERFACE_MODE_RGMII_RXID
|| interface == PHY_INTERFACE_MODE_RGMII_TXID) {
/* check clock */
if ((src_freq == 0) || (src_freq % GMAC_CLK_PLLOUT_250M != 0)) {
pr_err("error! invalid gmac pll freq %d\n", src_freq);
return;
}
div = src_freq / GMAC_CLK_PLLOUT_250M;
/* disable pll_clk_div */
reg = readl(gmac_clk_reg + GMAC_CLK_CFG3);
reg &= ~BIT(31);
writel(reg, gmac_clk_reg + GMAC_CLK_CFG3);
/* modify divider */
writel(div, gmac_clk_reg + GMAC_CLK_CFG3);
/* enable pll_clk_div */
reg = readl(gmac_clk_reg + GMAC_CLK_CFG3);
reg |= BIT(31);
writel(reg, gmac_clk_reg + GMAC_CLK_CFG3);
} else {
pr_err("phy interface %d not supported\n", interface);
return;
}
}
/* set gmac speed */
static void thead_dwmac_set_speed(void __iomem *gmac_clk_reg, int interface,
unsigned int speed)
{
volatile unsigned int reg;
if (gmac_clk_reg == NULL)
return;
if (interface == PHY_INTERFACE_MODE_MII) {
/* For MII, no internal PLL is used */
return;
} else if (interface == PHY_INTERFACE_MODE_GMII
|| interface == PHY_INTERFACE_MODE_RGMII
|| interface == PHY_INTERFACE_MODE_RGMII_ID
|| interface == PHY_INTERFACE_MODE_RGMII_RXID
|| interface == PHY_INTERFACE_MODE_RGMII_TXID) {
/* disable gtx_clk_div */
reg = readl(gmac_clk_reg + GMAC_CLK_CFG4);
reg &= ~BIT(31);
writel(reg, gmac_clk_reg + GMAC_CLK_CFG4);
/*
* modify divider
*/
/* gtx_clk_div */
if (speed == SPEED_1000) {
writel(GMAC_CLKDIV_125M, gmac_clk_reg + GMAC_CLK_CFG4);
} else if (speed == SPEED_100) {
writel(GMAC_CLKDIV_25M, gmac_clk_reg + GMAC_CLK_CFG4);
} else {
writel(GMAC_CLKDIV_25M / 10, gmac_clk_reg + GMAC_CLK_CFG4);
}
/* enable gtx_clk_div */
reg = readl(gmac_clk_reg + GMAC_CLK_CFG4);
reg |= BIT(31);
writel(reg, gmac_clk_reg + GMAC_CLK_CFG4);
} else {
pr_err("phy interface %d not supported\n", interface);
return;
}
}
/* enable gmac clock */
static void thead_dwmac_enable_clock(struct platform_device *pdev,
void __iomem *gmac_clk_reg, int interface)
{
struct device *dev = &pdev->dev;
volatile unsigned int reg;
if (gmac_clk_reg == NULL)
return;
reg = readl(gmac_clk_reg + GMAC_CLK_CFG0);
/* enable gmac_hclk */
reg |= BIT(14);
if (interface == PHY_INTERFACE_MODE_MII) {
reg |= BIT(8); /* enable gmac_rx_clk */
reg |= BIT(3); /* enable gmac_tx_clk */
} else if (interface == PHY_INTERFACE_MODE_GMII) {
reg |= BIT(8); /* enable gmac_rx_clk */
reg |= BIT(3); /* enable gmac_tx_clk */
reg |= BIT(6); /* enable gmac_tx_clk_out */
} else if (interface == PHY_INTERFACE_MODE_RGMII
|| interface == PHY_INTERFACE_MODE_RGMII_ID
|| interface == PHY_INTERFACE_MODE_RGMII_RXID
|| interface == PHY_INTERFACE_MODE_RGMII_TXID) {
reg |= BIT(8); /* enable gmac_rx_clk */
reg |= BIT(3); /* enable gmac_tx_clk */
reg |= BIT(6); /* enable gmac_tx_clk_out */
reg |= BIT(9); /* enable gmac_rx_clk_n */
reg |= BIT(4); /* enable gmac_tx_clk_n */
} else {
dev_err(dev, "phy interface %d not supported\n", interface);
return;
}
writel(reg, gmac_clk_reg + GMAC_CLK_CFG0);
}
#if 0
/* disable gmac clock */
static void thead_dwmac_disable_clock(struct platform_device *pdev,
void __iomem *gmac_clk_reg, int interface)
{
struct device *dev = &pdev->dev;
volatile unsigned int reg;
if (gmac_clk_reg == NULL)
return;
reg = readl(gmac_clk_reg + GMAC_CLK_CFG0);
/* disable gmac_hclk */
reg &= ~BIT(14);
if (interface == PHY_INTERFACE_MODE_MII) {
reg &= ~BIT(8); /* disable gmac_rx_clk */
reg &= ~BIT(3); /* disable gmac_tx_clk */
} else if (interface == PHY_INTERFACE_MODE_GMII) {
reg &= ~BIT(8); /* disable gmac_rx_clk */
reg &= ~BIT(3); /* disable gmac_tx_clk */
reg &= ~BIT(6); /* disable gmac_tx_clk_out */
} else if (interface == PHY_INTERFACE_MODE_RGMII
|| interface == PHY_INTERFACE_MODE_RGMII_ID
|| interface == PHY_INTERFACE_MODE_RGMII_RXID
|| interface == PHY_INTERFACE_MODE_RGMII_TXID) {
reg &= ~BIT(8); /* disable gmac_rx_clk */
reg &= ~BIT(3); /* disable gmac_tx_clk */
reg &= ~BIT(6); /* disable gmac_tx_clk_out */
reg &= ~BIT(9); /* disable gmac_rx_clk_n */
reg &= ~BIT(4); /* disable gmac_tx_clk_n */
} else {
dev_err(dev, "phy interface %d not supported\n", interface);
return;
}
writel(reg, gmac_clk_reg + GMAC_CLK_CFG0);
}
#endif
static int thead_dwmac_init(struct platform_device *pdev, void *bsp_priv)
{
struct thead_dwmac_priv_data *thead_plat_dat = bsp_priv;
struct device *dev = &pdev->dev;
struct device_node *np = pdev->dev.of_node;
struct resource *res;
void __iomem *ptr;
struct clk *clktmp;
int ret;
thead_plat_dat->id = of_alias_get_id(np, "ethernet");
if (thead_plat_dat->id < 0) {
thead_plat_dat->id = 0;
}
dev_info(dev, "id: %d\n", thead_plat_dat->id);
if (of_get_phy_mode(dev->of_node, &(thead_plat_dat->interface))) {
dev_err(dev, "of_get_phy_mode error\n");
return -1;
}
dev_info(dev, "phy interface: %d\n", thead_plat_dat->interface);
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy_if_reg");
if ((res != NULL) && (resource_type(res) == IORESOURCE_MEM)) {
ptr = devm_ioremap(dev, res->start, resource_size(res));
if (!ptr) {
dev_err(dev, "phy interface register not exist, skipped it\n");
} else {
thead_plat_dat->phy_if_reg = ptr;
}
}
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "txclk_dir_reg");
ptr = devm_ioremap_resource(dev, res);
if (IS_ERR(ptr)) {
dev_err(dev, "txclk_dir register not exist, skipped it\n");
} else {
thead_plat_dat->txclk_dir_reg = ptr;
}
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "clk_mgr_reg");
ptr = devm_ioremap_resource(dev, res);
if (IS_ERR(ptr)) {
dev_err(dev, "gmac_clk register not exist, skipped it\n");
} else {
thead_plat_dat->gmac_clk_reg = ptr;
}
/* get gmac pll clk */
clktmp = devm_clk_get(dev, "gmac_pll_clk");
if (IS_ERR(clktmp)) {
dev_err(dev, "gmac_pll_clk not exist, skipped it\n");
} else {
thead_plat_dat->gmac_pll_clk = clktmp;
ret = clk_prepare_enable(thead_plat_dat->gmac_pll_clk);
if (ret) {
dev_err(dev, "Failed to enable clk 'gmac_pll_clk'\n");
return -1;
}
thead_plat_dat->gmac_pll_clk_freq =
clk_get_rate(thead_plat_dat->gmac_pll_clk);
}
thead_dwmac_set_phy_if(pdev, thead_plat_dat->phy_if_reg,
thead_plat_dat->interface, thead_plat_dat->id);
thead_dwmac_set_txclk_dir(pdev, thead_plat_dat->txclk_dir_reg,
thead_plat_dat->interface);
thead_dwmac_set_clk_source(pdev, thead_plat_dat->gmac_clk_reg,
thead_plat_dat->interface);
thead_dwmac_set_clock_delay(pdev, thead_plat_dat->gmac_clk_reg,
thead_plat_dat->interface);
thead_dwmac_set_pll_250M(thead_plat_dat->gmac_clk_reg,
thead_plat_dat->interface,
thead_plat_dat->gmac_pll_clk_freq);
/* default speed is 1Gbps */
thead_dwmac_set_speed(thead_plat_dat->gmac_clk_reg,
thead_plat_dat->interface, SPEED_1000);
thead_dwmac_enable_clock(pdev, thead_plat_dat->gmac_clk_reg,
thead_plat_dat->interface);
return 0;
}
static void thead_dwmac_fix_speed(void *bsp_priv, unsigned int speed)
{
struct thead_dwmac_priv_data *thead_plat_dat = bsp_priv;
thead_dwmac_set_speed(thead_plat_dat->gmac_clk_reg,
thead_plat_dat->interface, speed);
}
/**
* dwmac1000_validate_mcast_bins - validates the number of Multicast filter bins
* @mcast_bins: Multicast filtering bins
* Description:
* this function validates the number of Multicast filtering bins specified
* by the configuration through the device tree. The Synopsys GMAC supports
* 64 bins, 128 bins, or 256 bins. "bins" refer to the division of CRC
* number space. 64 bins correspond to 6 bits of the CRC, 128 corresponds
* to 7 bits, and 256 refers to 8 bits of the CRC. Any other setting is
* invalid and will cause the filtering algorithm to use Multicast
* promiscuous mode.
*/
static int dwmac1000_validate_mcast_bins(int mcast_bins)
{
int x = mcast_bins;
switch (x) {
case HASH_TABLE_SIZE:
case 128:
case 256:
break;
default:
x = 0;
pr_info("Hash table entries set to unexpected value %d",
mcast_bins);
break;
}
return x;
}
/**
* dwmac1000_validate_ucast_entries - validate the Unicast address entries
* @ucast_entries: number of Unicast address entries
* Description:
* This function validates the number of Unicast address entries supported
* by a particular Synopsys 10/100/1000 controller. The Synopsys controller
* supports 1..32, 64, or 128 Unicast filter entries for it's Unicast filter
* logic. This function validates a valid, supported configuration is
* selected, and defaults to 1 Unicast address if an unsupported
* configuration is selected.
*/
static int dwmac1000_validate_ucast_entries(int ucast_entries)
{
int x = ucast_entries;
switch (x) {
case 1 ... 32:
case 64:
case 128:
break;
default:
x = 1;
pr_info("Unicast table entries set to unexpected value %d\n",
ucast_entries);
break;
}
return x;
}
static int thead_dwmac_probe(struct platform_device *pdev)
{
struct plat_stmmacenet_data *plat_dat;
struct stmmac_resources stmmac_res;
struct thead_dwmac_priv_data *thead_plat_dat;
struct device *dev = &pdev->dev;
struct device_node *np = pdev->dev.of_node;
int ret;
thead_plat_dat = devm_kzalloc(dev, sizeof(*thead_plat_dat), GFP_KERNEL);
if (thead_plat_dat == NULL) {
dev_err(&pdev->dev, "allocate memory failed\n");
return -ENOMEM;
}
ret = stmmac_get_platform_resources(pdev, &stmmac_res);
if (ret)
return ret;
if (pdev->dev.of_node) {
plat_dat = stmmac_probe_config_dt(pdev, &stmmac_res.mac);
if (IS_ERR(plat_dat)) {
dev_err(&pdev->dev, "dt configuration failed\n");
return PTR_ERR(plat_dat);
}
} else {
plat_dat = dev_get_platdata(&pdev->dev);
if (!plat_dat) {
dev_err(&pdev->dev, "no platform data provided\n");
return -EINVAL;
}
/* Set default value for multicast hash bins */
plat_dat->multicast_filter_bins = HASH_TABLE_SIZE;
/* Set default value for unicast filter entries */
plat_dat->unicast_filter_entries = 1;
}
/* Custom initialisation (if needed) */
if (plat_dat->init) {
ret = plat_dat->init(pdev, plat_dat->bsp_priv);
if (ret)
goto err_remove_config_dt;
}
/* populate bsp private data */
plat_dat->bsp_priv = thead_plat_dat;
plat_dat->fix_mac_speed = thead_dwmac_fix_speed;
of_property_read_u32(np, "max-frame-size", &plat_dat->maxmtu);
of_property_read_u32(np, "snps,multicast-filter-bins",
&plat_dat->multicast_filter_bins);
of_property_read_u32(np, "snps,perfect-filter-entries",
&plat_dat->unicast_filter_entries);
plat_dat->unicast_filter_entries = dwmac1000_validate_ucast_entries(
plat_dat->unicast_filter_entries);
plat_dat->multicast_filter_bins = dwmac1000_validate_mcast_bins(
plat_dat->multicast_filter_bins);
plat_dat->has_gmac = 1;
plat_dat->pmt = 1;
ret = thead_dwmac_init(pdev, plat_dat->bsp_priv);
if (ret)
goto err_exit;
ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
if (ret)
goto err_exit;
return 0;
err_exit:
if (plat_dat->exit)
plat_dat->exit(pdev, plat_dat->bsp_priv);
err_remove_config_dt:
if (pdev->dev.of_node)
stmmac_remove_config_dt(pdev, plat_dat);
return ret;
}
static const struct of_device_id thead_dwmac_match[] = {
{ .compatible = "thead,dwmac"},
{ }
};
MODULE_DEVICE_TABLE(of, thead_dwmac_match);
static struct platform_driver thead_dwmac_driver = {
.probe = thead_dwmac_probe,
.remove = stmmac_pltfr_remove,
.driver = {
.name = "thead_dwmac_eth",
.pm = &stmmac_pltfr_pm_ops,
.of_match_table = of_match_ptr(thead_dwmac_match),
},
};
module_platform_driver(thead_dwmac_driver);
MODULE_DESCRIPTION("T-HEAD dwmac driver");
MODULE_LICENSE("GPL v2");