summaryrefslogtreecommitdiffstats
path: root/utils/fetch_os_creds.sh
blob: c1e21f316298fb4c165759ffe4c2bea05f2d0e89 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
#!/bin/bash
##############################################################################
# Copyright (c) 2015 Ericsson AB and others.
# jose.lausuch@ericsson.com
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Apache License, Version 2.0
# which accompanies this distribution, and is available at
# http://www.apache.org/licenses/LICENSE-2.0
##############################################################################


usage() {
    echo "usage: $0 [-v] -d <destination> -i <installer_type> -a <installer_ip>" >&2
    echo "[-v] Virtualized deployment" >&2
}

info ()  {
    logger -s -t "fetch_os_creds.info" "$*"
}


error () {
    logger -s -t "fetch_os_creds.error" "$*"
    exit 1
}


verify_connectivity() {
    local ip=$1
    info "Verifying connectivity to $ip..."
    for i in $(seq 0 10); do
        if ping -c 1 -W 1 $ip > /dev/null; then
            info "$ip is reachable!"
            return 0
        fi
        sleep 1
    done
    error "Can not talk to $ip."
}


swap_to_public() {
    if [ "$1" != "" ]; then
        info "Exchanging keystone public IP in rc file to $public_ip"
        sed -i  "/OS_AUTH_URL/c\export OS_AUTH_URL=\'$public_ip'" $dest_path
        sed -i 's/internalURL/publicURL/g' $dest_path
    fi
}


: ${DEPLOY_TYPE:=''}

#Get options
while getopts ":d:i:a:h:v" optchar; do
    case "${optchar}" in
        d) dest_path=${OPTARG} ;;
        i) installer_type=${OPTARG} ;;
        a) installer_ip=${OPTARG} ;;
        v) DEPLOY_TYPE="virt" ;;
        *) echo "Non-option argument: '-${OPTARG}'" >&2
           usage
           exit 2
           ;;
    esac
done

# set vars from env if not provided by user as options
dest_path=${dest_path:-$HOME/opnfv-openrc.sh}
installer_type=${installer_type:-$INSTALLER_TYPE}
installer_ip=${installer_ip:-$INSTALLER_IP}

if [ -z $dest_path ] || [ -z $installer_type ] || [ -z $installer_ip ]; then
    usage
    exit 2
fi

# Checking if destination path is valid
if [ -d $dest_path ]; then
    error "Please provide the full destination path for the credentials file including the filename"
else
    # Check if we can create the file (e.g. path is correct)
    touch $dest_path || error "Cannot create the file specified. Check that the path is correct and run the script again."
fi


ssh_options="-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"

# Start fetching the files
if [ "$installer_type" == "fuel" ]; then
    #ip_fuel="10.20.0.2"
    verify_connectivity $installer_ip

    env=$(sshpass -p r00tme ssh 2>/dev/null $ssh_options root@${installer_ip} \
        'fuel env'|grep operational|head -1|awk '{print $1}') &> /dev/null
    if [ -z $env ]; then
        error "No operational environment detected in Fuel"
    fi
    env_id="${FUEL_ENV:-$env}"

    # Check if controller is alive (online='True')
    controller_ip=$(sshpass -p r00tme ssh 2>/dev/null $ssh_options root@${installer_ip} \
        "fuel node --env ${env_id} | grep controller | grep 'True\|  1' | awk -F\| '{print \$5}' | head -1" | \
        sed 's/ //g') &> /dev/null

    if [ -z $controller_ip ]; then
        error "The controller $controller_ip is not up. Please check that the POD is correctly deployed."
    fi

    info "Fetching rc file from controller $controller_ip..."
    sshpass -p r00tme ssh 2>/dev/null $ssh_options root@${installer_ip} \
        "scp $ssh_options ${controller_ip}:/root/openrc ." &> /dev/null
    sshpass -p r00tme scp 2>/dev/null $ssh_options root@${installer_ip}:~/openrc $dest_path &> /dev/null

    #This file contains the mgmt keystone API, we need the public one for our rc file
    admin_ip=$(cat $dest_path | grep "OS_AUTH_URL" | sed 's/^.*\=//' | sed "s/^\([\"']\)\(.*\)\1\$/\2/g" | sed s'/\/$//')
    public_ip=$(sshpass -p r00tme ssh $ssh_options root@${installer_ip} \
        "ssh ${controller_ip} 'source openrc; openstack endpoint list'" \
        | grep keystone | grep public | sed 's/ /\n/g' | grep ^http | head -1) &> /dev/null
        #| grep http | head -1 | cut -d '|' -f 4 | sed 's/v1\/.*/v1\//' | sed 's/ //g') &> /dev/null
    #NOTE: this is super ugly sed 's/v1\/.*/v1\//'OS_AUTH_URL
    # but sometimes the output of endpoint-list is like this: http://172.30.9.70:8004/v1/%(tenant_id)s
    # Fuel virtual need a fix

    #convert to v3 URL
    auth_url=$(cat $dest_path|grep AUTH_URL)
    if [[ -z `echo $auth_url |grep v3` ]]; then
        auth_url=$(echo $auth_url |sed "s|'$|v3&|")
    fi
    sed -i '/AUTH_URL/d' $dest_path
    echo $auth_url >> $dest_path

elif [ "$installer_type" == "apex" ]; then
    verify_connectivity $installer_ip

    # The credentials file is located in the Instack VM (192.0.2.1)
    # NOTE: This might change for bare metal deployments
    info "Fetching rc file from Instack VM $installer_ip..."
    if [ -f /root/.ssh/id_rsa ]; then
        chmod 600 /root/.ssh/id_rsa
    fi
    sudo scp $ssh_options root@$installer_ip:/home/stack/overcloudrc $dest_path

elif [ "$installer_type" == "compass" ]; then
    verify_connectivity $installer_ip
    controller_ip=$(sshpass -p'root' ssh 2>/dev/null $ssh_options root@${installer_ip} \
        'mysql -ucompass -pcompass -Dcompass -e"select *  from cluster;"' \
        | awk -F"," '{for(i=1;i<NF;i++)if($i~/\"host[1-5]\"/) {print $(i+1);break;}}'  \
        | grep -oP "\d+.\d+.\d+.\d+")

    if [ -z $controller_ip ]; then
        error "The controller $controller_ip is not up. Please check that the POD is correctly deployed."
    fi

    info "Fetching rc file from controller $controller_ip..."
    sshpass -p root ssh 2>/dev/null $ssh_options root@${installer_ip} \
        "scp $ssh_options ${controller_ip}:/opt/admin-openrc.sh ." &> /dev/null
    sshpass -p root scp 2>/dev/null $ssh_options root@${installer_ip}:~/admin-openrc.sh $dest_path &> /dev/null

    info "This file contains the mgmt keystone API, we need the public one for our rc file"
    grep "OS_AUTH_URL.*v2" $dest_path > /dev/null 2>&1
    if [ $?  -eq 0 ] ; then
        public_ip=$(sshpass -p root ssh $ssh_options root@${installer_ip} \
            "ssh ${controller_ip} 'source /opt/admin-openrc.sh; openstack endpoint show identity '" \
            | grep publicurl | awk '{print $4}')
    else
        public_ip=$(sshpass -p root ssh $ssh_options root@${installer_ip} \
            "ssh ${controller_ip} 'source /opt/admin-openrc.sh; \
                 openstack endpoint list --interface public --service identity '" \
            | grep identity | awk '{print $14}')
    fi
    info "public_ip: $public_ip"
    swap_to_public $public_ip


elif [ "$installer_type" == "joid" ]; then
    # do nothing...for the moment
    # we can either do a scp from the jumphost or use the -v option to transmit the param to the docker file
    echo "Do nothing, creds will be provided through volume option at docker creation for joid"

elif [ "$installer_type" == "foreman" ]; then
    #ip_foreman="172.30.10.73"
    controller="oscontroller1.opnfv.com"
    verify_connectivity $installer_ip

    # Check if controller is alive (here is more difficult to get the ip from a command like "fuel node")
    sshpass -p vagrant ssh $ssh_options root@${installer_ip} \
        "sshpass -p Op3nStack ssh $ssh_options root@${controller} 'ls'" &> /dev/null
    if [ $? -ne 0 ]; then
        error "The controller ${controller} is not up. Please check that the POD is correctly deployed."
    fi

    info "Fetching openrc from a Foreman Controller '${controller}'..."
    sshpass -p vagrant ssh $ssh_options root@${installer_ip} \
        "sshpass -p Op3nStack scp $ssh_options root@${controller}:~/keystonerc_admin ." &> /dev/null
    sshpass -p vagrant scp $ssh_options root@${installer_ip}:~/keystonerc_admin $dest_path &> /dev/null

    #This file contains the mgmt keystone API, we need the public one for our rc file
    admin_ip=$(cat $dest_path | grep "OS_AUTH_URL" | sed 's/^.*\=//' | sed "s/^\([\"']\)\(.*\)\1\$/\2/g" | sed s'/\/$//')
    public_ip=$(sshpass -p vagrant ssh $ssh_options root@${installer_ip} \
        "sshpass -p Op3nStack ssh $ssh_options root@${controller} \
        'source keystonerc_admin;keystone endpoint-list'" \
        | grep $admin_ip | sed 's/ /\n/g' | grep ^http | head -1) &> /dev/null

else
    error "Installer $installer is not supported by this script"
fi


if [ ! -f $dest_path ]; then
    error "There has been an error retrieving the credentials"
fi

echo "-------- Credentials: --------"
cat $dest_path

exit 0
id='n1112' href='#n1112'>1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199
/*
 * PHY drivers for the sungem ethernet driver.
 *
 * This file could be shared with other drivers.
 *
 * (c) 2002-2007, Benjamin Herrenscmidt (benh@kernel.crashing.org)
 *
 * TODO:
 *  - Add support for PHYs that provide an IRQ line
 *  - Eventually moved the entire polling state machine in
 *    there (out of the eth driver), so that it can easily be
 *    skipped on PHYs that implement it in hardware.
 *  - On LXT971 & BCM5201, Apple uses some chip specific regs
 *    to read the link status. Figure out why and if it makes
 *    sense to do the same (magic aneg ?)
 *  - Apple has some additional power management code for some
 *    Broadcom PHYs that they "hide" from the OpenSource version
 *    of darwin, still need to reverse engineer that
 */


#include <linux/module.h>

#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/mii.h>
#include <linux/ethtool.h>
#include <linux/delay.h>

#ifdef CONFIG_PPC_PMAC
#include <asm/prom.h>
#endif

#include <linux/sungem_phy.h>

/* Link modes of the BCM5400 PHY */
static const int phy_BCM5400_link_table[8][3] = {
	{ 0, 0, 0 },	/* No link */
	{ 0, 0, 0 },	/* 10BT Half Duplex */
	{ 1, 0, 0 },	/* 10BT Full Duplex */
	{ 0, 1, 0 },	/* 100BT Half Duplex */
	{ 0, 1, 0 },	/* 100BT Half Duplex */
	{ 1, 1, 0 },	/* 100BT Full Duplex*/
	{ 1, 0, 1 },	/* 1000BT */
	{ 1, 0, 1 },	/* 1000BT */
};

static inline int __sungem_phy_read(struct mii_phy* phy, int id, int reg)
{
	return phy->mdio_read(phy->dev, id, reg);
}

static inline void __sungem_phy_write(struct mii_phy* phy, int id, int reg, int val)
{
	phy->mdio_write(phy->dev, id, reg, val);
}

static inline int sungem_phy_read(struct mii_phy* phy, int reg)
{
	return phy->mdio_read(phy->dev, phy->mii_id, reg);
}

static inline void sungem_phy_write(struct mii_phy* phy, int reg, int val)
{
	phy->mdio_write(phy->dev, phy->mii_id, reg, val);
}

static int reset_one_mii_phy(struct mii_phy* phy, int phy_id)
{
	u16 val;
	int limit = 10000;

	val = __sungem_phy_read(phy, phy_id, MII_BMCR);
	val &= ~(BMCR_ISOLATE | BMCR_PDOWN);
	val |= BMCR_RESET;
	__sungem_phy_write(phy, phy_id, MII_BMCR, val);

	udelay(100);

	while (--limit) {
		val = __sungem_phy_read(phy, phy_id, MII_BMCR);
		if ((val & BMCR_RESET) == 0)
			break;
		udelay(10);
	}
	if ((val & BMCR_ISOLATE) && limit > 0)
		__sungem_phy_write(phy, phy_id, MII_BMCR, val & ~BMCR_ISOLATE);

	return limit <= 0;
}

static int bcm5201_init(struct mii_phy* phy)
{
	u16 data;

	data = sungem_phy_read(phy, MII_BCM5201_MULTIPHY);
	data &= ~MII_BCM5201_MULTIPHY_SUPERISOLATE;
	sungem_phy_write(phy, MII_BCM5201_MULTIPHY, data);

	sungem_phy_write(phy, MII_BCM5201_INTERRUPT, 0);

	return 0;
}

static int bcm5201_suspend(struct mii_phy* phy)
{
	sungem_phy_write(phy, MII_BCM5201_INTERRUPT, 0);
	sungem_phy_write(phy, MII_BCM5201_MULTIPHY, MII_BCM5201_MULTIPHY_SUPERISOLATE);

	return 0;
}

static int bcm5221_init(struct mii_phy* phy)
{
	u16 data;

	data = sungem_phy_read(phy, MII_BCM5221_TEST);
	sungem_phy_write(phy, MII_BCM5221_TEST,
		data | MII_BCM5221_TEST_ENABLE_SHADOWS);

	data = sungem_phy_read(phy, MII_BCM5221_SHDOW_AUX_STAT2);
	sungem_phy_write(phy, MII_BCM5221_SHDOW_AUX_STAT2,
		data | MII_BCM5221_SHDOW_AUX_STAT2_APD);

	data = sungem_phy_read(phy, MII_BCM5221_SHDOW_AUX_MODE4);
	sungem_phy_write(phy, MII_BCM5221_SHDOW_AUX_MODE4,
		data | MII_BCM5221_SHDOW_AUX_MODE4_CLKLOPWR);

	data = sungem_phy_read(phy, MII_BCM5221_TEST);
	sungem_phy_write(phy, MII_BCM5221_TEST,
		data & ~MII_BCM5221_TEST_ENABLE_SHADOWS);

	return 0;
}

static int bcm5221_suspend(struct mii_phy* phy)
{
	u16 data;

	data = sungem_phy_read(phy, MII_BCM5221_TEST);
	sungem_phy_write(phy, MII_BCM5221_TEST,
		data | MII_BCM5221_TEST_ENABLE_SHADOWS);

	data = sungem_phy_read(phy, MII_BCM5221_SHDOW_AUX_MODE4);
	sungem_phy_write(phy, MII_BCM5221_SHDOW_AUX_MODE4,
		  data | MII_BCM5221_SHDOW_AUX_MODE4_IDDQMODE);

	return 0;
}

static int bcm5241_init(struct mii_phy* phy)
{
	u16 data;

	data = sungem_phy_read(phy, MII_BCM5221_TEST);
	sungem_phy_write(phy, MII_BCM5221_TEST,
		data | MII_BCM5221_TEST_ENABLE_SHADOWS);

	data = sungem_phy_read(phy, MII_BCM5221_SHDOW_AUX_STAT2);
	sungem_phy_write(phy, MII_BCM5221_SHDOW_AUX_STAT2,
		data | MII_BCM5221_SHDOW_AUX_STAT2_APD);

	data = sungem_phy_read(phy, MII_BCM5221_SHDOW_AUX_MODE4);
	sungem_phy_write(phy, MII_BCM5221_SHDOW_AUX_MODE4,
		data & ~MII_BCM5241_SHDOW_AUX_MODE4_STANDBYPWR);

	data = sungem_phy_read(phy, MII_BCM5221_TEST);
	sungem_phy_write(phy, MII_BCM5221_TEST,
		data & ~MII_BCM5221_TEST_ENABLE_SHADOWS);

	return 0;
}

static int bcm5241_suspend(struct mii_phy* phy)
{
	u16 data;

	data = sungem_phy_read(phy, MII_BCM5221_TEST);
	sungem_phy_write(phy, MII_BCM5221_TEST,
		data | MII_BCM5221_TEST_ENABLE_SHADOWS);

	data = sungem_phy_read(phy, MII_BCM5221_SHDOW_AUX_MODE4);
	sungem_phy_write(phy, MII_BCM5221_SHDOW_AUX_MODE4,
		  data | MII_BCM5241_SHDOW_AUX_MODE4_STANDBYPWR);

	return 0;
}

static int bcm5400_init(struct mii_phy* phy)
{
	u16 data;

	/* Configure for gigabit full duplex */
	data = sungem_phy_read(phy, MII_BCM5400_AUXCONTROL);
	data |= MII_BCM5400_AUXCONTROL_PWR10BASET;
	sungem_phy_write(phy, MII_BCM5400_AUXCONTROL, data);

	data = sungem_phy_read(phy, MII_BCM5400_GB_CONTROL);
	data |= MII_BCM5400_GB_CONTROL_FULLDUPLEXCAP;
	sungem_phy_write(phy, MII_BCM5400_GB_CONTROL, data);

	udelay(100);

	/* Reset and configure cascaded 10/100 PHY */
	(void)reset_one_mii_phy(phy, 0x1f);

	data = __sungem_phy_read(phy, 0x1f, MII_BCM5201_MULTIPHY);
	data |= MII_BCM5201_MULTIPHY_SERIALMODE;
	__sungem_phy_write(phy, 0x1f, MII_BCM5201_MULTIPHY, data);

	data = sungem_phy_read(phy, MII_BCM5400_AUXCONTROL);
	data &= ~MII_BCM5400_AUXCONTROL_PWR10BASET;
	sungem_phy_write(phy, MII_BCM5400_AUXCONTROL, data);

	return 0;
}

static int bcm5400_suspend(struct mii_phy* phy)
{
#if 0 /* Commented out in Darwin... someone has those dawn docs ? */
	sungem_phy_write(phy, MII_BMCR, BMCR_PDOWN);
#endif
	return 0;
}

static int bcm5401_init(struct mii_phy* phy)
{
	u16 data;
	int rev;

	rev = sungem_phy_read(phy, MII_PHYSID2) & 0x000f;
	if (rev == 0 || rev == 3) {
		/* Some revisions of 5401 appear to need this
		 * initialisation sequence to disable, according
		 * to OF, "tap power management"
		 *
		 * WARNING ! OF and Darwin don't agree on the
		 * register addresses. OF seem to interpret the
		 * register numbers below as decimal
		 *
		 * Note: This should (and does) match tg3_init_5401phy_dsp
		 *       in the tg3.c driver. -DaveM
		 */
		sungem_phy_write(phy, 0x18, 0x0c20);
		sungem_phy_write(phy, 0x17, 0x0012);
		sungem_phy_write(phy, 0x15, 0x1804);
		sungem_phy_write(phy, 0x17, 0x0013);
		sungem_phy_write(phy, 0x15, 0x1204);
		sungem_phy_write(phy, 0x17, 0x8006);
		sungem_phy_write(phy, 0x15, 0x0132);
		sungem_phy_write(phy, 0x17, 0x8006);
		sungem_phy_write(phy, 0x15, 0x0232);
		sungem_phy_write(phy, 0x17, 0x201f);
		sungem_phy_write(phy, 0x15, 0x0a20);
	}

	/* Configure for gigabit full duplex */
	data = sungem_phy_read(phy, MII_BCM5400_GB_CONTROL);
	data |= MII_BCM5400_GB_CONTROL_FULLDUPLEXCAP;
	sungem_phy_write(phy, MII_BCM5400_GB_CONTROL, data);

	udelay(10);

	/* Reset and configure cascaded 10/100 PHY */
	(void)reset_one_mii_phy(phy, 0x1f);

	data = __sungem_phy_read(phy, 0x1f, MII_BCM5201_MULTIPHY);
	data |= MII_BCM5201_MULTIPHY_SERIALMODE;
	__sungem_phy_write(phy, 0x1f, MII_BCM5201_MULTIPHY, data);

	return 0;
}

static int bcm5401_suspend(struct mii_phy* phy)
{
#if 0 /* Commented out in Darwin... someone has those dawn docs ? */
	sungem_phy_write(phy, MII_BMCR, BMCR_PDOWN);
#endif
	return 0;
}

static int bcm5411_init(struct mii_phy* phy)
{
	u16 data;

	/* Here's some more Apple black magic to setup
	 * some voltage stuffs.
	 */
	sungem_phy_write(phy, 0x1c, 0x8c23);
	sungem_phy_write(phy, 0x1c, 0x8ca3);
	sungem_phy_write(phy, 0x1c, 0x8c23);

	/* Here, Apple seems to want to reset it, do
	 * it as well
	 */
	sungem_phy_write(phy, MII_BMCR, BMCR_RESET);
	sungem_phy_write(phy, MII_BMCR, 0x1340);

	data = sungem_phy_read(phy, MII_BCM5400_GB_CONTROL);
	data |= MII_BCM5400_GB_CONTROL_FULLDUPLEXCAP;
	sungem_phy_write(phy, MII_BCM5400_GB_CONTROL, data);

	udelay(10);

	/* Reset and configure cascaded 10/100 PHY */
	(void)reset_one_mii_phy(phy, 0x1f);

	return 0;
}

static int genmii_setup_aneg(struct mii_phy *phy, u32 advertise)
{
	u16 ctl, adv;

	phy->autoneg = 1;
	phy->speed = SPEED_10;
	phy->duplex = DUPLEX_HALF;
	phy->pause = 0;
	phy->advertising = advertise;

	/* Setup standard advertise */
	adv = sungem_phy_read(phy, MII_ADVERTISE);
	adv &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4);
	if (advertise & ADVERTISED_10baseT_Half)
		adv |= ADVERTISE_10HALF;
	if (advertise & ADVERTISED_10baseT_Full)
		adv |= ADVERTISE_10FULL;
	if (advertise & ADVERTISED_100baseT_Half)
		adv |= ADVERTISE_100HALF;
	if (advertise & ADVERTISED_100baseT_Full)
		adv |= ADVERTISE_100FULL;
	sungem_phy_write(phy, MII_ADVERTISE, adv);

	/* Start/Restart aneg */
	ctl = sungem_phy_read(phy, MII_BMCR);
	ctl |= (BMCR_ANENABLE | BMCR_ANRESTART);
	sungem_phy_write(phy, MII_BMCR, ctl);

	return 0;
}

static int genmii_setup_forced(struct mii_phy *phy, int speed, int fd)
{
	u16 ctl;

	phy->autoneg = 0;
	phy->speed = speed;
	phy->duplex = fd;
	phy->pause = 0;

	ctl = sungem_phy_read(phy, MII_BMCR);
	ctl &= ~(BMCR_FULLDPLX|BMCR_SPEED100|BMCR_ANENABLE);

	/* First reset the PHY */
	sungem_phy_write(phy, MII_BMCR, ctl | BMCR_RESET);

	/* Select speed & duplex */
	switch(speed) {
	case SPEED_10:
		break;
	case SPEED_100:
		ctl |= BMCR_SPEED100;
		break;
	case SPEED_1000:
	default:
		return -EINVAL;
	}
	if (fd == DUPLEX_FULL)
		ctl |= BMCR_FULLDPLX;
	sungem_phy_write(phy, MII_BMCR, ctl);

	return 0;
}

static int genmii_poll_link(struct mii_phy *phy)
{
	u16 status;

	(void)sungem_phy_read(phy, MII_BMSR);
	status = sungem_phy_read(phy, MII_BMSR);
	if ((status & BMSR_LSTATUS) == 0)
		return 0;
	if (phy->autoneg && !(status & BMSR_ANEGCOMPLETE))
		return 0;
	return 1;
}

static int genmii_read_link(struct mii_phy *phy)
{
	u16 lpa;

	if (phy->autoneg) {
		lpa = sungem_phy_read(phy, MII_LPA);

		if (lpa & (LPA_10FULL | LPA_100FULL))
			phy->duplex = DUPLEX_FULL;
		else
			phy->duplex = DUPLEX_HALF;
		if (lpa & (LPA_100FULL | LPA_100HALF))
			phy->speed = SPEED_100;
		else
			phy->speed = SPEED_10;
		phy->pause = 0;
	}
	/* On non-aneg, we assume what we put in BMCR is the speed,
	 * though magic-aneg shouldn't prevent this case from occurring
	 */

	 return 0;
}

static int generic_suspend(struct mii_phy* phy)
{
	sungem_phy_write(phy, MII_BMCR, BMCR_PDOWN);

	return 0;
}

static int bcm5421_init(struct mii_phy* phy)
{
	u16 data;
	unsigned int id;

	id = (sungem_phy_read(phy, MII_PHYSID1) << 16 | sungem_phy_read(phy, MII_PHYSID2));

	/* Revision 0 of 5421 needs some fixups */
	if (id == 0x002060e0) {
		/* This is borrowed from MacOS
		 */
		sungem_phy_write(phy, 0x18, 0x1007);
		data = sungem_phy_read(phy, 0x18);
		sungem_phy_write(phy, 0x18, data | 0x0400);
		sungem_phy_write(phy, 0x18, 0x0007);
		data = sungem_phy_read(phy, 0x18);
		sungem_phy_write(phy, 0x18, data | 0x0800);
		sungem_phy_write(phy, 0x17, 0x000a);
		data = sungem_phy_read(phy, 0x15);
		sungem_phy_write(phy, 0x15, data | 0x0200);
	}

	/* Pick up some init code from OF for K2 version */
	if ((id & 0xfffffff0) == 0x002062e0) {
		sungem_phy_write(phy, 4, 0x01e1);
		sungem_phy_write(phy, 9, 0x0300);
	}

	/* Check if we can enable automatic low power */
#ifdef CONFIG_PPC_PMAC
	if (phy->platform_data) {
		struct device_node *np = of_get_parent(phy->platform_data);
		int can_low_power = 1;
		if (np == NULL || of_get_property(np, "no-autolowpower", NULL))
			can_low_power = 0;
		if (can_low_power) {
			/* Enable automatic low-power */
			sungem_phy_write(phy, 0x1c, 0x9002);
			sungem_phy_write(phy, 0x1c, 0xa821);
			sungem_phy_write(phy, 0x1c, 0x941d);
		}
	}
#endif /* CONFIG_PPC_PMAC */

	return 0;
}

static int bcm54xx_setup_aneg(struct mii_phy *phy, u32 advertise)
{
	u16 ctl, adv;

	phy->autoneg = 1;
	phy->speed = SPEED_10;
	phy->duplex = DUPLEX_HALF;
	phy->pause = 0;
	phy->advertising = advertise;

	/* Setup standard advertise */
	adv = sungem_phy_read(phy, MII_ADVERTISE);
	adv &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4);
	if (advertise & ADVERTISED_10baseT_Half)
		adv |= ADVERTISE_10HALF;
	if (advertise & ADVERTISED_10baseT_Full)
		adv |= ADVERTISE_10FULL;
	if (advertise & ADVERTISED_100baseT_Half)
		adv |= ADVERTISE_100HALF;
	if (advertise & ADVERTISED_100baseT_Full)
		adv |= ADVERTISE_100FULL;
	if (advertise & ADVERTISED_Pause)
		adv |= ADVERTISE_PAUSE_CAP;
	if (advertise & ADVERTISED_Asym_Pause)
		adv |= ADVERTISE_PAUSE_ASYM;
	sungem_phy_write(phy, MII_ADVERTISE, adv);

	/* Setup 1000BT advertise */
	adv = sungem_phy_read(phy, MII_1000BASETCONTROL);
	adv &= ~(MII_1000BASETCONTROL_FULLDUPLEXCAP|MII_1000BASETCONTROL_HALFDUPLEXCAP);
	if (advertise & SUPPORTED_1000baseT_Half)
		adv |= MII_1000BASETCONTROL_HALFDUPLEXCAP;
	if (advertise & SUPPORTED_1000baseT_Full)
		adv |= MII_1000BASETCONTROL_FULLDUPLEXCAP;
	sungem_phy_write(phy, MII_1000BASETCONTROL, adv);

	/* Start/Restart aneg */
	ctl = sungem_phy_read(phy, MII_BMCR);
	ctl |= (BMCR_ANENABLE | BMCR_ANRESTART);
	sungem_phy_write(phy, MII_BMCR, ctl);

	return 0;
}

static int bcm54xx_setup_forced(struct mii_phy *phy, int speed, int fd)
{
	u16 ctl;

	phy->autoneg = 0;
	phy->speed = speed;
	phy->duplex = fd;
	phy->pause = 0;

	ctl = sungem_phy_read(phy, MII_BMCR);
	ctl &= ~(BMCR_FULLDPLX|BMCR_SPEED100|BMCR_SPD2|BMCR_ANENABLE);

	/* First reset the PHY */
	sungem_phy_write(phy, MII_BMCR, ctl | BMCR_RESET);

	/* Select speed & duplex */
	switch(speed) {
	case SPEED_10:
		break;
	case SPEED_100:
		ctl |= BMCR_SPEED100;
		break;
	case SPEED_1000:
		ctl |= BMCR_SPD2;
	}
	if (fd == DUPLEX_FULL)
		ctl |= BMCR_FULLDPLX;

	// XXX Should we set the sungem to GII now on 1000BT ?

	sungem_phy_write(phy, MII_BMCR, ctl);

	return 0;
}

static int bcm54xx_read_link(struct mii_phy *phy)
{
	int link_mode;
	u16 val;

	if (phy->autoneg) {
	    	val = sungem_phy_read(phy, MII_BCM5400_AUXSTATUS);
		link_mode = ((val & MII_BCM5400_AUXSTATUS_LINKMODE_MASK) >>
			     MII_BCM5400_AUXSTATUS_LINKMODE_SHIFT);
		phy->duplex = phy_BCM5400_link_table[link_mode][0] ?
			DUPLEX_FULL : DUPLEX_HALF;
		phy->speed = phy_BCM5400_link_table[link_mode][2] ?
				SPEED_1000 :
				(phy_BCM5400_link_table[link_mode][1] ?
				 SPEED_100 : SPEED_10);
		val = sungem_phy_read(phy, MII_LPA);
		phy->pause = (phy->duplex == DUPLEX_FULL) &&
			((val & LPA_PAUSE) != 0);
	}
	/* On non-aneg, we assume what we put in BMCR is the speed,
	 * though magic-aneg shouldn't prevent this case from occurring
	 */

	return 0;
}

static int marvell88e1111_init(struct mii_phy* phy)
{
	u16 rev;

	/* magic init sequence for rev 0 */
	rev = sungem_phy_read(phy, MII_PHYSID2) & 0x000f;
	if (rev == 0) {
		sungem_phy_write(phy, 0x1d, 0x000a);
		sungem_phy_write(phy, 0x1e, 0x0821);

		sungem_phy_write(phy, 0x1d, 0x0006);
		sungem_phy_write(phy, 0x1e, 0x8600);

		sungem_phy_write(phy, 0x1d, 0x000b);
		sungem_phy_write(phy, 0x1e, 0x0100);

		sungem_phy_write(phy, 0x1d, 0x0004);
		sungem_phy_write(phy, 0x1e, 0x4850);
	}
	return 0;
}

#define BCM5421_MODE_MASK	(1 << 5)

static int bcm5421_poll_link(struct mii_phy* phy)
{
	u32 phy_reg;
	int mode;

	/* find out in what mode we are */
	sungem_phy_write(phy, MII_NCONFIG, 0x1000);
	phy_reg = sungem_phy_read(phy, MII_NCONFIG);

	mode = (phy_reg & BCM5421_MODE_MASK) >> 5;

	if ( mode == BCM54XX_COPPER)
		return genmii_poll_link(phy);

	/* try to find out whether we have a link */
	sungem_phy_write(phy, MII_NCONFIG, 0x2000);
	phy_reg = sungem_phy_read(phy, MII_NCONFIG);

	if (phy_reg & 0x0020)
		return 0;
	else
		return 1;
}

static int bcm5421_read_link(struct mii_phy* phy)
{
	u32 phy_reg;
	int mode;

	/* find out in what mode we are */
	sungem_phy_write(phy, MII_NCONFIG, 0x1000);
	phy_reg = sungem_phy_read(phy, MII_NCONFIG);

	mode = (phy_reg & BCM5421_MODE_MASK ) >> 5;

	if ( mode == BCM54XX_COPPER)
		return bcm54xx_read_link(phy);

	phy->speed = SPEED_1000;

	/* find out whether we are running half- or full duplex */
	sungem_phy_write(phy, MII_NCONFIG, 0x2000);
	phy_reg = sungem_phy_read(phy, MII_NCONFIG);

	if ( (phy_reg & 0x0080) >> 7)
		phy->duplex |=  DUPLEX_HALF;
	else
		phy->duplex |=  DUPLEX_FULL;

	return 0;
}

static int bcm5421_enable_fiber(struct mii_phy* phy, int autoneg)
{
	/* enable fiber mode */
	sungem_phy_write(phy, MII_NCONFIG, 0x9020);
	/* LEDs active in both modes, autosense prio = fiber */
	sungem_phy_write(phy, MII_NCONFIG, 0x945f);

	if (!autoneg) {
		/* switch off fibre autoneg */
		sungem_phy_write(phy, MII_NCONFIG, 0xfc01);
		sungem_phy_write(phy, 0x0b, 0x0004);
	}

	phy->autoneg = autoneg;

	return 0;
}

#define BCM5461_FIBER_LINK	(1 << 2)
#define BCM5461_MODE_MASK	(3 << 1)

static int bcm5461_poll_link(struct mii_phy* phy)
{
	u32 phy_reg;
	int mode;

	/* find out in what mode we are */
	sungem_phy_write(phy, MII_NCONFIG, 0x7c00);
	phy_reg = sungem_phy_read(phy, MII_NCONFIG);

	mode = (phy_reg & BCM5461_MODE_MASK ) >> 1;

	if ( mode == BCM54XX_COPPER)
		return genmii_poll_link(phy);

	/* find out whether we have a link */
	sungem_phy_write(phy, MII_NCONFIG, 0x7000);
	phy_reg = sungem_phy_read(phy, MII_NCONFIG);

	if (phy_reg & BCM5461_FIBER_LINK)
		return 1;
	else
		return 0;
}

#define BCM5461_FIBER_DUPLEX	(1 << 3)

static int bcm5461_read_link(struct mii_phy* phy)
{
	u32 phy_reg;
	int mode;

	/* find out in what mode we are */
	sungem_phy_write(phy, MII_NCONFIG, 0x7c00);
	phy_reg = sungem_phy_read(phy, MII_NCONFIG);

	mode = (phy_reg & BCM5461_MODE_MASK ) >> 1;

	if ( mode == BCM54XX_COPPER) {
		return bcm54xx_read_link(phy);
	}

	phy->speed = SPEED_1000;

	/* find out whether we are running half- or full duplex */
	sungem_phy_write(phy, MII_NCONFIG, 0x7000);
	phy_reg = sungem_phy_read(phy, MII_NCONFIG);

	if (phy_reg & BCM5461_FIBER_DUPLEX)
		phy->duplex |=  DUPLEX_FULL;
	else
		phy->duplex |=  DUPLEX_HALF;

	return 0;
}

static int bcm5461_enable_fiber(struct mii_phy* phy, int autoneg)
{
	/* select fiber mode, enable 1000 base-X registers */
	sungem_phy_write(phy, MII_NCONFIG, 0xfc0b);

	if (autoneg) {
		/* enable fiber with no autonegotiation */
		sungem_phy_write(phy, MII_ADVERTISE, 0x01e0);
		sungem_phy_write(phy, MII_BMCR, 0x1140);
	} else {
		/* enable fiber with autonegotiation */
		sungem_phy_write(phy, MII_BMCR, 0x0140);
	}

	phy->autoneg = autoneg;

	return 0;
}

static int marvell_setup_aneg(struct mii_phy *phy, u32 advertise)
{
	u16 ctl, adv;

	phy->autoneg = 1;
	phy->speed = SPEED_10;
	phy->duplex = DUPLEX_HALF;
	phy->pause = 0;
	phy->advertising = advertise;

	/* Setup standard advertise */
	adv = sungem_phy_read(phy, MII_ADVERTISE);
	adv &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4);
	if (advertise & ADVERTISED_10baseT_Half)
		adv |= ADVERTISE_10HALF;
	if (advertise & ADVERTISED_10baseT_Full)
		adv |= ADVERTISE_10FULL;
	if (advertise & ADVERTISED_100baseT_Half)
		adv |= ADVERTISE_100HALF;
	if (advertise & ADVERTISED_100baseT_Full)
		adv |= ADVERTISE_100FULL;
	if (advertise & ADVERTISED_Pause)
		adv |= ADVERTISE_PAUSE_CAP;
	if (advertise & ADVERTISED_Asym_Pause)
		adv |= ADVERTISE_PAUSE_ASYM;
	sungem_phy_write(phy, MII_ADVERTISE, adv);

	/* Setup 1000BT advertise & enable crossover detect
	 * XXX How do we advertise 1000BT ? Darwin source is
	 * confusing here, they read from specific control and
	 * write to control... Someone has specs for those
	 * beasts ?
	 */
	adv = sungem_phy_read(phy, MII_M1011_PHY_SPEC_CONTROL);
	adv |= MII_M1011_PHY_SPEC_CONTROL_AUTO_MDIX;
	adv &= ~(MII_1000BASETCONTROL_FULLDUPLEXCAP |
			MII_1000BASETCONTROL_HALFDUPLEXCAP);
	if (advertise & SUPPORTED_1000baseT_Half)
		adv |= MII_1000BASETCONTROL_HALFDUPLEXCAP;
	if (advertise & SUPPORTED_1000baseT_Full)
		adv |= MII_1000BASETCONTROL_FULLDUPLEXCAP;
	sungem_phy_write(phy, MII_1000BASETCONTROL, adv);

	/* Start/Restart aneg */
	ctl = sungem_phy_read(phy, MII_BMCR);
	ctl |= (BMCR_ANENABLE | BMCR_ANRESTART);
	sungem_phy_write(phy, MII_BMCR, ctl);

	return 0;
}

static int marvell_setup_forced(struct mii_phy *phy, int speed, int fd)
{
	u16 ctl, ctl2;

	phy->autoneg = 0;
	phy->speed = speed;
	phy->duplex = fd;
	phy->pause = 0;

	ctl = sungem_phy_read(phy, MII_BMCR);
	ctl &= ~(BMCR_FULLDPLX|BMCR_SPEED100|BMCR_SPD2|BMCR_ANENABLE);
	ctl |= BMCR_RESET;

	/* Select speed & duplex */
	switch(speed) {
	case SPEED_10:
		break;
	case SPEED_100:
		ctl |= BMCR_SPEED100;
		break;
	/* I'm not sure about the one below, again, Darwin source is
	 * quite confusing and I lack chip specs
	 */
	case SPEED_1000:
		ctl |= BMCR_SPD2;
	}
	if (fd == DUPLEX_FULL)
		ctl |= BMCR_FULLDPLX;

	/* Disable crossover. Again, the way Apple does it is strange,
	 * though I don't assume they are wrong ;)
	 */
	ctl2 = sungem_phy_read(phy, MII_M1011_PHY_SPEC_CONTROL);
	ctl2 &= ~(MII_M1011_PHY_SPEC_CONTROL_MANUAL_MDIX |
		MII_M1011_PHY_SPEC_CONTROL_AUTO_MDIX |
		MII_1000BASETCONTROL_FULLDUPLEXCAP |
		MII_1000BASETCONTROL_HALFDUPLEXCAP);
	if (speed == SPEED_1000)
		ctl2 |= (fd == DUPLEX_FULL) ?
			MII_1000BASETCONTROL_FULLDUPLEXCAP :
			MII_1000BASETCONTROL_HALFDUPLEXCAP;
	sungem_phy_write(phy, MII_1000BASETCONTROL, ctl2);

	// XXX Should we set the sungem to GII now on 1000BT ?

	sungem_phy_write(phy, MII_BMCR, ctl);

	return 0;
}

static int marvell_read_link(struct mii_phy *phy)
{
	u16 status, pmask;

	if (phy->autoneg) {
		status = sungem_phy_read(phy, MII_M1011_PHY_SPEC_STATUS);
		if ((status & MII_M1011_PHY_SPEC_STATUS_RESOLVED) == 0)
			return -EAGAIN;
		if (status & MII_M1011_PHY_SPEC_STATUS_1000)
			phy->speed = SPEED_1000;
		else if (status & MII_M1011_PHY_SPEC_STATUS_100)
			phy->speed = SPEED_100;
		else
			phy->speed = SPEED_10;
		if (status & MII_M1011_PHY_SPEC_STATUS_FULLDUPLEX)
			phy->duplex = DUPLEX_FULL;
		else
			phy->duplex = DUPLEX_HALF;
		pmask = MII_M1011_PHY_SPEC_STATUS_TX_PAUSE |
			MII_M1011_PHY_SPEC_STATUS_RX_PAUSE;
		phy->pause = (status & pmask) == pmask;
	}
	/* On non-aneg, we assume what we put in BMCR is the speed,
	 * though magic-aneg shouldn't prevent this case from occurring
	 */

	return 0;
}

#define MII_BASIC_FEATURES \
	(SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full |	\
	 SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full |	\
	 SUPPORTED_Autoneg | SUPPORTED_TP | SUPPORTED_MII |	\
	 SUPPORTED_Pause)

/* On gigabit capable PHYs, we advertise Pause support but not asym pause
 * support for now as I'm not sure it's supported and Darwin doesn't do
 * it neither. --BenH.
 */
#define MII_GBIT_FEATURES \
	(MII_BASIC_FEATURES |	\
	 SUPPORTED_1000baseT_Half | SUPPORTED_1000baseT_Full)

/* Broadcom BCM 5201 */
static struct mii_phy_ops bcm5201_phy_ops = {
	.init		= bcm5201_init,
	.suspend	= bcm5201_suspend,
	.setup_aneg	= genmii_setup_aneg,
	.setup_forced	= genmii_setup_forced,
	.poll_link	= genmii_poll_link,
	.read_link	= genmii_read_link,
};

static struct mii_phy_def bcm5201_phy_def = {
	.phy_id		= 0x00406210,
	.phy_id_mask	= 0xfffffff0,
	.name		= "BCM5201",
	.features	= MII_BASIC_FEATURES,
	.magic_aneg	= 1,
	.ops		= &bcm5201_phy_ops
};

/* Broadcom BCM 5221 */
static struct mii_phy_ops bcm5221_phy_ops = {
	.suspend	= bcm5221_suspend,
	.init		= bcm5221_init,
	.setup_aneg	= genmii_setup_aneg,
	.setup_forced	= genmii_setup_forced,
	.poll_link	= genmii_poll_link,
	.read_link	= genmii_read_link,
};

static struct mii_phy_def bcm5221_phy_def = {
	.phy_id		= 0x004061e0,
	.phy_id_mask	= 0xfffffff0,
	.name		= "BCM5221",
	.features	= MII_BASIC_FEATURES,
	.magic_aneg	= 1,
	.ops		= &bcm5221_phy_ops
};

/* Broadcom BCM 5241 */
static struct mii_phy_ops bcm5241_phy_ops = {
	.suspend	= bcm5241_suspend,
	.init		= bcm5241_init,
	.setup_aneg	= genmii_setup_aneg,
	.setup_forced	= genmii_setup_forced,
	.poll_link	= genmii_poll_link,
	.read_link	= genmii_read_link,
};
static struct mii_phy_def bcm5241_phy_def = {
	.phy_id		= 0x0143bc30,
	.phy_id_mask	= 0xfffffff0,
	.name		= "BCM5241",
	.features	= MII_BASIC_FEATURES,
	.magic_aneg	= 1,
	.ops		= &bcm5241_phy_ops
};

/* Broadcom BCM 5400 */
static struct mii_phy_ops bcm5400_phy_ops = {
	.init		= bcm5400_init,
	.suspend	= bcm5400_suspend,
	.setup_aneg	= bcm54xx_setup_aneg,
	.setup_forced	= bcm54xx_setup_forced,
	.poll_link	= genmii_poll_link,
	.read_link	= bcm54xx_read_link,
};

static struct mii_phy_def bcm5400_phy_def = {
	.phy_id		= 0x00206040,
	.phy_id_mask	= 0xfffffff0,
	.name		= "BCM5400",
	.features	= MII_GBIT_FEATURES,
	.magic_aneg	= 1,
	.ops		= &bcm5400_phy_ops
};

/* Broadcom BCM 5401 */
static struct mii_phy_ops bcm5401_phy_ops = {
	.init		= bcm5401_init,
	.suspend	= bcm5401_suspend,
	.setup_aneg	= bcm54xx_setup_aneg,
	.setup_forced	= bcm54xx_setup_forced,
	.poll_link	= genmii_poll_link,
	.read_link	= bcm54xx_read_link,
};

static struct mii_phy_def bcm5401_phy_def = {
	.phy_id		= 0x00206050,
	.phy_id_mask	= 0xfffffff0,
	.name		= "BCM5401",
	.features	= MII_GBIT_FEATURES,
	.magic_aneg	= 1,
	.ops		= &bcm5401_phy_ops
};

/* Broadcom BCM 5411 */
static struct mii_phy_ops bcm5411_phy_ops = {
	.init		= bcm5411_init,
	.suspend	= generic_suspend,
	.setup_aneg	= bcm54xx_setup_aneg,
	.setup_forced	= bcm54xx_setup_forced,
	.poll_link	= genmii_poll_link,
	.read_link	= bcm54xx_read_link,
};

static struct mii_phy_def bcm5411_phy_def = {
	.phy_id		= 0x00206070,
	.phy_id_mask	= 0xfffffff0,
	.name		= "BCM5411",
	.features	= MII_GBIT_FEATURES,
	.magic_aneg	= 1,
	.ops		= &bcm5411_phy_ops
};

/* Broadcom BCM 5421 */
static struct mii_phy_ops bcm5421_phy_ops = {
	.init		= bcm5421_init,
	.suspend	= generic_suspend,
	.setup_aneg	= bcm54xx_setup_aneg,
	.setup_forced	= bcm54xx_setup_forced,
	.poll_link	= bcm5421_poll_link,
	.read_link	= bcm5421_read_link,
	.enable_fiber   = bcm5421_enable_fiber,
};

static struct mii_phy_def bcm5421_phy_def = {
	.phy_id		= 0x002060e0,
	.phy_id_mask	= 0xfffffff0,
	.name		= "BCM5421",
	.features	= MII_GBIT_FEATURES,
	.magic_aneg	= 1,
	.ops		= &bcm5421_phy_ops
};

/* Broadcom BCM 5421 built-in K2 */
static struct mii_phy_ops bcm5421k2_phy_ops = {
	.init		= bcm5421_init,
	.suspend	= generic_suspend,
	.setup_aneg	= bcm54xx_setup_aneg,
	.setup_forced	= bcm54xx_setup_forced,
	.poll_link	= genmii_poll_link,
	.read_link	= bcm54xx_read_link,
};

static struct mii_phy_def bcm5421k2_phy_def = {
	.phy_id		= 0x002062e0,
	.phy_id_mask	= 0xfffffff0,
	.name		= "BCM5421-K2",
	.features	= MII_GBIT_FEATURES,
	.magic_aneg	= 1,
	.ops		= &bcm5421k2_phy_ops
};

static struct mii_phy_ops bcm5461_phy_ops = {
	.init		= bcm5421_init,
	.suspend	= generic_suspend,
	.setup_aneg	= bcm54xx_setup_aneg,
	.setup_forced	= bcm54xx_setup_forced,
	.poll_link	= bcm5461_poll_link,
	.read_link	= bcm5461_read_link,
	.enable_fiber   = bcm5461_enable_fiber,
};

static struct mii_phy_def bcm5461_phy_def = {
	.phy_id		= 0x002060c0,
	.phy_id_mask	= 0xfffffff0,
	.name		= "BCM5461",
	.features	= MII_GBIT_FEATURES,
	.magic_aneg	= 1,
	.ops		= &bcm5461_phy_ops
};

/* Broadcom BCM 5462 built-in Vesta */
static struct mii_phy_ops bcm5462V_phy_ops = {
	.init		= bcm5421_init,
	.suspend	= generic_suspend,
	.setup_aneg	= bcm54xx_setup_aneg,
	.setup_forced	= bcm54xx_setup_forced,
	.poll_link	= genmii_poll_link,
	.read_link	= bcm54xx_read_link,
};

static struct mii_phy_def bcm5462V_phy_def = {
	.phy_id		= 0x002060d0,
	.phy_id_mask	= 0xfffffff0,
	.name		= "BCM5462-Vesta",
	.features	= MII_GBIT_FEATURES,
	.magic_aneg	= 1,
	.ops		= &bcm5462V_phy_ops
};

/* Marvell 88E1101 amd 88E1111 */
static struct mii_phy_ops marvell88e1101_phy_ops = {
	.suspend	= generic_suspend,
	.setup_aneg	= marvell_setup_aneg,
	.setup_forced	= marvell_setup_forced,
	.poll_link	= genmii_poll_link,
	.read_link	= marvell_read_link
};

static struct mii_phy_ops marvell88e1111_phy_ops = {
	.init		= marvell88e1111_init,
	.suspend	= generic_suspend,
	.setup_aneg	= marvell_setup_aneg,
	.setup_forced	= marvell_setup_forced,
	.poll_link	= genmii_poll_link,
	.read_link	= marvell_read_link
};

/* two revs in darwin for the 88e1101 ... I could use a datasheet
 * to get the proper names...
 */
static struct mii_phy_def marvell88e1101v1_phy_def = {
	.phy_id		= 0x01410c20,
	.phy_id_mask	= 0xfffffff0,
	.name		= "Marvell 88E1101v1",
	.features	= MII_GBIT_FEATURES,
	.magic_aneg	= 1,
	.ops		= &marvell88e1101_phy_ops
};
static struct mii_phy_def marvell88e1101v2_phy_def = {
	.phy_id		= 0x01410c60,
	.phy_id_mask	= 0xfffffff0,
	.name		= "Marvell 88E1101v2",
	.features	= MII_GBIT_FEATURES,
	.magic_aneg	= 1,
	.ops		= &marvell88e1101_phy_ops
};
static struct mii_phy_def marvell88e1111_phy_def = {
	.phy_id		= 0x01410cc0,
	.phy_id_mask	= 0xfffffff0,
	.name		= "Marvell 88E1111",
	.features	= MII_GBIT_FEATURES,
	.magic_aneg	= 1,
	.ops		= &marvell88e1111_phy_ops
};

/* Generic implementation for most 10/100 PHYs */
static struct mii_phy_ops generic_phy_ops = {
	.setup_aneg	= genmii_setup_aneg,
	.setup_forced	= genmii_setup_forced,
	.poll_link	= genmii_poll_link,
	.read_link	= genmii_read_link
};

static struct mii_phy_def genmii_phy_def = {
	.phy_id		= 0x00000000,
	.phy_id_mask	= 0x00000000,
	.name		= "Generic MII",
	.features	= MII_BASIC_FEATURES,
	.magic_aneg	= 0,
	.ops		= &generic_phy_ops
};

static struct mii_phy_def* mii_phy_table[] = {
	&bcm5201_phy_def,
	&bcm5221_phy_def,
	&bcm5241_phy_def,
	&bcm5400_phy_def,
	&bcm5401_phy_def,
	&bcm5411_phy_def,
	&bcm5421_phy_def,
	&bcm5421k2_phy_def,
	&bcm5461_phy_def,
	&bcm5462V_phy_def,
	&marvell88e1101v1_phy_def,
	&marvell88e1101v2_phy_def,
	&marvell88e1111_phy_def,
	&genmii_phy_def,
	NULL
};

int sungem_phy_probe(struct mii_phy *phy, int mii_id)
{
	int rc;
	u32 id;
	struct mii_phy_def* def;
	int i;

	/* We do not reset the mii_phy structure as the driver
	 * may re-probe the PHY regulary
	 */
	phy->mii_id = mii_id;

	/* Take PHY out of isloate mode and reset it. */
	rc = reset_one_mii_phy(phy, mii_id);
	if (rc)
		goto fail;

	/* Read ID and find matching entry */
	id = (sungem_phy_read(phy, MII_PHYSID1) << 16 | sungem_phy_read(phy, MII_PHYSID2));
	printk(KERN_DEBUG KBUILD_MODNAME ": " "PHY ID: %x, addr: %x\n",
	       id, mii_id);
	for (i=0; (def = mii_phy_table[i]) != NULL; i++)
		if ((id & def->phy_id_mask) == def->phy_id)
			break;
	/* Should never be NULL (we have a generic entry), but... */
	if (def == NULL)
		goto fail;

	phy->def = def;

	return 0;
fail:
	phy->speed = 0;
	phy->duplex = 0;
	phy->pause = 0;
	phy->advertising = 0;
	return -ENODEV;
}

EXPORT_SYMBOL(sungem_phy_probe);
MODULE_LICENSE("GPL");