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

adin1100.c (7351B)


      1// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
      2/*
      3 *  Driver for Analog Devices Industrial Ethernet T1L PHYs
      4 *
      5 * Copyright 2020 Analog Devices Inc.
      6 */
      7#include <linux/kernel.h>
      8#include <linux/bitfield.h>
      9#include <linux/delay.h>
     10#include <linux/errno.h>
     11#include <linux/init.h>
     12#include <linux/module.h>
     13#include <linux/mii.h>
     14#include <linux/phy.h>
     15#include <linux/property.h>
     16
     17#define PHY_ID_ADIN1100				0x0283bc81
     18
     19#define ADIN_FORCED_MODE			0x8000
     20#define   ADIN_FORCED_MODE_EN			BIT(0)
     21
     22#define ADIN_CRSM_SFT_RST			0x8810
     23#define   ADIN_CRSM_SFT_RST_EN			BIT(0)
     24
     25#define ADIN_CRSM_SFT_PD_CNTRL			0x8812
     26#define   ADIN_CRSM_SFT_PD_CNTRL_EN		BIT(0)
     27
     28#define ADIN_AN_PHY_INST_STATUS			0x8030
     29#define   ADIN_IS_CFG_SLV			BIT(2)
     30#define   ADIN_IS_CFG_MST			BIT(3)
     31
     32#define ADIN_CRSM_STAT				0x8818
     33#define   ADIN_CRSM_SFT_PD_RDY			BIT(1)
     34#define   ADIN_CRSM_SYS_RDY			BIT(0)
     35
     36#define ADIN_MSE_VAL				0x830B
     37
     38#define ADIN_SQI_MAX	7
     39
     40struct adin_mse_sqi_range {
     41	u16 start;
     42	u16 end;
     43};
     44
     45static const struct adin_mse_sqi_range adin_mse_sqi_map[] = {
     46	{ 0x0A74, 0xFFFF },
     47	{ 0x084E, 0x0A74 },
     48	{ 0x0698, 0x084E },
     49	{ 0x053D, 0x0698 },
     50	{ 0x0429, 0x053D },
     51	{ 0x034E, 0x0429 },
     52	{ 0x02A0, 0x034E },
     53	{ 0x0000, 0x02A0 },
     54};
     55
     56/**
     57 * struct adin_priv - ADIN PHY driver private data
     58 * @tx_level_2v4_able:		set if the PHY supports 2.4V TX levels (10BASE-T1L)
     59 * @tx_level_2v4:		set if the PHY requests 2.4V TX levels (10BASE-T1L)
     60 * @tx_level_prop_present:	set if the TX level is specified in DT
     61 */
     62struct adin_priv {
     63	unsigned int		tx_level_2v4_able:1;
     64	unsigned int		tx_level_2v4:1;
     65	unsigned int		tx_level_prop_present:1;
     66};
     67
     68static int adin_read_status(struct phy_device *phydev)
     69{
     70	int ret;
     71
     72	ret = genphy_c45_read_status(phydev);
     73	if (ret)
     74		return ret;
     75
     76	ret = phy_read_mmd(phydev, MDIO_MMD_AN, ADIN_AN_PHY_INST_STATUS);
     77	if (ret < 0)
     78		return ret;
     79
     80	if (ret & ADIN_IS_CFG_SLV)
     81		phydev->master_slave_state = MASTER_SLAVE_STATE_SLAVE;
     82
     83	if (ret & ADIN_IS_CFG_MST)
     84		phydev->master_slave_state = MASTER_SLAVE_STATE_MASTER;
     85
     86	return 0;
     87}
     88
     89static int adin_config_aneg(struct phy_device *phydev)
     90{
     91	struct adin_priv *priv = phydev->priv;
     92	int ret;
     93
     94	if (phydev->autoneg == AUTONEG_DISABLE) {
     95		ret = genphy_c45_pma_setup_forced(phydev);
     96		if (ret < 0)
     97			return ret;
     98
     99		if (priv->tx_level_prop_present && priv->tx_level_2v4)
    100			ret = phy_set_bits_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_B10L_PMA_CTRL,
    101					       MDIO_PMA_10T1L_CTRL_2V4_EN);
    102		else
    103			ret = phy_clear_bits_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_B10L_PMA_CTRL,
    104						 MDIO_PMA_10T1L_CTRL_2V4_EN);
    105		if (ret < 0)
    106			return ret;
    107
    108		/* Force PHY to use above configurations */
    109		return phy_set_bits_mmd(phydev, MDIO_MMD_AN, ADIN_FORCED_MODE, ADIN_FORCED_MODE_EN);
    110	}
    111
    112	ret = phy_clear_bits_mmd(phydev, MDIO_MMD_AN, ADIN_FORCED_MODE, ADIN_FORCED_MODE_EN);
    113	if (ret < 0)
    114		return ret;
    115
    116	/* Request increased transmit level from LP. */
    117	if (priv->tx_level_prop_present && priv->tx_level_2v4) {
    118		ret = phy_set_bits_mmd(phydev, MDIO_MMD_AN, MDIO_AN_T1_ADV_H,
    119				       MDIO_AN_T1_ADV_H_10L_TX_HI |
    120				       MDIO_AN_T1_ADV_H_10L_TX_HI_REQ);
    121		if (ret < 0)
    122			return ret;
    123	}
    124
    125	/* Disable 2.4 Vpp transmit level. */
    126	if ((priv->tx_level_prop_present && !priv->tx_level_2v4) || !priv->tx_level_2v4_able) {
    127		ret = phy_clear_bits_mmd(phydev, MDIO_MMD_AN, MDIO_AN_T1_ADV_H,
    128					 MDIO_AN_T1_ADV_H_10L_TX_HI |
    129					 MDIO_AN_T1_ADV_H_10L_TX_HI_REQ);
    130		if (ret < 0)
    131			return ret;
    132	}
    133
    134	return genphy_c45_config_aneg(phydev);
    135}
    136
    137static int adin_set_powerdown_mode(struct phy_device *phydev, bool en)
    138{
    139	int ret;
    140	int val;
    141
    142	val = en ? ADIN_CRSM_SFT_PD_CNTRL_EN : 0;
    143	ret = phy_write_mmd(phydev, MDIO_MMD_VEND1,
    144			    ADIN_CRSM_SFT_PD_CNTRL, val);
    145	if (ret < 0)
    146		return ret;
    147
    148	return phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1, ADIN_CRSM_STAT, ret,
    149					 (ret & ADIN_CRSM_SFT_PD_RDY) == val,
    150					 1000, 30000, true);
    151}
    152
    153static int adin_suspend(struct phy_device *phydev)
    154{
    155	return adin_set_powerdown_mode(phydev, true);
    156}
    157
    158static int adin_resume(struct phy_device *phydev)
    159{
    160	return adin_set_powerdown_mode(phydev, false);
    161}
    162
    163static int adin_set_loopback(struct phy_device *phydev, bool enable)
    164{
    165	if (enable)
    166		return phy_set_bits_mmd(phydev, MDIO_MMD_PCS, MDIO_PCS_10T1L_CTRL,
    167					BMCR_LOOPBACK);
    168
    169	/* PCS loopback (according to 10BASE-T1L spec) */
    170	return phy_clear_bits_mmd(phydev, MDIO_MMD_PCS, MDIO_PCS_10T1L_CTRL,
    171				 BMCR_LOOPBACK);
    172}
    173
    174static int adin_soft_reset(struct phy_device *phydev)
    175{
    176	int ret;
    177
    178	ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, ADIN_CRSM_SFT_RST, ADIN_CRSM_SFT_RST_EN);
    179	if (ret < 0)
    180		return ret;
    181
    182	return phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1, ADIN_CRSM_STAT, ret,
    183					 (ret & ADIN_CRSM_SYS_RDY),
    184					 10000, 30000, true);
    185}
    186
    187static int adin_get_features(struct phy_device *phydev)
    188{
    189	struct adin_priv *priv = phydev->priv;
    190	struct device *dev = &phydev->mdio.dev;
    191	int ret;
    192	u8 val;
    193
    194	ret = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_10T1L_STAT);
    195	if (ret < 0)
    196		return ret;
    197
    198	/* This depends on the voltage level from the power source */
    199	priv->tx_level_2v4_able = !!(ret & MDIO_PMA_10T1L_STAT_2V4_ABLE);
    200
    201	phydev_dbg(phydev, "PHY supports 2.4V TX level: %s\n",
    202		   priv->tx_level_2v4_able ? "yes" : "no");
    203
    204	priv->tx_level_prop_present = device_property_present(dev, "phy-10base-t1l-2.4vpp");
    205	if (priv->tx_level_prop_present) {
    206		ret = device_property_read_u8(dev, "phy-10base-t1l-2.4vpp", &val);
    207		if (ret < 0)
    208			return ret;
    209
    210		priv->tx_level_2v4 = val;
    211		if (!priv->tx_level_2v4 && priv->tx_level_2v4_able)
    212			phydev_info(phydev,
    213				    "PHY supports 2.4V TX level, but disabled via config\n");
    214	}
    215
    216	linkmode_set_bit_array(phy_basic_ports_array, ARRAY_SIZE(phy_basic_ports_array),
    217			       phydev->supported);
    218
    219	return genphy_c45_pma_read_abilities(phydev);
    220}
    221
    222static int adin_get_sqi(struct phy_device *phydev)
    223{
    224	u16 mse_val;
    225	int sqi;
    226	int ret;
    227
    228	ret = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_STAT1);
    229	if (ret < 0)
    230		return ret;
    231	else if (!(ret & MDIO_STAT1_LSTATUS))
    232		return 0;
    233
    234	ret = phy_read_mmd(phydev, MDIO_STAT1, ADIN_MSE_VAL);
    235	if (ret < 0)
    236		return ret;
    237
    238	mse_val = 0xFFFF & ret;
    239	for (sqi = 0; sqi < ARRAY_SIZE(adin_mse_sqi_map); sqi++) {
    240		if (mse_val >= adin_mse_sqi_map[sqi].start && mse_val <= adin_mse_sqi_map[sqi].end)
    241			return sqi;
    242	}
    243
    244	return -EINVAL;
    245}
    246
    247static int adin_get_sqi_max(struct phy_device *phydev)
    248{
    249	return ADIN_SQI_MAX;
    250}
    251
    252static int adin_probe(struct phy_device *phydev)
    253{
    254	struct device *dev = &phydev->mdio.dev;
    255	struct adin_priv *priv;
    256
    257	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
    258	if (!priv)
    259		return -ENOMEM;
    260
    261	phydev->priv = priv;
    262
    263	return 0;
    264}
    265
    266static struct phy_driver adin_driver[] = {
    267	{
    268		PHY_ID_MATCH_MODEL(PHY_ID_ADIN1100),
    269		.name			= "ADIN1100",
    270		.get_features		= adin_get_features,
    271		.soft_reset		= adin_soft_reset,
    272		.probe			= adin_probe,
    273		.config_aneg		= adin_config_aneg,
    274		.read_status		= adin_read_status,
    275		.set_loopback		= adin_set_loopback,
    276		.suspend		= adin_suspend,
    277		.resume			= adin_resume,
    278		.get_sqi		= adin_get_sqi,
    279		.get_sqi_max		= adin_get_sqi_max,
    280	},
    281};
    282
    283module_phy_driver(adin_driver);
    284
    285static struct mdio_device_id __maybe_unused adin_tbl[] = {
    286	{ PHY_ID_MATCH_MODEL(PHY_ID_ADIN1100) },
    287	{ }
    288};
    289
    290MODULE_DEVICE_TABLE(mdio, adin_tbl);
    291MODULE_DESCRIPTION("Analog Devices Industrial Ethernet T1L PHY driver");
    292MODULE_LICENSE("Dual BSD/GPL");