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

smsc.c (12586B)


      1// SPDX-License-Identifier: GPL-2.0+
      2/*
      3 * drivers/net/phy/smsc.c
      4 *
      5 * Driver for SMSC PHYs
      6 *
      7 * Author: Herbert Valerio Riedel
      8 *
      9 * Copyright (c) 2006 Herbert Valerio Riedel <hvr@gnu.org>
     10 *
     11 * Support added for SMSC LAN8187 and LAN8700 by steve.glendinning@shawell.net
     12 *
     13 */
     14
     15#include <linux/clk.h>
     16#include <linux/kernel.h>
     17#include <linux/module.h>
     18#include <linux/mii.h>
     19#include <linux/ethtool.h>
     20#include <linux/of.h>
     21#include <linux/phy.h>
     22#include <linux/netdevice.h>
     23#include <linux/smscphy.h>
     24
     25/* Vendor-specific PHY Definitions */
     26/* EDPD NLP / crossover time configuration */
     27#define PHY_EDPD_CONFIG			16
     28#define PHY_EDPD_CONFIG_EXT_CROSSOVER_	0x0001
     29
     30/* Control/Status Indication Register */
     31#define SPECIAL_CTRL_STS		27
     32#define SPECIAL_CTRL_STS_OVRRD_AMDIX_	0x8000
     33#define SPECIAL_CTRL_STS_AMDIX_ENABLE_	0x4000
     34#define SPECIAL_CTRL_STS_AMDIX_STATE_	0x2000
     35
     36struct smsc_hw_stat {
     37	const char *string;
     38	u8 reg;
     39	u8 bits;
     40};
     41
     42static struct smsc_hw_stat smsc_hw_stats[] = {
     43	{ "phy_symbol_errors", 26, 16},
     44};
     45
     46struct smsc_phy_priv {
     47	u16 intmask;
     48	bool energy_enable;
     49	struct clk *refclk;
     50};
     51
     52static int smsc_phy_ack_interrupt(struct phy_device *phydev)
     53{
     54	int rc = phy_read(phydev, MII_LAN83C185_ISF);
     55
     56	return rc < 0 ? rc : 0;
     57}
     58
     59static int smsc_phy_config_intr(struct phy_device *phydev)
     60{
     61	struct smsc_phy_priv *priv = phydev->priv;
     62	int rc;
     63
     64	if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
     65		rc = smsc_phy_ack_interrupt(phydev);
     66		if (rc)
     67			return rc;
     68
     69		priv->intmask = MII_LAN83C185_ISF_INT4 | MII_LAN83C185_ISF_INT6;
     70		if (priv->energy_enable)
     71			priv->intmask |= MII_LAN83C185_ISF_INT7;
     72
     73		rc = phy_write(phydev, MII_LAN83C185_IM, priv->intmask);
     74	} else {
     75		priv->intmask = 0;
     76
     77		rc = phy_write(phydev, MII_LAN83C185_IM, 0);
     78		if (rc)
     79			return rc;
     80
     81		rc = smsc_phy_ack_interrupt(phydev);
     82	}
     83
     84	return rc < 0 ? rc : 0;
     85}
     86
     87static irqreturn_t smsc_phy_handle_interrupt(struct phy_device *phydev)
     88{
     89	struct smsc_phy_priv *priv = phydev->priv;
     90	int irq_status;
     91
     92	irq_status = phy_read(phydev, MII_LAN83C185_ISF);
     93	if (irq_status < 0) {
     94		if (irq_status != -ENODEV)
     95			phy_error(phydev);
     96
     97		return IRQ_NONE;
     98	}
     99
    100	if (!(irq_status & priv->intmask))
    101		return IRQ_NONE;
    102
    103	phy_trigger_machine(phydev);
    104
    105	return IRQ_HANDLED;
    106}
    107
    108static int smsc_phy_config_init(struct phy_device *phydev)
    109{
    110	struct smsc_phy_priv *priv = phydev->priv;
    111	int rc;
    112
    113	if (!priv->energy_enable || phydev->irq != PHY_POLL)
    114		return 0;
    115
    116	rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS);
    117
    118	if (rc < 0)
    119		return rc;
    120
    121	/* Enable energy detect mode for this SMSC Transceivers */
    122	rc = phy_write(phydev, MII_LAN83C185_CTRL_STATUS,
    123		       rc | MII_LAN83C185_EDPWRDOWN);
    124	if (rc < 0)
    125		return rc;
    126
    127	return smsc_phy_ack_interrupt(phydev);
    128}
    129
    130static int smsc_phy_reset(struct phy_device *phydev)
    131{
    132	int rc = phy_read(phydev, MII_LAN83C185_SPECIAL_MODES);
    133	if (rc < 0)
    134		return rc;
    135
    136	/* If the SMSC PHY is in power down mode, then set it
    137	 * in all capable mode before using it.
    138	 */
    139	if ((rc & MII_LAN83C185_MODE_MASK) == MII_LAN83C185_MODE_POWERDOWN) {
    140		/* set "all capable" mode */
    141		rc |= MII_LAN83C185_MODE_ALL;
    142		phy_write(phydev, MII_LAN83C185_SPECIAL_MODES, rc);
    143	}
    144
    145	/* reset the phy */
    146	return genphy_soft_reset(phydev);
    147}
    148
    149static int lan911x_config_init(struct phy_device *phydev)
    150{
    151	return smsc_phy_ack_interrupt(phydev);
    152}
    153
    154static int lan87xx_config_aneg(struct phy_device *phydev)
    155{
    156	int rc;
    157	int val;
    158
    159	switch (phydev->mdix_ctrl) {
    160	case ETH_TP_MDI:
    161		val = SPECIAL_CTRL_STS_OVRRD_AMDIX_;
    162		break;
    163	case ETH_TP_MDI_X:
    164		val = SPECIAL_CTRL_STS_OVRRD_AMDIX_ |
    165			SPECIAL_CTRL_STS_AMDIX_STATE_;
    166		break;
    167	case ETH_TP_MDI_AUTO:
    168		val = SPECIAL_CTRL_STS_AMDIX_ENABLE_;
    169		break;
    170	default:
    171		return genphy_config_aneg(phydev);
    172	}
    173
    174	rc = phy_read(phydev, SPECIAL_CTRL_STS);
    175	if (rc < 0)
    176		return rc;
    177
    178	rc &= ~(SPECIAL_CTRL_STS_OVRRD_AMDIX_ |
    179		SPECIAL_CTRL_STS_AMDIX_ENABLE_ |
    180		SPECIAL_CTRL_STS_AMDIX_STATE_);
    181	rc |= val;
    182	phy_write(phydev, SPECIAL_CTRL_STS, rc);
    183
    184	phydev->mdix = phydev->mdix_ctrl;
    185	return genphy_config_aneg(phydev);
    186}
    187
    188static int lan95xx_config_aneg_ext(struct phy_device *phydev)
    189{
    190	int rc;
    191
    192	if (phydev->phy_id != 0x0007c0f0) /* not (LAN9500A or LAN9505A) */
    193		return lan87xx_config_aneg(phydev);
    194
    195	/* Extend Manual AutoMDIX timer */
    196	rc = phy_read(phydev, PHY_EDPD_CONFIG);
    197	if (rc < 0)
    198		return rc;
    199
    200	rc |= PHY_EDPD_CONFIG_EXT_CROSSOVER_;
    201	phy_write(phydev, PHY_EDPD_CONFIG, rc);
    202	return lan87xx_config_aneg(phydev);
    203}
    204
    205/*
    206 * The LAN87xx suffers from rare absence of the ENERGYON-bit when Ethernet cable
    207 * plugs in while LAN87xx is in Energy Detect Power-Down mode. This leads to
    208 * unstable detection of plugging in Ethernet cable.
    209 * This workaround disables Energy Detect Power-Down mode and waiting for
    210 * response on link pulses to detect presence of plugged Ethernet cable.
    211 * The Energy Detect Power-Down mode is enabled again in the end of procedure to
    212 * save approximately 220 mW of power if cable is unplugged.
    213 * The workaround is only applicable to poll mode. Energy Detect Power-Down may
    214 * not be used in interrupt mode lest link change detection becomes unreliable.
    215 */
    216static int lan87xx_read_status(struct phy_device *phydev)
    217{
    218	struct smsc_phy_priv *priv = phydev->priv;
    219
    220	int err = genphy_read_status(phydev);
    221
    222	if (!phydev->link && priv->energy_enable && phydev->irq == PHY_POLL) {
    223		/* Disable EDPD to wake up PHY */
    224		int rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS);
    225		if (rc < 0)
    226			return rc;
    227
    228		rc = phy_write(phydev, MII_LAN83C185_CTRL_STATUS,
    229			       rc & ~MII_LAN83C185_EDPWRDOWN);
    230		if (rc < 0)
    231			return rc;
    232
    233		/* Wait max 640 ms to detect energy and the timeout is not
    234		 * an actual error.
    235		 */
    236		read_poll_timeout(phy_read, rc,
    237				  rc & MII_LAN83C185_ENERGYON || rc < 0,
    238				  10000, 640000, true, phydev,
    239				  MII_LAN83C185_CTRL_STATUS);
    240		if (rc < 0)
    241			return rc;
    242
    243		/* Re-enable EDPD */
    244		rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS);
    245		if (rc < 0)
    246			return rc;
    247
    248		rc = phy_write(phydev, MII_LAN83C185_CTRL_STATUS,
    249			       rc | MII_LAN83C185_EDPWRDOWN);
    250		if (rc < 0)
    251			return rc;
    252	}
    253
    254	return err;
    255}
    256
    257static int smsc_get_sset_count(struct phy_device *phydev)
    258{
    259	return ARRAY_SIZE(smsc_hw_stats);
    260}
    261
    262static void smsc_get_strings(struct phy_device *phydev, u8 *data)
    263{
    264	int i;
    265
    266	for (i = 0; i < ARRAY_SIZE(smsc_hw_stats); i++) {
    267		strncpy(data + i * ETH_GSTRING_LEN,
    268		       smsc_hw_stats[i].string, ETH_GSTRING_LEN);
    269	}
    270}
    271
    272static u64 smsc_get_stat(struct phy_device *phydev, int i)
    273{
    274	struct smsc_hw_stat stat = smsc_hw_stats[i];
    275	int val;
    276	u64 ret;
    277
    278	val = phy_read(phydev, stat.reg);
    279	if (val < 0)
    280		ret = U64_MAX;
    281	else
    282		ret = val;
    283
    284	return ret;
    285}
    286
    287static void smsc_get_stats(struct phy_device *phydev,
    288			   struct ethtool_stats *stats, u64 *data)
    289{
    290	int i;
    291
    292	for (i = 0; i < ARRAY_SIZE(smsc_hw_stats); i++)
    293		data[i] = smsc_get_stat(phydev, i);
    294}
    295
    296static void smsc_phy_remove(struct phy_device *phydev)
    297{
    298	struct smsc_phy_priv *priv = phydev->priv;
    299
    300	clk_disable_unprepare(priv->refclk);
    301	clk_put(priv->refclk);
    302}
    303
    304static int smsc_phy_probe(struct phy_device *phydev)
    305{
    306	struct device *dev = &phydev->mdio.dev;
    307	struct device_node *of_node = dev->of_node;
    308	struct smsc_phy_priv *priv;
    309	int ret;
    310
    311	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
    312	if (!priv)
    313		return -ENOMEM;
    314
    315	priv->energy_enable = true;
    316
    317	if (of_property_read_bool(of_node, "smsc,disable-energy-detect"))
    318		priv->energy_enable = false;
    319
    320	phydev->priv = priv;
    321
    322	/* Make clk optional to keep DTB backward compatibility. */
    323	priv->refclk = clk_get_optional(dev, NULL);
    324	if (IS_ERR(priv->refclk))
    325		return dev_err_probe(dev, PTR_ERR(priv->refclk),
    326				     "Failed to request clock\n");
    327
    328	ret = clk_prepare_enable(priv->refclk);
    329	if (ret)
    330		return ret;
    331
    332	ret = clk_set_rate(priv->refclk, 50 * 1000 * 1000);
    333	if (ret) {
    334		clk_disable_unprepare(priv->refclk);
    335		return ret;
    336	}
    337
    338	return 0;
    339}
    340
    341static struct phy_driver smsc_phy_driver[] = {
    342{
    343	.phy_id		= 0x0007c0a0, /* OUI=0x00800f, Model#=0x0a */
    344	.phy_id_mask	= 0xfffffff0,
    345	.name		= "SMSC LAN83C185",
    346
    347	/* PHY_BASIC_FEATURES */
    348
    349	.probe		= smsc_phy_probe,
    350
    351	/* basic functions */
    352	.config_init	= smsc_phy_config_init,
    353	.soft_reset	= smsc_phy_reset,
    354
    355	/* IRQ related */
    356	.config_intr	= smsc_phy_config_intr,
    357	.handle_interrupt = smsc_phy_handle_interrupt,
    358
    359	.suspend	= genphy_suspend,
    360	.resume		= genphy_resume,
    361}, {
    362	.phy_id		= 0x0007c0b0, /* OUI=0x00800f, Model#=0x0b */
    363	.phy_id_mask	= 0xfffffff0,
    364	.name		= "SMSC LAN8187",
    365
    366	/* PHY_BASIC_FEATURES */
    367
    368	.probe		= smsc_phy_probe,
    369
    370	/* basic functions */
    371	.config_init	= smsc_phy_config_init,
    372	.soft_reset	= smsc_phy_reset,
    373
    374	/* IRQ related */
    375	.config_intr	= smsc_phy_config_intr,
    376	.handle_interrupt = smsc_phy_handle_interrupt,
    377
    378	/* Statistics */
    379	.get_sset_count = smsc_get_sset_count,
    380	.get_strings	= smsc_get_strings,
    381	.get_stats	= smsc_get_stats,
    382
    383	.suspend	= genphy_suspend,
    384	.resume		= genphy_resume,
    385}, {
    386	/* This covers internal PHY (phy_id: 0x0007C0C3) for
    387	 * LAN9500 (PID: 0x9500), LAN9514 (PID: 0xec00), LAN9505 (PID: 0x9505)
    388	 */
    389	.phy_id		= 0x0007c0c0, /* OUI=0x00800f, Model#=0x0c */
    390	.phy_id_mask	= 0xfffffff0,
    391	.name		= "SMSC LAN8700",
    392
    393	/* PHY_BASIC_FEATURES */
    394
    395	.probe		= smsc_phy_probe,
    396
    397	/* basic functions */
    398	.read_status	= lan87xx_read_status,
    399	.config_init	= smsc_phy_config_init,
    400	.soft_reset	= smsc_phy_reset,
    401	.config_aneg	= lan87xx_config_aneg,
    402
    403	/* IRQ related */
    404	.config_intr	= smsc_phy_config_intr,
    405	.handle_interrupt = smsc_phy_handle_interrupt,
    406
    407	/* Statistics */
    408	.get_sset_count = smsc_get_sset_count,
    409	.get_strings	= smsc_get_strings,
    410	.get_stats	= smsc_get_stats,
    411
    412	.suspend	= genphy_suspend,
    413	.resume		= genphy_resume,
    414}, {
    415	.phy_id		= 0x0007c0d0, /* OUI=0x00800f, Model#=0x0d */
    416	.phy_id_mask	= 0xfffffff0,
    417	.name		= "SMSC LAN911x Internal PHY",
    418
    419	/* PHY_BASIC_FEATURES */
    420
    421	.probe		= smsc_phy_probe,
    422
    423	/* basic functions */
    424	.config_init	= lan911x_config_init,
    425
    426	/* IRQ related */
    427	.config_intr	= smsc_phy_config_intr,
    428	.handle_interrupt = smsc_phy_handle_interrupt,
    429
    430	.suspend	= genphy_suspend,
    431	.resume		= genphy_resume,
    432}, {
    433	/* This covers internal PHY (phy_id: 0x0007C0F0) for
    434	 * LAN9500A (PID: 0x9E00), LAN9505A (PID: 0x9E01)
    435	 */
    436	.phy_id		= 0x0007c0f0, /* OUI=0x00800f, Model#=0x0f */
    437	.phy_id_mask	= 0xfffffff0,
    438	.name		= "SMSC LAN8710/LAN8720",
    439
    440	/* PHY_BASIC_FEATURES */
    441
    442	.probe		= smsc_phy_probe,
    443	.remove		= smsc_phy_remove,
    444
    445	/* basic functions */
    446	.read_status	= lan87xx_read_status,
    447	.config_init	= smsc_phy_config_init,
    448	.soft_reset	= smsc_phy_reset,
    449	.config_aneg	= lan95xx_config_aneg_ext,
    450
    451	/* IRQ related */
    452	.config_intr	= smsc_phy_config_intr,
    453	.handle_interrupt = smsc_phy_handle_interrupt,
    454
    455	/* Statistics */
    456	.get_sset_count = smsc_get_sset_count,
    457	.get_strings	= smsc_get_strings,
    458	.get_stats	= smsc_get_stats,
    459
    460	.suspend	= genphy_suspend,
    461	.resume		= genphy_resume,
    462}, {
    463	.phy_id		= 0x0007c110,
    464	.phy_id_mask	= 0xfffffff0,
    465	.name		= "SMSC LAN8740",
    466
    467	/* PHY_BASIC_FEATURES */
    468	.flags		= PHY_RST_AFTER_CLK_EN,
    469
    470	.probe		= smsc_phy_probe,
    471
    472	/* basic functions */
    473	.read_status	= lan87xx_read_status,
    474	.config_init	= smsc_phy_config_init,
    475	.soft_reset	= smsc_phy_reset,
    476
    477	/* IRQ related */
    478	.config_intr	= smsc_phy_config_intr,
    479	.handle_interrupt = smsc_phy_handle_interrupt,
    480
    481	/* Statistics */
    482	.get_sset_count = smsc_get_sset_count,
    483	.get_strings	= smsc_get_strings,
    484	.get_stats	= smsc_get_stats,
    485
    486	.suspend	= genphy_suspend,
    487	.resume		= genphy_resume,
    488}, {
    489	.phy_id		= 0x0007c130,	/* 0x0007c130 and 0x0007c131 */
    490	/* This mask (0xfffffff2) is to differentiate from
    491	 * LAN88xx (phy_id 0x0007c132)
    492	 * and allows future phy_id revisions.
    493	 */
    494	.phy_id_mask	= 0xfffffff2,
    495	.name		= "Microchip LAN8742",
    496
    497	/* PHY_BASIC_FEATURES */
    498	.flags		= PHY_RST_AFTER_CLK_EN,
    499
    500	.probe		= smsc_phy_probe,
    501
    502	/* basic functions */
    503	.read_status	= lan87xx_read_status,
    504	.config_init	= smsc_phy_config_init,
    505	.soft_reset	= smsc_phy_reset,
    506
    507	/* IRQ related */
    508	.config_intr	= smsc_phy_config_intr,
    509	.handle_interrupt = smsc_phy_handle_interrupt,
    510
    511	/* Statistics */
    512	.get_sset_count = smsc_get_sset_count,
    513	.get_strings	= smsc_get_strings,
    514	.get_stats	= smsc_get_stats,
    515
    516	.suspend	= genphy_suspend,
    517	.resume		= genphy_resume,
    518} };
    519
    520module_phy_driver(smsc_phy_driver);
    521
    522MODULE_DESCRIPTION("SMSC PHY driver");
    523MODULE_AUTHOR("Herbert Valerio Riedel");
    524MODULE_LICENSE("GPL");
    525
    526static struct mdio_device_id __maybe_unused smsc_tbl[] = {
    527	{ 0x0007c0a0, 0xfffffff0 },
    528	{ 0x0007c0b0, 0xfffffff0 },
    529	{ 0x0007c0c0, 0xfffffff0 },
    530	{ 0x0007c0d0, 0xfffffff0 },
    531	{ 0x0007c0f0, 0xfffffff0 },
    532	{ 0x0007c110, 0xfffffff0 },
    533	{ 0x0007c130, 0xfffffff2 },
    534	{ }
    535};
    536
    537MODULE_DEVICE_TABLE(mdio, smsc_tbl);