brintos

brintos / linux-shallow public Read only

0
0
Text · 10.0 KiB · 30b0034 Raw
399 lines · c
1// SPDX-License-Identifier: GPL-2.02/* $Date: 2005/10/24 23:18:13 $ $RCSfile: mv88e1xxx.c,v $ $Revision: 1.49 $ */3#include "common.h"4#include "mv88e1xxx.h"5#include "cphy.h"6#include "elmer0.h"7 8/* MV88E1XXX MDI crossover register values */9#define CROSSOVER_MDI   010#define CROSSOVER_MDIX  111#define CROSSOVER_AUTO  312 13#define INTR_ENABLE_MASK 0x6CA014 15/*16 * Set the bits given by 'bitval' in PHY register 'reg'.17 */18static void mdio_set_bit(struct cphy *cphy, int reg, u32 bitval)19{20	u32 val;21 22	(void) simple_mdio_read(cphy, reg, &val);23	(void) simple_mdio_write(cphy, reg, val | bitval);24}25 26/*27 * Clear the bits given by 'bitval' in PHY register 'reg'.28 */29static void mdio_clear_bit(struct cphy *cphy, int reg, u32 bitval)30{31	u32 val;32 33	(void) simple_mdio_read(cphy, reg, &val);34	(void) simple_mdio_write(cphy, reg, val & ~bitval);35}36 37/*38 * NAME:   phy_reset39 *40 * DESC:   Reset the given PHY's port. NOTE: This is not a global41 *         chip reset.42 *43 * PARAMS: cphy     - Pointer to PHY instance data.44 *45 * RETURN:  0 - Successful reset.46 *         -1 - Timeout.47 */48static int mv88e1xxx_reset(struct cphy *cphy, int wait)49{50	u32 ctl;51	int time_out = 1000;52 53	mdio_set_bit(cphy, MII_BMCR, BMCR_RESET);54 55	do {56		(void) simple_mdio_read(cphy, MII_BMCR, &ctl);57		ctl &= BMCR_RESET;58		if (ctl)59			udelay(1);60	} while (ctl && --time_out);61 62	return ctl ? -1 : 0;63}64 65static int mv88e1xxx_interrupt_enable(struct cphy *cphy)66{67	/* Enable PHY interrupts. */68	(void) simple_mdio_write(cphy, MV88E1XXX_INTERRUPT_ENABLE_REGISTER,69		   INTR_ENABLE_MASK);70 71	/* Enable Marvell interrupts through Elmer0. */72	if (t1_is_asic(cphy->adapter)) {73		u32 elmer;74 75		t1_tpi_read(cphy->adapter, A_ELMER0_INT_ENABLE, &elmer);76		elmer |= ELMER0_GP_BIT1;77		if (is_T2(cphy->adapter))78		    elmer |= ELMER0_GP_BIT2 | ELMER0_GP_BIT3 | ELMER0_GP_BIT4;79		t1_tpi_write(cphy->adapter, A_ELMER0_INT_ENABLE, elmer);80	}81	return 0;82}83 84static int mv88e1xxx_interrupt_disable(struct cphy *cphy)85{86	/* Disable all phy interrupts. */87	(void) simple_mdio_write(cphy, MV88E1XXX_INTERRUPT_ENABLE_REGISTER, 0);88 89	/* Disable Marvell interrupts through Elmer0. */90	if (t1_is_asic(cphy->adapter)) {91		u32 elmer;92 93		t1_tpi_read(cphy->adapter, A_ELMER0_INT_ENABLE, &elmer);94		elmer &= ~ELMER0_GP_BIT1;95		if (is_T2(cphy->adapter))96		    elmer &= ~(ELMER0_GP_BIT2|ELMER0_GP_BIT3|ELMER0_GP_BIT4);97		t1_tpi_write(cphy->adapter, A_ELMER0_INT_ENABLE, elmer);98	}99	return 0;100}101 102static int mv88e1xxx_interrupt_clear(struct cphy *cphy)103{104	u32 elmer;105 106	/* Clear PHY interrupts by reading the register. */107	(void) simple_mdio_read(cphy,108			MV88E1XXX_INTERRUPT_STATUS_REGISTER, &elmer);109 110	/* Clear Marvell interrupts through Elmer0. */111	if (t1_is_asic(cphy->adapter)) {112		t1_tpi_read(cphy->adapter, A_ELMER0_INT_CAUSE, &elmer);113		elmer |= ELMER0_GP_BIT1;114		if (is_T2(cphy->adapter))115		    elmer |= ELMER0_GP_BIT2|ELMER0_GP_BIT3|ELMER0_GP_BIT4;116		t1_tpi_write(cphy->adapter, A_ELMER0_INT_CAUSE, elmer);117	}118	return 0;119}120 121/*122 * Set the PHY speed and duplex.  This also disables auto-negotiation, except123 * for 1Gb/s, where auto-negotiation is mandatory.124 */125static int mv88e1xxx_set_speed_duplex(struct cphy *phy, int speed, int duplex)126{127	u32 ctl;128 129	(void) simple_mdio_read(phy, MII_BMCR, &ctl);130	if (speed >= 0) {131		ctl &= ~(BMCR_SPEED100 | BMCR_SPEED1000 | BMCR_ANENABLE);132		if (speed == SPEED_100)133			ctl |= BMCR_SPEED100;134		else if (speed == SPEED_1000)135			ctl |= BMCR_SPEED1000;136	}137	if (duplex >= 0) {138		ctl &= ~(BMCR_FULLDPLX | BMCR_ANENABLE);139		if (duplex == DUPLEX_FULL)140			ctl |= BMCR_FULLDPLX;141	}142	if (ctl & BMCR_SPEED1000)  /* auto-negotiation required for 1Gb/s */143		ctl |= BMCR_ANENABLE;144	(void) simple_mdio_write(phy, MII_BMCR, ctl);145	return 0;146}147 148static int mv88e1xxx_crossover_set(struct cphy *cphy, int crossover)149{150	u32 data32;151 152	(void) simple_mdio_read(cphy,153			MV88E1XXX_SPECIFIC_CNTRL_REGISTER, &data32);154	data32 &= ~V_PSCR_MDI_XOVER_MODE(M_PSCR_MDI_XOVER_MODE);155	data32 |= V_PSCR_MDI_XOVER_MODE(crossover);156	(void) simple_mdio_write(cphy,157			MV88E1XXX_SPECIFIC_CNTRL_REGISTER, data32);158	return 0;159}160 161static int mv88e1xxx_autoneg_enable(struct cphy *cphy)162{163	u32 ctl;164 165	(void) mv88e1xxx_crossover_set(cphy, CROSSOVER_AUTO);166 167	(void) simple_mdio_read(cphy, MII_BMCR, &ctl);168	/* restart autoneg for change to take effect */169	ctl |= BMCR_ANENABLE | BMCR_ANRESTART;170	(void) simple_mdio_write(cphy, MII_BMCR, ctl);171	return 0;172}173 174static int mv88e1xxx_autoneg_disable(struct cphy *cphy)175{176	u32 ctl;177 178	/*179	 * Crossover *must* be set to manual in order to disable auto-neg.180	 * The Alaska FAQs document highlights this point.181	 */182	(void) mv88e1xxx_crossover_set(cphy, CROSSOVER_MDI);183 184	/*185	 * Must include autoneg reset when disabling auto-neg. This186	 * is described in the Alaska FAQ document.187	 */188	(void) simple_mdio_read(cphy, MII_BMCR, &ctl);189	ctl &= ~BMCR_ANENABLE;190	(void) simple_mdio_write(cphy, MII_BMCR, ctl | BMCR_ANRESTART);191	return 0;192}193 194static int mv88e1xxx_autoneg_restart(struct cphy *cphy)195{196	mdio_set_bit(cphy, MII_BMCR, BMCR_ANRESTART);197	return 0;198}199 200static int mv88e1xxx_advertise(struct cphy *phy, unsigned int advertise_map)201{202	u32 val = 0;203 204	if (advertise_map &205	    (ADVERTISED_1000baseT_Half | ADVERTISED_1000baseT_Full)) {206		(void) simple_mdio_read(phy, MII_GBCR, &val);207		val &= ~(GBCR_ADV_1000HALF | GBCR_ADV_1000FULL);208		if (advertise_map & ADVERTISED_1000baseT_Half)209			val |= GBCR_ADV_1000HALF;210		if (advertise_map & ADVERTISED_1000baseT_Full)211			val |= GBCR_ADV_1000FULL;212	}213	(void) simple_mdio_write(phy, MII_GBCR, val);214 215	val = 1;216	if (advertise_map & ADVERTISED_10baseT_Half)217		val |= ADVERTISE_10HALF;218	if (advertise_map & ADVERTISED_10baseT_Full)219		val |= ADVERTISE_10FULL;220	if (advertise_map & ADVERTISED_100baseT_Half)221		val |= ADVERTISE_100HALF;222	if (advertise_map & ADVERTISED_100baseT_Full)223		val |= ADVERTISE_100FULL;224	if (advertise_map & ADVERTISED_PAUSE)225		val |= ADVERTISE_PAUSE;226	if (advertise_map & ADVERTISED_ASYM_PAUSE)227		val |= ADVERTISE_PAUSE_ASYM;228	(void) simple_mdio_write(phy, MII_ADVERTISE, val);229	return 0;230}231 232static int mv88e1xxx_set_loopback(struct cphy *cphy, int on)233{234	if (on)235		mdio_set_bit(cphy, MII_BMCR, BMCR_LOOPBACK);236	else237		mdio_clear_bit(cphy, MII_BMCR, BMCR_LOOPBACK);238	return 0;239}240 241static int mv88e1xxx_get_link_status(struct cphy *cphy, int *link_ok,242				     int *speed, int *duplex, int *fc)243{244	u32 status;245	int sp = -1, dplx = -1, pause = 0;246 247	(void) simple_mdio_read(cphy,248			MV88E1XXX_SPECIFIC_STATUS_REGISTER, &status);249	if ((status & V_PSSR_STATUS_RESOLVED) != 0) {250		if (status & V_PSSR_RX_PAUSE)251			pause |= PAUSE_RX;252		if (status & V_PSSR_TX_PAUSE)253			pause |= PAUSE_TX;254		dplx = (status & V_PSSR_DUPLEX) ? DUPLEX_FULL : DUPLEX_HALF;255		sp = G_PSSR_SPEED(status);256		if (sp == 0)257			sp = SPEED_10;258		else if (sp == 1)259			sp = SPEED_100;260		else261			sp = SPEED_1000;262	}263	if (link_ok)264		*link_ok = (status & V_PSSR_LINK) != 0;265	if (speed)266		*speed = sp;267	if (duplex)268		*duplex = dplx;269	if (fc)270		*fc = pause;271	return 0;272}273 274static int mv88e1xxx_downshift_set(struct cphy *cphy, int downshift_enable)275{276	u32 val;277 278	(void) simple_mdio_read(cphy,279		MV88E1XXX_EXT_PHY_SPECIFIC_CNTRL_REGISTER, &val);280 281	/*282	 * Set the downshift counter to 2 so we try to establish Gb link283	 * twice before downshifting.284	 */285	val &= ~(V_DOWNSHIFT_ENABLE | V_DOWNSHIFT_CNT(M_DOWNSHIFT_CNT));286 287	if (downshift_enable)288		val |= V_DOWNSHIFT_ENABLE | V_DOWNSHIFT_CNT(2);289	(void) simple_mdio_write(cphy,290			MV88E1XXX_EXT_PHY_SPECIFIC_CNTRL_REGISTER, val);291	return 0;292}293 294static int mv88e1xxx_interrupt_handler(struct cphy *cphy)295{296	int cphy_cause = 0;297	u32 status;298 299	/*300	 * Loop until cause reads zero. Need to handle bouncing interrupts.301	 */302	while (1) {303		u32 cause;304 305		(void) simple_mdio_read(cphy,306				MV88E1XXX_INTERRUPT_STATUS_REGISTER,307				&cause);308		cause &= INTR_ENABLE_MASK;309		if (!cause)310			break;311 312		if (cause & MV88E1XXX_INTR_LINK_CHNG) {313			(void) simple_mdio_read(cphy,314				MV88E1XXX_SPECIFIC_STATUS_REGISTER, &status);315 316			if (status & MV88E1XXX_INTR_LINK_CHNG)317				cphy->state |= PHY_LINK_UP;318			else {319				cphy->state &= ~PHY_LINK_UP;320				if (cphy->state & PHY_AUTONEG_EN)321					cphy->state &= ~PHY_AUTONEG_RDY;322				cphy_cause |= cphy_cause_link_change;323			}324		}325 326		if (cause & MV88E1XXX_INTR_AUTONEG_DONE)327			cphy->state |= PHY_AUTONEG_RDY;328 329		if ((cphy->state & (PHY_LINK_UP | PHY_AUTONEG_RDY)) ==330			(PHY_LINK_UP | PHY_AUTONEG_RDY))331				cphy_cause |= cphy_cause_link_change;332	}333	return cphy_cause;334}335 336static void mv88e1xxx_destroy(struct cphy *cphy)337{338	kfree(cphy);339}340 341static const struct cphy_ops mv88e1xxx_ops = {342	.destroy              = mv88e1xxx_destroy,343	.reset                = mv88e1xxx_reset,344	.interrupt_enable     = mv88e1xxx_interrupt_enable,345	.interrupt_disable    = mv88e1xxx_interrupt_disable,346	.interrupt_clear      = mv88e1xxx_interrupt_clear,347	.interrupt_handler    = mv88e1xxx_interrupt_handler,348	.autoneg_enable       = mv88e1xxx_autoneg_enable,349	.autoneg_disable      = mv88e1xxx_autoneg_disable,350	.autoneg_restart      = mv88e1xxx_autoneg_restart,351	.advertise            = mv88e1xxx_advertise,352	.set_loopback         = mv88e1xxx_set_loopback,353	.set_speed_duplex     = mv88e1xxx_set_speed_duplex,354	.get_link_status      = mv88e1xxx_get_link_status,355};356 357static struct cphy *mv88e1xxx_phy_create(struct net_device *dev, int phy_addr,358					 const struct mdio_ops *mdio_ops)359{360	struct adapter *adapter = netdev_priv(dev);361	struct cphy *cphy = kzalloc(sizeof(*cphy), GFP_KERNEL);362 363	if (!cphy)364		return NULL;365 366	cphy_init(cphy, dev, phy_addr, &mv88e1xxx_ops, mdio_ops);367 368	/* Configure particular PHY's to run in a different mode. */369	if ((board_info(adapter)->caps & SUPPORTED_TP) &&370	    board_info(adapter)->chip_phy == CHBT_PHY_88E1111) {371		/*372		 * Configure the PHY transmitter as class A to reduce EMI.373		 */374		(void) simple_mdio_write(cphy,375				MV88E1XXX_EXTENDED_ADDR_REGISTER, 0xB);376		(void) simple_mdio_write(cphy,377				MV88E1XXX_EXTENDED_REGISTER, 0x8004);378	}379	(void) mv88e1xxx_downshift_set(cphy, 1);   /* Enable downshift */380 381	/* LED */382	if (is_T2(adapter)) {383		(void) simple_mdio_write(cphy,384				MV88E1XXX_LED_CONTROL_REGISTER, 0x1);385	}386 387	return cphy;388}389 390static int mv88e1xxx_phy_reset(adapter_t* adapter)391{392	return 0;393}394 395const struct gphy t1_mv88e1xxx_ops = {396	.create = mv88e1xxx_phy_create,397	.reset =  mv88e1xxx_phy_reset398};399