diff mbox

[U-Boot,04/14] powerpc/fman: add 10GEC controller and PHY support

Message ID 1296103972-2696-5-git-send-email-Mingkai.hu@freescale.com
State Superseded, archived
Headers show

Commit Message

Mingkai Hu Jan. 27, 2011, 4:52 a.m. UTC
From: Kumar Gala <galak@kernel.crashing.org>

Signed-off-by: Kumar Gala <galak@kernel.crashing.org>
Signed-off-by: Mingkai Hu <Mingkai.hu@freescale.com>
---
 drivers/net/fm/tgec.c     |  104 ++++++++++++++++++++
 drivers/net/fm/tgec.h     |  230 +++++++++++++++++++++++++++++++++++++++++++++
 drivers/net/fm/tgec_phy.c |  155 ++++++++++++++++++++++++++++++
 3 files changed, 489 insertions(+), 0 deletions(-)
 create mode 100644 drivers/net/fm/tgec.c
 create mode 100644 drivers/net/fm/tgec.h
 create mode 100644 drivers/net/fm/tgec_phy.c
diff mbox

Patch

diff --git a/drivers/net/fm/tgec.c b/drivers/net/fm/tgec.c
new file mode 100644
index 0000000..02a2476
--- /dev/null
+++ b/drivers/net/fm/tgec.c
@@ -0,0 +1,104 @@ 
+/*
+ * Copyright 2009-2010 Freescale Semiconductor, Inc.
+ *	Dave Liu <daveliu@freescale.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <common.h>
+#include <asm/types.h>
+#include <asm/io.h>
+#include <asm/fsl_enet.h>
+
+#include "tgec.h"
+
+static void tgec_init_mac(struct fsl_enet_mac *mac)
+{
+	struct tgec *regs = mac->base;
+
+	/* mask all interrupt */
+	out_be32(&regs->imask, IMASK_MASK_ALL);
+
+	/* clear all events */
+	out_be32(&regs->ievent, IEVENT_CLEAR_ALL);
+
+	/* set the max receive length */
+	out_be32(&regs->maxfrm, mac->max_rx_len);
+
+	/* 1588 disable, insert second mac disable
+	 * payload length check disable, normal operation,
+	 * any rx error frame is discarded, clear counters,
+	 * pause frame ignore, no promiscuous, LAN mode
+	 * Rx CRC no strip, Tx CRC append, Rx disable and Tx disable
+	 */
+	out_be32(&regs->command_config, TGEC_CMD_CFG_INIT);
+	udelay(1000);
+	out_be32(&regs->command_config, TGEC_CMD_CFG_FINAL);
+
+	/* multicast frame reception for the hash entry disable */
+	out_be32(&regs->hashtable_ctrl, 0);
+}
+
+static void tgec_enable_mac(struct fsl_enet_mac *mac)
+{
+	struct tgec *regs = mac->base;
+
+	setbits_be32(&regs->command_config, TGEC_CMD_CFG_RXTX_EN);
+}
+
+static void tgec_disable_mac(struct fsl_enet_mac *mac)
+{
+	struct tgec *regs = mac->base;
+
+	clrbits_be32(&regs->command_config, TGEC_CMD_CFG_RXTX_EN);
+}
+
+static void tgec_set_mac_addr(struct fsl_enet_mac *mac, u8 *mac_addr)
+{
+	struct tgec *regs = mac->base;
+	u32 mac_addr0, mac_addr1;
+
+	/* if a station address of 0x12345678ABCD, perform a write to
+	   MAC_ADDR0 of 0x78563412,
+	   MAC_ADDR1 of 0x0000CDAB */
+
+	mac_addr0 = (mac_addr[3] << 24) | (mac_addr[2] << 16) | \
+			(mac_addr[1] << 8)  | (mac_addr[0]);
+	out_be32(&regs->mac_addr_0, mac_addr0);
+
+	mac_addr1 = ((mac_addr[5] << 8) | mac_addr[4]) & 0x0000ffff;
+	out_be32(&regs->mac_addr_1, mac_addr1);
+}
+
+static void tgec_set_interface_mode(struct fsl_enet_mac *mac,
+					enum fsl_phy_enet_if type, int speed)
+{
+	/* nothing right now */
+	return;
+}
+
+void init_tgec(struct fsl_enet_mac *mac, void *base,
+		void *phyregs, int max_rx_len)
+{
+	mac->base = base;
+	mac->phyregs = phyregs;
+	mac->max_rx_len = max_rx_len;
+	mac->init_mac = tgec_init_mac;
+	mac->enable_mac = tgec_enable_mac;
+	mac->disable_mac = tgec_disable_mac;
+	mac->set_mac_addr = tgec_set_mac_addr;
+	mac->set_if_mode = tgec_set_interface_mode;
+}
diff --git a/drivers/net/fm/tgec.h b/drivers/net/fm/tgec.h
new file mode 100644
index 0000000..8d64db2
--- /dev/null
+++ b/drivers/net/fm/tgec.h
@@ -0,0 +1,230 @@ 
+/*
+ * Copyright 2009-2010 Freescale Semiconductor, Inc.
+ *	Dave Liu <daveliu@freescale.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#ifndef __TGEC_H__
+#define __TGEC_H__
+
+#include "../fsl_phy.h"
+
+/* EC10G_ID - 10-gigabit ethernet MAC controller ID
+ */
+#define EC10G_ID_VER_MASK	0x0000ff00
+#define EC10G_ID_VER_SHIFT	8
+#define EC10G_ID_REV_MASK	0x000000ff
+
+/* COMMAND_CONFIG - command and configuration register
+ */
+#define TGEC_CMD_CFG_EN_TIMESTAMP	0x00100000 /* enable IEEE1588 */
+#define TGEC_CMD_CFG_TX_ADDR_INS_SEL	0x00080000 /* Tx mac address with second */
+#define TGEC_CMD_CFG_NO_LEN_CHK		0x00020000 /* payload length check disable */
+#define TGEC_CMD_CFG_SEND_IDLE		0x00010000 /* send XGMII idle sequences */
+#define TGEC_CMD_CFG_RX_ER_DISC		0x00004000 /* enable receive error frame discard */
+#define TGEC_CMD_CFG_CMD_FRM_EN		0x00002000 /* all command frames are accepted */
+#define TGEC_CMD_CFG_STAT_CLR		0x00001000 /* all statistics counters are cleared */
+#define TGEC_CMD_CFG_TX_ADDR_INS	0x00000200 /* overwrite the souce MAC address */
+#define TGEC_CMD_CFG_PAUSE_IGNORE	0x00000100 /* ignores received pause frames */
+#define TGEC_CMD_CFG_PAUSE_FWD		0x00000080 /* forwards pause frames to the user app */
+#define TGEC_CMD_CFG_CRC_FWD		0x00000040 /* MAC forward Rx CRC to user */
+#define TGEC_CMD_CFG_PAD_EN		0x00000020 /* MAC remove Rx padding */
+#define TGEC_CMD_CFG_PROM_EN		0x00000010 /* promiscuous mode enable */
+#define TGEC_CMD_CFG_WAN_MODE		0x00000008 /* WAN mode enable */
+#define TGEC_CMD_CFG_RX_EN		0x00000002 /* MAC receive path enable */
+#define TGEC_CMD_CFG_TX_EN		0x00000001 /* MAC transmit path enable */
+
+#define TGEC_CMD_CFG_RXTX_EN		(TGEC_CMD_CFG_RX_EN | TGEC_CMD_CFG_TX_EN)
+
+#define TGEC_CMD_CFG_INIT	(TGEC_CMD_CFG_NO_LEN_CHK | \
+				 TGEC_CMD_CFG_RX_ER_DISC | \
+				 TGEC_CMD_CFG_STAT_CLR | \
+				 TGEC_CMD_CFG_PAUSE_IGNORE | \
+				 TGEC_CMD_CFG_CRC_FWD)
+
+#define TGEC_CMD_CFG_FINAL	(TGEC_CMD_CFG_NO_LEN_CHK | \
+				 TGEC_CMD_CFG_RX_ER_DISC | \
+				 TGEC_CMD_CFG_PAUSE_IGNORE | \
+				 TGEC_CMD_CFG_CRC_FWD)
+/* HASHTABLE_CTRL - Hashtable control register
+ */
+#define HASHTABLE_CTRL_MCAST_EN	0x00000200 /* enable mulitcast Rx hash */
+#define HASHTABLE_CTRL_ADDR_MASK	0x000001ff
+
+/* TX_IPG_LENGTH - Transmit inter-packet gap length register
+ */
+#define TX_IPG_LENGTH_IPG_LEN_MASK	0x000003ff
+
+/* IMASK - interrupt mask register
+ */
+#define IMASK_MDIO_SCAN_EVENT	0x00010000 /* MDIO scan event mask */
+#define IMASK_MDIO_CMD_CMPL	0x00008000 /* MDIO cmd completion mask */
+#define IMASK_REM_FAULT		0x00004000 /* remote fault mask */
+#define IMASK_LOC_FAULT		0x00002000 /* local fault mask */
+#define IMASK_TX_ECC_ER		0x00001000 /* Tx frame ECC error mask */
+#define IMASK_TX_FIFO_UNFL	0x00000800 /* Tx FIFO underflow mask */
+#define IMASK_TX_ER		0x00000200 /* Tx frame error mask */
+#define IMASK_RX_FIFO_OVFL	0x00000100 /* Rx FIFO overflow mask */
+#define IMASK_RX_ECC_ER		0x00000080 /* Rx frame ECC error mask */
+#define IMASK_RX_JAB_FRM	0x00000040 /* Rx jabber frame mask */
+#define IMASK_RX_OVRSZ_FRM	0x00000020 /* Rx oversized frame mask */
+#define IMASK_RX_RUNT_FRM	0x00000010 /* Rx runt frame mask */
+#define IMASK_RX_FRAG_FRM	0x00000008 /* Rx fragment frame mask */
+#define IMASK_RX_LEN_ER		0x00000004 /* Rx payload length error mask */
+#define IMASK_RX_CRC_ER		0x00000002 /* Rx CRC error mask */
+#define IMASK_RX_ALIGN_ER	0x00000001 /* Rx alignment error mask */
+
+#define IMASK_MASK_ALL		0x00000000
+
+/* IEVENT - interrupt event register
+ */
+#define IEVENT_MDIO_SCAN_EVENT	0x00010000 /* MDIO scan event */
+#define IEVENT_MDIO_CMD_CMPL	0x00008000 /* MDIO cmd completion */
+#define IEVENT_REM_FAULT	0x00004000 /* remote fault */
+#define IEVENT_LOC_FAULT	0x00002000 /* local fault */
+#define IEVENT_TX_ECC_ER	0x00001000 /* Tx frame ECC error */
+#define IEVENT_TX_FIFO_UNFL	0x00000800 /* Tx FIFO underflow */
+#define IEVENT_TX_ER		0x00000200 /* Tx frame error */
+#define IEVENT_RX_FIFO_OVFL	0x00000100 /* Rx FIFO overflow */
+#define IEVENT_RX_ECC_ER	0x00000080 /* Rx frame ECC error */
+#define IEVENT_RX_JAB_FRM	0x00000040 /* Rx jabber frame */
+#define IEVENT_RX_OVRSZ_FRM	0x00000020 /* Rx oversized frame */
+#define IEVENT_RX_RUNT_FRM	0x00000010 /* Rx runt frame */
+#define IEVENT_RX_FRAG_FRM	0x00000008 /* Rx fragment frame */
+#define IEVENT_RX_LEN_ER	0x00000004 /* Rx payload length error */
+#define IEVENT_RX_CRC_ER	0x00000002 /* Rx CRC error */
+#define IEVENT_RX_ALIGN_ER	0x00000001 /* Rx alignment error */
+
+#define IEVENT_CLEAR_ALL	0xffffffff
+
+struct tgec {
+	/* 10GEC general control and status registers */
+	u32	tgec_id;	/* Controller ID register */
+	u32	res0;
+	u32	command_config;	/* Control and configuration register */
+	u32	mac_addr_0;	/* Lower 32 bits of 48-bit MAC address */
+	u32	mac_addr_1;	/* Upper 16 bits of 48-bit MAC address */
+	u32	maxfrm;		/* Maximum frame length register */
+	u32	pause_quant;	/* Pause quanta register */
+	u32	res1[4];
+	u32	hashtable_ctrl;	/* Hash table control register */
+	u32	res2[4];
+	u32	status;		/* MAC status register */
+	u32	tx_ipg_length;	/* Transmitter inter-packet-gap register */
+	u32	mac_addr_2;	/* Lower 32 bits of the second 48-bit MAC address */
+	u32	mac_addr_3;	/* Upper 16 bits of the second 48-bit MAC address */
+	u32	res3[4];
+	u32	imask;		/* Interrupt mask register */
+	u32	ievent;		/* Interrupt event register */
+	u32	res4[6];
+	/* 10GEC statistics counter registers */
+	u32	tx_frame_u;	/* Transmit frame counter upper */
+	u32	tx_frame_l;	/* Transmit frame counter lower */
+	u32	rx_frame_u;	/* Receive frame counter upper */
+	u32	rx_frame_l;	/* Receive frame counter lower */
+	u32	rx_frame_crc_err_u;	/* Receive frame check sequence error upper */
+	u32	rx_frame_crc_err_l;	/* Receive frame check sequence error lower */
+	u32	rx_align_err_u;	/* Receive alignment error upper */
+	u32	rx_align_err_l;	/* Receive alignment error lower */
+	u32	tx_pause_frame_u;	/* Transmit valid pause frame upper */
+	u32	tx_pause_frame_l;	/* Transmit valid pause frame lower */
+	u32	rx_pause_frame_u;	/* Receive valid pause frame upper */
+	u32	rx_pause_frame_l;	/* Receive valid pause frame upper */
+	u32	rx_long_err_u;	/* Receive too long frame error upper */
+	u32	rx_long_err_l;	/* Receive too long frame error lower */
+	u32	rx_frame_err_u;	/* Receive frame length error upper */
+	u32	rx_frame_err_l;	/* Receive frame length error lower */
+	u32	tx_vlan_u;	/* Transmit VLAN frame upper */
+	u32	tx_vlan_l;	/* Transmit VLAN frame lower */
+	u32	rx_vlan_u;	/* Receive VLAN frame upper */
+	u32	rx_vlan_l;	/* Receive VLAN frame lower */
+	u32	tx_oct_u;	/* Transmit octets upper */
+	u32	tx_oct_l;	/* Transmit octets lower */
+	u32	rx_oct_u;	/* Receive octets upper */
+	u32	rx_oct_l;	/* Receive octets lower */
+	u32	rx_uni_u;	/* Receive unicast frame upper */
+	u32	rx_uni_l;	/* Receive unicast frame lower */
+	u32	rx_multi_u;	/* Receive multicast frame upper */
+	u32	rx_multi_l;	/* Receive multicast frame lower */
+	u32	rx_brd_u;	/* Receive broadcast frame upper */
+	u32	rx_brd_l;	/* Receive broadcast frame lower */
+	u32	tx_frame_err_u;	/* Transmit frame error upper */
+	u32	tx_frame_err_l;	/* Transmit frame error lower */
+	u32	tx_uni_u;	/* Transmit unicast frame upper */
+	u32	tx_uni_l;	/* Transmit unicast frame lower */
+	u32	tx_multi_u;	/* Transmit multicast frame upper */
+	u32	tx_multi_l;	/* Transmit multicast frame lower */
+	u32	tx_brd_u;	/* Transmit broadcast frame upper */
+	u32	tx_brd_l;	/* Transmit broadcast frame lower */
+	u32	rx_drop_u;	/* Receive dropped packets upper */
+	u32	rx_drop_l;	/* Receive dropped packets lower */
+	u32	rx_eoct_u;	/* Receive ethernet octets upper */
+	u32	rx_eoct_l;	/* Receive ethernet octets lower */
+	u32	rx_pkt_u;	/* Receive packets upper */
+	u32	rx_pkt_l;	/* Receive packets lower */
+	u32	tx_undsz_u;	/* Undersized packet upper */
+	u32	tx_undsz_l;	/* Undersized packet lower */
+	u32	rx_64_u;	/* Receive 64 oct packet upper */
+	u32	rx_64_l;	/* Receive 64 oct packet lower */
+	u32	rx_127_u;	/* Receive 65 to 127 oct packet upper */
+	u32	rx_127_l;	/* Receive 65 to 127 oct packet lower */
+	u32	rx_255_u;	/* Receive 128 to 255 oct packet upper */
+	u32	rx_255_l;	/* Receive 128 to 255 oct packet lower */
+	u32	rx_511_u;	/* Receive 256 to 511 oct packet upper */
+	u32	rx_511_l;	/* Receive 256 to 511 oct packet lower */
+	u32	rx_1023_u;	/* Receive 512 to 1023 oct packet upper */
+	u32	rx_1023_l;	/* Receive 512 to 1023 oct packet lower */
+	u32	rx_1518_u;	/* Receive 1024 to 1518 oct packet upper */
+	u32	rx_1518_l;	/* Receive 1024 to 1518 oct packet lower */
+	u32	rx_1519_u;	/* Receive 1519 to max oct packet upper */
+	u32	rx_1519_l;	/* Receive 1519 to max oct packet lower */
+	u32	tx_oversz_u;	/* oversized packet upper */
+	u32	tx_oversz_l;	/* oversized packet lower */
+	u32	tx_jabber_u;	/* Jabber packet upper */
+	u32	tx_jabber_l;	/* Jabber packet lower */
+	u32	tx_frag_u;	/* Fragment packet upper */
+	u32	tx_frag_l;	/* Fragment packet lower */
+	u32	rx_err_u;	/* Receive frame error upper */
+	u32	rx_err_l;	/* Receive frame error lower */
+	u32	res5[0x39a];
+} __attribute__ ((packed));
+
+struct tgec_mdio_controller {
+	u32	res0[0xc];
+	u32	mdio_stat;	/* MDIO configuration and status */
+	u32	mdio_ctl;	/* MDIO control */
+	u32	mdio_data;	/* MDIO data */
+	u32	mdio_addr;	/* MDIO address */
+} __attribute__ ((packed));
+
+#define MDIO_STAT_CLKDIV(x)	(((x>>1) & 0xff) << 8)
+#define MDIO_STAT_BSY		(1 << 0)
+#define MDIO_STAT_RD_ER		(1 << 1)
+#define MDIO_CTL_DEV_ADDR(x)	(x & 0x1f)
+#define MDIO_CTL_PORT_ADDR(x)	((x & 0x1f) << 5)
+#define MDIO_CTL_PRE_DIS	(1 << 10)
+#define MDIO_CTL_SCAN_EN	(1 << 11)
+#define MDIO_CTL_POST_INC	(1 << 14)
+#define MDIO_CTL_READ		(1 << 15)
+
+#define MDIO_DATA(x)		(x & 0xffff)
+#define MDIO_DATA_BSY		(1 << 31)
+
+void init_tgec(struct fsl_enet_mac *mac, void *base, void *phyregs, int max_rx_len);
+struct phy_info *tgec_get_phy_info(struct mii_info *mii_info);
+
+#endif
diff --git a/drivers/net/fm/tgec_phy.c b/drivers/net/fm/tgec_phy.c
new file mode 100644
index 0000000..cf95e02
--- /dev/null
+++ b/drivers/net/fm/tgec_phy.c
@@ -0,0 +1,155 @@ 
+/*
+ * Copyright 2009-2010 Freescale Semiconductor, Inc.
+ *	Andy Fleming <afleming@freescale.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ * Some part is taken from tsec.c
+ */
+#include <common.h>
+#include <asm/io.h>
+
+#include "../fsl_phy.h"
+#include "tgec.h"
+
+/* Write value to the PHY for this device to the register at regnum, */
+/* waiting until the write is done before it returns.  All PHY */
+/* configuration has to be done through the TSEC1 MIIM regs */
+int tgec_write_phy_reg(struct tgec_mdio_controller *regs, int port_addr,
+			int dev_addr, int regnum, int value)
+{
+	u32 mdio_ctl;
+	u32 stat_val;
+
+	/* Wait till the bus is free */
+	stat_val = MDIO_STAT_CLKDIV(100);
+	out_be32(&regs->mdio_stat, stat_val);
+	while ((in_be32(&regs->mdio_stat)) & MDIO_STAT_BSY);
+
+	/* Set the port and dev addr */
+	mdio_ctl = MDIO_CTL_PORT_ADDR(port_addr) | MDIO_CTL_DEV_ADDR(dev_addr);
+	out_be32(&regs->mdio_ctl, mdio_ctl);
+
+	/* Set the register address */
+	out_be32(&regs->mdio_addr, regnum & 0xffff);
+
+	/* Wait till the bus is free */
+	while ((in_be32(&regs->mdio_stat)) & MDIO_STAT_BSY);
+
+	/* Write the value to the register */
+	out_be32(&regs->mdio_data, MDIO_DATA(value));
+
+	/* Wait till the MDIO write is complete */
+	while ((in_be32(&regs->mdio_data)) & MDIO_DATA_BSY);
+
+	return 0;
+}
+
+/* Reads from register regnum in the PHY for device dev, */
+/* returning the value.  Clears miimcom first.  All PHY */
+/* configuration has to be done through the TSEC1 MIIM regs */
+int tgec_read_phy_reg(struct tgec_mdio_controller *regs, int port_addr,
+	int dev_addr, int regnum)
+{
+	u32 mdio_ctl;
+	u32 stat_val;
+
+	stat_val = MDIO_STAT_CLKDIV(100);
+	out_be32(&regs->mdio_stat, stat_val);
+	/* Wait till the bus is free */
+	while ((in_be32(&regs->mdio_stat)) & MDIO_STAT_BSY);
+
+	/* Set the Port and Device Addrs */
+	mdio_ctl = MDIO_CTL_PORT_ADDR(port_addr) | MDIO_CTL_DEV_ADDR(dev_addr);
+	out_be32(&regs->mdio_ctl, mdio_ctl);
+
+	/* Set the register address */
+	out_be32(&regs->mdio_addr, regnum & 0xffff);
+
+	/* Wait till the bus is free */
+	while ((in_be32(&regs->mdio_stat)) & MDIO_STAT_BSY);
+
+	/* Initiate the read */
+	mdio_ctl |= MDIO_CTL_READ;
+	out_be32(&regs->mdio_ctl, mdio_ctl);
+
+	/* Wait till the MDIO write is complete */
+	while ((in_be32(&regs->mdio_data)) & MDIO_DATA_BSY);
+
+	return in_be32(&regs->mdio_data) & 0xffff;
+}
+
+int tgec_phy_write(struct mii_info *mii_info, int dev_addr, int regnum,
+		int value)
+{
+	struct tgec_mdio_controller *regs = mii_info->phyregs;
+
+	return tgec_write_phy_reg(regs, mii_info->mii_id, dev_addr, regnum,
+			value);
+}
+
+int tgec_phy_read(struct mii_info *mii_info, int dev_addr, int regnum)
+{
+	struct tgec_mdio_controller *regs = mii_info->phyregs;
+
+	return tgec_read_phy_reg(regs, mii_info->mii_id, dev_addr, regnum);
+}
+
+/* For now, just assume 10G, and use AN as basis for link state */
+int tgec_shutdown_phy(struct mii_info *mii_info)
+{
+	return 0;
+}
+int tgec_startup_phy(struct mii_info *mii_info)
+{
+	mii_info->link = 1;
+	mii_info->speed = 1000;
+	mii_info->duplex = 1;
+	return 0;
+}
+
+int tgec_config_phy(struct mii_info *mii_info)
+{
+	return 0;
+}
+
+int tn2020_config_phy(struct mii_info *mii_info)
+{
+	if (mii_info->port == PORT_FIBRE) {
+		tgec_phy_write(mii_info, 30, 93, 2);
+		tgec_phy_write(mii_info, 7, 0, 0x3200);
+	}
+
+	return 0;
+}
+
+/* Giant hack. */
+struct phy_info phy_info_tgec = {
+	"Teranetics TN2020",
+	0xf00,
+	0xf00,
+	&tn2020_config_phy,
+	&tgec_startup_phy,
+	&tgec_shutdown_phy,
+};
+
+struct phy_info *tgec_phy_info[] = {
+	&phy_info_tgec,
+};
+
+struct phy_info *tgec_get_phy_info(struct mii_info *mii_info)
+{
+	return &phy_info_tgec;
+}