xref: /linux/drivers/net/phy/swphy.c (revision 4b4193256c8d3bc3a5397b5cd9494c2ad386317d)
1a2443fd1SAndrew Lunn // SPDX-License-Identifier: GPL-2.0+
25ae68b0cSRussell King /*
35ae68b0cSRussell King  * Software PHY emulation
45ae68b0cSRussell King  *
5*4f39467eSRussell King  * Code taken from fixed_phy.c by Russell King.
65ae68b0cSRussell King  *
75ae68b0cSRussell King  * Author: Vitaly Bordug <vbordug@ru.mvista.com>
85ae68b0cSRussell King  *         Anton Vorontsov <avorontsov@ru.mvista.com>
95ae68b0cSRussell King  *
105ae68b0cSRussell King  * Copyright (c) 2006-2007 MontaVista Software, Inc.
115ae68b0cSRussell King  */
125ae68b0cSRussell King #include <linux/export.h>
135ae68b0cSRussell King #include <linux/mii.h>
145ae68b0cSRussell King #include <linux/phy.h>
155ae68b0cSRussell King #include <linux/phy_fixed.h>
165ae68b0cSRussell King 
175ae68b0cSRussell King #include "swphy.h"
185ae68b0cSRussell King 
1937688e3fSRussell King #define MII_REGS_NUM 29
2037688e3fSRussell King 
210629bf17SRussell King struct swmii_regs {
220629bf17SRussell King 	u16 bmsr;
230629bf17SRussell King 	u16 lpa;
240629bf17SRussell King 	u16 lpagb;
252441ba48SHeiner Kallweit 	u16 estat;
260629bf17SRussell King };
270629bf17SRussell King 
280629bf17SRussell King enum {
290629bf17SRussell King 	SWMII_SPEED_10 = 0,
300629bf17SRussell King 	SWMII_SPEED_100,
310629bf17SRussell King 	SWMII_SPEED_1000,
320629bf17SRussell King 	SWMII_DUPLEX_HALF = 0,
330629bf17SRussell King 	SWMII_DUPLEX_FULL,
340629bf17SRussell King };
350629bf17SRussell King 
360629bf17SRussell King /*
370629bf17SRussell King  * These two tables get bitwise-anded together to produce the final result.
380629bf17SRussell King  * This means the speed table must contain both duplex settings, and the
390629bf17SRussell King  * duplex table must contain all speed settings.
400629bf17SRussell King  */
410629bf17SRussell King static const struct swmii_regs speed[] = {
420629bf17SRussell King 	[SWMII_SPEED_10] = {
430629bf17SRussell King 		.lpa   = LPA_10FULL | LPA_10HALF,
440629bf17SRussell King 	},
450629bf17SRussell King 	[SWMII_SPEED_100] = {
460629bf17SRussell King 		.bmsr  = BMSR_100FULL | BMSR_100HALF,
470629bf17SRussell King 		.lpa   = LPA_100FULL | LPA_100HALF,
480629bf17SRussell King 	},
490629bf17SRussell King 	[SWMII_SPEED_1000] = {
500629bf17SRussell King 		.bmsr  = BMSR_ESTATEN,
510629bf17SRussell King 		.lpagb = LPA_1000FULL | LPA_1000HALF,
522441ba48SHeiner Kallweit 		.estat = ESTATUS_1000_TFULL | ESTATUS_1000_THALF,
530629bf17SRussell King 	},
540629bf17SRussell King };
550629bf17SRussell King 
560629bf17SRussell King static const struct swmii_regs duplex[] = {
570629bf17SRussell King 	[SWMII_DUPLEX_HALF] = {
580629bf17SRussell King 		.bmsr  = BMSR_ESTATEN | BMSR_100HALF,
590629bf17SRussell King 		.lpa   = LPA_10HALF | LPA_100HALF,
600629bf17SRussell King 		.lpagb = LPA_1000HALF,
612441ba48SHeiner Kallweit 		.estat = ESTATUS_1000_THALF,
620629bf17SRussell King 	},
630629bf17SRussell King 	[SWMII_DUPLEX_FULL] = {
640629bf17SRussell King 		.bmsr  = BMSR_ESTATEN | BMSR_100FULL,
650629bf17SRussell King 		.lpa   = LPA_10FULL | LPA_100FULL,
660629bf17SRussell King 		.lpagb = LPA_1000FULL,
672441ba48SHeiner Kallweit 		.estat = ESTATUS_1000_TFULL,
680629bf17SRussell King 	},
690629bf17SRussell King };
700629bf17SRussell King 
swphy_decode_speed(int speed)710629bf17SRussell King static int swphy_decode_speed(int speed)
720629bf17SRussell King {
730629bf17SRussell King 	switch (speed) {
740629bf17SRussell King 	case 1000:
750629bf17SRussell King 		return SWMII_SPEED_1000;
760629bf17SRussell King 	case 100:
770629bf17SRussell King 		return SWMII_SPEED_100;
780629bf17SRussell King 	case 10:
790629bf17SRussell King 		return SWMII_SPEED_10;
800629bf17SRussell King 	default:
810629bf17SRussell King 		return -EINVAL;
820629bf17SRussell King 	}
830629bf17SRussell King }
840629bf17SRussell King 
855ae68b0cSRussell King /**
8668888ce0SRussell King  * swphy_validate_state - validate the software phy status
8768888ce0SRussell King  * @state: software phy status
8868888ce0SRussell King  *
8968888ce0SRussell King  * This checks that we can represent the state stored in @state can be
9068888ce0SRussell King  * represented in the emulated MII registers.  Returns 0 if it can,
9168888ce0SRussell King  * otherwise returns -EINVAL.
9268888ce0SRussell King  */
swphy_validate_state(const struct fixed_phy_status * state)9368888ce0SRussell King int swphy_validate_state(const struct fixed_phy_status *state)
9468888ce0SRussell King {
9568888ce0SRussell King 	int err;
9668888ce0SRussell King 
9768888ce0SRussell King 	if (state->link) {
9868888ce0SRussell King 		err = swphy_decode_speed(state->speed);
9968888ce0SRussell King 		if (err < 0) {
10068888ce0SRussell King 			pr_warn("swphy: unknown speed\n");
10168888ce0SRussell King 			return -EINVAL;
10268888ce0SRussell King 		}
10368888ce0SRussell King 	}
10468888ce0SRussell King 	return 0;
10568888ce0SRussell King }
10668888ce0SRussell King EXPORT_SYMBOL_GPL(swphy_validate_state);
10768888ce0SRussell King 
10868888ce0SRussell King /**
10937688e3fSRussell King  * swphy_read_reg - return a MII register from the fixed phy state
11037688e3fSRussell King  * @reg: MII register
1115ae68b0cSRussell King  * @state: fixed phy status
1125ae68b0cSRussell King  *
11337688e3fSRussell King  * Return the MII @reg register generated from the fixed phy state @state.
1145ae68b0cSRussell King  */
swphy_read_reg(int reg,const struct fixed_phy_status * state)11537688e3fSRussell King int swphy_read_reg(int reg, const struct fixed_phy_status *state)
1165ae68b0cSRussell King {
1170629bf17SRussell King 	int speed_index, duplex_index;
1185ae68b0cSRussell King 	u16 bmsr = BMSR_ANEGCAPABLE;
1192441ba48SHeiner Kallweit 	u16 estat = 0;
1205ae68b0cSRussell King 	u16 lpagb = 0;
1215ae68b0cSRussell King 	u16 lpa = 0;
1225ae68b0cSRussell King 
12337688e3fSRussell King 	if (reg > MII_REGS_NUM)
12437688e3fSRussell King 		return -1;
12537688e3fSRussell King 
1260629bf17SRussell King 	speed_index = swphy_decode_speed(state->speed);
12768888ce0SRussell King 	if (WARN_ON(speed_index < 0))
12837688e3fSRussell King 		return 0;
1290629bf17SRussell King 
1300629bf17SRussell King 	duplex_index = state->duplex ? SWMII_DUPLEX_FULL : SWMII_DUPLEX_HALF;
1310629bf17SRussell King 
1320629bf17SRussell King 	bmsr |= speed[speed_index].bmsr & duplex[duplex_index].bmsr;
1332441ba48SHeiner Kallweit 	estat |= speed[speed_index].estat & duplex[duplex_index].estat;
1345ae68b0cSRussell King 
1355ae68b0cSRussell King 	if (state->link) {
1365ae68b0cSRussell King 		bmsr |= BMSR_LSTATUS | BMSR_ANEGCOMPLETE;
1375ae68b0cSRussell King 
1380629bf17SRussell King 		lpa   |= speed[speed_index].lpa   & duplex[duplex_index].lpa;
1390629bf17SRussell King 		lpagb |= speed[speed_index].lpagb & duplex[duplex_index].lpagb;
1405ae68b0cSRussell King 
1415ae68b0cSRussell King 		if (state->pause)
1425ae68b0cSRussell King 			lpa |= LPA_PAUSE_CAP;
1435ae68b0cSRussell King 
1445ae68b0cSRussell King 		if (state->asym_pause)
1455ae68b0cSRussell King 			lpa |= LPA_PAUSE_ASYM;
1465ae68b0cSRussell King 	}
1475ae68b0cSRussell King 
14837688e3fSRussell King 	switch (reg) {
14937688e3fSRussell King 	case MII_BMCR:
150726097d6SHeiner Kallweit 		return BMCR_ANENABLE;
15137688e3fSRussell King 	case MII_BMSR:
15237688e3fSRussell King 		return bmsr;
15337688e3fSRussell King 	case MII_PHYSID1:
15437688e3fSRussell King 	case MII_PHYSID2:
15537688e3fSRussell King 		return 0;
15637688e3fSRussell King 	case MII_LPA:
15737688e3fSRussell King 		return lpa;
15837688e3fSRussell King 	case MII_STAT1000:
15937688e3fSRussell King 		return lpagb;
1602441ba48SHeiner Kallweit 	case MII_ESTATUS:
1612441ba48SHeiner Kallweit 		return estat;
1625ae68b0cSRussell King 
16337688e3fSRussell King 	/*
16437688e3fSRussell King 	 * We do not support emulating Clause 45 over Clause 22 register
16537688e3fSRussell King 	 * reads.  Return an error instead of bogus data.
16637688e3fSRussell King 	 */
16737688e3fSRussell King 	case MII_MMD_CTRL:
16837688e3fSRussell King 	case MII_MMD_DATA:
16937688e3fSRussell King 		return -1;
17037688e3fSRussell King 
17137688e3fSRussell King 	default:
17237688e3fSRussell King 		return 0xffff;
1735ae68b0cSRussell King 	}
17437688e3fSRussell King }
17537688e3fSRussell King EXPORT_SYMBOL_GPL(swphy_read_reg);
176