marvell.c 6.8 KB
Newer Older
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
/*
 * drivers/net/phy/marvell.c
 *
 * Driver for Marvell PHYs
 *
 * Author: Andy Fleming
 *
 * Copyright (c) 2004 Freescale Semiconductor, Inc.
 *
 * 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.
 *
 */
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/unistd.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/spinlock.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/mii.h>
#include <linux/ethtool.h>
#include <linux/phy.h>

#include <asm/io.h>
#include <asm/irq.h>
#include <asm/uaccess.h>

#define MII_M1011_IEVENT		0x13
#define MII_M1011_IEVENT_CLEAR		0x0000

#define MII_M1011_IMASK			0x12
#define MII_M1011_IMASK_INIT		0x6400
#define MII_M1011_IMASK_CLEAR		0x0000

45 46 47 48 49 50 51 52 53 54 55 56
#define MII_M1011_PHY_SCR		0x10
#define MII_M1011_PHY_SCR_AUTO_CROSS	0x0060

#define MII_M1145_PHY_EXT_CR		0x14
#define MII_M1145_RGMII_RX_DELAY	0x0080
#define MII_M1145_RGMII_TX_DELAY	0x0002

#define M1145_DEV_FLAGS_RESISTANCE	0x00000001

#define MII_M1111_PHY_LED_CONTROL	0x18
#define MII_M1111_PHY_LED_DIRECT	0x4100
#define MII_M1111_PHY_LED_COMBINE	0x411c
57 58 59 60 61 62
#define MII_M1111_PHY_EXT_CR		0x14
#define MII_M1111_RX_DELAY		0x80
#define MII_M1111_TX_DELAY		0x2
#define MII_M1111_PHY_EXT_SR		0x1b
#define MII_M1111_HWCFG_MODE_MASK	0xf
#define MII_M1111_HWCFG_MODE_RGMII	0xb
63

64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
MODULE_DESCRIPTION("Marvell PHY driver");
MODULE_AUTHOR("Andy Fleming");
MODULE_LICENSE("GPL");

static int marvell_ack_interrupt(struct phy_device *phydev)
{
	int err;

	/* Clear the interrupts by reading the reg */
	err = phy_read(phydev, MII_M1011_IEVENT);

	if (err < 0)
		return err;

	return 0;
}

static int marvell_config_intr(struct phy_device *phydev)
{
	int err;

85
	if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
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
		err = phy_write(phydev, MII_M1011_IMASK, MII_M1011_IMASK_INIT);
	else
		err = phy_write(phydev, MII_M1011_IMASK, MII_M1011_IMASK_CLEAR);

	return err;
}

static int marvell_config_aneg(struct phy_device *phydev)
{
	int err;

	/* The Marvell PHY has an errata which requires
	 * that certain registers get written in order
	 * to restart autonegotiation */
	err = phy_write(phydev, MII_BMCR, BMCR_RESET);

	if (err < 0)
		return err;

	err = phy_write(phydev, 0x1d, 0x1f);
	if (err < 0)
		return err;

	err = phy_write(phydev, 0x1e, 0x200c);
	if (err < 0)
		return err;

	err = phy_write(phydev, 0x1d, 0x5);
	if (err < 0)
		return err;

	err = phy_write(phydev, 0x1e, 0);
	if (err < 0)
		return err;

	err = phy_write(phydev, 0x1e, 0x100);
	if (err < 0)
		return err;

125 126 127 128 129 130 131 132 133
	err = phy_write(phydev, MII_M1011_PHY_SCR,
			MII_M1011_PHY_SCR_AUTO_CROSS);
	if (err < 0)
		return err;

	err = phy_write(phydev, MII_M1111_PHY_LED_CONTROL,
			MII_M1111_PHY_LED_DIRECT);
	if (err < 0)
		return err;
134 135 136 137 138 139

	err = genphy_config_aneg(phydev);

	return err;
}

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
static int m88e1111_config_init(struct phy_device *phydev)
{
	int err;

	if ((phydev->interface == PHY_INTERFACE_MODE_RGMII) ||
	    (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID)) {
		int temp;

		if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) {
			temp = phy_read(phydev, MII_M1111_PHY_EXT_CR);
			if (temp < 0)
				return temp;

			temp |= (MII_M1111_RX_DELAY | MII_M1111_TX_DELAY);

			err = phy_write(phydev, MII_M1111_PHY_EXT_CR, temp);
			if (err < 0)
				return err;
		}

		temp = phy_read(phydev, MII_M1111_PHY_EXT_SR);
		if (temp < 0)
			return temp;

		temp &= ~(MII_M1111_HWCFG_MODE_MASK);
		temp |= MII_M1111_HWCFG_MODE_RGMII;

		err = phy_write(phydev, MII_M1111_PHY_EXT_SR, temp);
		if (err < 0)
			return err;
	}

	err = phy_write(phydev, MII_BMCR, BMCR_RESET);
	if (err < 0)
		return err;

	return 0;
}

179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199
static int m88e1145_config_init(struct phy_device *phydev)
{
	int err;

	/* Take care of errata E0 & E1 */
	err = phy_write(phydev, 0x1d, 0x001b);
	if (err < 0)
		return err;

	err = phy_write(phydev, 0x1e, 0x418f);
	if (err < 0)
		return err;

	err = phy_write(phydev, 0x1d, 0x0016);
	if (err < 0)
		return err;

	err = phy_write(phydev, 0x1e, 0xa2da);
	if (err < 0)
		return err;

200
	if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) {
201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239
		int temp = phy_read(phydev, MII_M1145_PHY_EXT_CR);
		if (temp < 0)
			return temp;

		temp |= (MII_M1145_RGMII_RX_DELAY | MII_M1145_RGMII_TX_DELAY);

		err = phy_write(phydev, MII_M1145_PHY_EXT_CR, temp);
		if (err < 0)
			return err;

		if (phydev->dev_flags & M1145_DEV_FLAGS_RESISTANCE) {
			err = phy_write(phydev, 0x1d, 0x0012);
			if (err < 0)
				return err;

			temp = phy_read(phydev, 0x1e);
			if (temp < 0)
				return temp;

			temp &= 0xf03f;
			temp |= 2 << 9;	/* 36 ohm */
			temp |= 2 << 6;	/* 39 ohm */

			err = phy_write(phydev, 0x1e, temp);
			if (err < 0)
				return err;

			err = phy_write(phydev, 0x1d, 0x3);
			if (err < 0)
				return err;

			err = phy_write(phydev, 0x1e, 0x8000);
			if (err < 0)
				return err;
		}
	}

	return 0;
}
240 241

static struct phy_driver m88e1101_driver = {
242 243 244 245 246 247 248 249 250 251 252 253
	.phy_id = 0x01410c60,
	.phy_id_mask = 0xfffffff0,
	.name = "Marvell 88E1101",
	.features = PHY_GBIT_FEATURES,
	.flags = PHY_HAS_INTERRUPT,
	.config_aneg = &marvell_config_aneg,
	.read_status = &genphy_read_status,
	.ack_interrupt = &marvell_ack_interrupt,
	.config_intr = &marvell_config_intr,
	.driver = {.owner = THIS_MODULE,},
};

254
static struct phy_driver m88e1111_driver = {
255 256 257 258 259 260 261 262 263
	.phy_id = 0x01410cc0,
	.phy_id_mask = 0xfffffff0,
	.name = "Marvell 88E1111",
	.features = PHY_GBIT_FEATURES,
	.flags = PHY_HAS_INTERRUPT,
	.config_aneg = &marvell_config_aneg,
	.read_status = &genphy_read_status,
	.ack_interrupt = &marvell_ack_interrupt,
	.config_intr = &marvell_config_intr,
264
	.config_init = &m88e1111_config_init,
265 266 267 268 269 270 271 272 273 274 275 276 277 278 279
	.driver = {.owner = THIS_MODULE,},
};

static struct phy_driver m88e1145_driver = {
	.phy_id = 0x01410cd0,
	.phy_id_mask = 0xfffffff0,
	.name = "Marvell 88E1145",
	.features = PHY_GBIT_FEATURES,
	.flags = PHY_HAS_INTERRUPT,
	.config_init = &m88e1145_config_init,
	.config_aneg = &marvell_config_aneg,
	.read_status = &genphy_read_status,
	.ack_interrupt = &marvell_ack_interrupt,
	.config_intr = &marvell_config_intr,
	.driver = {.owner = THIS_MODULE,},
280 281 282 283
};

static int __init marvell_init(void)
{
284 285 286 287 288 289
	int ret;

	ret = phy_driver_register(&m88e1101_driver);
	if (ret)
		return ret;

290
	ret = phy_driver_register(&m88e1111_driver);
291
	if (ret)
292
		goto err1111;
293 294 295 296 297 298 299

	ret = phy_driver_register(&m88e1145_driver);
	if (ret)
		goto err1145;

	return 0;

300 301 302
err1145:
	phy_driver_unregister(&m88e1111_driver);
err1111:
303 304
	phy_driver_unregister(&m88e1101_driver);
	return ret;
305 306 307 308 309
}

static void __exit marvell_exit(void)
{
	phy_driver_unregister(&m88e1101_driver);
310
	phy_driver_unregister(&m88e1111_driver);
311
	phy_driver_unregister(&m88e1145_driver);
312 313 314 315
}

module_init(marvell_init);
module_exit(marvell_exit);