blob: b809dd4e52776533417667e7dd8cb54761707a27 [file] [log] [blame]
/*
* Agere Systems Inc.
* 10/100/1000 Base-T Ethernet Driver for the ET1310 and ET131x series MACs
*
* Copyright * 2005 Agere Systems Inc.
* All rights reserved.
* http://www.agere.com
*
* Copyright (c) 2011 Mark Einon <mark.einon@gmail.com>
*
*------------------------------------------------------------------------------
*
* et1310_phy.c - Routines for configuring and accessing the PHY
*
*------------------------------------------------------------------------------
*
* SOFTWARE LICENSE
*
* This software is provided subject to the following terms and conditions,
* which you should read carefully before using the software. Using this
* software indicates your acceptance of these terms and conditions. If you do
* not agree with these terms and conditions, do not use the software.
*
* Copyright * 2005 Agere Systems Inc.
* All rights reserved.
*
* Redistribution and use in source or binary forms, with or without
* modifications, are permitted provided that the following conditions are met:
*
* . Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following Disclaimer as comments in the code as
* well as in the documentation and/or other materials provided with the
* distribution.
*
* . Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following Disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* . Neither the name of Agere Systems Inc. nor the names of the contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* Disclaimer
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, INFRINGEMENT AND THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ANY
* USE, MODIFICATION OR DISTRIBUTION OF THIS SOFTWARE IS SOLELY AT THE USERS OWN
* RISK. IN NO EVENT SHALL AGERE SYSTEMS INC. OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, INCLUDING, BUT NOT LIMITED TO, CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
*/
#include "et131x_defs.h"
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/ptrace.h>
#include <linux/ctype.h>
#include <linux/string.h>
#include <linux/timer.h>
#include <linux/interrupt.h>
#include <linux/in.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/bitops.h>
#include <asm/system.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/if_arp.h>
#include <linux/ioport.h>
#include <linux/random.h>
#include <linux/phy.h>
#include "et1310_phy.h"
#include "et131x_adapter.h"
#include "et1310_address_map.h"
#include "et1310_tx.h"
#include "et1310_rx.h"
#include "et131x.h"
int et131x_mdio_read(struct mii_bus *bus, int phy_addr, int reg)
{
struct net_device *netdev = bus->priv;
struct et131x_adapter *adapter = netdev_priv(netdev);
u16 value;
int ret;
ret = et131x_phy_mii_read(adapter, phy_addr, reg, &value);
if (ret < 0)
return ret;
else
return value;
}
int et131x_mdio_write(struct mii_bus *bus, int phy_addr, int reg, u16 value)
{
struct net_device *netdev = bus->priv;
struct et131x_adapter *adapter = netdev_priv(netdev);
return et131x_mii_write(adapter, reg, value);
}
int et131x_mdio_reset(struct mii_bus *bus)
{
struct net_device *netdev = bus->priv;
struct et131x_adapter *adapter = netdev_priv(netdev);
et131x_mii_write(adapter, MII_BMCR, BMCR_RESET);
return 0;
}
int et131x_mii_read(struct et131x_adapter *adapter, u8 reg, u16 *value)
{
struct phy_device *phydev = adapter->phydev;
if (!phydev)
return -EIO;
return et131x_phy_mii_read(adapter, phydev->addr, reg, value);
}
/**
* et131x_phy_mii_read - Read from the PHY through the MII Interface on the MAC
* @adapter: pointer to our private adapter structure
* @addr: the address of the transceiver
* @reg: the register to read
* @value: pointer to a 16-bit value in which the value will be stored
*
* Returns 0 on success, errno on failure (as defined in errno.h)
*/
int et131x_phy_mii_read(struct et131x_adapter *adapter, u8 addr,
u8 reg, u16 *value)
{
struct mac_regs __iomem *mac = &adapter->regs->mac;
int status = 0;
u32 delay = 0;
u32 mii_addr;
u32 mii_cmd;
u32 mii_indicator;
/* Save a local copy of the registers we are dealing with so we can
* set them back
*/
mii_addr = readl(&mac->mii_mgmt_addr);
mii_cmd = readl(&mac->mii_mgmt_cmd);
/* Stop the current operation */
writel(0, &mac->mii_mgmt_cmd);
/* Set up the register we need to read from on the correct PHY */
writel(MII_ADDR(addr, reg), &mac->mii_mgmt_addr);
writel(0x1, &mac->mii_mgmt_cmd);
do {
udelay(50);
delay++;
mii_indicator = readl(&mac->mii_mgmt_indicator);
} while ((mii_indicator & MGMT_WAIT) && delay < 50);
/* If we hit the max delay, we could not read the register */
if (delay == 50) {
dev_warn(&adapter->pdev->dev,
"reg 0x%08x could not be read\n", reg);
dev_warn(&adapter->pdev->dev, "status is 0x%08x\n",
mii_indicator);
status = -EIO;
}
/* If we hit here we were able to read the register and we need to
* return the value to the caller */
*value = readl(&mac->mii_mgmt_stat) & 0xFFFF;
/* Stop the read operation */
writel(0, &mac->mii_mgmt_cmd);
/* set the registers we touched back to the state at which we entered
* this function
*/
writel(mii_addr, &mac->mii_mgmt_addr);
writel(mii_cmd, &mac->mii_mgmt_cmd);
return status;
}
/**
* et131x_mii_write - Write to a PHY register through the MII interface of the MAC
* @adapter: pointer to our private adapter structure
* @reg: the register to read
* @value: 16-bit value to write
*
* FIXME: one caller in netdev still
*
* Return 0 on success, errno on failure (as defined in errno.h)
*/
int et131x_mii_write(struct et131x_adapter *adapter, u8 reg, u16 value)
{
struct mac_regs __iomem *mac = &adapter->regs->mac;
struct phy_device *phydev = adapter->phydev;
int status = 0;
u8 addr;
u32 delay = 0;
u32 mii_addr;
u32 mii_cmd;
u32 mii_indicator;
if (!phydev)
return -EIO;
addr = phydev->addr;
/* Save a local copy of the registers we are dealing with so we can
* set them back
*/
mii_addr = readl(&mac->mii_mgmt_addr);
mii_cmd = readl(&mac->mii_mgmt_cmd);
/* Stop the current operation */
writel(0, &mac->mii_mgmt_cmd);
/* Set up the register we need to write to on the correct PHY */
writel(MII_ADDR(addr, reg), &mac->mii_mgmt_addr);
/* Add the value to write to the registers to the mac */
writel(value, &mac->mii_mgmt_ctrl);
do {
udelay(50);
delay++;
mii_indicator = readl(&mac->mii_mgmt_indicator);
} while ((mii_indicator & MGMT_BUSY) && delay < 100);
/* If we hit the max delay, we could not write the register */
if (delay == 100) {
u16 tmp;
dev_warn(&adapter->pdev->dev,
"reg 0x%08x could not be written", reg);
dev_warn(&adapter->pdev->dev, "status is 0x%08x\n",
mii_indicator);
dev_warn(&adapter->pdev->dev, "command is 0x%08x\n",
readl(&mac->mii_mgmt_cmd));
et131x_mii_read(adapter, reg, &tmp);
status = -EIO;
}
/* Stop the write operation */
writel(0, &mac->mii_mgmt_cmd);
/*
* set the registers we touched back to the state at which we entered
* this function
*/
writel(mii_addr, &mac->mii_mgmt_addr);
writel(mii_cmd, &mac->mii_mgmt_cmd);
return status;
}
/**
* et1310_phy_power_down - PHY power control
* @adapter: device to control
* @down: true for off/false for back on
*
* one hundred, ten, one thousand megs
* How would you like to have your LAN accessed
* Can't you see that this code processed
* Phy power, phy power..
*/
void et1310_phy_power_down(struct et131x_adapter *adapter, bool down)
{
u16 data;
et131x_mii_read(adapter, MII_BMCR, &data);
data &= ~BMCR_PDOWN;
if (down)
data |= BMCR_PDOWN;
et131x_mii_write(adapter, MII_BMCR, data);
}
/* Still used from _mac for BIT_READ */
void et1310_phy_access_mii_bit(struct et131x_adapter *adapter, u16 action,
u16 regnum, u16 bitnum, u8 *value)
{
u16 reg;
u16 mask = 0x0001 << bitnum;
/* Read the requested register */
et131x_mii_read(adapter, regnum, &reg);
switch (action) {
case TRUEPHY_BIT_READ:
*value = (reg & mask) >> bitnum;
break;
case TRUEPHY_BIT_SET:
et131x_mii_write(adapter, regnum, reg | mask);
break;
case TRUEPHY_BIT_CLEAR:
et131x_mii_write(adapter, regnum, reg & ~mask);
break;
default:
break;
}
}
/**
* et131x_xcvr_init - Init the phy if we are setting it into force mode
* @adapter: pointer to our private adapter structure
*
*/
void et131x_xcvr_init(struct et131x_adapter *adapter)
{
u16 imr;
u16 isr;
u16 lcr2;
et131x_mii_read(adapter, PHY_INTERRUPT_STATUS, &isr);
et131x_mii_read(adapter, PHY_INTERRUPT_MASK, &imr);
/* Set the link status interrupt only. Bad behavior when link status
* and auto neg are set, we run into a nested interrupt problem
*/
imr |= (ET_PHY_INT_MASK_AUTONEGSTAT &
ET_PHY_INT_MASK_LINKSTAT &
ET_PHY_INT_MASK_ENABLE);
et131x_mii_write(adapter, PHY_INTERRUPT_MASK, imr);
/* Set the LED behavior such that LED 1 indicates speed (off =
* 10Mbits, blink = 100Mbits, on = 1000Mbits) and LED 2 indicates
* link and activity (on for link, blink off for activity).
*
* NOTE: Some customizations have been added here for specific
* vendors; The LED behavior is now determined by vendor data in the
* EEPROM. However, the above description is the default.
*/
if ((adapter->eeprom_data[1] & 0x4) == 0) {
et131x_mii_read(adapter, PHY_LED_2, &lcr2);
lcr2 &= (ET_LED2_LED_100TX & ET_LED2_LED_1000T);
lcr2 |= (LED_VAL_LINKON_ACTIVE << LED_LINK_SHIFT);
if ((adapter->eeprom_data[1] & 0x8) == 0)
lcr2 |= (LED_VAL_1000BT_100BTX << LED_TXRX_SHIFT);
else
lcr2 |= (LED_VAL_LINKON << LED_TXRX_SHIFT);
et131x_mii_write(adapter, PHY_LED_2, lcr2);
}
}