brintos

brintos / linux-shallow public Read only

0
0
Text · 10.4 KiB · 7ea32fb Raw
415 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Driver for the Texas Instruments DP83TC811 PHY4 *5 * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/6 *7 */8 9#include <linux/ethtool.h>10#include <linux/etherdevice.h>11#include <linux/kernel.h>12#include <linux/mii.h>13#include <linux/module.h>14#include <linux/of.h>15#include <linux/phy.h>16#include <linux/netdevice.h>17 18#define DP83TC811_PHY_ID	0x2000a25319#define DP83811_DEVADDR		0x1f20 21#define MII_DP83811_SGMII_CTRL	0x0922#define MII_DP83811_INT_STAT1	0x1223#define MII_DP83811_INT_STAT2	0x1324#define MII_DP83811_INT_STAT3	0x1825#define MII_DP83811_RESET_CTRL	0x1f26 27#define DP83811_HW_RESET	BIT(15)28#define DP83811_SW_RESET	BIT(14)29 30/* INT_STAT1 bits */31#define DP83811_RX_ERR_HF_INT_EN	BIT(0)32#define DP83811_MS_TRAINING_INT_EN	BIT(1)33#define DP83811_ANEG_COMPLETE_INT_EN	BIT(2)34#define DP83811_ESD_EVENT_INT_EN	BIT(3)35#define DP83811_WOL_INT_EN		BIT(4)36#define DP83811_LINK_STAT_INT_EN	BIT(5)37#define DP83811_ENERGY_DET_INT_EN	BIT(6)38#define DP83811_LINK_QUAL_INT_EN	BIT(7)39 40/* INT_STAT2 bits */41#define DP83811_JABBER_DET_INT_EN	BIT(0)42#define DP83811_POLARITY_INT_EN		BIT(1)43#define DP83811_SLEEP_MODE_INT_EN	BIT(2)44#define DP83811_OVERTEMP_INT_EN		BIT(3)45#define DP83811_OVERVOLTAGE_INT_EN	BIT(6)46#define DP83811_UNDERVOLTAGE_INT_EN	BIT(7)47 48/* INT_STAT3 bits */49#define DP83811_LPS_INT_EN	BIT(0)50#define DP83811_NO_FRAME_INT_EN	BIT(3)51#define DP83811_POR_DONE_INT_EN	BIT(4)52 53#define MII_DP83811_RXSOP1	0x04a554#define MII_DP83811_RXSOP2	0x04a655#define MII_DP83811_RXSOP3	0x04a756 57/* WoL Registers */58#define MII_DP83811_WOL_CFG	0x04a059#define MII_DP83811_WOL_STAT	0x04a160#define MII_DP83811_WOL_DA1	0x04a261#define MII_DP83811_WOL_DA2	0x04a362#define MII_DP83811_WOL_DA3	0x04a463 64/* WoL bits */65#define DP83811_WOL_MAGIC_EN	BIT(0)66#define DP83811_WOL_SECURE_ON	BIT(5)67#define DP83811_WOL_EN		BIT(7)68#define DP83811_WOL_INDICATION_SEL BIT(8)69#define DP83811_WOL_CLR_INDICATION BIT(11)70 71/* SGMII CTRL bits */72#define DP83811_TDR_AUTO		BIT(8)73#define DP83811_SGMII_EN		BIT(12)74#define DP83811_SGMII_AUTO_NEG_EN	BIT(13)75#define DP83811_SGMII_TX_ERR_DIS	BIT(14)76#define DP83811_SGMII_SOFT_RESET	BIT(15)77 78static int dp83811_ack_interrupt(struct phy_device *phydev)79{80	int err;81 82	err = phy_read(phydev, MII_DP83811_INT_STAT1);83	if (err < 0)84		return err;85 86	err = phy_read(phydev, MII_DP83811_INT_STAT2);87	if (err < 0)88		return err;89 90	err = phy_read(phydev, MII_DP83811_INT_STAT3);91	if (err < 0)92		return err;93 94	return 0;95}96 97static int dp83811_set_wol(struct phy_device *phydev,98			   struct ethtool_wolinfo *wol)99{100	struct net_device *ndev = phydev->attached_dev;101	const u8 *mac;102	u16 value;103 104	if (wol->wolopts & (WAKE_MAGIC | WAKE_MAGICSECURE)) {105		mac = (const u8 *)ndev->dev_addr;106 107		if (!is_valid_ether_addr(mac))108			return -EINVAL;109 110		/* MAC addresses start with byte 5, but stored in mac[0].111		 * 811 PHYs store bytes 4|5, 2|3, 0|1112		 */113		phy_write_mmd(phydev, DP83811_DEVADDR, MII_DP83811_WOL_DA1,114			      (mac[1] << 8) | mac[0]);115		phy_write_mmd(phydev, DP83811_DEVADDR, MII_DP83811_WOL_DA2,116			      (mac[3] << 8) | mac[2]);117		phy_write_mmd(phydev, DP83811_DEVADDR, MII_DP83811_WOL_DA3,118			      (mac[5] << 8) | mac[4]);119 120		value = phy_read_mmd(phydev, DP83811_DEVADDR,121				     MII_DP83811_WOL_CFG);122		if (wol->wolopts & WAKE_MAGIC)123			value |= DP83811_WOL_MAGIC_EN;124		else125			value &= ~DP83811_WOL_MAGIC_EN;126 127		if (wol->wolopts & WAKE_MAGICSECURE) {128			phy_write_mmd(phydev, DP83811_DEVADDR,129				      MII_DP83811_RXSOP1,130				      (wol->sopass[1] << 8) | wol->sopass[0]);131			phy_write_mmd(phydev, DP83811_DEVADDR,132				      MII_DP83811_RXSOP2,133				      (wol->sopass[3] << 8) | wol->sopass[2]);134			phy_write_mmd(phydev, DP83811_DEVADDR,135				      MII_DP83811_RXSOP3,136				      (wol->sopass[5] << 8) | wol->sopass[4]);137			value |= DP83811_WOL_SECURE_ON;138		} else {139			value &= ~DP83811_WOL_SECURE_ON;140		}141 142		/* Clear any pending WoL interrupt */143		phy_read(phydev, MII_DP83811_INT_STAT1);144 145		value |= DP83811_WOL_EN | DP83811_WOL_INDICATION_SEL |146			 DP83811_WOL_CLR_INDICATION;147 148		return phy_write_mmd(phydev, DP83811_DEVADDR,149				     MII_DP83811_WOL_CFG, value);150	} else {151		return phy_clear_bits_mmd(phydev, DP83811_DEVADDR,152					  MII_DP83811_WOL_CFG, DP83811_WOL_EN);153	}154 155}156 157static void dp83811_get_wol(struct phy_device *phydev,158			    struct ethtool_wolinfo *wol)159{160	u16 sopass_val;161	int value;162 163	wol->supported = (WAKE_MAGIC | WAKE_MAGICSECURE);164	wol->wolopts = 0;165 166	value = phy_read_mmd(phydev, DP83811_DEVADDR, MII_DP83811_WOL_CFG);167 168	if (value & DP83811_WOL_MAGIC_EN)169		wol->wolopts |= WAKE_MAGIC;170 171	if (value & DP83811_WOL_SECURE_ON) {172		sopass_val = phy_read_mmd(phydev, DP83811_DEVADDR,173					  MII_DP83811_RXSOP1);174		wol->sopass[0] = (sopass_val & 0xff);175		wol->sopass[1] = (sopass_val >> 8);176 177		sopass_val = phy_read_mmd(phydev, DP83811_DEVADDR,178					  MII_DP83811_RXSOP2);179		wol->sopass[2] = (sopass_val & 0xff);180		wol->sopass[3] = (sopass_val >> 8);181 182		sopass_val = phy_read_mmd(phydev, DP83811_DEVADDR,183					  MII_DP83811_RXSOP3);184		wol->sopass[4] = (sopass_val & 0xff);185		wol->sopass[5] = (sopass_val >> 8);186 187		wol->wolopts |= WAKE_MAGICSECURE;188	}189 190	/* WoL is not enabled so set wolopts to 0 */191	if (!(value & DP83811_WOL_EN))192		wol->wolopts = 0;193}194 195static int dp83811_config_intr(struct phy_device *phydev)196{197	int misr_status, err;198 199	if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {200		err = dp83811_ack_interrupt(phydev);201		if (err)202			return err;203 204		misr_status = phy_read(phydev, MII_DP83811_INT_STAT1);205		if (misr_status < 0)206			return misr_status;207 208		misr_status |= (DP83811_RX_ERR_HF_INT_EN |209				DP83811_MS_TRAINING_INT_EN |210				DP83811_ANEG_COMPLETE_INT_EN |211				DP83811_ESD_EVENT_INT_EN |212				DP83811_WOL_INT_EN |213				DP83811_LINK_STAT_INT_EN |214				DP83811_ENERGY_DET_INT_EN |215				DP83811_LINK_QUAL_INT_EN);216 217		err = phy_write(phydev, MII_DP83811_INT_STAT1, misr_status);218		if (err < 0)219			return err;220 221		misr_status = phy_read(phydev, MII_DP83811_INT_STAT2);222		if (misr_status < 0)223			return misr_status;224 225		misr_status |= (DP83811_JABBER_DET_INT_EN |226				DP83811_POLARITY_INT_EN |227				DP83811_SLEEP_MODE_INT_EN |228				DP83811_OVERTEMP_INT_EN |229				DP83811_OVERVOLTAGE_INT_EN |230				DP83811_UNDERVOLTAGE_INT_EN);231 232		err = phy_write(phydev, MII_DP83811_INT_STAT2, misr_status);233		if (err < 0)234			return err;235 236		misr_status = phy_read(phydev, MII_DP83811_INT_STAT3);237		if (misr_status < 0)238			return misr_status;239 240		misr_status |= (DP83811_LPS_INT_EN |241				DP83811_NO_FRAME_INT_EN |242				DP83811_POR_DONE_INT_EN);243 244		err = phy_write(phydev, MII_DP83811_INT_STAT3, misr_status);245 246	} else {247		err = phy_write(phydev, MII_DP83811_INT_STAT1, 0);248		if (err < 0)249			return err;250 251		err = phy_write(phydev, MII_DP83811_INT_STAT2, 0);252		if (err < 0)253			return err;254 255		err = phy_write(phydev, MII_DP83811_INT_STAT3, 0);256		if (err < 0)257			return err;258 259		err = dp83811_ack_interrupt(phydev);260	}261 262	return err;263}264 265static irqreturn_t dp83811_handle_interrupt(struct phy_device *phydev)266{267	bool trigger_machine = false;268	int irq_status;269 270	/* The INT_STAT registers 1, 2 and 3 are holding the interrupt status271	 * in the upper half (15:8), while the lower half (7:0) is used for272	 * controlling the interrupt enable state of those individual interrupt273	 * sources. To determine the possible interrupt sources, just read the274	 * INT_STAT* register and use it directly to know which interrupts have275	 * been enabled previously or not.276	 */277	irq_status = phy_read(phydev, MII_DP83811_INT_STAT1);278	if (irq_status < 0) {279		phy_error(phydev);280		return IRQ_NONE;281	}282	if (irq_status & ((irq_status & GENMASK(7, 0)) << 8))283		trigger_machine = true;284 285	irq_status = phy_read(phydev, MII_DP83811_INT_STAT2);286	if (irq_status < 0) {287		phy_error(phydev);288		return IRQ_NONE;289	}290	if (irq_status & ((irq_status & GENMASK(7, 0)) << 8))291		trigger_machine = true;292 293	irq_status = phy_read(phydev, MII_DP83811_INT_STAT3);294	if (irq_status < 0) {295		phy_error(phydev);296		return IRQ_NONE;297	}298	if (irq_status & ((irq_status & GENMASK(7, 0)) << 8))299		trigger_machine = true;300 301	if (!trigger_machine)302		return IRQ_NONE;303 304	phy_trigger_machine(phydev);305 306	return IRQ_HANDLED;307}308 309static int dp83811_config_aneg(struct phy_device *phydev)310{311	int value, err;312 313	if (phydev->interface == PHY_INTERFACE_MODE_SGMII) {314		value = phy_read(phydev, MII_DP83811_SGMII_CTRL);315		if (phydev->autoneg == AUTONEG_ENABLE) {316			err = phy_write(phydev, MII_DP83811_SGMII_CTRL,317					(DP83811_SGMII_AUTO_NEG_EN | value));318			if (err < 0)319				return err;320		} else {321			err = phy_write(phydev, MII_DP83811_SGMII_CTRL,322					(~DP83811_SGMII_AUTO_NEG_EN & value));323			if (err < 0)324				return err;325		}326	}327 328	return genphy_config_aneg(phydev);329}330 331static int dp83811_config_init(struct phy_device *phydev)332{333	int value, err;334 335	value = phy_read(phydev, MII_DP83811_SGMII_CTRL);336	if (phydev->interface == PHY_INTERFACE_MODE_SGMII) {337		err = phy_write(phydev, MII_DP83811_SGMII_CTRL,338					(DP83811_SGMII_EN | value));339	} else {340		err = phy_write(phydev, MII_DP83811_SGMII_CTRL,341				(~DP83811_SGMII_EN & value));342	}343 344	if (err < 0)345 346		return err;347 348	value = DP83811_WOL_MAGIC_EN | DP83811_WOL_SECURE_ON | DP83811_WOL_EN;349 350	return phy_clear_bits_mmd(phydev, DP83811_DEVADDR, MII_DP83811_WOL_CFG,351				  value);352}353 354static int dp83811_phy_reset(struct phy_device *phydev)355{356	int err;357 358	err = phy_write(phydev, MII_DP83811_RESET_CTRL, DP83811_HW_RESET);359	if (err < 0)360		return err;361 362	return 0;363}364 365static int dp83811_suspend(struct phy_device *phydev)366{367	int value;368 369	value = phy_read_mmd(phydev, DP83811_DEVADDR, MII_DP83811_WOL_CFG);370 371	if (!(value & DP83811_WOL_EN))372		genphy_suspend(phydev);373 374	return 0;375}376 377static int dp83811_resume(struct phy_device *phydev)378{379	genphy_resume(phydev);380 381	phy_set_bits_mmd(phydev, DP83811_DEVADDR, MII_DP83811_WOL_CFG,382			 DP83811_WOL_CLR_INDICATION);383 384	return 0;385}386 387static struct phy_driver dp83811_driver[] = {388	{389		.phy_id = DP83TC811_PHY_ID,390		.phy_id_mask = 0xfffffff0,391		.name = "TI DP83TC811",392		/* PHY_BASIC_FEATURES */393		.config_init = dp83811_config_init,394		.config_aneg = dp83811_config_aneg,395		.soft_reset = dp83811_phy_reset,396		.get_wol = dp83811_get_wol,397		.set_wol = dp83811_set_wol,398		.config_intr = dp83811_config_intr,399		.handle_interrupt = dp83811_handle_interrupt,400		.suspend = dp83811_suspend,401		.resume = dp83811_resume,402	 },403};404module_phy_driver(dp83811_driver);405 406static struct mdio_device_id __maybe_unused dp83811_tbl[] = {407	{ DP83TC811_PHY_ID, 0xfffffff0 },408	{ },409};410MODULE_DEVICE_TABLE(mdio, dp83811_tbl);411 412MODULE_DESCRIPTION("Texas Instruments DP83TC811 PHY driver");413MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com");414MODULE_LICENSE("GPL");415