brintos

brintos / linux-shallow public Read only

0
0
Text · 4.8 KiB · 145703f Raw
228 lines · c
1// SPDX-License-Identifier: GPL-2.02/* Driver for 100BASE-TX PHY embedded into NXP SJA1110 switch3 *4 * Copyright 2022-2023 NXP5 */6 7#include <linux/kernel.h>8#include <linux/mii.h>9#include <linux/module.h>10#include <linux/phy.h>11 12#define PHY_ID_CBTX_SJA1110			0x001bb02013 14/* Registers */15#define  CBTX_MODE_CTRL_STAT			0x1116#define  CBTX_PDOWN_CTRL			0x1817#define  CBTX_RX_ERR_COUNTER			0x1a18#define  CBTX_IRQ_STAT				0x1d19#define  CBTX_IRQ_ENABLE			0x1e20 21/* Fields */22#define CBTX_MODE_CTRL_STAT_AUTO_MDIX_EN	BIT(7)23#define CBTX_MODE_CTRL_STAT_MDIX_MODE		BIT(6)24 25#define CBTX_PDOWN_CTL_TRUE_PDOWN		BIT(0)26 27#define CBTX_IRQ_ENERGYON			BIT(7)28#define CBTX_IRQ_AN_COMPLETE			BIT(6)29#define CBTX_IRQ_REM_FAULT			BIT(5)30#define CBTX_IRQ_LINK_DOWN			BIT(4)31#define CBTX_IRQ_AN_LP_ACK			BIT(3)32#define CBTX_IRQ_PARALLEL_DETECT_FAULT		BIT(2)33#define CBTX_IRQ_AN_PAGE_RECV			BIT(1)34 35static int cbtx_soft_reset(struct phy_device *phydev)36{37	int ret;38 39	/* Can't soft reset unless we remove PHY from true power down mode */40	ret = phy_clear_bits(phydev, CBTX_PDOWN_CTRL,41			     CBTX_PDOWN_CTL_TRUE_PDOWN);42	if (ret)43		return ret;44 45	return genphy_soft_reset(phydev);46}47 48static int cbtx_config_init(struct phy_device *phydev)49{50	/* Wait for cbtx_config_aneg() to kick in and apply this */51	phydev->mdix_ctrl = ETH_TP_MDI_AUTO;52 53	return 0;54}55 56static int cbtx_mdix_status(struct phy_device *phydev)57{58	int ret;59 60	ret = phy_read(phydev, CBTX_MODE_CTRL_STAT);61	if (ret < 0)62		return ret;63 64	if (ret & CBTX_MODE_CTRL_STAT_MDIX_MODE)65		phydev->mdix = ETH_TP_MDI_X;66	else67		phydev->mdix = ETH_TP_MDI;68 69	return 0;70}71 72static int cbtx_read_status(struct phy_device *phydev)73{74	int ret;75 76	ret = cbtx_mdix_status(phydev);77	if (ret)78		return ret;79 80	return genphy_read_status(phydev);81}82 83static int cbtx_mdix_config(struct phy_device *phydev)84{85	int ret;86 87	switch (phydev->mdix_ctrl) {88	case ETH_TP_MDI_AUTO:89		return phy_set_bits(phydev, CBTX_MODE_CTRL_STAT,90				    CBTX_MODE_CTRL_STAT_AUTO_MDIX_EN);91	case ETH_TP_MDI:92		ret = phy_clear_bits(phydev, CBTX_MODE_CTRL_STAT,93				     CBTX_MODE_CTRL_STAT_AUTO_MDIX_EN);94		if (ret)95			return ret;96 97		return phy_clear_bits(phydev, CBTX_MODE_CTRL_STAT,98				      CBTX_MODE_CTRL_STAT_MDIX_MODE);99	case ETH_TP_MDI_X:100		ret = phy_clear_bits(phydev, CBTX_MODE_CTRL_STAT,101				     CBTX_MODE_CTRL_STAT_AUTO_MDIX_EN);102		if (ret)103			return ret;104 105		return phy_set_bits(phydev, CBTX_MODE_CTRL_STAT,106				    CBTX_MODE_CTRL_STAT_MDIX_MODE);107	}108 109	return 0;110}111 112static int cbtx_config_aneg(struct phy_device *phydev)113{114	int ret;115 116	ret = cbtx_mdix_config(phydev);117	if (ret)118		return ret;119 120	return genphy_config_aneg(phydev);121}122 123static int cbtx_ack_interrupts(struct phy_device *phydev)124{125	return phy_read(phydev, CBTX_IRQ_STAT);126}127 128static int cbtx_config_intr(struct phy_device *phydev)129{130	int ret;131 132	if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {133		ret = cbtx_ack_interrupts(phydev);134		if (ret < 0)135			return ret;136 137		ret = phy_write(phydev, CBTX_IRQ_ENABLE, CBTX_IRQ_LINK_DOWN |138				CBTX_IRQ_AN_COMPLETE | CBTX_IRQ_ENERGYON);139		if (ret)140			return ret;141	} else {142		ret = phy_write(phydev, CBTX_IRQ_ENABLE, 0);143		if (ret)144			return ret;145 146		ret = cbtx_ack_interrupts(phydev);147		if (ret < 0)148			return ret;149	}150 151	return 0;152}153 154static irqreturn_t cbtx_handle_interrupt(struct phy_device *phydev)155{156	int irq_stat, irq_enabled;157 158	irq_stat = cbtx_ack_interrupts(phydev);159	if (irq_stat < 0) {160		phy_error(phydev);161		return IRQ_NONE;162	}163 164	irq_enabled = phy_read(phydev, CBTX_IRQ_ENABLE);165	if (irq_enabled < 0) {166		phy_error(phydev);167		return IRQ_NONE;168	}169 170	if (!(irq_enabled & irq_stat))171		return IRQ_NONE;172 173	phy_trigger_machine(phydev);174 175	return IRQ_HANDLED;176}177 178static int cbtx_get_sset_count(struct phy_device *phydev)179{180	return 1;181}182 183static void cbtx_get_strings(struct phy_device *phydev, u8 *data)184{185	strncpy(data, "100btx_rx_err", ETH_GSTRING_LEN);186}187 188static void cbtx_get_stats(struct phy_device *phydev,189			   struct ethtool_stats *stats, u64 *data)190{191	int ret;192 193	ret = phy_read(phydev, CBTX_RX_ERR_COUNTER);194	data[0] = (ret < 0) ? U64_MAX : ret;195}196 197static struct phy_driver cbtx_driver[] = {198	{199		PHY_ID_MATCH_MODEL(PHY_ID_CBTX_SJA1110),200		.name			= "NXP CBTX (SJA1110)",201		/* PHY_BASIC_FEATURES */202		.soft_reset		= cbtx_soft_reset,203		.config_init		= cbtx_config_init,204		.suspend		= genphy_suspend,205		.resume			= genphy_resume,206		.config_intr		= cbtx_config_intr,207		.handle_interrupt	= cbtx_handle_interrupt,208		.read_status		= cbtx_read_status,209		.config_aneg		= cbtx_config_aneg,210		.get_sset_count		= cbtx_get_sset_count,211		.get_strings		= cbtx_get_strings,212		.get_stats		= cbtx_get_stats,213	},214};215 216module_phy_driver(cbtx_driver);217 218static struct mdio_device_id __maybe_unused cbtx_tbl[] = {219	{ PHY_ID_MATCH_MODEL(PHY_ID_CBTX_SJA1110) },220	{ },221};222 223MODULE_DEVICE_TABLE(mdio, cbtx_tbl);224 225MODULE_AUTHOR("Vladimir Oltean <vladimir.oltean@nxp.com>");226MODULE_DESCRIPTION("NXP CBTX PHY driver");227MODULE_LICENSE("GPL");228