Commit 429a372e authored by David S. Miller's avatar David S. Miller

Merge branch 'stmmac-tegra186'

Thierry Reding says:

====================
net: stmmac: Fixes and Tegra186 support

This series of patches start with a few cleanups that I ran across while
adding Tegra186 support to the stmmac driver. It then adds code for FIFO
size parsing from feature registers and finally enables support for the
incarnation of the Synopsys DWC QOS IP found on NVIDIA Tegra186 SoCs.

This is based on next-20170310.

Changes in v2:
- address review comments by Mikko and Joao
- add two additional cleanup patches
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 8f48ba71 e6ea2d16
......@@ -28,9 +28,9 @@ Optional properties:
clocks may be specified in derived bindings.
- clock-names: One name for each entry in the clocks property, the
first one should be "stmmaceth" and the second one should be "pclk".
- clk_ptp_ref: this is the PTP reference clock; in case of the PTP is
available this clock is used for programming the Timestamp Addend Register.
If not passed then the system clock will be used and this is fine on some
- ptp_ref: this is the PTP reference clock; in case of the PTP is available
this clock is used for programming the Timestamp Addend Register. If not
passed then the system clock will be used and this is fine on some
platforms.
- tx-fifo-depth: See ethernet.txt file in the same directory
- rx-fifo-depth: See ethernet.txt file in the same directory
......
......@@ -324,6 +324,9 @@ struct dma_features {
unsigned int number_tx_queues;
/* Alternate (enhanced) DESC mode */
unsigned int enh_desc;
/* TX and RX FIFO sizes */
unsigned int tx_fifo_size;
unsigned int rx_fifo_size;
};
/* GMAC TX FIFO is 8K, Rx FIFO is 16K */
......
......@@ -14,16 +14,34 @@
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/device.h>
#include <linux/gpio/consumer.h>
#include <linux/ethtool.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/ioport.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/of_net.h>
#include <linux/mfd/syscon.h>
#include <linux/platform_device.h>
#include <linux/reset.h>
#include <linux/stmmac.h>
#include "stmmac_platform.h"
#include "dwmac4.h"
struct tegra_eqos {
struct device *dev;
void __iomem *regs;
struct reset_control *rst;
struct clk *clk_master;
struct clk *clk_slave;
struct clk *clk_tx;
struct clk *clk_rx;
struct gpio_desc *reset;
};
static int dwc_eth_dwmac_config_dt(struct platform_device *pdev,
struct plat_stmmacenet_data *plat_dat)
......@@ -106,13 +124,309 @@ static int dwc_eth_dwmac_config_dt(struct platform_device *pdev,
return 0;
}
static void *dwc_qos_probe(struct platform_device *pdev,
struct plat_stmmacenet_data *plat_dat,
struct stmmac_resources *stmmac_res)
{
int err;
plat_dat->stmmac_clk = devm_clk_get(&pdev->dev, "apb_pclk");
if (IS_ERR(plat_dat->stmmac_clk)) {
dev_err(&pdev->dev, "apb_pclk clock not found.\n");
return ERR_CAST(plat_dat->stmmac_clk);
}
err = clk_prepare_enable(plat_dat->stmmac_clk);
if (err < 0) {
dev_err(&pdev->dev, "failed to enable apb_pclk clock: %d\n",
err);
return ERR_PTR(err);
}
plat_dat->pclk = devm_clk_get(&pdev->dev, "phy_ref_clk");
if (IS_ERR(plat_dat->pclk)) {
dev_err(&pdev->dev, "phy_ref_clk clock not found.\n");
err = PTR_ERR(plat_dat->pclk);
goto disable;
}
err = clk_prepare_enable(plat_dat->pclk);
if (err < 0) {
dev_err(&pdev->dev, "failed to enable phy_ref clock: %d\n",
err);
goto disable;
}
return NULL;
disable:
clk_disable_unprepare(plat_dat->stmmac_clk);
return ERR_PTR(err);
}
static int dwc_qos_remove(struct platform_device *pdev)
{
struct net_device *ndev = platform_get_drvdata(pdev);
struct stmmac_priv *priv = netdev_priv(ndev);
clk_disable_unprepare(priv->plat->pclk);
clk_disable_unprepare(priv->plat->stmmac_clk);
return 0;
}
#define SDMEMCOMPPADCTRL 0x8800
#define SDMEMCOMPPADCTRL_PAD_E_INPUT_OR_E_PWRD BIT(31)
#define AUTO_CAL_CONFIG 0x8804
#define AUTO_CAL_CONFIG_START BIT(31)
#define AUTO_CAL_CONFIG_ENABLE BIT(29)
#define AUTO_CAL_STATUS 0x880c
#define AUTO_CAL_STATUS_ACTIVE BIT(31)
static void tegra_eqos_fix_speed(void *priv, unsigned int speed)
{
struct tegra_eqos *eqos = priv;
unsigned long rate = 125000000;
bool needs_calibration = false;
u32 value;
int err;
switch (speed) {
case SPEED_1000:
needs_calibration = true;
rate = 125000000;
break;
case SPEED_100:
needs_calibration = true;
rate = 25000000;
break;
case SPEED_10:
rate = 2500000;
break;
default:
dev_err(eqos->dev, "invalid speed %u\n", speed);
break;
}
if (needs_calibration) {
/* calibrate */
value = readl(eqos->regs + SDMEMCOMPPADCTRL);
value |= SDMEMCOMPPADCTRL_PAD_E_INPUT_OR_E_PWRD;
writel(value, eqos->regs + SDMEMCOMPPADCTRL);
udelay(1);
value = readl(eqos->regs + AUTO_CAL_CONFIG);
value |= AUTO_CAL_CONFIG_START | AUTO_CAL_CONFIG_ENABLE;
writel(value, eqos->regs + AUTO_CAL_CONFIG);
err = readl_poll_timeout_atomic(eqos->regs + AUTO_CAL_STATUS,
value,
value & AUTO_CAL_STATUS_ACTIVE,
1, 10);
if (err < 0) {
dev_err(eqos->dev, "calibration did not start\n");
goto failed;
}
err = readl_poll_timeout_atomic(eqos->regs + AUTO_CAL_STATUS,
value,
(value & AUTO_CAL_STATUS_ACTIVE) == 0,
20, 200);
if (err < 0) {
dev_err(eqos->dev, "calibration didn't finish\n");
goto failed;
}
failed:
value = readl(eqos->regs + SDMEMCOMPPADCTRL);
value &= ~SDMEMCOMPPADCTRL_PAD_E_INPUT_OR_E_PWRD;
writel(value, eqos->regs + SDMEMCOMPPADCTRL);
} else {
value = readl(eqos->regs + AUTO_CAL_CONFIG);
value &= ~AUTO_CAL_CONFIG_ENABLE;
writel(value, eqos->regs + AUTO_CAL_CONFIG);
}
err = clk_set_rate(eqos->clk_tx, rate);
if (err < 0)
dev_err(eqos->dev, "failed to set TX rate: %d\n", err);
}
static int tegra_eqos_init(struct platform_device *pdev, void *priv)
{
struct tegra_eqos *eqos = priv;
unsigned long rate;
u32 value;
rate = clk_get_rate(eqos->clk_slave);
value = (rate / 1000000) - 1;
writel(value, eqos->regs + GMAC_1US_TIC_COUNTER);
return 0;
}
static void *tegra_eqos_probe(struct platform_device *pdev,
struct plat_stmmacenet_data *data,
struct stmmac_resources *res)
{
struct tegra_eqos *eqos;
int err;
eqos = devm_kzalloc(&pdev->dev, sizeof(*eqos), GFP_KERNEL);
if (!eqos) {
err = -ENOMEM;
goto error;
}
eqos->dev = &pdev->dev;
eqos->regs = res->addr;
eqos->clk_master = devm_clk_get(&pdev->dev, "master_bus");
if (IS_ERR(eqos->clk_master)) {
err = PTR_ERR(eqos->clk_master);
goto error;
}
err = clk_prepare_enable(eqos->clk_master);
if (err < 0)
goto error;
eqos->clk_slave = devm_clk_get(&pdev->dev, "slave_bus");
if (IS_ERR(eqos->clk_slave)) {
err = PTR_ERR(eqos->clk_slave);
goto disable_master;
}
data->stmmac_clk = eqos->clk_slave;
err = clk_prepare_enable(eqos->clk_slave);
if (err < 0)
goto disable_master;
eqos->clk_rx = devm_clk_get(&pdev->dev, "rx");
if (IS_ERR(eqos->clk_rx)) {
err = PTR_ERR(eqos->clk_rx);
goto disable_slave;
}
err = clk_prepare_enable(eqos->clk_rx);
if (err < 0)
goto disable_slave;
eqos->clk_tx = devm_clk_get(&pdev->dev, "tx");
if (IS_ERR(eqos->clk_tx)) {
err = PTR_ERR(eqos->clk_tx);
goto disable_rx;
}
err = clk_prepare_enable(eqos->clk_tx);
if (err < 0)
goto disable_rx;
eqos->reset = devm_gpiod_get(&pdev->dev, "phy-reset", GPIOD_OUT_HIGH);
if (IS_ERR(eqos->reset)) {
err = PTR_ERR(eqos->reset);
goto disable_tx;
}
usleep_range(2000, 4000);
gpiod_set_value(eqos->reset, 0);
eqos->rst = devm_reset_control_get(&pdev->dev, "eqos");
if (IS_ERR(eqos->rst)) {
err = PTR_ERR(eqos->rst);
goto reset_phy;
}
err = reset_control_assert(eqos->rst);
if (err < 0)
goto reset_phy;
usleep_range(2000, 4000);
err = reset_control_deassert(eqos->rst);
if (err < 0)
goto reset_phy;
usleep_range(2000, 4000);
data->fix_mac_speed = tegra_eqos_fix_speed;
data->init = tegra_eqos_init;
data->bsp_priv = eqos;
err = tegra_eqos_init(pdev, eqos);
if (err < 0)
goto reset;
out:
return eqos;
reset:
reset_control_assert(eqos->rst);
reset_phy:
gpiod_set_value(eqos->reset, 1);
disable_tx:
clk_disable_unprepare(eqos->clk_tx);
disable_rx:
clk_disable_unprepare(eqos->clk_rx);
disable_slave:
clk_disable_unprepare(eqos->clk_slave);
disable_master:
clk_disable_unprepare(eqos->clk_master);
error:
eqos = ERR_PTR(err);
goto out;
}
static int tegra_eqos_remove(struct platform_device *pdev)
{
struct tegra_eqos *eqos = get_stmmac_bsp_priv(&pdev->dev);
reset_control_assert(eqos->rst);
gpiod_set_value(eqos->reset, 1);
clk_disable_unprepare(eqos->clk_tx);
clk_disable_unprepare(eqos->clk_rx);
clk_disable_unprepare(eqos->clk_slave);
clk_disable_unprepare(eqos->clk_master);
return 0;
}
struct dwc_eth_dwmac_data {
void *(*probe)(struct platform_device *pdev,
struct plat_stmmacenet_data *data,
struct stmmac_resources *res);
int (*remove)(struct platform_device *pdev);
};
static const struct dwc_eth_dwmac_data dwc_qos_data = {
.probe = dwc_qos_probe,
.remove = dwc_qos_remove,
};
static const struct dwc_eth_dwmac_data tegra_eqos_data = {
.probe = tegra_eqos_probe,
.remove = tegra_eqos_remove,
};
static int dwc_eth_dwmac_probe(struct platform_device *pdev)
{
const struct dwc_eth_dwmac_data *data;
struct plat_stmmacenet_data *plat_dat;
struct stmmac_resources stmmac_res;
struct resource *res;
void *priv;
int ret;
data = of_device_get_match_data(&pdev->dev);
memset(&stmmac_res, 0, sizeof(struct stmmac_resources));
/**
......@@ -138,39 +452,26 @@ static int dwc_eth_dwmac_probe(struct platform_device *pdev)
if (IS_ERR(plat_dat))
return PTR_ERR(plat_dat);
plat_dat->stmmac_clk = devm_clk_get(&pdev->dev, "apb_pclk");
if (IS_ERR(plat_dat->stmmac_clk)) {
dev_err(&pdev->dev, "apb_pclk clock not found.\n");
ret = PTR_ERR(plat_dat->stmmac_clk);
plat_dat->stmmac_clk = NULL;
goto err_remove_config_dt;
}
clk_prepare_enable(plat_dat->stmmac_clk);
plat_dat->pclk = devm_clk_get(&pdev->dev, "phy_ref_clk");
if (IS_ERR(plat_dat->pclk)) {
dev_err(&pdev->dev, "phy_ref_clk clock not found.\n");
ret = PTR_ERR(plat_dat->pclk);
plat_dat->pclk = NULL;
goto err_out_clk_dis_phy;
priv = data->probe(pdev, plat_dat, &stmmac_res);
if (IS_ERR(priv)) {
ret = PTR_ERR(priv);
dev_err(&pdev->dev, "failed to probe subdriver: %d\n", ret);
goto remove_config;
}
clk_prepare_enable(plat_dat->pclk);
ret = dwc_eth_dwmac_config_dt(pdev, plat_dat);
if (ret)
goto err_out_clk_dis_aper;
goto remove;
ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
if (ret)
goto err_out_clk_dis_aper;
goto remove;
return 0;
return ret;
err_out_clk_dis_aper:
clk_disable_unprepare(plat_dat->pclk);
err_out_clk_dis_phy:
clk_disable_unprepare(plat_dat->stmmac_clk);
err_remove_config_dt:
remove:
data->remove(pdev);
remove_config:
stmmac_remove_config_dt(pdev, plat_dat);
return ret;
......@@ -178,11 +479,29 @@ static int dwc_eth_dwmac_probe(struct platform_device *pdev)
static int dwc_eth_dwmac_remove(struct platform_device *pdev)
{
return stmmac_pltfr_remove(pdev);
struct net_device *ndev = platform_get_drvdata(pdev);
struct stmmac_priv *priv = netdev_priv(ndev);
const struct dwc_eth_dwmac_data *data;
int err;
data = of_device_get_match_data(&pdev->dev);
err = stmmac_dvr_remove(&pdev->dev);
if (err < 0)
dev_err(&pdev->dev, "failed to remove platform: %d\n", err);
err = data->remove(pdev);
if (err < 0)
dev_err(&pdev->dev, "failed to remove subdriver: %d\n", err);
stmmac_remove_config_dt(pdev, priv->plat);
return err;
}
static const struct of_device_id dwc_eth_dwmac_match[] = {
{ .compatible = "snps,dwc-qos-ethernet-4.10", },
{ .compatible = "snps,dwc-qos-ethernet-4.10", .data = &dwc_qos_data },
{ .compatible = "nvidia,tegra186-eqos", .data = &tegra_eqos_data },
{ }
};
MODULE_DEVICE_TABLE(of, dwc_eth_dwmac_match);
......
......@@ -25,6 +25,7 @@
#define GMAC_RXQ_CTRL0 0x000000a0
#define GMAC_INT_STATUS 0x000000b0
#define GMAC_INT_EN 0x000000b4
#define GMAC_1US_TIC_COUNTER 0x000000dc
#define GMAC_PCS_BASE 0x000000e0
#define GMAC_PHYIF_CONTROL_STATUS 0x000000f8
#define GMAC_PMT 0x000000c0
......@@ -148,6 +149,8 @@ enum power_event {
/* MAC HW features1 bitmap */
#define GMAC_HW_FEAT_AVSEL BIT(20)
#define GMAC_HW_TSOEN BIT(18)
#define GMAC_HW_TXFIFOSIZE GENMASK(10, 6)
#define GMAC_HW_RXFIFOSIZE GENMASK(4, 0)
/* MAC HW features2 bitmap */
#define GMAC_HW_FEAT_TXCHCNT GENMASK(21, 18)
......@@ -180,6 +183,7 @@ enum power_event {
#define MTL_OP_MODE_TSF BIT(1)
#define MTL_OP_MODE_TQS_MASK GENMASK(24, 16)
#define MTL_OP_MODE_TQS_SHIFT 16
#define MTL_OP_MODE_TTC_MASK 0x70
#define MTL_OP_MODE_TTC_SHIFT 4
......@@ -193,6 +197,17 @@ enum power_event {
#define MTL_OP_MODE_TTC_384 (6 << MTL_OP_MODE_TTC_SHIFT)
#define MTL_OP_MODE_TTC_512 (7 << MTL_OP_MODE_TTC_SHIFT)
#define MTL_OP_MODE_RQS_MASK GENMASK(29, 20)
#define MTL_OP_MODE_RQS_SHIFT 20
#define MTL_OP_MODE_RFD_MASK GENMASK(19, 14)
#define MTL_OP_MODE_RFD_SHIFT 14
#define MTL_OP_MODE_RFA_MASK GENMASK(13, 8)
#define MTL_OP_MODE_RFA_SHIFT 8
#define MTL_OP_MODE_EHFC BIT(7)
#define MTL_OP_MODE_RTC_MASK 0x18
#define MTL_OP_MODE_RTC_SHIFT 3
......
......@@ -183,8 +183,9 @@ static void dwmac4_rx_watchdog(void __iomem *ioaddr, u32 riwt)
}
static void dwmac4_dma_chan_op_mode(void __iomem *ioaddr, int txmode,
int rxmode, u32 channel)
int rxmode, u32 channel, int rxfifosz)
{
unsigned int rqs = rxfifosz / 256 - 1;
u32 mtl_tx_op, mtl_rx_op, mtl_rx_int;
/* Following code only done for channel 0, other channels not yet
......@@ -250,6 +251,53 @@ static void dwmac4_dma_chan_op_mode(void __iomem *ioaddr, int txmode,
mtl_rx_op |= MTL_OP_MODE_RTC_128;
}
mtl_rx_op &= ~MTL_OP_MODE_RQS_MASK;
mtl_rx_op |= rqs << MTL_OP_MODE_RQS_SHIFT;
/* enable flow control only if each channel gets 4 KiB or more FIFO */
if (rxfifosz >= 4096) {
unsigned int rfd, rfa;
mtl_rx_op |= MTL_OP_MODE_EHFC;
/* Set Threshold for Activating Flow Control to min 2 frames,
* i.e. 1500 * 2 = 3000 bytes.
*
* Set Threshold for Deactivating Flow Control to min 1 frame,
* i.e. 1500 bytes.
*/
switch (rxfifosz) {
case 4096:
/* This violates the above formula because of FIFO size
* limit therefore overflow may occur in spite of this.
*/
rfd = 0x03; /* Full-2.5K */
rfa = 0x01; /* Full-1.5K */
break;
case 8192:
rfd = 0x06; /* Full-4K */
rfa = 0x0a; /* Full-6K */
break;
case 16384:
rfd = 0x06; /* Full-4K */
rfa = 0x12; /* Full-10K */
break;
default:
rfd = 0x06; /* Full-4K */
rfa = 0x1e; /* Full-16K */
break;
}
mtl_rx_op &= ~MTL_OP_MODE_RFD_MASK;
mtl_rx_op |= rfd << MTL_OP_MODE_RFD_SHIFT;
mtl_rx_op &= ~MTL_OP_MODE_RFA_MASK;
mtl_rx_op |= rfa << MTL_OP_MODE_RFA_SHIFT;
}
writel(mtl_rx_op, ioaddr + MTL_CHAN_RX_OP_MODE(channel));
/* Enable MTL RX overflow */
......@@ -262,7 +310,7 @@ static void dwmac4_dma_operation_mode(void __iomem *ioaddr, int txmode,
int rxmode, int rxfifosz)
{
/* Only Channel 0 is actually configured and used */
dwmac4_dma_chan_op_mode(ioaddr, txmode, rxmode, 0);
dwmac4_dma_chan_op_mode(ioaddr, txmode, rxmode, 0, rxfifosz);
}
static void dwmac4_get_hw_feature(void __iomem *ioaddr,
......@@ -294,6 +342,11 @@ static void dwmac4_get_hw_feature(void __iomem *ioaddr,
hw_cap = readl(ioaddr + GMAC_HW_FEATURE1);
dma_cap->av = (hw_cap & GMAC_HW_FEAT_AVSEL) >> 20;
dma_cap->tsoen = (hw_cap & GMAC_HW_TSOEN) >> 18;
/* RX and TX FIFO sizes are encoded as log2(n / 128). Undo that by
* shifting and store the sizes in bytes.
*/
dma_cap->tx_fifo_size = 128 << ((hw_cap & GMAC_HW_TXFIFOSIZE) >> 6);
dma_cap->rx_fifo_size = 128 << ((hw_cap & GMAC_HW_RXFIFOSIZE) >> 0);
/* MAC HW feature2 */
hw_cap = readl(ioaddr + GMAC_HW_FEATURE2);
/* TX and RX number of channels */
......
......@@ -1281,6 +1281,9 @@ static void stmmac_dma_operation_mode(struct stmmac_priv *priv)
{
int rxfifosz = priv->plat->rx_fifo_size;
if (rxfifosz == 0)
rxfifosz = priv->dma_cap.rx_fifo_size;
if (priv->plat->force_thresh_dma_mode)
priv->hw->dma->dma_mode(priv->ioaddr, tc, tc, rxfifosz);
else if (priv->plat->force_sf_dma_mode || priv->plat->tx_coe) {
......@@ -1711,6 +1714,10 @@ static int stmmac_hw_setup(struct net_device *dev, bool init_ptp)
stmmac_mmc_setup(priv);
if (init_ptp) {
ret = clk_prepare_enable(priv->plat->clk_ptp_ref);
if (ret < 0)
netdev_warn(priv->dev, "failed to enable PTP reference clock: %d\n", ret);
ret = stmmac_init_ptp(priv);
if (ret == -EOPNOTSUPP)
netdev_warn(priv->dev, "PTP not supported by HW\n");
......@@ -1754,6 +1761,13 @@ static int stmmac_hw_setup(struct net_device *dev, bool init_ptp)
return 0;
}
static void stmmac_hw_teardown(struct net_device *dev)
{
struct stmmac_priv *priv = netdev_priv(dev);
clk_disable_unprepare(priv->plat->clk_ptp_ref);
}
/**
* stmmac_open - open entry point of the driver
* @dev : pointer to the device structure.
......@@ -1821,7 +1835,7 @@ static int stmmac_open(struct net_device *dev)
netdev_err(priv->dev,
"%s: ERROR: allocating the IRQ %d (error: %d)\n",
__func__, dev->irq, ret);
goto init_error;
goto irq_error;
}
/* Request the Wake IRQ in case of another line is used for WoL */
......@@ -1858,7 +1872,12 @@ static int stmmac_open(struct net_device *dev)
free_irq(priv->wol_irq, dev);
wolirq_error:
free_irq(dev->irq, dev);
irq_error:
if (dev->phydev)
phy_stop(dev->phydev);
del_timer_sync(&priv->txtimer);
stmmac_hw_teardown(dev);
init_error:
free_dma_desc_resources(priv);
dma_desc_error:
......@@ -2063,6 +2082,8 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)
des = skb_frag_dma_map(priv->device, frag, 0,
skb_frag_size(frag),
DMA_TO_DEVICE);
if (dma_mapping_error(priv->device, des))
goto dma_map_err;
stmmac_tso_allocator(priv, des, skb_frag_size(frag),
(i == nfrags - 1));
......
......@@ -359,13 +359,12 @@ stmmac_probe_config_dt(struct platform_device *pdev, const char **mac)
clk_prepare_enable(plat->pclk);
/* Fall-back to main clock in case of no PTP ref is passed */
plat->clk_ptp_ref = devm_clk_get(&pdev->dev, "clk_ptp_ref");
plat->clk_ptp_ref = devm_clk_get(&pdev->dev, "ptp_ref");
if (IS_ERR(plat->clk_ptp_ref)) {
plat->clk_ptp_rate = clk_get_rate(plat->stmmac_clk);
plat->clk_ptp_ref = NULL;
dev_warn(&pdev->dev, "PTP uses main clock\n");
} else {
clk_prepare_enable(plat->clk_ptp_ref);
plat->clk_ptp_rate = clk_get_rate(plat->clk_ptp_ref);
dev_dbg(&pdev->dev, "PTP rate %d\n", plat->clk_ptp_rate);
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment