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
607 lines
16 KiB
C
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");
|