From 8d0e122d3d4979e32c6ad0d340d8c9b31a8b4d95 Mon Sep 17 00:00:00 2001 From: Alexey Sheplyakov Date: Fri, 20 Mar 2020 14:00:56 +0400 Subject: [PATCH 606/625] stmmac: Baikal-M dwmac driver (cherry picked from commit 5fd65e2b57a4873946822e99fb74b07caa73c341) --- drivers/net/ethernet/stmicro/stmmac/Kconfig | 10 + drivers/net/ethernet/stmicro/stmmac/Makefile | 1 + .../ethernet/stmicro/stmmac/dwmac-baikal.c | 223 ++++++++++++++++++ 3 files changed, 234 insertions(+) create mode 100644 drivers/net/ethernet/stmicro/stmmac/dwmac-baikal.c diff --git a/drivers/net/ethernet/stmicro/stmmac/Kconfig b/drivers/net/ethernet/stmicro/stmmac/Kconfig index 53f14c5a9e02..26a3f62a36cc 100644 --- a/drivers/net/ethernet/stmicro/stmmac/Kconfig +++ b/drivers/net/ethernet/stmicro/stmmac/Kconfig @@ -66,6 +66,16 @@ config DWMAC_ANARION This selects the Anarion SoC glue layer support for the stmmac driver. +config DWMAC_BAIKAL + tristate "Baikal Electronics DWMAC support" + default y if MIPS_BAIKAL || ARCH_BAIKAL + depends on OF + help + Support for Baikal Electronics DWMAC Ethernet. + + This selects the Baikal-T/M SoC glue layer support for the stmmac + device driver. + config DWMAC_IPQ806X tristate "QCA IPQ806x DWMAC support" default ARCH_QCOM diff --git a/drivers/net/ethernet/stmicro/stmmac/Makefile b/drivers/net/ethernet/stmicro/stmmac/Makefile index 24e6145d4eae..64970f60f536 100644 --- a/drivers/net/ethernet/stmicro/stmmac/Makefile +++ b/drivers/net/ethernet/stmicro/stmmac/Makefile @@ -13,6 +13,7 @@ stmmac-$(CONFIG_STMMAC_SELFTESTS) += stmmac_selftests.o # Ordering matters. Generic driver must be last. obj-$(CONFIG_STMMAC_PLATFORM) += stmmac-platform.o obj-$(CONFIG_DWMAC_ANARION) += dwmac-anarion.o +obj-$(CONFIG_DWMAC_BAIKAL) += dwmac-baikal.o obj-$(CONFIG_DWMAC_IPQ806X) += dwmac-ipq806x.o obj-$(CONFIG_DWMAC_LPC18XX) += dwmac-lpc18xx.o obj-$(CONFIG_DWMAC_MEDIATEK) += dwmac-mediatek.o diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-baikal.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-baikal.c new file mode 100644 index 000000000000..646051cd500d --- /dev/null +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-baikal.c @@ -0,0 +1,223 @@ +/* + * Baikal Electronics SoCs DWMAC glue layer + * + * Copyright (C) 2015,2016 Baikal Electronics JSC + * Author: + * Dmitry Dunaev + * All bugs by Alexey Sheplyakov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include + +#include "stmmac.h" +#include "stmmac_platform.h" +#include "common.h" +#include "dwmac_dma.h" +#include "dwmac1000_dma.h" + +#define MAC_GPIO 0x000000e0 /* GPIO register */ +#define MAC_GPIO_GPO0 (1 << 8) /* 0-output port */ + +struct baikal_dwmac { + struct device *dev; + struct clk *tx2_clk; +}; + +static int baikal_dwmac_dma_reset(void __iomem *ioaddr) +{ + int err; + u32 value = readl(ioaddr + DMA_BUS_MODE); + + /* DMA SW reset */ + value |= DMA_BUS_MODE_SFT_RESET; + writel(value, ioaddr + DMA_BUS_MODE); + + udelay(10); + /* Clear PHY reset */ + value = readl(ioaddr + MAC_GPIO); + value |= MAC_GPIO_GPO0; + writel(value, ioaddr + MAC_GPIO); + pr_info("PHY re-inited for Baikal DWMAC\n"); + + err = readl_poll_timeout(ioaddr + DMA_BUS_MODE, value, + !(value & DMA_BUS_MODE_SFT_RESET), + 10000, 1000000); + if (err) + return -EBUSY; + + return 0; +} + +static const struct stmmac_dma_ops baikal_dwmac_dma_ops = { + .reset = baikal_dwmac_dma_reset, + .init = dwmac1000_dma_init, + .init_rx_chan = dwmac1000_dma_init_rx, + .init_tx_chan = dwmac1000_dma_init_tx, + .axi = dwmac1000_dma_axi, + .dump_regs = dwmac1000_dump_dma_regs, + .dma_rx_mode = dwmac1000_dma_operation_mode_rx, + .dma_tx_mode = dwmac1000_dma_operation_mode_tx, + .enable_dma_transmission = dwmac_enable_dma_transmission, + .enable_dma_irq = dwmac_enable_dma_irq, + .disable_dma_irq = dwmac_disable_dma_irq, + .start_tx = dwmac_dma_start_tx, + .stop_tx = dwmac_dma_stop_tx, + .start_rx = dwmac_dma_start_rx, + .stop_rx = dwmac_dma_stop_rx, + .dma_interrupt = dwmac_dma_interrupt, + .get_hw_feature = dwmac1000_get_hw_feature, + .rx_watchdog = dwmac1000_rx_watchdog, +}; + +static struct mac_device_info* baikal_dwmac_setup(void *ppriv) +{ + struct mac_device_info *mac, *old_mac; + struct stmmac_priv *priv = ppriv; + int ret; + + mac = devm_kzalloc(priv->device, sizeof(*mac), GFP_KERNEL); + if (!mac) + return NULL; + + mac->dma = &baikal_dwmac_dma_ops; + old_mac = priv->hw; + priv->hw = mac; + ret = dwmac1000_setup(priv); + priv->hw = old_mac; + if (ret) { + dev_err(priv->device, "dwmac1000_setup: error %d", ret); + return NULL; + } + return mac; +} + +static void baikal_dwmac_fix_mac_speed(void *priv, unsigned int speed) +{ + struct baikal_dwmac *dwmac = priv; + unsigned long tx2_clk_freq = 0; + dev_info(dwmac->dev, "fix_mac_speed new speed %u\n", speed); + switch (speed) { + case SPEED_1000: + tx2_clk_freq = 250000000; + break; + case SPEED_100: + tx2_clk_freq = 50000000; + break; + case SPEED_10: + tx2_clk_freq = 5000000; + break; + } + if (dwmac->tx2_clk && tx2_clk_freq != 0) { + dev_info(dwmac->dev, "setting TX2 clock frequency to %lu\n", tx2_clk_freq); + clk_set_rate(dwmac->tx2_clk, tx2_clk_freq); + + } + +} + + +static int dwmac_baikal_probe(struct platform_device *pdev) +{ + struct plat_stmmacenet_data *plat_dat; + struct stmmac_resources stmmac_res; + struct baikal_dwmac *dwmac; + int ret; + + ret = stmmac_get_platform_resources(pdev, &stmmac_res); + if (ret) + return ret; + + ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); + if (ret) { + dev_warn(&pdev->dev, "No suitable DMA available\n"); + 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; + } + + dwmac = devm_kzalloc(&pdev->dev, sizeof(*dwmac), GFP_KERNEL); + if (!dwmac) { + ret = -ENOMEM; + goto err_remove_config_dt; + } + + dwmac->dev = &pdev->dev; + dwmac->tx2_clk = devm_clk_get(dwmac->dev, "tx2_clk"); + if (IS_ERR(dwmac->tx2_clk)) { + dev_warn(&pdev->dev, "coldn't get TX2 clock\n"); + dwmac->tx2_clk = NULL; + } + plat_dat->fix_mac_speed = baikal_dwmac_fix_mac_speed; + plat_dat->bsp_priv = dwmac; + + plat_dat->has_gmac = 1; + plat_dat->enh_desc = 1; + plat_dat->tx_coe = 1; + plat_dat->rx_coe = 1; + // TODO: set CSR correct clock in dts! + plat_dat->clk_csr = 3; + plat_dat->setup = baikal_dwmac_setup; + + dev_info(&pdev->dev, "Baikal Electronics DWMAC glue driver\n"); + + ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res); + if (ret) + goto err_remove_config_dt; + + return 0; + +err_remove_config_dt: + stmmac_remove_config_dt(pdev, plat_dat); + + return ret; +} + +static const struct of_device_id dwmac_baikal_match[] = { + { .compatible = "be,dwmac-3.710"}, + { .compatible = "be,dwmac"}, + { } +}; +MODULE_DEVICE_TABLE(of, dwmac_baikal_match); + +static struct platform_driver dwmac_baikal_driver = { + .probe = dwmac_baikal_probe, + .remove = stmmac_pltfr_remove, + .driver = { + .name = "baikal-dwmac", + .pm = &stmmac_pltfr_pm_ops, + .of_match_table = of_match_ptr(dwmac_baikal_match), + }, +}; +module_platform_driver(dwmac_baikal_driver); + +MODULE_DESCRIPTION("Baikal dwmac glue driver"); +MODULE_LICENSE("GPL v2"); -- 2.31.1