162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*****************************************************************************
362306a36Sopenharmony_ci *                                                                           *
462306a36Sopenharmony_ci * File: mv88x201x.c                                                         *
562306a36Sopenharmony_ci * $Revision: 1.12 $                                                         *
662306a36Sopenharmony_ci * $Date: 2005/04/15 19:27:14 $                                              *
762306a36Sopenharmony_ci * Description:                                                              *
862306a36Sopenharmony_ci *  Marvell PHY (mv88x201x) functionality.                                   *
962306a36Sopenharmony_ci *  part of the Chelsio 10Gb Ethernet Driver.                                *
1062306a36Sopenharmony_ci *                                                                           *
1162306a36Sopenharmony_ci *                                                                           *
1262306a36Sopenharmony_ci * http://www.chelsio.com                                                    *
1362306a36Sopenharmony_ci *                                                                           *
1462306a36Sopenharmony_ci * Copyright (c) 2003 - 2005 Chelsio Communications, Inc.                    *
1562306a36Sopenharmony_ci * All rights reserved.                                                      *
1662306a36Sopenharmony_ci *                                                                           *
1762306a36Sopenharmony_ci * Maintainers: maintainers@chelsio.com                                      *
1862306a36Sopenharmony_ci *                                                                           *
1962306a36Sopenharmony_ci * Authors: Dimitrios Michailidis   <dm@chelsio.com>                         *
2062306a36Sopenharmony_ci *          Tina Yang               <tainay@chelsio.com>                     *
2162306a36Sopenharmony_ci *          Felix Marti             <felix@chelsio.com>                      *
2262306a36Sopenharmony_ci *          Scott Bardone           <sbardone@chelsio.com>                   *
2362306a36Sopenharmony_ci *          Kurt Ottaway            <kottaway@chelsio.com>                   *
2462306a36Sopenharmony_ci *          Frank DiMambro          <frank@chelsio.com>                      *
2562306a36Sopenharmony_ci *                                                                           *
2662306a36Sopenharmony_ci * History:                                                                  *
2762306a36Sopenharmony_ci *                                                                           *
2862306a36Sopenharmony_ci ****************************************************************************/
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#include "cphy.h"
3162306a36Sopenharmony_ci#include "elmer0.h"
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci/*
3462306a36Sopenharmony_ci * The 88x2010 Rev C. requires some link status registers * to be read
3562306a36Sopenharmony_ci * twice in order to get the right values. Future * revisions will fix
3662306a36Sopenharmony_ci * this problem and then this macro * can disappear.
3762306a36Sopenharmony_ci */
3862306a36Sopenharmony_ci#define MV88x2010_LINK_STATUS_BUGS    1
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_cistatic int led_init(struct cphy *cphy)
4162306a36Sopenharmony_ci{
4262306a36Sopenharmony_ci	/* Setup the LED registers so we can turn on/off.
4362306a36Sopenharmony_ci	 * Writing these bits maps control to another
4462306a36Sopenharmony_ci	 * register. mmd(0x1) addr(0x7)
4562306a36Sopenharmony_ci	 */
4662306a36Sopenharmony_ci	cphy_mdio_write(cphy, MDIO_MMD_PCS, 0x8304, 0xdddd);
4762306a36Sopenharmony_ci	return 0;
4862306a36Sopenharmony_ci}
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_cistatic int led_link(struct cphy *cphy, u32 do_enable)
5162306a36Sopenharmony_ci{
5262306a36Sopenharmony_ci	u32 led = 0;
5362306a36Sopenharmony_ci#define LINK_ENABLE_BIT 0x1
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_CTRL2, &led);
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	if (do_enable & LINK_ENABLE_BIT) {
5862306a36Sopenharmony_ci		led |= LINK_ENABLE_BIT;
5962306a36Sopenharmony_ci		cphy_mdio_write(cphy, MDIO_MMD_PMAPMD, MDIO_CTRL2, led);
6062306a36Sopenharmony_ci	} else {
6162306a36Sopenharmony_ci		led &= ~LINK_ENABLE_BIT;
6262306a36Sopenharmony_ci		cphy_mdio_write(cphy, MDIO_MMD_PMAPMD, MDIO_CTRL2, led);
6362306a36Sopenharmony_ci	}
6462306a36Sopenharmony_ci	return 0;
6562306a36Sopenharmony_ci}
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci/* Port Reset */
6862306a36Sopenharmony_cistatic int mv88x201x_reset(struct cphy *cphy, int wait)
6962306a36Sopenharmony_ci{
7062306a36Sopenharmony_ci	/* This can be done through registers.  It is not required since
7162306a36Sopenharmony_ci	 * a full chip reset is used.
7262306a36Sopenharmony_ci	 */
7362306a36Sopenharmony_ci	return 0;
7462306a36Sopenharmony_ci}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistatic int mv88x201x_interrupt_enable(struct cphy *cphy)
7762306a36Sopenharmony_ci{
7862306a36Sopenharmony_ci	/* Enable PHY LASI interrupts. */
7962306a36Sopenharmony_ci	cphy_mdio_write(cphy, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_CTRL,
8062306a36Sopenharmony_ci			MDIO_PMA_LASI_LSALARM);
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	/* Enable Marvell interrupts through Elmer0. */
8362306a36Sopenharmony_ci	if (t1_is_asic(cphy->adapter)) {
8462306a36Sopenharmony_ci		u32 elmer;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci		t1_tpi_read(cphy->adapter, A_ELMER0_INT_ENABLE, &elmer);
8762306a36Sopenharmony_ci		elmer |= ELMER0_GP_BIT6;
8862306a36Sopenharmony_ci		t1_tpi_write(cphy->adapter, A_ELMER0_INT_ENABLE, elmer);
8962306a36Sopenharmony_ci	}
9062306a36Sopenharmony_ci	return 0;
9162306a36Sopenharmony_ci}
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_cistatic int mv88x201x_interrupt_disable(struct cphy *cphy)
9462306a36Sopenharmony_ci{
9562306a36Sopenharmony_ci	/* Disable PHY LASI interrupts. */
9662306a36Sopenharmony_ci	cphy_mdio_write(cphy, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_CTRL, 0x0);
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	/* Disable Marvell interrupts through Elmer0. */
9962306a36Sopenharmony_ci	if (t1_is_asic(cphy->adapter)) {
10062306a36Sopenharmony_ci		u32 elmer;
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci		t1_tpi_read(cphy->adapter, A_ELMER0_INT_ENABLE, &elmer);
10362306a36Sopenharmony_ci		elmer &= ~ELMER0_GP_BIT6;
10462306a36Sopenharmony_ci		t1_tpi_write(cphy->adapter, A_ELMER0_INT_ENABLE, elmer);
10562306a36Sopenharmony_ci	}
10662306a36Sopenharmony_ci	return 0;
10762306a36Sopenharmony_ci}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_cistatic int mv88x201x_interrupt_clear(struct cphy *cphy)
11062306a36Sopenharmony_ci{
11162306a36Sopenharmony_ci	u32 elmer;
11262306a36Sopenharmony_ci	u32 val;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci#ifdef MV88x2010_LINK_STATUS_BUGS
11562306a36Sopenharmony_ci	/* Required to read twice before clear takes affect. */
11662306a36Sopenharmony_ci	cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_RXSTAT, &val);
11762306a36Sopenharmony_ci	cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_TXSTAT, &val);
11862306a36Sopenharmony_ci	cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_STAT, &val);
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	/* Read this register after the others above it else
12162306a36Sopenharmony_ci	 * the register doesn't clear correctly.
12262306a36Sopenharmony_ci	 */
12362306a36Sopenharmony_ci	cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_STAT1, &val);
12462306a36Sopenharmony_ci#endif
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	/* Clear link status. */
12762306a36Sopenharmony_ci	cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_STAT1, &val);
12862306a36Sopenharmony_ci	/* Clear PHY LASI interrupts. */
12962306a36Sopenharmony_ci	cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_STAT, &val);
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci#ifdef MV88x2010_LINK_STATUS_BUGS
13262306a36Sopenharmony_ci	/* Do it again. */
13362306a36Sopenharmony_ci	cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_RXSTAT, &val);
13462306a36Sopenharmony_ci	cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_TXSTAT, &val);
13562306a36Sopenharmony_ci#endif
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	/* Clear Marvell interrupts through Elmer0. */
13862306a36Sopenharmony_ci	if (t1_is_asic(cphy->adapter)) {
13962306a36Sopenharmony_ci		t1_tpi_read(cphy->adapter, A_ELMER0_INT_CAUSE, &elmer);
14062306a36Sopenharmony_ci		elmer |= ELMER0_GP_BIT6;
14162306a36Sopenharmony_ci		t1_tpi_write(cphy->adapter, A_ELMER0_INT_CAUSE, elmer);
14262306a36Sopenharmony_ci	}
14362306a36Sopenharmony_ci	return 0;
14462306a36Sopenharmony_ci}
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_cistatic int mv88x201x_interrupt_handler(struct cphy *cphy)
14762306a36Sopenharmony_ci{
14862306a36Sopenharmony_ci	/* Clear interrupts */
14962306a36Sopenharmony_ci	mv88x201x_interrupt_clear(cphy);
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	/* We have only enabled link change interrupts and so
15262306a36Sopenharmony_ci	 * cphy_cause must be a link change interrupt.
15362306a36Sopenharmony_ci	 */
15462306a36Sopenharmony_ci	return cphy_cause_link_change;
15562306a36Sopenharmony_ci}
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_cistatic int mv88x201x_set_loopback(struct cphy *cphy, int on)
15862306a36Sopenharmony_ci{
15962306a36Sopenharmony_ci	return 0;
16062306a36Sopenharmony_ci}
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_cistatic int mv88x201x_get_link_status(struct cphy *cphy, int *link_ok,
16362306a36Sopenharmony_ci				     int *speed, int *duplex, int *fc)
16462306a36Sopenharmony_ci{
16562306a36Sopenharmony_ci	u32 val = 0;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	if (link_ok) {
16862306a36Sopenharmony_ci		/* Read link status. */
16962306a36Sopenharmony_ci		cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_STAT1, &val);
17062306a36Sopenharmony_ci		val &= MDIO_STAT1_LSTATUS;
17162306a36Sopenharmony_ci		*link_ok = (val == MDIO_STAT1_LSTATUS);
17262306a36Sopenharmony_ci		/* Turn on/off Link LED */
17362306a36Sopenharmony_ci		led_link(cphy, *link_ok);
17462306a36Sopenharmony_ci	}
17562306a36Sopenharmony_ci	if (speed)
17662306a36Sopenharmony_ci		*speed = SPEED_10000;
17762306a36Sopenharmony_ci	if (duplex)
17862306a36Sopenharmony_ci		*duplex = DUPLEX_FULL;
17962306a36Sopenharmony_ci	if (fc)
18062306a36Sopenharmony_ci		*fc = PAUSE_RX | PAUSE_TX;
18162306a36Sopenharmony_ci	return 0;
18262306a36Sopenharmony_ci}
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_cistatic void mv88x201x_destroy(struct cphy *cphy)
18562306a36Sopenharmony_ci{
18662306a36Sopenharmony_ci	kfree(cphy);
18762306a36Sopenharmony_ci}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_cistatic const struct cphy_ops mv88x201x_ops = {
19062306a36Sopenharmony_ci	.destroy           = mv88x201x_destroy,
19162306a36Sopenharmony_ci	.reset             = mv88x201x_reset,
19262306a36Sopenharmony_ci	.interrupt_enable  = mv88x201x_interrupt_enable,
19362306a36Sopenharmony_ci	.interrupt_disable = mv88x201x_interrupt_disable,
19462306a36Sopenharmony_ci	.interrupt_clear   = mv88x201x_interrupt_clear,
19562306a36Sopenharmony_ci	.interrupt_handler = mv88x201x_interrupt_handler,
19662306a36Sopenharmony_ci	.get_link_status   = mv88x201x_get_link_status,
19762306a36Sopenharmony_ci	.set_loopback      = mv88x201x_set_loopback,
19862306a36Sopenharmony_ci	.mmds              = (MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS |
19962306a36Sopenharmony_ci			      MDIO_DEVS_PHYXS | MDIO_DEVS_WIS),
20062306a36Sopenharmony_ci};
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_cistatic struct cphy *mv88x201x_phy_create(struct net_device *dev, int phy_addr,
20362306a36Sopenharmony_ci					 const struct mdio_ops *mdio_ops)
20462306a36Sopenharmony_ci{
20562306a36Sopenharmony_ci	u32 val;
20662306a36Sopenharmony_ci	struct cphy *cphy = kzalloc(sizeof(*cphy), GFP_KERNEL);
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	if (!cphy)
20962306a36Sopenharmony_ci		return NULL;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	cphy_init(cphy, dev, phy_addr, &mv88x201x_ops, mdio_ops);
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	/* Commands the PHY to enable XFP's clock. */
21462306a36Sopenharmony_ci	cphy_mdio_read(cphy, MDIO_MMD_PCS, 0x8300, &val);
21562306a36Sopenharmony_ci	cphy_mdio_write(cphy, MDIO_MMD_PCS, 0x8300, val | 1);
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	/* Clear link status. Required because of a bug in the PHY.  */
21862306a36Sopenharmony_ci	cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_STAT2, &val);
21962306a36Sopenharmony_ci	cphy_mdio_read(cphy, MDIO_MMD_PCS, MDIO_STAT2, &val);
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	/* Allows for Link,Ack LED turn on/off */
22262306a36Sopenharmony_ci	led_init(cphy);
22362306a36Sopenharmony_ci	return cphy;
22462306a36Sopenharmony_ci}
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci/* Chip Reset */
22762306a36Sopenharmony_cistatic int mv88x201x_phy_reset(adapter_t *adapter)
22862306a36Sopenharmony_ci{
22962306a36Sopenharmony_ci	u32 val;
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	t1_tpi_read(adapter, A_ELMER0_GPO, &val);
23262306a36Sopenharmony_ci	val &= ~4;
23362306a36Sopenharmony_ci	t1_tpi_write(adapter, A_ELMER0_GPO, val);
23462306a36Sopenharmony_ci	msleep(100);
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	t1_tpi_write(adapter, A_ELMER0_GPO, val | 4);
23762306a36Sopenharmony_ci	msleep(1000);
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	/* Now lets enable the Laser. Delay 100us */
24062306a36Sopenharmony_ci	t1_tpi_read(adapter, A_ELMER0_GPO, &val);
24162306a36Sopenharmony_ci	val |= 0x8000;
24262306a36Sopenharmony_ci	t1_tpi_write(adapter, A_ELMER0_GPO, val);
24362306a36Sopenharmony_ci	udelay(100);
24462306a36Sopenharmony_ci	return 0;
24562306a36Sopenharmony_ci}
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ciconst struct gphy t1_mv88x201x_ops = {
24862306a36Sopenharmony_ci	.create = mv88x201x_phy_create,
24962306a36Sopenharmony_ci	.reset = mv88x201x_phy_reset
25062306a36Sopenharmony_ci};
251