18c2ecf20Sopenharmony_ci/*****************************************************************************
28c2ecf20Sopenharmony_ci *                                                                           *
38c2ecf20Sopenharmony_ci * File: mv88x201x.c                                                         *
48c2ecf20Sopenharmony_ci * $Revision: 1.12 $                                                         *
58c2ecf20Sopenharmony_ci * $Date: 2005/04/15 19:27:14 $                                              *
68c2ecf20Sopenharmony_ci * Description:                                                              *
78c2ecf20Sopenharmony_ci *  Marvell PHY (mv88x201x) functionality.                                   *
88c2ecf20Sopenharmony_ci *  part of the Chelsio 10Gb Ethernet Driver.                                *
98c2ecf20Sopenharmony_ci *                                                                           *
108c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or modify      *
118c2ecf20Sopenharmony_ci * it under the terms of the GNU General Public License, version 2, as       *
128c2ecf20Sopenharmony_ci * published by the Free Software Foundation.                                *
138c2ecf20Sopenharmony_ci *                                                                           *
148c2ecf20Sopenharmony_ci * You should have received a copy of the GNU General Public License along   *
158c2ecf20Sopenharmony_ci * with this program; if not, see <http://www.gnu.org/licenses/>.            *
168c2ecf20Sopenharmony_ci *                                                                           *
178c2ecf20Sopenharmony_ci * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED    *
188c2ecf20Sopenharmony_ci * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF      *
198c2ecf20Sopenharmony_ci * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.                     *
208c2ecf20Sopenharmony_ci *                                                                           *
218c2ecf20Sopenharmony_ci * http://www.chelsio.com                                                    *
228c2ecf20Sopenharmony_ci *                                                                           *
238c2ecf20Sopenharmony_ci * Copyright (c) 2003 - 2005 Chelsio Communications, Inc.                    *
248c2ecf20Sopenharmony_ci * All rights reserved.                                                      *
258c2ecf20Sopenharmony_ci *                                                                           *
268c2ecf20Sopenharmony_ci * Maintainers: maintainers@chelsio.com                                      *
278c2ecf20Sopenharmony_ci *                                                                           *
288c2ecf20Sopenharmony_ci * Authors: Dimitrios Michailidis   <dm@chelsio.com>                         *
298c2ecf20Sopenharmony_ci *          Tina Yang               <tainay@chelsio.com>                     *
308c2ecf20Sopenharmony_ci *          Felix Marti             <felix@chelsio.com>                      *
318c2ecf20Sopenharmony_ci *          Scott Bardone           <sbardone@chelsio.com>                   *
328c2ecf20Sopenharmony_ci *          Kurt Ottaway            <kottaway@chelsio.com>                   *
338c2ecf20Sopenharmony_ci *          Frank DiMambro          <frank@chelsio.com>                      *
348c2ecf20Sopenharmony_ci *                                                                           *
358c2ecf20Sopenharmony_ci * History:                                                                  *
368c2ecf20Sopenharmony_ci *                                                                           *
378c2ecf20Sopenharmony_ci ****************************************************************************/
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci#include "cphy.h"
408c2ecf20Sopenharmony_ci#include "elmer0.h"
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci/*
438c2ecf20Sopenharmony_ci * The 88x2010 Rev C. requires some link status registers * to be read
448c2ecf20Sopenharmony_ci * twice in order to get the right values. Future * revisions will fix
458c2ecf20Sopenharmony_ci * this problem and then this macro * can disappear.
468c2ecf20Sopenharmony_ci */
478c2ecf20Sopenharmony_ci#define MV88x2010_LINK_STATUS_BUGS    1
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_cistatic int led_init(struct cphy *cphy)
508c2ecf20Sopenharmony_ci{
518c2ecf20Sopenharmony_ci	/* Setup the LED registers so we can turn on/off.
528c2ecf20Sopenharmony_ci	 * Writing these bits maps control to another
538c2ecf20Sopenharmony_ci	 * register. mmd(0x1) addr(0x7)
548c2ecf20Sopenharmony_ci	 */
558c2ecf20Sopenharmony_ci	cphy_mdio_write(cphy, MDIO_MMD_PCS, 0x8304, 0xdddd);
568c2ecf20Sopenharmony_ci	return 0;
578c2ecf20Sopenharmony_ci}
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_cistatic int led_link(struct cphy *cphy, u32 do_enable)
608c2ecf20Sopenharmony_ci{
618c2ecf20Sopenharmony_ci	u32 led = 0;
628c2ecf20Sopenharmony_ci#define LINK_ENABLE_BIT 0x1
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_CTRL2, &led);
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	if (do_enable & LINK_ENABLE_BIT) {
678c2ecf20Sopenharmony_ci		led |= LINK_ENABLE_BIT;
688c2ecf20Sopenharmony_ci		cphy_mdio_write(cphy, MDIO_MMD_PMAPMD, MDIO_CTRL2, led);
698c2ecf20Sopenharmony_ci	} else {
708c2ecf20Sopenharmony_ci		led &= ~LINK_ENABLE_BIT;
718c2ecf20Sopenharmony_ci		cphy_mdio_write(cphy, MDIO_MMD_PMAPMD, MDIO_CTRL2, led);
728c2ecf20Sopenharmony_ci	}
738c2ecf20Sopenharmony_ci	return 0;
748c2ecf20Sopenharmony_ci}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci/* Port Reset */
778c2ecf20Sopenharmony_cistatic int mv88x201x_reset(struct cphy *cphy, int wait)
788c2ecf20Sopenharmony_ci{
798c2ecf20Sopenharmony_ci	/* This can be done through registers.  It is not required since
808c2ecf20Sopenharmony_ci	 * a full chip reset is used.
818c2ecf20Sopenharmony_ci	 */
828c2ecf20Sopenharmony_ci	return 0;
838c2ecf20Sopenharmony_ci}
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_cistatic int mv88x201x_interrupt_enable(struct cphy *cphy)
868c2ecf20Sopenharmony_ci{
878c2ecf20Sopenharmony_ci	/* Enable PHY LASI interrupts. */
888c2ecf20Sopenharmony_ci	cphy_mdio_write(cphy, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_CTRL,
898c2ecf20Sopenharmony_ci			MDIO_PMA_LASI_LSALARM);
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	/* Enable Marvell interrupts through Elmer0. */
928c2ecf20Sopenharmony_ci	if (t1_is_asic(cphy->adapter)) {
938c2ecf20Sopenharmony_ci		u32 elmer;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci		t1_tpi_read(cphy->adapter, A_ELMER0_INT_ENABLE, &elmer);
968c2ecf20Sopenharmony_ci		elmer |= ELMER0_GP_BIT6;
978c2ecf20Sopenharmony_ci		t1_tpi_write(cphy->adapter, A_ELMER0_INT_ENABLE, elmer);
988c2ecf20Sopenharmony_ci	}
998c2ecf20Sopenharmony_ci	return 0;
1008c2ecf20Sopenharmony_ci}
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_cistatic int mv88x201x_interrupt_disable(struct cphy *cphy)
1038c2ecf20Sopenharmony_ci{
1048c2ecf20Sopenharmony_ci	/* Disable PHY LASI interrupts. */
1058c2ecf20Sopenharmony_ci	cphy_mdio_write(cphy, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_CTRL, 0x0);
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	/* Disable Marvell interrupts through Elmer0. */
1088c2ecf20Sopenharmony_ci	if (t1_is_asic(cphy->adapter)) {
1098c2ecf20Sopenharmony_ci		u32 elmer;
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci		t1_tpi_read(cphy->adapter, A_ELMER0_INT_ENABLE, &elmer);
1128c2ecf20Sopenharmony_ci		elmer &= ~ELMER0_GP_BIT6;
1138c2ecf20Sopenharmony_ci		t1_tpi_write(cphy->adapter, A_ELMER0_INT_ENABLE, elmer);
1148c2ecf20Sopenharmony_ci	}
1158c2ecf20Sopenharmony_ci	return 0;
1168c2ecf20Sopenharmony_ci}
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_cistatic int mv88x201x_interrupt_clear(struct cphy *cphy)
1198c2ecf20Sopenharmony_ci{
1208c2ecf20Sopenharmony_ci	u32 elmer;
1218c2ecf20Sopenharmony_ci	u32 val;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci#ifdef MV88x2010_LINK_STATUS_BUGS
1248c2ecf20Sopenharmony_ci	/* Required to read twice before clear takes affect. */
1258c2ecf20Sopenharmony_ci	cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_RXSTAT, &val);
1268c2ecf20Sopenharmony_ci	cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_TXSTAT, &val);
1278c2ecf20Sopenharmony_ci	cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_STAT, &val);
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	/* Read this register after the others above it else
1308c2ecf20Sopenharmony_ci	 * the register doesn't clear correctly.
1318c2ecf20Sopenharmony_ci	 */
1328c2ecf20Sopenharmony_ci	cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_STAT1, &val);
1338c2ecf20Sopenharmony_ci#endif
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	/* Clear link status. */
1368c2ecf20Sopenharmony_ci	cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_STAT1, &val);
1378c2ecf20Sopenharmony_ci	/* Clear PHY LASI interrupts. */
1388c2ecf20Sopenharmony_ci	cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_STAT, &val);
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci#ifdef MV88x2010_LINK_STATUS_BUGS
1418c2ecf20Sopenharmony_ci	/* Do it again. */
1428c2ecf20Sopenharmony_ci	cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_RXSTAT, &val);
1438c2ecf20Sopenharmony_ci	cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_TXSTAT, &val);
1448c2ecf20Sopenharmony_ci#endif
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	/* Clear Marvell interrupts through Elmer0. */
1478c2ecf20Sopenharmony_ci	if (t1_is_asic(cphy->adapter)) {
1488c2ecf20Sopenharmony_ci		t1_tpi_read(cphy->adapter, A_ELMER0_INT_CAUSE, &elmer);
1498c2ecf20Sopenharmony_ci		elmer |= ELMER0_GP_BIT6;
1508c2ecf20Sopenharmony_ci		t1_tpi_write(cphy->adapter, A_ELMER0_INT_CAUSE, elmer);
1518c2ecf20Sopenharmony_ci	}
1528c2ecf20Sopenharmony_ci	return 0;
1538c2ecf20Sopenharmony_ci}
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_cistatic int mv88x201x_interrupt_handler(struct cphy *cphy)
1568c2ecf20Sopenharmony_ci{
1578c2ecf20Sopenharmony_ci	/* Clear interrupts */
1588c2ecf20Sopenharmony_ci	mv88x201x_interrupt_clear(cphy);
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	/* We have only enabled link change interrupts and so
1618c2ecf20Sopenharmony_ci	 * cphy_cause must be a link change interrupt.
1628c2ecf20Sopenharmony_ci	 */
1638c2ecf20Sopenharmony_ci	return cphy_cause_link_change;
1648c2ecf20Sopenharmony_ci}
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_cistatic int mv88x201x_set_loopback(struct cphy *cphy, int on)
1678c2ecf20Sopenharmony_ci{
1688c2ecf20Sopenharmony_ci	return 0;
1698c2ecf20Sopenharmony_ci}
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_cistatic int mv88x201x_get_link_status(struct cphy *cphy, int *link_ok,
1728c2ecf20Sopenharmony_ci				     int *speed, int *duplex, int *fc)
1738c2ecf20Sopenharmony_ci{
1748c2ecf20Sopenharmony_ci	u32 val = 0;
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	if (link_ok) {
1778c2ecf20Sopenharmony_ci		/* Read link status. */
1788c2ecf20Sopenharmony_ci		cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_STAT1, &val);
1798c2ecf20Sopenharmony_ci		val &= MDIO_STAT1_LSTATUS;
1808c2ecf20Sopenharmony_ci		*link_ok = (val == MDIO_STAT1_LSTATUS);
1818c2ecf20Sopenharmony_ci		/* Turn on/off Link LED */
1828c2ecf20Sopenharmony_ci		led_link(cphy, *link_ok);
1838c2ecf20Sopenharmony_ci	}
1848c2ecf20Sopenharmony_ci	if (speed)
1858c2ecf20Sopenharmony_ci		*speed = SPEED_10000;
1868c2ecf20Sopenharmony_ci	if (duplex)
1878c2ecf20Sopenharmony_ci		*duplex = DUPLEX_FULL;
1888c2ecf20Sopenharmony_ci	if (fc)
1898c2ecf20Sopenharmony_ci		*fc = PAUSE_RX | PAUSE_TX;
1908c2ecf20Sopenharmony_ci	return 0;
1918c2ecf20Sopenharmony_ci}
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_cistatic void mv88x201x_destroy(struct cphy *cphy)
1948c2ecf20Sopenharmony_ci{
1958c2ecf20Sopenharmony_ci	kfree(cphy);
1968c2ecf20Sopenharmony_ci}
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_cistatic const struct cphy_ops mv88x201x_ops = {
1998c2ecf20Sopenharmony_ci	.destroy           = mv88x201x_destroy,
2008c2ecf20Sopenharmony_ci	.reset             = mv88x201x_reset,
2018c2ecf20Sopenharmony_ci	.interrupt_enable  = mv88x201x_interrupt_enable,
2028c2ecf20Sopenharmony_ci	.interrupt_disable = mv88x201x_interrupt_disable,
2038c2ecf20Sopenharmony_ci	.interrupt_clear   = mv88x201x_interrupt_clear,
2048c2ecf20Sopenharmony_ci	.interrupt_handler = mv88x201x_interrupt_handler,
2058c2ecf20Sopenharmony_ci	.get_link_status   = mv88x201x_get_link_status,
2068c2ecf20Sopenharmony_ci	.set_loopback      = mv88x201x_set_loopback,
2078c2ecf20Sopenharmony_ci	.mmds              = (MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS |
2088c2ecf20Sopenharmony_ci			      MDIO_DEVS_PHYXS | MDIO_DEVS_WIS),
2098c2ecf20Sopenharmony_ci};
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_cistatic struct cphy *mv88x201x_phy_create(struct net_device *dev, int phy_addr,
2128c2ecf20Sopenharmony_ci					 const struct mdio_ops *mdio_ops)
2138c2ecf20Sopenharmony_ci{
2148c2ecf20Sopenharmony_ci	u32 val;
2158c2ecf20Sopenharmony_ci	struct cphy *cphy = kzalloc(sizeof(*cphy), GFP_KERNEL);
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	if (!cphy)
2188c2ecf20Sopenharmony_ci		return NULL;
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	cphy_init(cphy, dev, phy_addr, &mv88x201x_ops, mdio_ops);
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	/* Commands the PHY to enable XFP's clock. */
2238c2ecf20Sopenharmony_ci	cphy_mdio_read(cphy, MDIO_MMD_PCS, 0x8300, &val);
2248c2ecf20Sopenharmony_ci	cphy_mdio_write(cphy, MDIO_MMD_PCS, 0x8300, val | 1);
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	/* Clear link status. Required because of a bug in the PHY.  */
2278c2ecf20Sopenharmony_ci	cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_STAT2, &val);
2288c2ecf20Sopenharmony_ci	cphy_mdio_read(cphy, MDIO_MMD_PCS, MDIO_STAT2, &val);
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	/* Allows for Link,Ack LED turn on/off */
2318c2ecf20Sopenharmony_ci	led_init(cphy);
2328c2ecf20Sopenharmony_ci	return cphy;
2338c2ecf20Sopenharmony_ci}
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci/* Chip Reset */
2368c2ecf20Sopenharmony_cistatic int mv88x201x_phy_reset(adapter_t *adapter)
2378c2ecf20Sopenharmony_ci{
2388c2ecf20Sopenharmony_ci	u32 val;
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	t1_tpi_read(adapter, A_ELMER0_GPO, &val);
2418c2ecf20Sopenharmony_ci	val &= ~4;
2428c2ecf20Sopenharmony_ci	t1_tpi_write(adapter, A_ELMER0_GPO, val);
2438c2ecf20Sopenharmony_ci	msleep(100);
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	t1_tpi_write(adapter, A_ELMER0_GPO, val | 4);
2468c2ecf20Sopenharmony_ci	msleep(1000);
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	/* Now lets enable the Laser. Delay 100us */
2498c2ecf20Sopenharmony_ci	t1_tpi_read(adapter, A_ELMER0_GPO, &val);
2508c2ecf20Sopenharmony_ci	val |= 0x8000;
2518c2ecf20Sopenharmony_ci	t1_tpi_write(adapter, A_ELMER0_GPO, val);
2528c2ecf20Sopenharmony_ci	udelay(100);
2538c2ecf20Sopenharmony_ci	return 0;
2548c2ecf20Sopenharmony_ci}
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ciconst struct gphy t1_mv88x201x_ops = {
2578c2ecf20Sopenharmony_ci	.create = mv88x201x_phy_create,
2588c2ecf20Sopenharmony_ci	.reset = mv88x201x_phy_reset
2598c2ecf20Sopenharmony_ci};
260