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-meson8b.c (15199B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Amlogic Meson8b, Meson8m2 and GXBB DWMAC glue layer
      4 *
      5 * Copyright (C) 2016 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
      6 */
      7
      8#include <linux/bitfield.h>
      9#include <linux/clk.h>
     10#include <linux/clk-provider.h>
     11#include <linux/device.h>
     12#include <linux/ethtool.h>
     13#include <linux/io.h>
     14#include <linux/ioport.h>
     15#include <linux/module.h>
     16#include <linux/of_device.h>
     17#include <linux/of_net.h>
     18#include <linux/mfd/syscon.h>
     19#include <linux/platform_device.h>
     20#include <linux/stmmac.h>
     21
     22#include "stmmac_platform.h"
     23
     24#define PRG_ETH0			0x0
     25
     26#define PRG_ETH0_RGMII_MODE		BIT(0)
     27
     28#define PRG_ETH0_EXT_PHY_MODE_MASK	GENMASK(2, 0)
     29#define PRG_ETH0_EXT_RGMII_MODE		1
     30#define PRG_ETH0_EXT_RMII_MODE		4
     31
     32/* mux to choose between fclk_div2 (bit unset) and mpll2 (bit set) */
     33#define PRG_ETH0_CLK_M250_SEL_MASK	GENMASK(4, 4)
     34
     35/* TX clock delay in ns = "8ns / 4 * tx_dly_val" (where 8ns are exactly one
     36 * cycle of the 125MHz RGMII TX clock):
     37 * 0ns = 0x0, 2ns = 0x1, 4ns = 0x2, 6ns = 0x3
     38 */
     39#define PRG_ETH0_TXDLY_MASK		GENMASK(6, 5)
     40
     41/* divider for the result of m250_sel */
     42#define PRG_ETH0_CLK_M250_DIV_SHIFT	7
     43#define PRG_ETH0_CLK_M250_DIV_WIDTH	3
     44
     45#define PRG_ETH0_RGMII_TX_CLK_EN	10
     46
     47#define PRG_ETH0_INVERTED_RMII_CLK	BIT(11)
     48#define PRG_ETH0_TX_AND_PHY_REF_CLK	BIT(12)
     49
     50/* Bypass (= 0, the signal from the GPIO input directly connects to the
     51 * internal sampling) or enable (= 1) the internal logic for RXEN and RXD[3:0]
     52 * timing tuning.
     53 */
     54#define PRG_ETH0_ADJ_ENABLE		BIT(13)
     55/* Controls whether the RXEN and RXD[3:0] signals should be aligned with the
     56 * input RX rising/falling edge and sent to the Ethernet internals. This sets
     57 * the automatically delay and skew automatically (internally).
     58 */
     59#define PRG_ETH0_ADJ_SETUP		BIT(14)
     60/* An internal counter based on the "timing-adjustment" clock. The counter is
     61 * cleared on both, the falling and rising edge of the RX_CLK. This selects the
     62 * delay (= the counter value) when to start sampling RXEN and RXD[3:0].
     63 */
     64#define PRG_ETH0_ADJ_DELAY		GENMASK(19, 15)
     65/* Adjusts the skew between each bit of RXEN and RXD[3:0]. If a signal has a
     66 * large input delay, the bit for that signal (RXEN = bit 0, RXD[3] = bit 1,
     67 * ...) can be configured to be 1 to compensate for a delay of about 1ns.
     68 */
     69#define PRG_ETH0_ADJ_SKEW		GENMASK(24, 20)
     70
     71#define PRG_ETH1			0x4
     72
     73/* Defined for adding a delay to the input RX_CLK for better timing.
     74 * Each step is 200ps. These bits are used with external RGMII PHYs
     75 * because RGMII RX only has the small window. cfg_rxclk_dly can
     76 * adjust the window between RX_CLK and RX_DATA and improve the stability
     77 * of "rx data valid".
     78 */
     79#define PRG_ETH1_CFG_RXCLK_DLY		GENMASK(19, 16)
     80
     81struct meson8b_dwmac;
     82
     83struct meson8b_dwmac_data {
     84	int (*set_phy_mode)(struct meson8b_dwmac *dwmac);
     85	bool has_prg_eth1_rgmii_rx_delay;
     86};
     87
     88struct meson8b_dwmac {
     89	struct device			*dev;
     90	void __iomem			*regs;
     91
     92	const struct meson8b_dwmac_data	*data;
     93	phy_interface_t			phy_mode;
     94	struct clk			*rgmii_tx_clk;
     95	u32				tx_delay_ns;
     96	u32				rx_delay_ps;
     97	struct clk			*timing_adj_clk;
     98};
     99
    100struct meson8b_dwmac_clk_configs {
    101	struct clk_mux		m250_mux;
    102	struct clk_divider	m250_div;
    103	struct clk_fixed_factor	fixed_div2;
    104	struct clk_gate		rgmii_tx_en;
    105};
    106
    107static void meson8b_dwmac_mask_bits(struct meson8b_dwmac *dwmac, u32 reg,
    108				    u32 mask, u32 value)
    109{
    110	u32 data;
    111
    112	data = readl(dwmac->regs + reg);
    113	data &= ~mask;
    114	data |= (value & mask);
    115
    116	writel(data, dwmac->regs + reg);
    117}
    118
    119static struct clk *meson8b_dwmac_register_clk(struct meson8b_dwmac *dwmac,
    120					      const char *name_suffix,
    121					      const struct clk_parent_data *parents,
    122					      int num_parents,
    123					      const struct clk_ops *ops,
    124					      struct clk_hw *hw)
    125{
    126	struct clk_init_data init = { };
    127	char clk_name[32];
    128
    129	snprintf(clk_name, sizeof(clk_name), "%s#%s", dev_name(dwmac->dev),
    130		 name_suffix);
    131
    132	init.name = clk_name;
    133	init.ops = ops;
    134	init.flags = CLK_SET_RATE_PARENT;
    135	init.parent_data = parents;
    136	init.num_parents = num_parents;
    137
    138	hw->init = &init;
    139
    140	return devm_clk_register(dwmac->dev, hw);
    141}
    142
    143static int meson8b_init_rgmii_tx_clk(struct meson8b_dwmac *dwmac)
    144{
    145	struct clk *clk;
    146	struct device *dev = dwmac->dev;
    147	static const struct clk_parent_data mux_parents[] = {
    148		{ .fw_name = "clkin0", },
    149		{ .index = -1, },
    150	};
    151	static const struct clk_div_table div_table[] = {
    152		{ .div = 2, .val = 2, },
    153		{ .div = 3, .val = 3, },
    154		{ .div = 4, .val = 4, },
    155		{ .div = 5, .val = 5, },
    156		{ .div = 6, .val = 6, },
    157		{ .div = 7, .val = 7, },
    158		{ /* end of array */ }
    159	};
    160	struct meson8b_dwmac_clk_configs *clk_configs;
    161	struct clk_parent_data parent_data = { };
    162
    163	clk_configs = devm_kzalloc(dev, sizeof(*clk_configs), GFP_KERNEL);
    164	if (!clk_configs)
    165		return -ENOMEM;
    166
    167	clk_configs->m250_mux.reg = dwmac->regs + PRG_ETH0;
    168	clk_configs->m250_mux.shift = __ffs(PRG_ETH0_CLK_M250_SEL_MASK);
    169	clk_configs->m250_mux.mask = PRG_ETH0_CLK_M250_SEL_MASK >>
    170				     clk_configs->m250_mux.shift;
    171	clk = meson8b_dwmac_register_clk(dwmac, "m250_sel", mux_parents,
    172					 ARRAY_SIZE(mux_parents), &clk_mux_ops,
    173					 &clk_configs->m250_mux.hw);
    174	if (WARN_ON(IS_ERR(clk)))
    175		return PTR_ERR(clk);
    176
    177	parent_data.hw = &clk_configs->m250_mux.hw;
    178	clk_configs->m250_div.reg = dwmac->regs + PRG_ETH0;
    179	clk_configs->m250_div.shift = PRG_ETH0_CLK_M250_DIV_SHIFT;
    180	clk_configs->m250_div.width = PRG_ETH0_CLK_M250_DIV_WIDTH;
    181	clk_configs->m250_div.table = div_table;
    182	clk_configs->m250_div.flags = CLK_DIVIDER_ALLOW_ZERO |
    183				      CLK_DIVIDER_ROUND_CLOSEST;
    184	clk = meson8b_dwmac_register_clk(dwmac, "m250_div", &parent_data, 1,
    185					 &clk_divider_ops,
    186					 &clk_configs->m250_div.hw);
    187	if (WARN_ON(IS_ERR(clk)))
    188		return PTR_ERR(clk);
    189
    190	parent_data.hw = &clk_configs->m250_div.hw;
    191	clk_configs->fixed_div2.mult = 1;
    192	clk_configs->fixed_div2.div = 2;
    193	clk = meson8b_dwmac_register_clk(dwmac, "fixed_div2", &parent_data, 1,
    194					 &clk_fixed_factor_ops,
    195					 &clk_configs->fixed_div2.hw);
    196	if (WARN_ON(IS_ERR(clk)))
    197		return PTR_ERR(clk);
    198
    199	parent_data.hw = &clk_configs->fixed_div2.hw;
    200	clk_configs->rgmii_tx_en.reg = dwmac->regs + PRG_ETH0;
    201	clk_configs->rgmii_tx_en.bit_idx = PRG_ETH0_RGMII_TX_CLK_EN;
    202	clk = meson8b_dwmac_register_clk(dwmac, "rgmii_tx_en", &parent_data, 1,
    203					 &clk_gate_ops,
    204					 &clk_configs->rgmii_tx_en.hw);
    205	if (WARN_ON(IS_ERR(clk)))
    206		return PTR_ERR(clk);
    207
    208	dwmac->rgmii_tx_clk = clk;
    209
    210	return 0;
    211}
    212
    213static int meson8b_set_phy_mode(struct meson8b_dwmac *dwmac)
    214{
    215	switch (dwmac->phy_mode) {
    216	case PHY_INTERFACE_MODE_RGMII:
    217	case PHY_INTERFACE_MODE_RGMII_RXID:
    218	case PHY_INTERFACE_MODE_RGMII_ID:
    219	case PHY_INTERFACE_MODE_RGMII_TXID:
    220		/* enable RGMII mode */
    221		meson8b_dwmac_mask_bits(dwmac, PRG_ETH0,
    222					PRG_ETH0_RGMII_MODE,
    223					PRG_ETH0_RGMII_MODE);
    224		break;
    225	case PHY_INTERFACE_MODE_RMII:
    226		/* disable RGMII mode -> enables RMII mode */
    227		meson8b_dwmac_mask_bits(dwmac, PRG_ETH0,
    228					PRG_ETH0_RGMII_MODE, 0);
    229		break;
    230	default:
    231		dev_err(dwmac->dev, "fail to set phy-mode %s\n",
    232			phy_modes(dwmac->phy_mode));
    233		return -EINVAL;
    234	}
    235
    236	return 0;
    237}
    238
    239static int meson_axg_set_phy_mode(struct meson8b_dwmac *dwmac)
    240{
    241	switch (dwmac->phy_mode) {
    242	case PHY_INTERFACE_MODE_RGMII:
    243	case PHY_INTERFACE_MODE_RGMII_RXID:
    244	case PHY_INTERFACE_MODE_RGMII_ID:
    245	case PHY_INTERFACE_MODE_RGMII_TXID:
    246		/* enable RGMII mode */
    247		meson8b_dwmac_mask_bits(dwmac, PRG_ETH0,
    248					PRG_ETH0_EXT_PHY_MODE_MASK,
    249					PRG_ETH0_EXT_RGMII_MODE);
    250		break;
    251	case PHY_INTERFACE_MODE_RMII:
    252		/* disable RGMII mode -> enables RMII mode */
    253		meson8b_dwmac_mask_bits(dwmac, PRG_ETH0,
    254					PRG_ETH0_EXT_PHY_MODE_MASK,
    255					PRG_ETH0_EXT_RMII_MODE);
    256		break;
    257	default:
    258		dev_err(dwmac->dev, "fail to set phy-mode %s\n",
    259			phy_modes(dwmac->phy_mode));
    260		return -EINVAL;
    261	}
    262
    263	return 0;
    264}
    265
    266static int meson8b_devm_clk_prepare_enable(struct meson8b_dwmac *dwmac,
    267					   struct clk *clk)
    268{
    269	int ret;
    270
    271	ret = clk_prepare_enable(clk);
    272	if (ret)
    273		return ret;
    274
    275	devm_add_action_or_reset(dwmac->dev,
    276				 (void(*)(void *))clk_disable_unprepare,
    277				 dwmac->rgmii_tx_clk);
    278
    279	return 0;
    280}
    281
    282static int meson8b_init_rgmii_delays(struct meson8b_dwmac *dwmac)
    283{
    284	u32 tx_dly_config, rx_adj_config, cfg_rxclk_dly, delay_config;
    285	int ret;
    286
    287	rx_adj_config = 0;
    288	cfg_rxclk_dly = 0;
    289	tx_dly_config = FIELD_PREP(PRG_ETH0_TXDLY_MASK,
    290				   dwmac->tx_delay_ns >> 1);
    291
    292	if (dwmac->data->has_prg_eth1_rgmii_rx_delay)
    293		cfg_rxclk_dly = FIELD_PREP(PRG_ETH1_CFG_RXCLK_DLY,
    294					   dwmac->rx_delay_ps / 200);
    295	else if (dwmac->rx_delay_ps == 2000)
    296		rx_adj_config = PRG_ETH0_ADJ_ENABLE | PRG_ETH0_ADJ_SETUP;
    297
    298	switch (dwmac->phy_mode) {
    299	case PHY_INTERFACE_MODE_RGMII:
    300		delay_config = tx_dly_config | rx_adj_config;
    301		break;
    302	case PHY_INTERFACE_MODE_RGMII_RXID:
    303		delay_config = tx_dly_config;
    304		cfg_rxclk_dly = 0;
    305		break;
    306	case PHY_INTERFACE_MODE_RGMII_TXID:
    307		delay_config = rx_adj_config;
    308		break;
    309	case PHY_INTERFACE_MODE_RGMII_ID:
    310	case PHY_INTERFACE_MODE_RMII:
    311		delay_config = 0;
    312		cfg_rxclk_dly = 0;
    313		break;
    314	default:
    315		dev_err(dwmac->dev, "unsupported phy-mode %s\n",
    316			phy_modes(dwmac->phy_mode));
    317		return -EINVAL;
    318	}
    319
    320	if (delay_config & PRG_ETH0_ADJ_ENABLE) {
    321		if (!dwmac->timing_adj_clk) {
    322			dev_err(dwmac->dev,
    323				"The timing-adjustment clock is mandatory for the RX delay re-timing\n");
    324			return -EINVAL;
    325		}
    326
    327		/* The timing adjustment logic is driven by a separate clock */
    328		ret = meson8b_devm_clk_prepare_enable(dwmac,
    329						      dwmac->timing_adj_clk);
    330		if (ret) {
    331			dev_err(dwmac->dev,
    332				"Failed to enable the timing-adjustment clock\n");
    333			return ret;
    334		}
    335	}
    336
    337	meson8b_dwmac_mask_bits(dwmac, PRG_ETH0, PRG_ETH0_TXDLY_MASK |
    338				PRG_ETH0_ADJ_ENABLE | PRG_ETH0_ADJ_SETUP |
    339				PRG_ETH0_ADJ_DELAY | PRG_ETH0_ADJ_SKEW,
    340				delay_config);
    341
    342	meson8b_dwmac_mask_bits(dwmac, PRG_ETH1, PRG_ETH1_CFG_RXCLK_DLY,
    343				cfg_rxclk_dly);
    344
    345	return 0;
    346}
    347
    348static int meson8b_init_prg_eth(struct meson8b_dwmac *dwmac)
    349{
    350	int ret;
    351
    352	if (phy_interface_mode_is_rgmii(dwmac->phy_mode)) {
    353		/* only relevant for RMII mode -> disable in RGMII mode */
    354		meson8b_dwmac_mask_bits(dwmac, PRG_ETH0,
    355					PRG_ETH0_INVERTED_RMII_CLK, 0);
    356
    357		/* Configure the 125MHz RGMII TX clock, the IP block changes
    358		 * the output automatically (= without us having to configure
    359		 * a register) based on the line-speed (125MHz for Gbit speeds,
    360		 * 25MHz for 100Mbit/s and 2.5MHz for 10Mbit/s).
    361		 */
    362		ret = clk_set_rate(dwmac->rgmii_tx_clk, 125 * 1000 * 1000);
    363		if (ret) {
    364			dev_err(dwmac->dev,
    365				"failed to set RGMII TX clock\n");
    366			return ret;
    367		}
    368
    369		ret = meson8b_devm_clk_prepare_enable(dwmac,
    370						      dwmac->rgmii_tx_clk);
    371		if (ret) {
    372			dev_err(dwmac->dev,
    373				"failed to enable the RGMII TX clock\n");
    374			return ret;
    375		}
    376	} else {
    377		/* invert internal clk_rmii_i to generate 25/2.5 tx_rx_clk */
    378		meson8b_dwmac_mask_bits(dwmac, PRG_ETH0,
    379					PRG_ETH0_INVERTED_RMII_CLK,
    380					PRG_ETH0_INVERTED_RMII_CLK);
    381	}
    382
    383	/* enable TX_CLK and PHY_REF_CLK generator */
    384	meson8b_dwmac_mask_bits(dwmac, PRG_ETH0, PRG_ETH0_TX_AND_PHY_REF_CLK,
    385				PRG_ETH0_TX_AND_PHY_REF_CLK);
    386
    387	return 0;
    388}
    389
    390static int meson8b_dwmac_probe(struct platform_device *pdev)
    391{
    392	struct plat_stmmacenet_data *plat_dat;
    393	struct stmmac_resources stmmac_res;
    394	struct meson8b_dwmac *dwmac;
    395	int ret;
    396
    397	ret = stmmac_get_platform_resources(pdev, &stmmac_res);
    398	if (ret)
    399		return ret;
    400
    401	plat_dat = stmmac_probe_config_dt(pdev, stmmac_res.mac);
    402	if (IS_ERR(plat_dat))
    403		return PTR_ERR(plat_dat);
    404
    405	dwmac = devm_kzalloc(&pdev->dev, sizeof(*dwmac), GFP_KERNEL);
    406	if (!dwmac) {
    407		ret = -ENOMEM;
    408		goto err_remove_config_dt;
    409	}
    410
    411	dwmac->data = (const struct meson8b_dwmac_data *)
    412		of_device_get_match_data(&pdev->dev);
    413	if (!dwmac->data) {
    414		ret = -EINVAL;
    415		goto err_remove_config_dt;
    416	}
    417	dwmac->regs = devm_platform_ioremap_resource(pdev, 1);
    418	if (IS_ERR(dwmac->regs)) {
    419		ret = PTR_ERR(dwmac->regs);
    420		goto err_remove_config_dt;
    421	}
    422
    423	dwmac->dev = &pdev->dev;
    424	ret = of_get_phy_mode(pdev->dev.of_node, &dwmac->phy_mode);
    425	if (ret) {
    426		dev_err(&pdev->dev, "missing phy-mode property\n");
    427		goto err_remove_config_dt;
    428	}
    429
    430	/* use 2ns as fallback since this value was previously hardcoded */
    431	if (of_property_read_u32(pdev->dev.of_node, "amlogic,tx-delay-ns",
    432				 &dwmac->tx_delay_ns))
    433		dwmac->tx_delay_ns = 2;
    434
    435	/* RX delay defaults to 0ps since this is what many boards use */
    436	if (of_property_read_u32(pdev->dev.of_node, "rx-internal-delay-ps",
    437				 &dwmac->rx_delay_ps)) {
    438		if (!of_property_read_u32(pdev->dev.of_node,
    439					  "amlogic,rx-delay-ns",
    440					  &dwmac->rx_delay_ps))
    441			/* convert ns to ps */
    442			dwmac->rx_delay_ps *= 1000;
    443	}
    444
    445	if (dwmac->data->has_prg_eth1_rgmii_rx_delay) {
    446		if (dwmac->rx_delay_ps > 3000 || dwmac->rx_delay_ps % 200) {
    447			dev_err(dwmac->dev,
    448				"The RGMII RX delay range is 0..3000ps in 200ps steps");
    449			ret = -EINVAL;
    450			goto err_remove_config_dt;
    451		}
    452	} else {
    453		if (dwmac->rx_delay_ps != 0 && dwmac->rx_delay_ps != 2000) {
    454			dev_err(dwmac->dev,
    455				"The only allowed RGMII RX delays values are: 0ps, 2000ps");
    456			ret = -EINVAL;
    457			goto err_remove_config_dt;
    458		}
    459	}
    460
    461	dwmac->timing_adj_clk = devm_clk_get_optional(dwmac->dev,
    462						      "timing-adjustment");
    463	if (IS_ERR(dwmac->timing_adj_clk)) {
    464		ret = PTR_ERR(dwmac->timing_adj_clk);
    465		goto err_remove_config_dt;
    466	}
    467
    468	ret = meson8b_init_rgmii_delays(dwmac);
    469	if (ret)
    470		goto err_remove_config_dt;
    471
    472	ret = meson8b_init_rgmii_tx_clk(dwmac);
    473	if (ret)
    474		goto err_remove_config_dt;
    475
    476	ret = dwmac->data->set_phy_mode(dwmac);
    477	if (ret)
    478		goto err_remove_config_dt;
    479
    480	ret = meson8b_init_prg_eth(dwmac);
    481	if (ret)
    482		goto err_remove_config_dt;
    483
    484	plat_dat->bsp_priv = dwmac;
    485
    486	ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
    487	if (ret)
    488		goto err_remove_config_dt;
    489
    490	return 0;
    491
    492err_remove_config_dt:
    493	stmmac_remove_config_dt(pdev, plat_dat);
    494
    495	return ret;
    496}
    497
    498static const struct meson8b_dwmac_data meson8b_dwmac_data = {
    499	.set_phy_mode = meson8b_set_phy_mode,
    500	.has_prg_eth1_rgmii_rx_delay = false,
    501};
    502
    503static const struct meson8b_dwmac_data meson_axg_dwmac_data = {
    504	.set_phy_mode = meson_axg_set_phy_mode,
    505	.has_prg_eth1_rgmii_rx_delay = false,
    506};
    507
    508static const struct meson8b_dwmac_data meson_g12a_dwmac_data = {
    509	.set_phy_mode = meson_axg_set_phy_mode,
    510	.has_prg_eth1_rgmii_rx_delay = true,
    511};
    512
    513static const struct of_device_id meson8b_dwmac_match[] = {
    514	{
    515		.compatible = "amlogic,meson8b-dwmac",
    516		.data = &meson8b_dwmac_data,
    517	},
    518	{
    519		.compatible = "amlogic,meson8m2-dwmac",
    520		.data = &meson8b_dwmac_data,
    521	},
    522	{
    523		.compatible = "amlogic,meson-gxbb-dwmac",
    524		.data = &meson8b_dwmac_data,
    525	},
    526	{
    527		.compatible = "amlogic,meson-axg-dwmac",
    528		.data = &meson_axg_dwmac_data,
    529	},
    530	{
    531		.compatible = "amlogic,meson-g12a-dwmac",
    532		.data = &meson_g12a_dwmac_data,
    533	},
    534	{ }
    535};
    536MODULE_DEVICE_TABLE(of, meson8b_dwmac_match);
    537
    538static struct platform_driver meson8b_dwmac_driver = {
    539	.probe  = meson8b_dwmac_probe,
    540	.remove = stmmac_pltfr_remove,
    541	.driver = {
    542		.name           = "meson8b-dwmac",
    543		.pm		= &stmmac_pltfr_pm_ops,
    544		.of_match_table = meson8b_dwmac_match,
    545	},
    546};
    547module_platform_driver(meson8b_dwmac_driver);
    548
    549MODULE_AUTHOR("Martin Blumenstingl <martin.blumenstingl@googlemail.com>");
    550MODULE_DESCRIPTION("Amlogic Meson8b, Meson8m2 and GXBB DWMAC glue layer");
    551MODULE_LICENSE("GPL v2");