933 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Marvell 88Q2XXX automotive 100BASE-T1/1000BASE-T1 PHY driver4 *5 * Derived from Marvell Q222x API6 *7 * Copyright (C) 2024 Liebherr-Electronics and Drives GmbH8 */9#include <linux/ethtool_netlink.h>10#include <linux/marvell_phy.h>11#include <linux/phy.h>12#include <linux/hwmon.h>13 14#define PHY_ID_88Q2220_REVB0 (MARVELL_PHY_ID_88Q2220 | 0x1)15#define PHY_ID_88Q2220_REVB1 (MARVELL_PHY_ID_88Q2220 | 0x2)16#define PHY_ID_88Q2220_REVB2 (MARVELL_PHY_ID_88Q2220 | 0x3)17 18#define MDIO_MMD_AN_MV_STAT 3276919#define MDIO_MMD_AN_MV_STAT_ANEG 0x010020#define MDIO_MMD_AN_MV_STAT_LOCAL_RX 0x100021#define MDIO_MMD_AN_MV_STAT_REMOTE_RX 0x200022#define MDIO_MMD_AN_MV_STAT_LOCAL_MASTER 0x400023#define MDIO_MMD_AN_MV_STAT_MS_CONF_FAULT 0x800024 25#define MDIO_MMD_AN_MV_STAT2 3279426#define MDIO_MMD_AN_MV_STAT2_AN_RESOLVED 0x080027#define MDIO_MMD_AN_MV_STAT2_100BT1 0x200028#define MDIO_MMD_AN_MV_STAT2_1000BT1 0x400029 30#define MDIO_MMD_PCS_MV_INT_EN 3278431#define MDIO_MMD_PCS_MV_INT_EN_LINK_UP 0x004032#define MDIO_MMD_PCS_MV_INT_EN_LINK_DOWN 0x008033#define MDIO_MMD_PCS_MV_INT_EN_100BT1 0x100034 35#define MDIO_MMD_PCS_MV_GPIO_INT_STAT 3278536#define MDIO_MMD_PCS_MV_GPIO_INT_STAT_LINK_UP 0x004037#define MDIO_MMD_PCS_MV_GPIO_INT_STAT_LINK_DOWN 0x008038#define MDIO_MMD_PCS_MV_GPIO_INT_STAT_100BT1_GEN 0x100039 40#define MDIO_MMD_PCS_MV_GPIO_INT_CTRL 3278741#define MDIO_MMD_PCS_MV_GPIO_INT_CTRL_TRI_DIS 0x080042 43#define MDIO_MMD_PCS_MV_TEMP_SENSOR1 3283344#define MDIO_MMD_PCS_MV_TEMP_SENSOR1_RAW_INT 0x000145#define MDIO_MMD_PCS_MV_TEMP_SENSOR1_INT 0x004046#define MDIO_MMD_PCS_MV_TEMP_SENSOR1_INT_EN 0x008047 48#define MDIO_MMD_PCS_MV_TEMP_SENSOR2 3283449#define MDIO_MMD_PCS_MV_TEMP_SENSOR2_DIS_MASK 0xc00050 51#define MDIO_MMD_PCS_MV_TEMP_SENSOR3 3283552#define MDIO_MMD_PCS_MV_TEMP_SENSOR3_INT_THRESH_MASK 0xff0053#define MDIO_MMD_PCS_MV_TEMP_SENSOR3_MASK 0x00ff54 55#define MDIO_MMD_PCS_MV_100BT1_STAT1 3303256#define MDIO_MMD_PCS_MV_100BT1_STAT1_IDLE_ERROR 0x00ff57#define MDIO_MMD_PCS_MV_100BT1_STAT1_JABBER 0x010058#define MDIO_MMD_PCS_MV_100BT1_STAT1_LINK 0x020059#define MDIO_MMD_PCS_MV_100BT1_STAT1_LOCAL_RX 0x100060#define MDIO_MMD_PCS_MV_100BT1_STAT1_REMOTE_RX 0x200061#define MDIO_MMD_PCS_MV_100BT1_STAT1_LOCAL_MASTER 0x400062 63#define MDIO_MMD_PCS_MV_100BT1_STAT2 3303364#define MDIO_MMD_PCS_MV_100BT1_STAT2_JABBER 0x000165#define MDIO_MMD_PCS_MV_100BT1_STAT2_POL 0x000266#define MDIO_MMD_PCS_MV_100BT1_STAT2_LINK 0x000467#define MDIO_MMD_PCS_MV_100BT1_STAT2_ANGE 0x000868 69#define MDIO_MMD_PCS_MV_100BT1_INT_EN 3304270#define MDIO_MMD_PCS_MV_100BT1_INT_EN_LINKEVENT 0x040071 72#define MDIO_MMD_PCS_MV_COPPER_INT_STAT 3304373#define MDIO_MMD_PCS_MV_COPPER_INT_STAT_LINKEVENT 0x040074 75#define MDIO_MMD_PCS_MV_RX_STAT 3332876 77#define MDIO_MMD_PCS_MV_TDR_RESET 6522678#define MDIO_MMD_PCS_MV_TDR_RESET_TDR_RST 0x100079 80#define MDIO_MMD_PCS_MV_TDR_OFF_SHORT_CABLE 6524181 82#define MDIO_MMD_PCS_MV_TDR_OFF_LONG_CABLE 6524283 84#define MDIO_MMD_PCS_MV_TDR_STATUS 6524585#define MDIO_MMD_PCS_MV_TDR_STATUS_MASK 0x000386#define MDIO_MMD_PCS_MV_TDR_STATUS_OFF 0x000187#define MDIO_MMD_PCS_MV_TDR_STATUS_ON 0x000288#define MDIO_MMD_PCS_MV_TDR_STATUS_DIST_MASK 0xff0089#define MDIO_MMD_PCS_MV_TDR_STATUS_VCT_STAT_MASK 0x00f090#define MDIO_MMD_PCS_MV_TDR_STATUS_VCT_STAT_SHORT 0x003091#define MDIO_MMD_PCS_MV_TDR_STATUS_VCT_STAT_OPEN 0x00e092#define MDIO_MMD_PCS_MV_TDR_STATUS_VCT_STAT_OK 0x007093#define MDIO_MMD_PCS_MV_TDR_STATUS_VCT_STAT_IN_PROGR 0x008094#define MDIO_MMD_PCS_MV_TDR_STATUS_VCT_STAT_NOISE 0x005095 96#define MDIO_MMD_PCS_MV_TDR_OFF_CUTOFF 6524697 98struct mmd_val {99 int devad;100 u32 regnum;101 u16 val;102};103 104static const struct mmd_val mv88q222x_revb0_init_seq0[] = {105 { MDIO_MMD_PCS, 0x8033, 0x6801 },106 { MDIO_MMD_AN, MDIO_AN_T1_CTRL, 0x0 },107 { MDIO_MMD_PMAPMD, MDIO_CTRL1,108 MDIO_CTRL1_LPOWER | MDIO_PMA_CTRL1_SPEED1000 },109 { MDIO_MMD_PCS, 0xfe1b, 0x48 },110 { MDIO_MMD_PCS, 0xffe4, 0x6b6 },111 { MDIO_MMD_PMAPMD, MDIO_CTRL1, 0x0 },112 { MDIO_MMD_PCS, MDIO_CTRL1, 0x0 },113};114 115static const struct mmd_val mv88q222x_revb0_init_seq1[] = {116 { MDIO_MMD_PCS, 0xfe79, 0x0 },117 { MDIO_MMD_PCS, 0xfe07, 0x125a },118 { MDIO_MMD_PCS, 0xfe09, 0x1288 },119 { MDIO_MMD_PCS, 0xfe08, 0x2588 },120 { MDIO_MMD_PCS, 0xfe11, 0x1105 },121 { MDIO_MMD_PCS, 0xfe72, 0x042c },122 { MDIO_MMD_PCS, 0xfbba, 0xcb2 },123 { MDIO_MMD_PCS, 0xfbbb, 0xc4a },124 { MDIO_MMD_AN, 0x8032, 0x2020 },125 { MDIO_MMD_AN, 0x8031, 0xa28 },126 { MDIO_MMD_AN, 0x8031, 0xc28 },127 { MDIO_MMD_PCS, 0xffdb, 0xfc10 },128 { MDIO_MMD_PCS, 0xfe1b, 0x58 },129 { MDIO_MMD_PCS, 0xfe79, 0x4 },130 { MDIO_MMD_PCS, 0xfe5f, 0xe8 },131 { MDIO_MMD_PCS, 0xfe05, 0x755c },132};133 134static const struct mmd_val mv88q222x_revb1_init_seq0[] = {135 { MDIO_MMD_PCS, 0xffe4, 0x0007 },136 { MDIO_MMD_AN, MDIO_AN_T1_CTRL, 0x0 },137 { MDIO_MMD_PCS, 0xffe3, 0x7000 },138 { MDIO_MMD_PMAPMD, MDIO_CTRL1, 0x0840 },139};140 141static const struct mmd_val mv88q222x_revb2_init_seq0[] = {142 { MDIO_MMD_PCS, 0xffe4, 0x0007 },143 { MDIO_MMD_AN, MDIO_AN_T1_CTRL, 0x0 },144 { MDIO_MMD_PMAPMD, MDIO_CTRL1, 0x0840 },145};146 147static const struct mmd_val mv88q222x_revb1_revb2_init_seq1[] = {148 { MDIO_MMD_PCS, 0xfe07, 0x125a },149 { MDIO_MMD_PCS, 0xfe09, 0x1288 },150 { MDIO_MMD_PCS, 0xfe08, 0x2588 },151 { MDIO_MMD_PCS, 0xfe72, 0x042c },152 { MDIO_MMD_PCS, 0xffe4, 0x0071 },153 { MDIO_MMD_PCS, 0xffe4, 0x0001 },154 { MDIO_MMD_PCS, 0xfe1b, 0x0048 },155 { MDIO_MMD_PMAPMD, 0x0000, 0x0000 },156 { MDIO_MMD_PCS, 0x0000, 0x0000 },157 { MDIO_MMD_PCS, 0xffdb, 0xfc10 },158 { MDIO_MMD_PCS, 0xfe1b, 0x58 },159 { MDIO_MMD_PCS, 0xfcad, 0x030c },160 { MDIO_MMD_PCS, 0x8032, 0x6001 },161 { MDIO_MMD_PCS, 0xfdff, 0x05a5 },162 { MDIO_MMD_PCS, 0xfdec, 0xdbaf },163 { MDIO_MMD_PCS, 0xfcab, 0x1054 },164 { MDIO_MMD_PCS, 0xfcac, 0x1483 },165 { MDIO_MMD_PCS, 0x8033, 0xc801 },166 { MDIO_MMD_AN, 0x8032, 0x2020 },167 { MDIO_MMD_AN, 0x8031, 0xa28 },168 { MDIO_MMD_AN, 0x8031, 0xc28 },169 { MDIO_MMD_PCS, 0xfbba, 0x0cb2 },170 { MDIO_MMD_PCS, 0xfbbb, 0x0c4a },171 { MDIO_MMD_PCS, 0xfe5f, 0xe8 },172 { MDIO_MMD_PCS, 0xfe05, 0x755c },173 { MDIO_MMD_PCS, 0xfa20, 0x002a },174 { MDIO_MMD_PCS, 0xfe11, 0x1105 },175};176 177static int mv88q2xxx_soft_reset(struct phy_device *phydev)178{179 int ret;180 int val;181 182 ret = phy_write_mmd(phydev, MDIO_MMD_PCS,183 MDIO_PCS_1000BT1_CTRL, MDIO_PCS_1000BT1_CTRL_RESET);184 if (ret < 0)185 return ret;186 187 return phy_read_mmd_poll_timeout(phydev, MDIO_MMD_PCS,188 MDIO_PCS_1000BT1_CTRL, val,189 !(val & MDIO_PCS_1000BT1_CTRL_RESET),190 50000, 600000, true);191}192 193static int mv88q2xxx_read_link_gbit(struct phy_device *phydev)194{195 int ret;196 bool link = false;197 198 /* Read vendor specific Auto-Negotiation status register to get local199 * and remote receiver status according to software initialization200 * guide. However, when not in polling mode the local and remote201 * receiver status are not evaluated due to the Marvell 88Q2xxx APIs.202 */203 ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_MMD_AN_MV_STAT);204 if (ret < 0) {205 return ret;206 } else if (((ret & MDIO_MMD_AN_MV_STAT_LOCAL_RX) &&207 (ret & MDIO_MMD_AN_MV_STAT_REMOTE_RX)) ||208 !phy_polling_mode(phydev)) {209 /* The link state is latched low so that momentary link210 * drops can be detected. Do not double-read the status211 * in polling mode to detect such short link drops except212 * the link was already down.213 */214 if (!phy_polling_mode(phydev) || !phydev->link) {215 ret = phy_read_mmd(phydev, MDIO_MMD_PCS,216 MDIO_PCS_1000BT1_STAT);217 if (ret < 0)218 return ret;219 else if (ret & MDIO_PCS_1000BT1_STAT_LINK)220 link = true;221 }222 223 if (!link) {224 ret = phy_read_mmd(phydev, MDIO_MMD_PCS,225 MDIO_PCS_1000BT1_STAT);226 if (ret < 0)227 return ret;228 else if (ret & MDIO_PCS_1000BT1_STAT_LINK)229 link = true;230 }231 }232 233 phydev->link = link;234 235 return 0;236}237 238static int mv88q2xxx_read_link_100m(struct phy_device *phydev)239{240 int ret;241 242 /* The link state is latched low so that momentary link243 * drops can be detected. Do not double-read the status244 * in polling mode to detect such short link drops except245 * the link was already down. In case we are not polling,246 * we always read the realtime status.247 */248 if (!phy_polling_mode(phydev)) {249 phydev->link = false;250 ret = phy_read_mmd(phydev, MDIO_MMD_PCS,251 MDIO_MMD_PCS_MV_100BT1_STAT2);252 if (ret < 0)253 return ret;254 255 if (ret & MDIO_MMD_PCS_MV_100BT1_STAT2_LINK)256 phydev->link = true;257 258 return 0;259 } else if (!phydev->link) {260 ret = phy_read_mmd(phydev, MDIO_MMD_PCS,261 MDIO_MMD_PCS_MV_100BT1_STAT1);262 if (ret < 0)263 return ret;264 else if (ret & MDIO_MMD_PCS_MV_100BT1_STAT1_LINK)265 goto out;266 }267 268 ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_MMD_PCS_MV_100BT1_STAT1);269 if (ret < 0)270 return ret;271 272out:273 /* Check if we have link and if the remote and local receiver are ok */274 if ((ret & MDIO_MMD_PCS_MV_100BT1_STAT1_LINK) &&275 (ret & MDIO_MMD_PCS_MV_100BT1_STAT1_LOCAL_RX) &&276 (ret & MDIO_MMD_PCS_MV_100BT1_STAT1_REMOTE_RX))277 phydev->link = true;278 else279 phydev->link = false;280 281 return 0;282}283 284static int mv88q2xxx_read_link(struct phy_device *phydev)285{286 /* The 88Q2XXX PHYs do not have the PMA/PMD status register available,287 * therefore we need to read the link status from the vendor specific288 * registers depending on the speed.289 */290 291 if (phydev->speed == SPEED_1000)292 return mv88q2xxx_read_link_gbit(phydev);293 else if (phydev->speed == SPEED_100)294 return mv88q2xxx_read_link_100m(phydev);295 296 phydev->link = false;297 return 0;298}299 300static int mv88q2xxx_read_master_slave_state(struct phy_device *phydev)301{302 int ret;303 304 phydev->master_slave_state = MASTER_SLAVE_STATE_UNKNOWN;305 ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_MMD_AN_MV_STAT);306 if (ret < 0)307 return ret;308 309 if (ret & MDIO_MMD_AN_MV_STAT_LOCAL_MASTER)310 phydev->master_slave_state = MASTER_SLAVE_STATE_MASTER;311 else312 phydev->master_slave_state = MASTER_SLAVE_STATE_SLAVE;313 314 return 0;315}316 317static int mv88q2xxx_read_aneg_speed(struct phy_device *phydev)318{319 int ret;320 321 phydev->speed = SPEED_UNKNOWN;322 ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_MMD_AN_MV_STAT2);323 if (ret < 0)324 return ret;325 326 if (!(ret & MDIO_MMD_AN_MV_STAT2_AN_RESOLVED))327 return 0;328 329 if (ret & MDIO_MMD_AN_MV_STAT2_100BT1)330 phydev->speed = SPEED_100;331 else if (ret & MDIO_MMD_AN_MV_STAT2_1000BT1)332 phydev->speed = SPEED_1000;333 334 return 0;335}336 337static int mv88q2xxx_read_status(struct phy_device *phydev)338{339 int ret;340 341 if (phydev->autoneg == AUTONEG_ENABLE) {342 /* We have to get the negotiated speed first, otherwise we are343 * not able to read the link.344 */345 ret = mv88q2xxx_read_aneg_speed(phydev);346 if (ret < 0)347 return ret;348 349 ret = mv88q2xxx_read_link(phydev);350 if (ret < 0)351 return ret;352 353 ret = genphy_c45_read_lpa(phydev);354 if (ret < 0)355 return ret;356 357 ret = genphy_c45_baset1_read_status(phydev);358 if (ret < 0)359 return ret;360 361 ret = mv88q2xxx_read_master_slave_state(phydev);362 if (ret < 0)363 return ret;364 365 phy_resolve_aneg_linkmode(phydev);366 367 return 0;368 }369 370 ret = mv88q2xxx_read_link(phydev);371 if (ret < 0)372 return ret;373 374 return genphy_c45_read_pma(phydev);375}376 377static int mv88q2xxx_get_features(struct phy_device *phydev)378{379 int ret;380 381 ret = genphy_c45_pma_read_abilities(phydev);382 if (ret)383 return ret;384 385 /* We need to read the baset1 extended abilities manually because the386 * PHY does not signalize it has the extended abilities register387 * available.388 */389 ret = genphy_c45_pma_baset1_read_abilities(phydev);390 if (ret)391 return ret;392 393 /* The PHY signalizes it supports autonegotiation. Unfortunately, so394 * far it was not possible to get a link even when following the init395 * sequence provided by Marvell. Disable it for now until a proper396 * workaround is found or a new PHY revision is released.397 */398 if (phydev->drv->phy_id == MARVELL_PHY_ID_88Q2110)399 linkmode_clear_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,400 phydev->supported);401 402 return 0;403}404 405static int mv88q2xxx_config_aneg(struct phy_device *phydev)406{407 int ret;408 409 ret = genphy_c45_config_aneg(phydev);410 if (ret)411 return ret;412 413 return phydev->drv->soft_reset(phydev);414}415 416static int mv88q2xxx_config_init(struct phy_device *phydev)417{418 /* The 88Q2XXX PHYs do have the extended ability register available, but419 * register MDIO_PMA_EXTABLE where they should signalize it does not420 * work according to specification. Therefore, we force it here.421 */422 phydev->pma_extable = MDIO_PMA_EXTABLE_BT1;423 424 /* Configure interrupt with default settings, output is driven low for425 * active interrupt and high for inactive.426 */427 if (phy_interrupt_is_valid(phydev))428 return phy_set_bits_mmd(phydev, MDIO_MMD_PCS,429 MDIO_MMD_PCS_MV_GPIO_INT_CTRL,430 MDIO_MMD_PCS_MV_GPIO_INT_CTRL_TRI_DIS);431 432 return 0;433}434 435static int mv88q2xxx_get_sqi(struct phy_device *phydev)436{437 int ret;438 439 if (phydev->speed == SPEED_100) {440 /* Read the SQI from the vendor specific receiver status441 * register442 */443 ret = phy_read_mmd(phydev, MDIO_MMD_PCS,444 MDIO_MMD_PCS_MV_RX_STAT);445 if (ret < 0)446 return ret;447 448 ret = ret >> 12;449 } else {450 /* Read from vendor specific registers, they are not documented451 * but can be found in the Software Initialization Guide. Only452 * revisions >= A0 are supported.453 */454 ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, 0xfc5d, 0xff, 0xac);455 if (ret < 0)456 return ret;457 458 ret = phy_read_mmd(phydev, MDIO_MMD_PCS, 0xfc88);459 if (ret < 0)460 return ret;461 }462 463 return ret & 0x0f;464}465 466static int mv88q2xxx_get_sqi_max(struct phy_device *phydev)467{468 return 15;469}470 471static int mv88q2xxx_config_intr(struct phy_device *phydev)472{473 int ret;474 475 if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {476 /* Enable interrupts for 1000BASE-T1 link up and down events477 * and enable general interrupts for 100BASE-T1.478 */479 ret = phy_write_mmd(phydev, MDIO_MMD_PCS,480 MDIO_MMD_PCS_MV_INT_EN,481 MDIO_MMD_PCS_MV_INT_EN_LINK_UP |482 MDIO_MMD_PCS_MV_INT_EN_LINK_DOWN |483 MDIO_MMD_PCS_MV_INT_EN_100BT1);484 if (ret < 0)485 return ret;486 487 /* Enable interrupts for 100BASE-T1 link events */488 return phy_write_mmd(phydev, MDIO_MMD_PCS,489 MDIO_MMD_PCS_MV_100BT1_INT_EN,490 MDIO_MMD_PCS_MV_100BT1_INT_EN_LINKEVENT);491 } else {492 ret = phy_write_mmd(phydev, MDIO_MMD_PCS,493 MDIO_MMD_PCS_MV_INT_EN, 0);494 if (ret < 0)495 return ret;496 497 return phy_write_mmd(phydev, MDIO_MMD_PCS,498 MDIO_MMD_PCS_MV_100BT1_INT_EN, 0);499 }500}501 502static irqreturn_t mv88q2xxx_handle_interrupt(struct phy_device *phydev)503{504 bool trigger_machine = false;505 int irq;506 507 /* Before we can acknowledge the 100BT1 general interrupt, that is in508 * the 1000BT1 interrupt status register, we have to acknowledge any509 * interrupts that are related to it. Therefore we read first the 100BT1510 * interrupt status register, followed by reading the 1000BT1 interrupt511 * status register.512 */513 514 irq = phy_read_mmd(phydev, MDIO_MMD_PCS,515 MDIO_MMD_PCS_MV_COPPER_INT_STAT);516 if (irq < 0) {517 phy_error(phydev);518 return IRQ_NONE;519 }520 521 /* Check link status for 100BT1 */522 if (irq & MDIO_MMD_PCS_MV_COPPER_INT_STAT_LINKEVENT)523 trigger_machine = true;524 525 irq = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_MMD_PCS_MV_GPIO_INT_STAT);526 if (irq < 0) {527 phy_error(phydev);528 return IRQ_NONE;529 }530 531 /* Check link status for 1000BT1 */532 if ((irq & MDIO_MMD_PCS_MV_GPIO_INT_STAT_LINK_UP) ||533 (irq & MDIO_MMD_PCS_MV_GPIO_INT_STAT_LINK_DOWN))534 trigger_machine = true;535 536 if (!trigger_machine)537 return IRQ_NONE;538 539 phy_trigger_machine(phydev);540 541 return IRQ_HANDLED;542}543 544static int mv88q2xxx_suspend(struct phy_device *phydev)545{546 int ret;547 548 /* Disable PHY interrupts */549 if (phy_interrupt_is_valid(phydev)) {550 phydev->interrupts = PHY_INTERRUPT_DISABLED;551 ret = mv88q2xxx_config_intr(phydev);552 if (ret)553 return ret;554 }555 556 return phy_set_bits_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_CTRL1,557 MDIO_CTRL1_LPOWER);558}559 560static int mv88q2xxx_resume(struct phy_device *phydev)561{562 int ret;563 564 /* Enable PHY interrupts */565 if (phy_interrupt_is_valid(phydev)) {566 phydev->interrupts = PHY_INTERRUPT_ENABLED;567 ret = mv88q2xxx_config_intr(phydev);568 if (ret)569 return ret;570 }571 572 return phy_clear_bits_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_CTRL1,573 MDIO_CTRL1_LPOWER);574}575 576#if IS_ENABLED(CONFIG_HWMON)577static const struct hwmon_channel_info * const mv88q2xxx_hwmon_info[] = {578 HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_ALARM),579 NULL580};581 582static umode_t mv88q2xxx_hwmon_is_visible(const void *data,583 enum hwmon_sensor_types type,584 u32 attr, int channel)585{586 switch (attr) {587 case hwmon_temp_input:588 return 0444;589 case hwmon_temp_max:590 return 0644;591 case hwmon_temp_alarm:592 return 0444;593 default:594 return 0;595 }596}597 598static int mv88q2xxx_hwmon_read(struct device *dev,599 enum hwmon_sensor_types type,600 u32 attr, int channel, long *val)601{602 struct phy_device *phydev = dev_get_drvdata(dev);603 int ret;604 605 switch (attr) {606 case hwmon_temp_input:607 ret = phy_read_mmd(phydev, MDIO_MMD_PCS,608 MDIO_MMD_PCS_MV_TEMP_SENSOR3);609 if (ret < 0)610 return ret;611 612 ret = FIELD_GET(MDIO_MMD_PCS_MV_TEMP_SENSOR3_MASK, ret);613 *val = (ret - 75) * 1000;614 return 0;615 case hwmon_temp_max:616 ret = phy_read_mmd(phydev, MDIO_MMD_PCS,617 MDIO_MMD_PCS_MV_TEMP_SENSOR3);618 if (ret < 0)619 return ret;620 621 ret = FIELD_GET(MDIO_MMD_PCS_MV_TEMP_SENSOR3_INT_THRESH_MASK,622 ret);623 *val = (ret - 75) * 1000;624 return 0;625 case hwmon_temp_alarm:626 ret = phy_read_mmd(phydev, MDIO_MMD_PCS,627 MDIO_MMD_PCS_MV_TEMP_SENSOR1);628 if (ret < 0)629 return ret;630 631 *val = !!(ret & MDIO_MMD_PCS_MV_TEMP_SENSOR1_RAW_INT);632 return 0;633 default:634 return -EOPNOTSUPP;635 }636}637 638static int mv88q2xxx_hwmon_write(struct device *dev,639 enum hwmon_sensor_types type, u32 attr,640 int channel, long val)641{642 struct phy_device *phydev = dev_get_drvdata(dev);643 644 switch (attr) {645 case hwmon_temp_max:646 clamp_val(val, -75000, 180000);647 val = (val / 1000) + 75;648 val = FIELD_PREP(MDIO_MMD_PCS_MV_TEMP_SENSOR3_INT_THRESH_MASK,649 val);650 return phy_modify_mmd(phydev, MDIO_MMD_PCS,651 MDIO_MMD_PCS_MV_TEMP_SENSOR3,652 MDIO_MMD_PCS_MV_TEMP_SENSOR3_INT_THRESH_MASK,653 val);654 default:655 return -EOPNOTSUPP;656 }657}658 659static const struct hwmon_ops mv88q2xxx_hwmon_hwmon_ops = {660 .is_visible = mv88q2xxx_hwmon_is_visible,661 .read = mv88q2xxx_hwmon_read,662 .write = mv88q2xxx_hwmon_write,663};664 665static const struct hwmon_chip_info mv88q2xxx_hwmon_chip_info = {666 .ops = &mv88q2xxx_hwmon_hwmon_ops,667 .info = mv88q2xxx_hwmon_info,668};669 670static int mv88q2xxx_hwmon_probe(struct phy_device *phydev)671{672 struct device *dev = &phydev->mdio.dev;673 struct device *hwmon;674 char *hwmon_name;675 int ret;676 677 /* Enable temperature sense */678 ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, MDIO_MMD_PCS_MV_TEMP_SENSOR2,679 MDIO_MMD_PCS_MV_TEMP_SENSOR2_DIS_MASK, 0);680 if (ret < 0)681 return ret;682 683 hwmon_name = devm_hwmon_sanitize_name(dev, dev_name(dev));684 if (IS_ERR(hwmon_name))685 return PTR_ERR(hwmon_name);686 687 hwmon = devm_hwmon_device_register_with_info(dev,688 hwmon_name,689 phydev,690 &mv88q2xxx_hwmon_chip_info,691 NULL);692 693 return PTR_ERR_OR_ZERO(hwmon);694}695 696#else697static int mv88q2xxx_hwmon_probe(struct phy_device *phydev)698{699 return 0;700}701#endif702 703static int mv88q2xxx_probe(struct phy_device *phydev)704{705 return mv88q2xxx_hwmon_probe(phydev);706}707 708static int mv88q222x_soft_reset(struct phy_device *phydev)709{710 int ret;711 712 /* Enable RESET of DCL */713 if (phydev->autoneg == AUTONEG_ENABLE || phydev->speed == SPEED_1000) {714 ret = phy_write_mmd(phydev, MDIO_MMD_PCS, 0xfe1b, 0x48);715 if (ret < 0)716 return ret;717 }718 719 ret = phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_PCS_1000BT1_CTRL,720 MDIO_PCS_1000BT1_CTRL_RESET);721 if (ret < 0)722 return ret;723 724 ret = phy_write_mmd(phydev, MDIO_MMD_PCS, 0xffe4, 0xc);725 if (ret < 0)726 return ret;727 728 /* Disable RESET of DCL */729 if (phydev->autoneg == AUTONEG_ENABLE || phydev->speed == SPEED_1000)730 return phy_write_mmd(phydev, MDIO_MMD_PCS, 0xfe1b, 0x58);731 732 return 0;733}734 735static int mv88q222x_write_mmd_vals(struct phy_device *phydev,736 const struct mmd_val *vals, size_t len)737{738 int ret;739 740 for (; len; vals++, len--) {741 ret = phy_write_mmd(phydev, vals->devad, vals->regnum,742 vals->val);743 if (ret < 0)744 return ret;745 }746 747 return 0;748}749 750static int mv88q222x_revb0_config_init(struct phy_device *phydev)751{752 int ret;753 754 ret = mv88q222x_write_mmd_vals(phydev, mv88q222x_revb0_init_seq0,755 ARRAY_SIZE(mv88q222x_revb0_init_seq0));756 if (ret < 0)757 return ret;758 759 usleep_range(5000, 10000);760 761 ret = mv88q222x_write_mmd_vals(phydev, mv88q222x_revb0_init_seq1,762 ARRAY_SIZE(mv88q222x_revb0_init_seq1));763 if (ret < 0)764 return ret;765 766 return mv88q2xxx_config_init(phydev);767}768 769static int mv88q222x_revb1_revb2_config_init(struct phy_device *phydev)770{771 bool is_rev_b1 = phydev->c45_ids.device_ids[MDIO_MMD_PMAPMD] == PHY_ID_88Q2220_REVB1;772 int ret;773 774 if (is_rev_b1)775 ret = mv88q222x_write_mmd_vals(phydev, mv88q222x_revb1_init_seq0,776 ARRAY_SIZE(mv88q222x_revb1_init_seq0));777 else778 ret = mv88q222x_write_mmd_vals(phydev, mv88q222x_revb2_init_seq0,779 ARRAY_SIZE(mv88q222x_revb2_init_seq0));780 if (ret < 0)781 return ret;782 783 usleep_range(3000, 5000);784 785 ret = mv88q222x_write_mmd_vals(phydev, mv88q222x_revb1_revb2_init_seq1,786 ARRAY_SIZE(mv88q222x_revb1_revb2_init_seq1));787 if (ret < 0)788 return ret;789 790 return mv88q2xxx_config_init(phydev);791}792 793static int mv88q222x_config_init(struct phy_device *phydev)794{795 if (phydev->c45_ids.device_ids[MDIO_MMD_PMAPMD] == PHY_ID_88Q2220_REVB0)796 return mv88q222x_revb0_config_init(phydev);797 else798 return mv88q222x_revb1_revb2_config_init(phydev);799}800 801static int mv88q222x_cable_test_start(struct phy_device *phydev)802{803 int ret;804 805 ret = phy_write_mmd(phydev, MDIO_MMD_PCS,806 MDIO_MMD_PCS_MV_TDR_OFF_CUTOFF, 0x0058);807 if (ret < 0)808 return ret;809 810 ret = phy_write_mmd(phydev, MDIO_MMD_PCS,811 MDIO_MMD_PCS_MV_TDR_OFF_LONG_CABLE, 0x00eb);812 if (ret < 0)813 return ret;814 815 ret = phy_write_mmd(phydev, MDIO_MMD_PCS,816 MDIO_MMD_PCS_MV_TDR_OFF_SHORT_CABLE, 0x010e);817 if (ret < 0)818 return ret;819 820 ret = phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_MMD_PCS_MV_TDR_RESET,821 0x0d90);822 if (ret < 0)823 return ret;824 825 ret = phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_MMD_PCS_MV_TDR_STATUS,826 MDIO_MMD_PCS_MV_TDR_STATUS_ON);827 if (ret < 0)828 return ret;829 830 /* According to the Marvell API the test is finished within 500 ms */831 msleep(500);832 833 return 0;834}835 836static int mv88q222x_cable_test_get_status(struct phy_device *phydev,837 bool *finished)838{839 int ret, status;840 u32 dist;841 842 status = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_MMD_PCS_MV_TDR_STATUS);843 if (status < 0)844 return status;845 846 ret = phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_MMD_PCS_MV_TDR_RESET,847 MDIO_MMD_PCS_MV_TDR_RESET_TDR_RST | 0xd90);848 if (ret < 0)849 return ret;850 851 /* Test could not be finished */852 if (FIELD_GET(MDIO_MMD_PCS_MV_TDR_STATUS_MASK, status) !=853 MDIO_MMD_PCS_MV_TDR_STATUS_OFF)854 return -ETIMEDOUT;855 856 *finished = true;857 /* Fault length reported in meters, convert to centimeters */858 dist = FIELD_GET(MDIO_MMD_PCS_MV_TDR_STATUS_DIST_MASK, status) * 100;859 switch (status & MDIO_MMD_PCS_MV_TDR_STATUS_VCT_STAT_MASK) {860 case MDIO_MMD_PCS_MV_TDR_STATUS_VCT_STAT_OPEN:861 ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_A,862 ETHTOOL_A_CABLE_RESULT_CODE_OPEN);863 ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_A,864 dist);865 break;866 case MDIO_MMD_PCS_MV_TDR_STATUS_VCT_STAT_SHORT:867 ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_A,868 ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT);869 ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_A,870 dist);871 break;872 case MDIO_MMD_PCS_MV_TDR_STATUS_VCT_STAT_OK:873 ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_A,874 ETHTOOL_A_CABLE_RESULT_CODE_OK);875 break;876 default:877 ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_A,878 ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC);879 }880 881 return 0;882}883 884static struct phy_driver mv88q2xxx_driver[] = {885 {886 .phy_id = MARVELL_PHY_ID_88Q2110,887 .phy_id_mask = MARVELL_PHY_ID_MASK,888 .name = "mv88q2110",889 .get_features = mv88q2xxx_get_features,890 .config_aneg = mv88q2xxx_config_aneg,891 .config_init = mv88q2xxx_config_init,892 .read_status = mv88q2xxx_read_status,893 .soft_reset = mv88q2xxx_soft_reset,894 .set_loopback = genphy_c45_loopback,895 .get_sqi = mv88q2xxx_get_sqi,896 .get_sqi_max = mv88q2xxx_get_sqi_max,897 },898 {899 .phy_id = MARVELL_PHY_ID_88Q2220,900 .phy_id_mask = MARVELL_PHY_ID_MASK,901 .name = "mv88q2220",902 .flags = PHY_POLL_CABLE_TEST,903 .probe = mv88q2xxx_probe,904 .get_features = mv88q2xxx_get_features,905 .config_aneg = mv88q2xxx_config_aneg,906 .aneg_done = genphy_c45_aneg_done,907 .config_init = mv88q222x_config_init,908 .read_status = mv88q2xxx_read_status,909 .soft_reset = mv88q222x_soft_reset,910 .config_intr = mv88q2xxx_config_intr,911 .handle_interrupt = mv88q2xxx_handle_interrupt,912 .set_loopback = genphy_c45_loopback,913 .cable_test_start = mv88q222x_cable_test_start,914 .cable_test_get_status = mv88q222x_cable_test_get_status,915 .get_sqi = mv88q2xxx_get_sqi,916 .get_sqi_max = mv88q2xxx_get_sqi_max,917 .suspend = mv88q2xxx_suspend,918 .resume = mv88q2xxx_resume,919 },920};921 922module_phy_driver(mv88q2xxx_driver);923 924static struct mdio_device_id __maybe_unused mv88q2xxx_tbl[] = {925 { MARVELL_PHY_ID_88Q2110, MARVELL_PHY_ID_MASK },926 { MARVELL_PHY_ID_88Q2220, MARVELL_PHY_ID_MASK },927 { /*sentinel*/ }928};929MODULE_DEVICE_TABLE(mdio, mv88q2xxx_tbl);930 931MODULE_DESCRIPTION("Marvell 88Q2XXX 100/1000BASE-T1 Automotive Ethernet PHY driver");932MODULE_LICENSE("GPL");933