Commit f6ed1b3b authored by David Daney's avatar David Daney Committed by Ralf Baechle

Staging: octeon-ethernet: Convert to use PHY Abstraction Layer.

The octeon-ethernet driver shares an mdio bus with the octeon-mgmt
driver.  Here we convert the octeon-ethernet driver to use the PHY
Abstraction Layer.
Signed-off-by: default avatarDavid Daney <ddaney@caviumnetworks.com>
Signed-off-by: default avatarRalf Baechle <ralf@linux-mips.org>
parent d6aa60a1
config OCTEON_ETHERNET
tristate "Cavium Networks Octeon Ethernet support"
depends on CPU_CAVIUM_OCTEON
select MII
select PHYLIB
select MDIO_OCTEON
help
This driver supports the builtin ethernet ports on Cavium
Networks' products in the Octeon family. This driver supports the
......
......@@ -26,7 +26,8 @@
**********************************************************************/
#include <linux/kernel.h>
#include <linux/ethtool.h>
#include <linux/mii.h>
#include <linux/phy.h>
#include <net/dst.h>
#include <asm/octeon/octeon.h>
......@@ -34,86 +35,12 @@
#include "ethernet-defines.h"
#include "octeon-ethernet.h"
#include "ethernet-mdio.h"
#include "ethernet-util.h"
#include "cvmx-helper-board.h"
#include "cvmx-smix-defs.h"
DECLARE_MUTEX(mdio_sem);
/**
* Perform an MII read. Called by the generic MII routines
*
* @dev: Device to perform read for
* @phy_id: The MII phy id
* @location: Register location to read
* Returns Result from the read or zero on failure
*/
static int cvm_oct_mdio_read(struct net_device *dev, int phy_id, int location)
{
union cvmx_smix_cmd smi_cmd;
union cvmx_smix_rd_dat smi_rd;
smi_cmd.u64 = 0;
smi_cmd.s.phy_op = 1;
smi_cmd.s.phy_adr = phy_id;
smi_cmd.s.reg_adr = location;
cvmx_write_csr(CVMX_SMIX_CMD(0), smi_cmd.u64);
do {
if (!in_interrupt())
yield();
smi_rd.u64 = cvmx_read_csr(CVMX_SMIX_RD_DAT(0));
} while (smi_rd.s.pending);
if (smi_rd.s.val)
return smi_rd.s.dat;
else
return 0;
}
static int cvm_oct_mdio_dummy_read(struct net_device *dev, int phy_id,
int location)
{
return 0xffff;
}
/**
* Perform an MII write. Called by the generic MII routines
*
* @dev: Device to perform write for
* @phy_id: The MII phy id
* @location: Register location to write
* @val: Value to write
*/
static void cvm_oct_mdio_write(struct net_device *dev, int phy_id, int location,
int val)
{
union cvmx_smix_cmd smi_cmd;
union cvmx_smix_wr_dat smi_wr;
smi_wr.u64 = 0;
smi_wr.s.dat = val;
cvmx_write_csr(CVMX_SMIX_WR_DAT(0), smi_wr.u64);
smi_cmd.u64 = 0;
smi_cmd.s.phy_op = 0;
smi_cmd.s.phy_adr = phy_id;
smi_cmd.s.reg_adr = location;
cvmx_write_csr(CVMX_SMIX_CMD(0), smi_cmd.u64);
do {
if (!in_interrupt())
yield();
smi_wr.u64 = cvmx_read_csr(CVMX_SMIX_WR_DAT(0));
} while (smi_wr.s.pending);
}
static void cvm_oct_mdio_dummy_write(struct net_device *dev, int phy_id,
int location, int val)
{
}
static void cvm_oct_get_drvinfo(struct net_device *dev,
struct ethtool_drvinfo *info)
{
......@@ -125,49 +52,37 @@ static void cvm_oct_get_drvinfo(struct net_device *dev,
static int cvm_oct_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
{
struct octeon_ethernet *priv = netdev_priv(dev);
int ret;
down(&mdio_sem);
ret = mii_ethtool_gset(&priv->mii_info, cmd);
up(&mdio_sem);
if (priv->phydev)
return phy_ethtool_gset(priv->phydev, cmd);
return ret;
return -EINVAL;
}
static int cvm_oct_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
{
struct octeon_ethernet *priv = netdev_priv(dev);
int ret;
down(&mdio_sem);
ret = mii_ethtool_sset(&priv->mii_info, cmd);
up(&mdio_sem);
if (!capable(CAP_NET_ADMIN))
return -EPERM;
if (priv->phydev)
return phy_ethtool_sset(priv->phydev, cmd);
return ret;
return -EINVAL;
}
static int cvm_oct_nway_reset(struct net_device *dev)
{
struct octeon_ethernet *priv = netdev_priv(dev);
int ret;
down(&mdio_sem);
ret = mii_nway_restart(&priv->mii_info);
up(&mdio_sem);
if (!capable(CAP_NET_ADMIN))
return -EPERM;
return ret;
}
if (priv->phydev)
return phy_start_aneg(priv->phydev);
static u32 cvm_oct_get_link(struct net_device *dev)
{
struct octeon_ethernet *priv = netdev_priv(dev);
u32 ret;
down(&mdio_sem);
ret = mii_link_ok(&priv->mii_info);
up(&mdio_sem);
return ret;
return -EINVAL;
}
const struct ethtool_ops cvm_oct_ethtool_ops = {
......@@ -175,7 +90,7 @@ const struct ethtool_ops cvm_oct_ethtool_ops = {
.get_settings = cvm_oct_get_settings,
.set_settings = cvm_oct_set_settings,
.nway_reset = cvm_oct_nway_reset,
.get_link = cvm_oct_get_link,
.get_link = ethtool_op_get_link,
.get_sg = ethtool_op_get_sg,
.get_tx_csum = ethtool_op_get_tx_csum,
};
......@@ -191,41 +106,78 @@ const struct ethtool_ops cvm_oct_ethtool_ops = {
int cvm_oct_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
{
struct octeon_ethernet *priv = netdev_priv(dev);
struct mii_ioctl_data *data = if_mii(rq);
unsigned int duplex_chg;
int ret;
down(&mdio_sem);
ret = generic_mii_ioctl(&priv->mii_info, data, cmd, &duplex_chg);
up(&mdio_sem);
if (!netif_running(dev))
return -EINVAL;
if (!priv->phydev)
return -EINVAL;
return phy_mii_ioctl(priv->phydev, if_mii(rq), cmd);
}
return ret;
static void cvm_oct_adjust_link(struct net_device *dev)
{
struct octeon_ethernet *priv = netdev_priv(dev);
cvmx_helper_link_info_t link_info;
if (priv->last_link != priv->phydev->link) {
priv->last_link = priv->phydev->link;
link_info.u64 = 0;
link_info.s.link_up = priv->last_link ? 1 : 0;
link_info.s.full_duplex = priv->phydev->duplex ? 1 : 0;
link_info.s.speed = priv->phydev->speed;
cvmx_helper_link_set( priv->port, link_info);
if (priv->last_link) {
netif_carrier_on(dev);
if (priv->queue != -1)
DEBUGPRINT("%s: %u Mbps %s duplex, "
"port %2d, queue %2d\n",
dev->name, priv->phydev->speed,
priv->phydev->duplex ?
"Full" : "Half",
priv->port, priv->queue);
else
DEBUGPRINT("%s: %u Mbps %s duplex, "
"port %2d, POW\n",
dev->name, priv->phydev->speed,
priv->phydev->duplex ?
"Full" : "Half",
priv->port);
} else {
netif_carrier_off(dev);
DEBUGPRINT("%s: Link down\n", dev->name);
}
}
}
/**
* Setup the MDIO device structures
* Setup the PHY
*
* @dev: Device to setup
*
* Returns Zero on success, negative on failure
*/
int cvm_oct_mdio_setup_device(struct net_device *dev)
int cvm_oct_phy_setup_device(struct net_device *dev)
{
struct octeon_ethernet *priv = netdev_priv(dev);
int phy_id = cvmx_helper_board_get_mii_address(priv->port);
if (phy_id != -1) {
priv->mii_info.dev = dev;
priv->mii_info.phy_id = phy_id;
priv->mii_info.phy_id_mask = 0xff;
priv->mii_info.supports_gmii = 1;
priv->mii_info.reg_num_mask = 0x1f;
priv->mii_info.mdio_read = cvm_oct_mdio_read;
priv->mii_info.mdio_write = cvm_oct_mdio_write;
} else {
/* Supply dummy MDIO routines so the kernel won't crash
if the user tries to read them */
priv->mii_info.mdio_read = cvm_oct_mdio_dummy_read;
priv->mii_info.mdio_write = cvm_oct_mdio_dummy_write;
int phy_addr = cvmx_helper_board_get_mii_address(priv->port);
if (phy_addr != -1) {
char phy_id[20];
snprintf(phy_id, sizeof(phy_id), PHY_ID_FMT, "0", phy_addr);
priv->phydev = phy_connect(dev, phy_id, cvm_oct_adjust_link, 0,
PHY_INTERFACE_MODE_GMII);
if (IS_ERR(priv->phydev)) {
priv->phydev = NULL;
return -1;
}
priv->last_link = 0;
phy_start_aneg(priv->phydev);
}
return 0;
}
......@@ -43,4 +43,4 @@
extern const struct ethtool_ops cvm_oct_ethtool_ops;
int cvm_oct_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
int cvm_oct_mdio_setup_device(struct net_device *dev);
int cvm_oct_phy_setup_device(struct net_device *dev);
......@@ -25,7 +25,6 @@
* Contact Cavium Networks for more information
**********************************************************************/
#include <linux/kernel.h>
#include <linux/mii.h>
#include <linux/seq_file.h>
#include <linux/proc_fs.h>
#include <net/dst.h>
......@@ -38,112 +37,6 @@
#include "cvmx-helper.h"
#include "cvmx-pip.h"
static unsigned long long cvm_oct_stats_read_switch(struct net_device *dev,
int phy_id, int offset)
{
struct octeon_ethernet *priv = netdev_priv(dev);
priv->mii_info.mdio_write(dev, phy_id, 0x1d, 0xcc00 | offset);
return ((uint64_t) priv->mii_info.
mdio_read(dev, phy_id,
0x1e) << 16) | (uint64_t) priv->mii_info.
mdio_read(dev, phy_id, 0x1f);
}
static int cvm_oct_stats_switch_show(struct seq_file *m, void *v)
{
static const int ports[] = { 0, 1, 2, 3, 9, -1 };
struct net_device *dev = cvm_oct_device[0];
int index = 0;
while (ports[index] != -1) {
/* Latch port */
struct octeon_ethernet *priv = netdev_priv(dev);
priv->mii_info.mdio_write(dev, 0x1b, 0x1d,
0xdc00 | ports[index]);
seq_printf(m, "\nSwitch Port %d\n", ports[index]);
seq_printf(m, "InGoodOctets: %12llu\t"
"OutOctets: %12llu\t"
"64 Octets: %12llu\n",
cvm_oct_stats_read_switch(dev, 0x1b,
0x00) |
(cvm_oct_stats_read_switch(dev, 0x1b, 0x01) << 32),
cvm_oct_stats_read_switch(dev, 0x1b,
0x0E) |
(cvm_oct_stats_read_switch(dev, 0x1b, 0x0F) << 32),
cvm_oct_stats_read_switch(dev, 0x1b, 0x08));
seq_printf(m, "InBadOctets: %12llu\t"
"OutUnicast: %12llu\t"
"65-127 Octets: %12llu\n",
cvm_oct_stats_read_switch(dev, 0x1b, 0x02),
cvm_oct_stats_read_switch(dev, 0x1b, 0x10),
cvm_oct_stats_read_switch(dev, 0x1b, 0x09));
seq_printf(m, "InUnicast: %12llu\t"
"OutBroadcasts: %12llu\t"
"128-255 Octets: %12llu\n",
cvm_oct_stats_read_switch(dev, 0x1b, 0x04),
cvm_oct_stats_read_switch(dev, 0x1b, 0x13),
cvm_oct_stats_read_switch(dev, 0x1b, 0x0A));
seq_printf(m, "InBroadcasts: %12llu\t"
"OutMulticasts: %12llu\t"
"256-511 Octets: %12llu\n",
cvm_oct_stats_read_switch(dev, 0x1b, 0x06),
cvm_oct_stats_read_switch(dev, 0x1b, 0x12),
cvm_oct_stats_read_switch(dev, 0x1b, 0x0B));
seq_printf(m, "InMulticasts: %12llu\t"
"OutPause: %12llu\t"
"512-1023 Octets:%12llu\n",
cvm_oct_stats_read_switch(dev, 0x1b, 0x07),
cvm_oct_stats_read_switch(dev, 0x1b, 0x15),
cvm_oct_stats_read_switch(dev, 0x1b, 0x0C));
seq_printf(m, "InPause: %12llu\t"
"Excessive: %12llu\t"
"1024-Max Octets:%12llu\n",
cvm_oct_stats_read_switch(dev, 0x1b, 0x16),
cvm_oct_stats_read_switch(dev, 0x1b, 0x11),
cvm_oct_stats_read_switch(dev, 0x1b, 0x0D));
seq_printf(m, "InUndersize: %12llu\t"
"Collisions: %12llu\n",
cvm_oct_stats_read_switch(dev, 0x1b, 0x18),
cvm_oct_stats_read_switch(dev, 0x1b, 0x1E));
seq_printf(m, "InFragments: %12llu\t"
"Deferred: %12llu\n",
cvm_oct_stats_read_switch(dev, 0x1b, 0x19),
cvm_oct_stats_read_switch(dev, 0x1b, 0x05));
seq_printf(m, "InOversize: %12llu\t"
"Single: %12llu\n",
cvm_oct_stats_read_switch(dev, 0x1b, 0x1A),
cvm_oct_stats_read_switch(dev, 0x1b, 0x14));
seq_printf(m, "InJabber: %12llu\t"
"Multiple: %12llu\n",
cvm_oct_stats_read_switch(dev, 0x1b, 0x1B),
cvm_oct_stats_read_switch(dev, 0x1b, 0x17));
seq_printf(m, "In RxErr: %12llu\t"
"OutFCSErr: %12llu\n",
cvm_oct_stats_read_switch(dev, 0x1b, 0x1C),
cvm_oct_stats_read_switch(dev, 0x1b, 0x03));
seq_printf(m, "InFCSErr: %12llu\t"
"Late: %12llu\n",
cvm_oct_stats_read_switch(dev, 0x1b, 0x1D),
cvm_oct_stats_read_switch(dev, 0x1b, 0x1F));
index++;
}
return 0;
}
/**
* User is reading /proc/octeon_ethernet_stats
*
......@@ -215,11 +108,6 @@ static int cvm_oct_stats_show(struct seq_file *m, void *v)
}
}
if (cvm_oct_device[0]) {
priv = netdev_priv(cvm_oct_device[0]);
if (priv->imode == CVMX_HELPER_INTERFACE_MODE_GMII)
cvm_oct_stats_switch_show(m, v);
}
return 0;
}
......
......@@ -147,32 +147,36 @@ static void cvm_oct_rgmii_poll(struct net_device *dev)
cvmx_write_csr(CVMX_GMXX_RXX_INT_REG(index, interface),
gmxx_rxx_int_reg.u64);
}
link_info = cvmx_helper_link_autoconf(priv->port);
priv->link_info = link_info.u64;
if (priv->phydev == NULL) {
link_info = cvmx_helper_link_autoconf(priv->port);
priv->link_info = link_info.u64;
}
spin_unlock_irqrestore(&global_register_lock, flags);
/* Tell Linux */
if (link_info.s.link_up) {
if (!netif_carrier_ok(dev))
netif_carrier_on(dev);
if (priv->queue != -1)
DEBUGPRINT
("%s: %u Mbps %s duplex, port %2d, queue %2d\n",
dev->name, link_info.s.speed,
(link_info.s.full_duplex) ? "Full" : "Half",
priv->port, priv->queue);
else
DEBUGPRINT("%s: %u Mbps %s duplex, port %2d, POW\n",
dev->name, link_info.s.speed,
(link_info.s.full_duplex) ? "Full" : "Half",
priv->port);
} else {
if (netif_carrier_ok(dev))
netif_carrier_off(dev);
DEBUGPRINT("%s: Link down\n", dev->name);
if (priv->phydev == NULL) {
/* Tell core. */
if (link_info.s.link_up) {
if (!netif_carrier_ok(dev))
netif_carrier_on(dev);
if (priv->queue != -1)
DEBUGPRINT("%s: %u Mbps %s duplex, "
"port %2d, queue %2d\n",
dev->name, link_info.s.speed,
(link_info.s.full_duplex) ?
"Full" : "Half",
priv->port, priv->queue);
else
DEBUGPRINT("%s: %u Mbps %s duplex, "
"port %2d, POW\n",
dev->name, link_info.s.speed,
(link_info.s.full_duplex) ?
"Full" : "Half",
priv->port);
} else {
if (netif_carrier_ok(dev))
netif_carrier_off(dev);
DEBUGPRINT("%s: Link down\n", dev->name);
}
}
}
......
......@@ -113,7 +113,7 @@ int cvm_oct_sgmii_init(struct net_device *dev)
struct octeon_ethernet *priv = netdev_priv(dev);
cvm_oct_common_init(dev);
dev->netdev_ops->ndo_stop(dev);
if (!octeon_is_simulation())
if (!octeon_is_simulation() && priv->phydev == NULL)
priv->poll = cvm_oct_sgmii_poll;
/* FIXME: Need autoneg logic */
......
......@@ -112,7 +112,7 @@ int cvm_oct_xaui_init(struct net_device *dev)
struct octeon_ethernet *priv = netdev_priv(dev);
cvm_oct_common_init(dev);
dev->netdev_ops->ndo_stop(dev);
if (!octeon_is_simulation())
if (!octeon_is_simulation() && priv->phydev == NULL)
priv->poll = cvm_oct_xaui_poll;
return 0;
......
......@@ -30,7 +30,7 @@
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/delay.h>
#include <linux/mii.h>
#include <linux/phy.h>
#include <net/dst.h>
......@@ -132,8 +132,6 @@ static struct timer_list cvm_oct_poll_timer;
*/
struct net_device *cvm_oct_device[TOTAL_NUMBER_OF_PORTS];
extern struct semaphore mdio_sem;
/**
* Periodic timer tick for slow management operations
*
......@@ -160,13 +158,8 @@ static void cvm_do_timer(unsigned long arg)
goto out;
priv = netdev_priv(cvm_oct_device[port]);
if (priv->poll) {
/* skip polling if we don't get the lock */
if (!down_trylock(&mdio_sem)) {
priv->poll(cvm_oct_device[port]);
up(&mdio_sem);
}
}
if (priv->poll)
priv->poll(cvm_oct_device[port]);
queues_per_port = cvmx_pko_get_num_queues(port);
/* Drain any pending packets in the free list */
......@@ -524,7 +517,7 @@ int cvm_oct_common_init(struct net_device *dev)
dev->features |= NETIF_F_LLTX;
SET_ETHTOOL_OPS(dev, &cvm_oct_ethtool_ops);
cvm_oct_mdio_setup_device(dev);
cvm_oct_phy_setup_device(dev);
dev->netdev_ops->ndo_set_mac_address(dev, &sa);
dev->netdev_ops->ndo_change_mtu(dev, dev->mtu);
......@@ -540,7 +533,10 @@ int cvm_oct_common_init(struct net_device *dev)
void cvm_oct_common_uninit(struct net_device *dev)
{
/* Currently nothing to do */
struct octeon_ethernet *priv = netdev_priv(dev);
if (priv->phydev)
phy_disconnect(priv->phydev);
}
static const struct net_device_ops cvm_oct_npi_netdev_ops = {
......@@ -627,6 +623,8 @@ static const struct net_device_ops cvm_oct_pow_netdev_ops = {
#endif
};
extern void octeon_mdiobus_force_mod_depencency(void);
/**
* Module/ driver initialization. Creates the linux network
* devices.
......@@ -640,6 +638,7 @@ static int __init cvm_oct_init_module(void)
int fau = FAU_NUM_PACKET_BUFFERS_TO_FREE;
int qos;
octeon_mdiobus_force_mod_depencency();
pr_notice("cavium-ethernet %s\n", OCTEON_ETHERNET_VERSION);
if (OCTEON_IS_MODEL(OCTEON_CN52XX))
......
......@@ -50,9 +50,9 @@ struct octeon_ethernet {
/* List of outstanding tx buffers per queue */
struct sk_buff_head tx_free_list[16];
/* Device statistics */
struct net_device_stats stats
; /* Generic MII info structure */
struct mii_if_info mii_info;
struct net_device_stats stats;
struct phy_device *phydev;
unsigned int last_link;
/* Last negotiated link state */
uint64_t link_info;
/* Called periodically to check link status */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment