diff options
Diffstat (limited to 'drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c')
-rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c | 436 |
1 files changed, 436 insertions, 0 deletions
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c new file mode 100644 index 000000000..710d74357 --- /dev/null +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c @@ -0,0 +1,436 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * dwmac-sti.c - STMicroelectronics DWMAC Specific Glue layer + * + * Copyright (C) 2003-2014 STMicroelectronics (R&D) Limited + * Author: Srinivas Kandagatla <srinivas.kandagatla@st.com> + * Contributors: Giuseppe Cavallaro <peppe.cavallaro@st.com> + */ + +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/platform_device.h> +#include <linux/stmmac.h> +#include <linux/phy.h> +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/clk.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/of_net.h> + +#include "stmmac_platform.h" + +#define DWMAC_125MHZ 125000000 +#define DWMAC_50MHZ 50000000 +#define DWMAC_25MHZ 25000000 +#define DWMAC_2_5MHZ 2500000 + +#define IS_PHY_IF_MODE_RGMII(iface) (iface == PHY_INTERFACE_MODE_RGMII || \ + iface == PHY_INTERFACE_MODE_RGMII_ID || \ + iface == PHY_INTERFACE_MODE_RGMII_RXID || \ + iface == PHY_INTERFACE_MODE_RGMII_TXID) + +#define IS_PHY_IF_MODE_GBIT(iface) (IS_PHY_IF_MODE_RGMII(iface) || \ + iface == PHY_INTERFACE_MODE_GMII) + +/* STiH4xx register definitions (STiH415/STiH416/STiH407/STiH410 families) + * + * Below table summarizes the clock requirement and clock sources for + * supported phy interface modes with link speeds. + * ________________________________________________ + *| PHY_MODE | 1000 Mbit Link | 100 Mbit Link | + * ------------------------------------------------ + *| MII | n/a | 25Mhz | + *| | | txclk | + * ------------------------------------------------ + *| GMII | 125Mhz | 25Mhz | + *| | clk-125/txclk | txclk | + * ------------------------------------------------ + *| RGMII | 125Mhz | 25Mhz | + *| | clk-125/txclk | clkgen | + *| | clkgen | | + * ------------------------------------------------ + *| RMII | n/a | 25Mhz | + *| | |clkgen/phyclk-in | + * ------------------------------------------------ + * + * Register Configuration + *------------------------------- + * src |BIT(8)| BIT(7)| BIT(6)| + *------------------------------- + * txclk | 0 | n/a | 1 | + *------------------------------- + * ck_125| 0 | n/a | 0 | + *------------------------------- + * phyclk| 1 | 0 | n/a | + *------------------------------- + * clkgen| 1 | 1 | n/a | + *------------------------------- + */ + +#define STIH4XX_RETIME_SRC_MASK GENMASK(8, 6) +#define STIH4XX_ETH_SEL_TX_RETIME_CLK BIT(8) +#define STIH4XX_ETH_SEL_INTERNAL_NOTEXT_PHYCLK BIT(7) +#define STIH4XX_ETH_SEL_TXCLK_NOT_CLK125 BIT(6) + +/* STiD127 register definitions + *----------------------- + * src |BIT(6)| BIT(7)| + *----------------------- + * MII | 1 | n/a | + *----------------------- + * RMII | n/a | 1 | + * clkgen| | | + *----------------------- + * RMII | n/a | 0 | + * phyclk| | | + *----------------------- + * RGMII | 1 | n/a | + * clkgen| | | + *----------------------- + */ + +#define STID127_RETIME_SRC_MASK GENMASK(7, 6) +#define STID127_ETH_SEL_INTERNAL_NOTEXT_PHYCLK BIT(7) +#define STID127_ETH_SEL_INTERNAL_NOTEXT_TXCLK BIT(6) + +#define ENMII_MASK GENMASK(5, 5) +#define ENMII BIT(5) +#define EN_MASK GENMASK(1, 1) +#define EN BIT(1) + +/* + * 3 bits [4:2] + * 000-GMII/MII + * 001-RGMII + * 010-SGMII + * 100-RMII + */ +#define MII_PHY_SEL_MASK GENMASK(4, 2) +#define ETH_PHY_SEL_RMII BIT(4) +#define ETH_PHY_SEL_SGMII BIT(3) +#define ETH_PHY_SEL_RGMII BIT(2) +#define ETH_PHY_SEL_GMII 0x0 +#define ETH_PHY_SEL_MII 0x0 + +struct sti_dwmac { + phy_interface_t interface; /* MII interface */ + bool ext_phyclk; /* Clock from external PHY */ + u32 tx_retime_src; /* TXCLK Retiming*/ + struct clk *clk; /* PHY clock */ + u32 ctrl_reg; /* GMAC glue-logic control register */ + int clk_sel_reg; /* GMAC ext clk selection register */ + struct regmap *regmap; + bool gmac_en; + u32 speed; + void (*fix_retime_src)(void *priv, unsigned int speed); +}; + +struct sti_dwmac_of_data { + void (*fix_retime_src)(void *priv, unsigned int speed); +}; + +static u32 phy_intf_sels[] = { + [PHY_INTERFACE_MODE_MII] = ETH_PHY_SEL_MII, + [PHY_INTERFACE_MODE_GMII] = ETH_PHY_SEL_GMII, + [PHY_INTERFACE_MODE_RGMII] = ETH_PHY_SEL_RGMII, + [PHY_INTERFACE_MODE_RGMII_ID] = ETH_PHY_SEL_RGMII, + [PHY_INTERFACE_MODE_SGMII] = ETH_PHY_SEL_SGMII, + [PHY_INTERFACE_MODE_RMII] = ETH_PHY_SEL_RMII, +}; + +enum { + TX_RETIME_SRC_NA = 0, + TX_RETIME_SRC_TXCLK = 1, + TX_RETIME_SRC_CLK_125, + TX_RETIME_SRC_PHYCLK, + TX_RETIME_SRC_CLKGEN, +}; + +static u32 stih4xx_tx_retime_val[] = { + [TX_RETIME_SRC_TXCLK] = STIH4XX_ETH_SEL_TXCLK_NOT_CLK125, + [TX_RETIME_SRC_CLK_125] = 0x0, + [TX_RETIME_SRC_PHYCLK] = STIH4XX_ETH_SEL_TX_RETIME_CLK, + [TX_RETIME_SRC_CLKGEN] = STIH4XX_ETH_SEL_TX_RETIME_CLK + | STIH4XX_ETH_SEL_INTERNAL_NOTEXT_PHYCLK, +}; + +static void stih4xx_fix_retime_src(void *priv, u32 spd) +{ + struct sti_dwmac *dwmac = priv; + u32 src = dwmac->tx_retime_src; + u32 reg = dwmac->ctrl_reg; + u32 freq = 0; + + if (dwmac->interface == PHY_INTERFACE_MODE_MII) { + src = TX_RETIME_SRC_TXCLK; + } else if (dwmac->interface == PHY_INTERFACE_MODE_RMII) { + if (dwmac->ext_phyclk) { + src = TX_RETIME_SRC_PHYCLK; + } else { + src = TX_RETIME_SRC_CLKGEN; + freq = DWMAC_50MHZ; + } + } else if (IS_PHY_IF_MODE_RGMII(dwmac->interface)) { + /* On GiGa clk source can be either ext or from clkgen */ + if (spd == SPEED_1000) { + freq = DWMAC_125MHZ; + } else { + /* Switch to clkgen for these speeds */ + src = TX_RETIME_SRC_CLKGEN; + if (spd == SPEED_100) + freq = DWMAC_25MHZ; + else if (spd == SPEED_10) + freq = DWMAC_2_5MHZ; + } + } + + if (src == TX_RETIME_SRC_CLKGEN && freq) + clk_set_rate(dwmac->clk, freq); + + regmap_update_bits(dwmac->regmap, reg, STIH4XX_RETIME_SRC_MASK, + stih4xx_tx_retime_val[src]); +} + +static void stid127_fix_retime_src(void *priv, u32 spd) +{ + struct sti_dwmac *dwmac = priv; + u32 reg = dwmac->ctrl_reg; + u32 freq = 0; + u32 val = 0; + + if (dwmac->interface == PHY_INTERFACE_MODE_MII) { + val = STID127_ETH_SEL_INTERNAL_NOTEXT_TXCLK; + } else if (dwmac->interface == PHY_INTERFACE_MODE_RMII) { + if (!dwmac->ext_phyclk) { + val = STID127_ETH_SEL_INTERNAL_NOTEXT_PHYCLK; + freq = DWMAC_50MHZ; + } + } else if (IS_PHY_IF_MODE_RGMII(dwmac->interface)) { + val = STID127_ETH_SEL_INTERNAL_NOTEXT_TXCLK; + if (spd == SPEED_1000) + freq = DWMAC_125MHZ; + else if (spd == SPEED_100) + freq = DWMAC_25MHZ; + else if (spd == SPEED_10) + freq = DWMAC_2_5MHZ; + } + + if (freq) + clk_set_rate(dwmac->clk, freq); + + regmap_update_bits(dwmac->regmap, reg, STID127_RETIME_SRC_MASK, val); +} + +static int sti_dwmac_set_mode(struct sti_dwmac *dwmac) +{ + struct regmap *regmap = dwmac->regmap; + int iface = dwmac->interface; + u32 reg = dwmac->ctrl_reg; + u32 val; + + if (dwmac->gmac_en) + regmap_update_bits(regmap, reg, EN_MASK, EN); + + regmap_update_bits(regmap, reg, MII_PHY_SEL_MASK, phy_intf_sels[iface]); + + val = (iface == PHY_INTERFACE_MODE_REVMII) ? 0 : ENMII; + regmap_update_bits(regmap, reg, ENMII_MASK, val); + + dwmac->fix_retime_src(dwmac, dwmac->speed); + + return 0; +} + +static int sti_dwmac_parse_data(struct sti_dwmac *dwmac, + struct platform_device *pdev) +{ + struct resource *res; + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct regmap *regmap; + int err; + + /* clk selection from extra syscfg register */ + dwmac->clk_sel_reg = -ENXIO; + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sti-clkconf"); + if (res) + dwmac->clk_sel_reg = res->start; + + regmap = syscon_regmap_lookup_by_phandle(np, "st,syscon"); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + err = of_property_read_u32_index(np, "st,syscon", 1, &dwmac->ctrl_reg); + if (err) { + dev_err(dev, "Can't get sysconfig ctrl offset (%d)\n", err); + return err; + } + + err = of_get_phy_mode(np, &dwmac->interface); + if (err && err != -ENODEV) { + dev_err(dev, "Can't get phy-mode\n"); + return err; + } + + dwmac->regmap = regmap; + dwmac->gmac_en = of_property_read_bool(np, "st,gmac_en"); + dwmac->ext_phyclk = of_property_read_bool(np, "st,ext-phyclk"); + dwmac->tx_retime_src = TX_RETIME_SRC_NA; + dwmac->speed = SPEED_100; + + if (IS_PHY_IF_MODE_GBIT(dwmac->interface)) { + const char *rs; + + dwmac->tx_retime_src = TX_RETIME_SRC_CLKGEN; + + err = of_property_read_string(np, "st,tx-retime-src", &rs); + if (err < 0) { + dev_warn(dev, "Use internal clock source\n"); + } else { + if (!strcasecmp(rs, "clk_125")) + dwmac->tx_retime_src = TX_RETIME_SRC_CLK_125; + else if (!strcasecmp(rs, "txclk")) + dwmac->tx_retime_src = TX_RETIME_SRC_TXCLK; + } + dwmac->speed = SPEED_1000; + } + + dwmac->clk = devm_clk_get(dev, "sti-ethclk"); + if (IS_ERR(dwmac->clk)) { + dev_warn(dev, "No phy clock provided...\n"); + dwmac->clk = NULL; + } + + return 0; +} + +static int sti_dwmac_probe(struct platform_device *pdev) +{ + struct plat_stmmacenet_data *plat_dat; + const struct sti_dwmac_of_data *data; + struct stmmac_resources stmmac_res; + struct sti_dwmac *dwmac; + int ret; + + data = of_device_get_match_data(&pdev->dev); + if (!data) { + dev_err(&pdev->dev, "No OF match data provided\n"); + return -EINVAL; + } + + ret = stmmac_get_platform_resources(pdev, &stmmac_res); + if (ret) + return ret; + + plat_dat = stmmac_probe_config_dt(pdev, stmmac_res.mac); + if (IS_ERR(plat_dat)) + return PTR_ERR(plat_dat); + + dwmac = devm_kzalloc(&pdev->dev, sizeof(*dwmac), GFP_KERNEL); + if (!dwmac) { + ret = -ENOMEM; + goto err_remove_config_dt; + } + + ret = sti_dwmac_parse_data(dwmac, pdev); + if (ret) { + dev_err(&pdev->dev, "Unable to parse OF data\n"); + goto err_remove_config_dt; + } + + dwmac->fix_retime_src = data->fix_retime_src; + + plat_dat->bsp_priv = dwmac; + plat_dat->fix_mac_speed = data->fix_retime_src; + + ret = clk_prepare_enable(dwmac->clk); + if (ret) + goto err_remove_config_dt; + + ret = sti_dwmac_set_mode(dwmac); + if (ret) + goto disable_clk; + + ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res); + if (ret) + goto disable_clk; + + return 0; + +disable_clk: + clk_disable_unprepare(dwmac->clk); +err_remove_config_dt: + stmmac_remove_config_dt(pdev, plat_dat); + + return ret; +} + +static int sti_dwmac_remove(struct platform_device *pdev) +{ + struct sti_dwmac *dwmac = get_stmmac_bsp_priv(&pdev->dev); + int ret = stmmac_dvr_remove(&pdev->dev); + + clk_disable_unprepare(dwmac->clk); + + return ret; +} + +#ifdef CONFIG_PM_SLEEP +static int sti_dwmac_suspend(struct device *dev) +{ + struct sti_dwmac *dwmac = get_stmmac_bsp_priv(dev); + int ret = stmmac_suspend(dev); + + clk_disable_unprepare(dwmac->clk); + + return ret; +} + +static int sti_dwmac_resume(struct device *dev) +{ + struct sti_dwmac *dwmac = get_stmmac_bsp_priv(dev); + + clk_prepare_enable(dwmac->clk); + sti_dwmac_set_mode(dwmac); + + return stmmac_resume(dev); +} +#endif /* CONFIG_PM_SLEEP */ + +static SIMPLE_DEV_PM_OPS(sti_dwmac_pm_ops, sti_dwmac_suspend, + sti_dwmac_resume); + +static const struct sti_dwmac_of_data stih4xx_dwmac_data = { + .fix_retime_src = stih4xx_fix_retime_src, +}; + +static const struct sti_dwmac_of_data stid127_dwmac_data = { + .fix_retime_src = stid127_fix_retime_src, +}; + +static const struct of_device_id sti_dwmac_match[] = { + { .compatible = "st,stih415-dwmac", .data = &stih4xx_dwmac_data}, + { .compatible = "st,stih416-dwmac", .data = &stih4xx_dwmac_data}, + { .compatible = "st,stid127-dwmac", .data = &stid127_dwmac_data}, + { .compatible = "st,stih407-dwmac", .data = &stih4xx_dwmac_data}, + { } +}; +MODULE_DEVICE_TABLE(of, sti_dwmac_match); + +static struct platform_driver sti_dwmac_driver = { + .probe = sti_dwmac_probe, + .remove = sti_dwmac_remove, + .driver = { + .name = "sti-dwmac", + .pm = &sti_dwmac_pm_ops, + .of_match_table = sti_dwmac_match, + }, +}; +module_platform_driver(sti_dwmac_driver); + +MODULE_AUTHOR("Srinivas Kandagatla <srinivas.kandagatla@st.com>"); +MODULE_DESCRIPTION("STMicroelectronics DWMAC Specific Glue layer"); +MODULE_LICENSE("GPL"); |