national.c 4.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/*
 * drivers/net/phy/national.c
 *
 * Driver for National Semiconductor PHYs
 *
 * Author: Stuart Menefy <stuart.menefy@st.com>
 * Maintainer: Giuseppe Cavallaro <peppe.cavallaro@st.com>
 *
 * Copyright (c) 2008 STMicroelectronics Limited
 *
 * 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.
 *
 */

J
Joe Perches 已提交
18 19
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

20 21 22 23 24 25 26
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mii.h>
#include <linux/ethtool.h>
#include <linux/phy.h>
#include <linux/netdevice.h>

J
Joe Perches 已提交
27 28
#define DEBUG

29 30 31
/* DP83865 phy identifier values */
#define DP83865_PHY_ID	0x20005c7a

32 33 34
#define DP83865_INT_STATUS	0x14
#define DP83865_INT_MASK	0x15
#define DP83865_INT_CLEAR	0x17
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

#define DP83865_INT_REMOTE_FAULT 0x0008
#define DP83865_INT_ANE_COMPLETED 0x0010
#define DP83865_INT_LINK_CHANGE	0xe000
#define DP83865_INT_MASK_DEFAULT (DP83865_INT_REMOTE_FAULT | \
				DP83865_INT_ANE_COMPLETED | \
				DP83865_INT_LINK_CHANGE)

/* Advanced proprietary configuration */
#define NS_EXP_MEM_CTL	0x16
#define NS_EXP_MEM_DATA	0x1d
#define NS_EXP_MEM_ADD	0x1e

#define LED_CTRL_REG 0x13
#define AN_FALLBACK_AN 0x0001
#define AN_FALLBACK_CRC 0x0002
#define AN_FALLBACK_IE 0x0004
#define ALL_FALLBACK_ON (AN_FALLBACK_AN |  AN_FALLBACK_CRC | AN_FALLBACK_IE)

enum hdx_loopback {
	hdx_loopback_on = 0,
	hdx_loopback_off = 1,
};

static u8 ns_exp_read(struct phy_device *phydev, u16 reg)
{
	phy_write(phydev, NS_EXP_MEM_ADD, reg);
	return phy_read(phydev, NS_EXP_MEM_DATA);
}

static void ns_exp_write(struct phy_device *phydev, u16 reg, u8 data)
{
	phy_write(phydev, NS_EXP_MEM_ADD, reg);
	phy_write(phydev, NS_EXP_MEM_DATA, data);
}

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

	if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
76
		err = phy_write(phydev, DP83865_INT_MASK,
77 78
				DP83865_INT_MASK_DEFAULT);
	else
79
		err = phy_write(phydev, DP83865_INT_MASK, 0);
80 81 82 83 84 85

	return err;
}

static int ns_ack_interrupt(struct phy_device *phydev)
{
86
	int ret = phy_read(phydev, DP83865_INT_STATUS);
87 88 89
	if (ret < 0)
		return ret;

90 91 92 93 94
	/* Clear the interrupt status bit by writing a “1”
	 * to the corresponding bit in INT_CLEAR (2:0 are reserved) */
	ret = phy_write(phydev, DP83865_INT_CLEAR, ret & ~0x7);

	return ret;
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
}

static void ns_giga_speed_fallback(struct phy_device *phydev, int mode)
{
	int bmcr = phy_read(phydev, MII_BMCR);

	phy_write(phydev, MII_BMCR, (bmcr | BMCR_PDOWN));

	/* Enable 8 bit expended memory read/write (no auto increment) */
	phy_write(phydev, NS_EXP_MEM_CTL, 0);
	phy_write(phydev, NS_EXP_MEM_ADD, 0x1C0);
	phy_write(phydev, NS_EXP_MEM_DATA, 0x0008);
	phy_write(phydev, MII_BMCR, (bmcr & ~BMCR_PDOWN));
	phy_write(phydev, LED_CTRL_REG, mode);
}

static void ns_10_base_t_hdx_loopack(struct phy_device *phydev, int disable)
{
	if (disable)
		ns_exp_write(phydev, 0x1c0, ns_exp_read(phydev, 0x1c0) | 1);
	else
		ns_exp_write(phydev, 0x1c0,
			     ns_exp_read(phydev, 0x1c0) & 0xfffe);

J
Joe Perches 已提交
119 120
	pr_debug("10BASE-T HDX loopback %s\n",
		 (ns_exp_read(phydev, 0x1c0) & 0x0001) ? "off" : "on");
121 122 123 124 125 126 127 128 129 130 131
}

static int ns_config_init(struct phy_device *phydev)
{
	ns_giga_speed_fallback(phydev, ALL_FALLBACK_ON);
	/* In the latest MAC or switches design, the 10 Mbps loopback
	   is desired to be turned off. */
	ns_10_base_t_hdx_loopack(phydev, hdx_loopback_off);
	return ns_ack_interrupt(phydev);
}

132
static struct phy_driver dp83865_driver[] = { {
133 134 135 136 137 138 139 140 141 142
	.phy_id = DP83865_PHY_ID,
	.phy_id_mask = 0xfffffff0,
	.name = "NatSemi DP83865",
	.features = PHY_GBIT_FEATURES | SUPPORTED_Pause | SUPPORTED_Asym_Pause,
	.flags = PHY_HAS_INTERRUPT,
	.config_init = ns_config_init,
	.config_aneg = genphy_config_aneg,
	.read_status = genphy_read_status,
	.ack_interrupt = ns_ack_interrupt,
	.config_intr = ns_config_intr,
143
} };
144

145
module_phy_driver(dp83865_driver);
146 147 148 149 150

MODULE_DESCRIPTION("NatSemi PHY driver");
MODULE_AUTHOR("Stuart Menefy");
MODULE_LICENSE("GPL");

151
static struct mdio_device_id __maybe_unused ns_tbl[] = {
152 153 154 155 156
	{ DP83865_PHY_ID, 0xfffffff0 },
	{ }
};

MODULE_DEVICE_TABLE(mdio, ns_tbl);