cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

dwmac-imx.c (7563B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * dwmac-imx.c - DWMAC Specific Glue layer for NXP imx8
      4 *
      5 * Copyright 2020 NXP
      6 *
      7 */
      8
      9#include <linux/clk.h>
     10#include <linux/gpio/consumer.h>
     11#include <linux/kernel.h>
     12#include <linux/mfd/syscon.h>
     13#include <linux/module.h>
     14#include <linux/of.h>
     15#include <linux/of_device.h>
     16#include <linux/of_net.h>
     17#include <linux/phy.h>
     18#include <linux/platform_device.h>
     19#include <linux/pm_wakeirq.h>
     20#include <linux/regmap.h>
     21#include <linux/slab.h>
     22#include <linux/stmmac.h>
     23
     24#include "stmmac_platform.h"
     25
     26#define GPR_ENET_QOS_INTF_MODE_MASK	GENMASK(21, 16)
     27#define GPR_ENET_QOS_INTF_SEL_MII	(0x0 << 16)
     28#define GPR_ENET_QOS_INTF_SEL_RMII	(0x4 << 16)
     29#define GPR_ENET_QOS_INTF_SEL_RGMII	(0x1 << 16)
     30#define GPR_ENET_QOS_CLK_GEN_EN		(0x1 << 19)
     31#define GPR_ENET_QOS_CLK_TX_CLK_SEL	(0x1 << 20)
     32#define GPR_ENET_QOS_RGMII_EN		(0x1 << 21)
     33
     34struct imx_dwmac_ops {
     35	u32 addr_width;
     36	bool mac_rgmii_txclk_auto_adj;
     37
     38	int (*set_intf_mode)(struct plat_stmmacenet_data *plat_dat);
     39};
     40
     41struct imx_priv_data {
     42	struct device *dev;
     43	struct clk *clk_tx;
     44	struct clk *clk_mem;
     45	struct regmap *intf_regmap;
     46	u32 intf_reg_off;
     47	bool rmii_refclk_ext;
     48
     49	const struct imx_dwmac_ops *ops;
     50	struct plat_stmmacenet_data *plat_dat;
     51};
     52
     53static int imx8mp_set_intf_mode(struct plat_stmmacenet_data *plat_dat)
     54{
     55	struct imx_priv_data *dwmac = plat_dat->bsp_priv;
     56	int val;
     57
     58	switch (plat_dat->interface) {
     59	case PHY_INTERFACE_MODE_MII:
     60		val = GPR_ENET_QOS_INTF_SEL_MII;
     61		break;
     62	case PHY_INTERFACE_MODE_RMII:
     63		val = GPR_ENET_QOS_INTF_SEL_RMII;
     64		val |= (dwmac->rmii_refclk_ext ? 0 : GPR_ENET_QOS_CLK_TX_CLK_SEL);
     65		break;
     66	case PHY_INTERFACE_MODE_RGMII:
     67	case PHY_INTERFACE_MODE_RGMII_ID:
     68	case PHY_INTERFACE_MODE_RGMII_RXID:
     69	case PHY_INTERFACE_MODE_RGMII_TXID:
     70		val = GPR_ENET_QOS_INTF_SEL_RGMII |
     71		      GPR_ENET_QOS_RGMII_EN;
     72		break;
     73	default:
     74		pr_debug("imx dwmac doesn't support %d interface\n",
     75			 plat_dat->interface);
     76		return -EINVAL;
     77	}
     78
     79	val |= GPR_ENET_QOS_CLK_GEN_EN;
     80	return regmap_update_bits(dwmac->intf_regmap, dwmac->intf_reg_off,
     81				  GPR_ENET_QOS_INTF_MODE_MASK, val);
     82};
     83
     84static int
     85imx8dxl_set_intf_mode(struct plat_stmmacenet_data *plat_dat)
     86{
     87	int ret = 0;
     88
     89	/* TBD: depends on imx8dxl scu interfaces to be upstreamed */
     90	return ret;
     91}
     92
     93static int imx_dwmac_clks_config(void *priv, bool enabled)
     94{
     95	struct imx_priv_data *dwmac = priv;
     96	int ret = 0;
     97
     98	if (enabled) {
     99		ret = clk_prepare_enable(dwmac->clk_mem);
    100		if (ret) {
    101			dev_err(dwmac->dev, "mem clock enable failed\n");
    102			return ret;
    103		}
    104
    105		ret = clk_prepare_enable(dwmac->clk_tx);
    106		if (ret) {
    107			dev_err(dwmac->dev, "tx clock enable failed\n");
    108			clk_disable_unprepare(dwmac->clk_mem);
    109			return ret;
    110		}
    111	} else {
    112		clk_disable_unprepare(dwmac->clk_tx);
    113		clk_disable_unprepare(dwmac->clk_mem);
    114	}
    115
    116	return ret;
    117}
    118
    119static int imx_dwmac_init(struct platform_device *pdev, void *priv)
    120{
    121	struct plat_stmmacenet_data *plat_dat;
    122	struct imx_priv_data *dwmac = priv;
    123	int ret;
    124
    125	plat_dat = dwmac->plat_dat;
    126
    127	if (dwmac->ops->set_intf_mode) {
    128		ret = dwmac->ops->set_intf_mode(plat_dat);
    129		if (ret)
    130			return ret;
    131	}
    132
    133	return 0;
    134}
    135
    136static void imx_dwmac_exit(struct platform_device *pdev, void *priv)
    137{
    138	/* nothing to do now */
    139}
    140
    141static void imx_dwmac_fix_speed(void *priv, unsigned int speed)
    142{
    143	struct plat_stmmacenet_data *plat_dat;
    144	struct imx_priv_data *dwmac = priv;
    145	unsigned long rate;
    146	int err;
    147
    148	plat_dat = dwmac->plat_dat;
    149
    150	if (dwmac->ops->mac_rgmii_txclk_auto_adj ||
    151	    (plat_dat->interface == PHY_INTERFACE_MODE_RMII) ||
    152	    (plat_dat->interface == PHY_INTERFACE_MODE_MII))
    153		return;
    154
    155	switch (speed) {
    156	case SPEED_1000:
    157		rate = 125000000;
    158		break;
    159	case SPEED_100:
    160		rate = 25000000;
    161		break;
    162	case SPEED_10:
    163		rate = 2500000;
    164		break;
    165	default:
    166		dev_err(dwmac->dev, "invalid speed %u\n", speed);
    167		return;
    168	}
    169
    170	err = clk_set_rate(dwmac->clk_tx, rate);
    171	if (err < 0)
    172		dev_err(dwmac->dev, "failed to set tx rate %lu\n", rate);
    173}
    174
    175static int
    176imx_dwmac_parse_dt(struct imx_priv_data *dwmac, struct device *dev)
    177{
    178	struct device_node *np = dev->of_node;
    179	int err = 0;
    180
    181	if (of_get_property(np, "snps,rmii_refclk_ext", NULL))
    182		dwmac->rmii_refclk_ext = true;
    183
    184	dwmac->clk_tx = devm_clk_get(dev, "tx");
    185	if (IS_ERR(dwmac->clk_tx)) {
    186		dev_err(dev, "failed to get tx clock\n");
    187		return PTR_ERR(dwmac->clk_tx);
    188	}
    189
    190	dwmac->clk_mem = NULL;
    191	if (of_machine_is_compatible("fsl,imx8dxl")) {
    192		dwmac->clk_mem = devm_clk_get(dev, "mem");
    193		if (IS_ERR(dwmac->clk_mem)) {
    194			dev_err(dev, "failed to get mem clock\n");
    195			return PTR_ERR(dwmac->clk_mem);
    196		}
    197	}
    198
    199	if (of_machine_is_compatible("fsl,imx8mp")) {
    200		/* Binding doc describes the property:
    201		   is required by i.MX8MP.
    202		   is optional for i.MX8DXL.
    203		 */
    204		dwmac->intf_regmap = syscon_regmap_lookup_by_phandle(np, "intf_mode");
    205		if (IS_ERR(dwmac->intf_regmap))
    206			return PTR_ERR(dwmac->intf_regmap);
    207
    208		err = of_property_read_u32_index(np, "intf_mode", 1, &dwmac->intf_reg_off);
    209		if (err) {
    210			dev_err(dev, "Can't get intf mode reg offset (%d)\n", err);
    211			return err;
    212		}
    213	}
    214
    215	return err;
    216}
    217
    218static int imx_dwmac_probe(struct platform_device *pdev)
    219{
    220	struct plat_stmmacenet_data *plat_dat;
    221	struct stmmac_resources stmmac_res;
    222	struct imx_priv_data *dwmac;
    223	const struct imx_dwmac_ops *data;
    224	int ret;
    225
    226	ret = stmmac_get_platform_resources(pdev, &stmmac_res);
    227	if (ret)
    228		return ret;
    229
    230	dwmac = devm_kzalloc(&pdev->dev, sizeof(*dwmac), GFP_KERNEL);
    231	if (!dwmac)
    232		return -ENOMEM;
    233
    234	plat_dat = stmmac_probe_config_dt(pdev, stmmac_res.mac);
    235	if (IS_ERR(plat_dat))
    236		return PTR_ERR(plat_dat);
    237
    238	data = of_device_get_match_data(&pdev->dev);
    239	if (!data) {
    240		dev_err(&pdev->dev, "failed to get match data\n");
    241		ret = -EINVAL;
    242		goto err_match_data;
    243	}
    244
    245	dwmac->ops = data;
    246	dwmac->dev = &pdev->dev;
    247
    248	ret = imx_dwmac_parse_dt(dwmac, &pdev->dev);
    249	if (ret) {
    250		dev_err(&pdev->dev, "failed to parse OF data\n");
    251		goto err_parse_dt;
    252	}
    253
    254	plat_dat->addr64 = dwmac->ops->addr_width;
    255	plat_dat->init = imx_dwmac_init;
    256	plat_dat->exit = imx_dwmac_exit;
    257	plat_dat->clks_config = imx_dwmac_clks_config;
    258	plat_dat->fix_mac_speed = imx_dwmac_fix_speed;
    259	plat_dat->bsp_priv = dwmac;
    260	dwmac->plat_dat = plat_dat;
    261
    262	ret = imx_dwmac_clks_config(dwmac, true);
    263	if (ret)
    264		goto err_clks_config;
    265
    266	ret = imx_dwmac_init(pdev, dwmac);
    267	if (ret)
    268		goto err_dwmac_init;
    269
    270	ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
    271	if (ret)
    272		goto err_drv_probe;
    273
    274	return 0;
    275
    276err_drv_probe:
    277	imx_dwmac_exit(pdev, plat_dat->bsp_priv);
    278err_dwmac_init:
    279	imx_dwmac_clks_config(dwmac, false);
    280err_clks_config:
    281err_parse_dt:
    282err_match_data:
    283	stmmac_remove_config_dt(pdev, plat_dat);
    284	return ret;
    285}
    286
    287static struct imx_dwmac_ops imx8mp_dwmac_data = {
    288	.addr_width = 34,
    289	.mac_rgmii_txclk_auto_adj = false,
    290	.set_intf_mode = imx8mp_set_intf_mode,
    291};
    292
    293static struct imx_dwmac_ops imx8dxl_dwmac_data = {
    294	.addr_width = 32,
    295	.mac_rgmii_txclk_auto_adj = true,
    296	.set_intf_mode = imx8dxl_set_intf_mode,
    297};
    298
    299static const struct of_device_id imx_dwmac_match[] = {
    300	{ .compatible = "nxp,imx8mp-dwmac-eqos", .data = &imx8mp_dwmac_data },
    301	{ .compatible = "nxp,imx8dxl-dwmac-eqos", .data = &imx8dxl_dwmac_data },
    302	{ }
    303};
    304MODULE_DEVICE_TABLE(of, imx_dwmac_match);
    305
    306static struct platform_driver imx_dwmac_driver = {
    307	.probe  = imx_dwmac_probe,
    308	.remove = stmmac_pltfr_remove,
    309	.driver = {
    310		.name           = "imx-dwmac",
    311		.pm		= &stmmac_pltfr_pm_ops,
    312		.of_match_table = imx_dwmac_match,
    313	},
    314};
    315module_platform_driver(imx_dwmac_driver);
    316
    317MODULE_AUTHOR("NXP");
    318MODULE_DESCRIPTION("NXP imx8 DWMAC Specific Glue layer");
    319MODULE_LICENSE("GPL v2");