cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

mv88e1xxx.c (10276B)


      1// SPDX-License-Identifier: GPL-2.0
      2/* $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   0
     10#define CROSSOVER_MDIX  1
     11#define CROSSOVER_AUTO  3
     12
     13#define INTR_ENABLE_MASK 0x6CA0
     14
     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_reset
     39 *
     40 * DESC:   Reset the given PHY's port. NOTE: This is not a global
     41 *         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, except
    123 * 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. This
    186	 * 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	else
    237		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		else
    261			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 link
    283	 * 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_reset
    398};