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