openwrt/target/linux/ramips/files/drivers/net/ethernet/ralink/mdio_rt2880.c

233 lines
5.4 KiB
C

/*
* 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; version 2 of the License
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
*
* Copyright (C) 2009-2013 John Crispin <blogic@openwrt.org>
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/dma-mapping.h>
#include <linux/init.h>
#include <linux/skbuff.h>
#include <linux/etherdevice.h>
#include <linux/ethtool.h>
#include <linux/platform_device.h>
#include <linux/phy.h>
#include <linux/of_device.h>
#include <linux/clk.h>
#include <linux/of_net.h>
#include <linux/of_mdio.h>
#include "ralink_soc_eth.h"
#include "mdio_rt2880.h"
#include "mdio.h"
#define FE_MDIO_RETRY 1000
static unsigned char *rt2880_speed_str(struct fe_priv *priv)
{
switch (priv->phy->speed[0]) {
case SPEED_1000:
return "1000";
case SPEED_100:
return "100";
case SPEED_10:
return "10";
}
return "?";
}
void rt2880_mdio_link_adjust(struct fe_priv *priv, int port)
{
u32 mdio_cfg;
if (!priv->link[0]) {
netif_carrier_off(priv->netdev);
netdev_info(priv->netdev, "link down\n");
return;
}
mdio_cfg = FE_MDIO_CFG_TX_CLK_SKEW_200 |
FE_MDIO_CFG_RX_CLK_SKEW_200 |
FE_MDIO_CFG_GP1_FRC_EN;
if (priv->phy->duplex[0] == DUPLEX_FULL)
mdio_cfg |= FE_MDIO_CFG_GP1_DUPLEX;
if (priv->phy->tx_fc[0])
mdio_cfg |= FE_MDIO_CFG_GP1_FC_TX;
if (priv->phy->rx_fc[0])
mdio_cfg |= FE_MDIO_CFG_GP1_FC_RX;
switch (priv->phy->speed[0]) {
case SPEED_10:
mdio_cfg |= FE_MDIO_CFG_GP1_SPEED_10;
break;
case SPEED_100:
mdio_cfg |= FE_MDIO_CFG_GP1_SPEED_100;
break;
case SPEED_1000:
mdio_cfg |= FE_MDIO_CFG_GP1_SPEED_1000;
break;
default:
BUG();
}
fe_w32(mdio_cfg, FE_MDIO_CFG);
netif_carrier_on(priv->netdev);
netdev_info(priv->netdev, "link up (%sMbps/%s duplex)\n",
rt2880_speed_str(priv),
(DUPLEX_FULL == priv->phy->duplex[0]) ? "Full" : "Half");
}
static int rt2880_mdio_wait_ready(struct fe_priv *priv)
{
int retries;
retries = FE_MDIO_RETRY;
while (1) {
u32 t;
t = fe_r32(FE_MDIO_ACCESS);
if ((t & (0x1 << 31)) == 0)
return 0;
if (retries-- == 0)
break;
udelay(1);
}
dev_err(priv->device, "MDIO operation timed out\n");
return -ETIMEDOUT;
}
int rt2880_mdio_read(struct mii_bus *bus, int phy_addr, int phy_reg)
{
struct fe_priv *priv = bus->priv;
int err;
u32 t;
err = rt2880_mdio_wait_ready(priv);
if (err)
return 0xffff;
t = (phy_addr << 24) | (phy_reg << 16);
fe_w32(t, FE_MDIO_ACCESS);
t |= (1 << 31);
fe_w32(t, FE_MDIO_ACCESS);
err = rt2880_mdio_wait_ready(priv);
if (err)
return 0xffff;
pr_debug("%s: addr=%04x, reg=%04x, value=%04x\n", __func__,
phy_addr, phy_reg, fe_r32(FE_MDIO_ACCESS) & 0xffff);
return fe_r32(FE_MDIO_ACCESS) & 0xffff;
}
int rt2880_mdio_write(struct mii_bus *bus, int phy_addr, int phy_reg, u16 val)
{
struct fe_priv *priv = bus->priv;
int err;
u32 t;
pr_debug("%s: addr=%04x, reg=%04x, value=%04x\n", __func__,
phy_addr, phy_reg, fe_r32(FE_MDIO_ACCESS) & 0xffff);
err = rt2880_mdio_wait_ready(priv);
if (err)
return err;
t = (1 << 30) | (phy_addr << 24) | (phy_reg << 16) | val;
fe_w32(t, FE_MDIO_ACCESS);
t |= (1 << 31);
fe_w32(t, FE_MDIO_ACCESS);
return rt2880_mdio_wait_ready(priv);
}
void rt2880_port_init(struct fe_priv *priv, struct device_node *np)
{
const __be32 *id = of_get_property(np, "reg", NULL);
const __be32 *link;
int size;
int phy_mode;
if (!id || (be32_to_cpu(*id) != 0)) {
pr_err("%s: invalid port id\n", np->name);
return;
}
priv->phy->phy_fixed[0] = of_get_property(np, "ralink,fixed-link", &size);
if (priv->phy->phy_fixed[0] && (size != (4 * sizeof(*priv->phy->phy_fixed[0])))) {
pr_err("%s: invalid fixed link property\n", np->name);
priv->phy->phy_fixed[0] = NULL;
return;
}
phy_mode = of_get_phy_mode(np);
switch (phy_mode) {
case PHY_INTERFACE_MODE_RGMII:
break;
case PHY_INTERFACE_MODE_MII:
break;
case PHY_INTERFACE_MODE_RMII:
break;
default:
if (!priv->phy->phy_fixed[0])
dev_err(priv->device, "port %d - invalid phy mode\n", priv->phy->speed[0]);
break;
}
priv->phy->phy_node[0] = of_parse_phandle(np, "phy-handle", 0);
if (!priv->phy->phy_node[0] && !priv->phy->phy_fixed[0])
return;
if (priv->phy->phy_fixed[0]) {
link = priv->phy->phy_fixed[0];
priv->phy->speed[0] = be32_to_cpup(link++);
priv->phy->duplex[0] = be32_to_cpup(link++);
priv->phy->tx_fc[0] = be32_to_cpup(link++);
priv->phy->rx_fc[0] = be32_to_cpup(link++);
priv->link[0] = 1;
switch (priv->phy->speed[0]) {
case SPEED_10:
break;
case SPEED_100:
break;
case SPEED_1000:
break;
default:
dev_err(priv->device, "invalid link speed: %d\n", priv->phy->speed[0]);
priv->phy->phy_fixed[0] = 0;
return;
}
dev_info(priv->device, "using fixed link parameters\n");
rt2880_mdio_link_adjust(priv, 0);
return;
}
if (priv->phy->phy_node[0] && priv->mii_bus->phy_map[0]) {
fe_connect_phy_node(priv, priv->phy->phy_node[0]);
}
return;
}