174 lines
4.2 KiB
C
174 lines
4.2 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.
|
|
*
|
|
* Copyright (C) 2009-2016 John Crispin <blogic@openwrt.org>
|
|
* Copyright (C) 2009-2016 Felix Fietkau <nbd@openwrt.org>
|
|
* Copyright (C) 2013-2016 Michael Lee <igvtee@gmail.com>
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/types.h>
|
|
|
|
#include "mtk_eth_soc.h"
|
|
#include "gsw_mt7620.h"
|
|
#include "mdio.h"
|
|
|
|
static int mt7620_mii_busy_wait(struct mt7620_gsw *gsw)
|
|
{
|
|
unsigned long t_start = jiffies;
|
|
|
|
while (1) {
|
|
if (!(mtk_switch_r32(gsw,
|
|
gsw->piac_offset + MT7620_GSW_REG_PIAC) &
|
|
GSW_MDIO_ACCESS))
|
|
return 0;
|
|
if (time_after(jiffies, t_start + GSW_REG_PHY_TIMEOUT))
|
|
break;
|
|
}
|
|
|
|
dev_err(gsw->dev, "mdio: MDIO timeout\n");
|
|
return -1;
|
|
}
|
|
|
|
u32 _mt7620_mii_write(struct mt7620_gsw *gsw, u32 phy_addr,
|
|
u32 phy_register, u32 write_data)
|
|
{
|
|
if (mt7620_mii_busy_wait(gsw))
|
|
return -1;
|
|
|
|
write_data &= 0xffff;
|
|
|
|
mtk_switch_w32(gsw, GSW_MDIO_ACCESS | GSW_MDIO_START | GSW_MDIO_WRITE |
|
|
(phy_register << GSW_MDIO_REG_SHIFT) |
|
|
(phy_addr << GSW_MDIO_ADDR_SHIFT) | write_data,
|
|
MT7620_GSW_REG_PIAC);
|
|
|
|
if (mt7620_mii_busy_wait(gsw))
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL_GPL(_mt7620_mii_write);
|
|
|
|
u32 _mt7620_mii_read(struct mt7620_gsw *gsw, int phy_addr, int phy_reg)
|
|
{
|
|
u32 d;
|
|
|
|
if (mt7620_mii_busy_wait(gsw))
|
|
return 0xffff;
|
|
|
|
mtk_switch_w32(gsw, GSW_MDIO_ACCESS | GSW_MDIO_START | GSW_MDIO_READ |
|
|
(phy_reg << GSW_MDIO_REG_SHIFT) |
|
|
(phy_addr << GSW_MDIO_ADDR_SHIFT),
|
|
MT7620_GSW_REG_PIAC);
|
|
|
|
if (mt7620_mii_busy_wait(gsw))
|
|
return 0xffff;
|
|
|
|
d = mtk_switch_r32(gsw, MT7620_GSW_REG_PIAC) & 0xffff;
|
|
|
|
return d;
|
|
}
|
|
EXPORT_SYMBOL_GPL(_mt7620_mii_read);
|
|
|
|
int mt7620_mdio_write(struct mii_bus *bus, int phy_addr, int phy_reg, u16 val)
|
|
{
|
|
struct mtk_eth *eth = bus->priv;
|
|
struct mt7620_gsw *gsw = (struct mt7620_gsw *)eth->sw_priv;
|
|
|
|
return _mt7620_mii_write(gsw, phy_addr, phy_reg, val);
|
|
}
|
|
|
|
int mt7620_mdio_read(struct mii_bus *bus, int phy_addr, int phy_reg)
|
|
{
|
|
struct mtk_eth *eth = bus->priv;
|
|
struct mt7620_gsw *gsw = (struct mt7620_gsw *)eth->sw_priv;
|
|
|
|
return _mt7620_mii_read(gsw, phy_addr, phy_reg);
|
|
}
|
|
|
|
void mt7530_mdio_w32(struct mt7620_gsw *gsw, u32 reg, u32 val)
|
|
{
|
|
_mt7620_mii_write(gsw, 0x1f, 0x1f, (reg >> 6) & 0x3ff);
|
|
_mt7620_mii_write(gsw, 0x1f, (reg >> 2) & 0xf, val & 0xffff);
|
|
_mt7620_mii_write(gsw, 0x1f, 0x10, val >> 16);
|
|
}
|
|
EXPORT_SYMBOL_GPL(mt7530_mdio_w32);
|
|
|
|
u32 mt7530_mdio_r32(struct mt7620_gsw *gsw, u32 reg)
|
|
{
|
|
u16 high, low;
|
|
|
|
_mt7620_mii_write(gsw, 0x1f, 0x1f, (reg >> 6) & 0x3ff);
|
|
low = _mt7620_mii_read(gsw, 0x1f, (reg >> 2) & 0xf);
|
|
high = _mt7620_mii_read(gsw, 0x1f, 0x10);
|
|
|
|
return (high << 16) | (low & 0xffff);
|
|
}
|
|
EXPORT_SYMBOL_GPL(mt7530_mdio_r32);
|
|
|
|
void mt7530_mdio_m32(struct mt7620_gsw *gsw, u32 mask, u32 set, u32 reg)
|
|
{
|
|
u32 val = mt7530_mdio_r32(gsw, reg);
|
|
|
|
val &= ~mask;
|
|
val |= set;
|
|
mt7530_mdio_w32(gsw, reg, val);
|
|
}
|
|
EXPORT_SYMBOL_GPL(mt7530_mdio_m32);
|
|
|
|
static unsigned char *mtk_speed_str(int speed)
|
|
{
|
|
switch (speed) {
|
|
case 2:
|
|
case SPEED_1000:
|
|
return "1000";
|
|
case 1:
|
|
case SPEED_100:
|
|
return "100";
|
|
case 0:
|
|
case SPEED_10:
|
|
return "10";
|
|
}
|
|
|
|
return "? ";
|
|
}
|
|
|
|
int mt7620_has_carrier(struct mtk_eth *eth)
|
|
{
|
|
struct mt7620_gsw *gsw = (struct mt7620_gsw *)eth->sw_priv;
|
|
int i;
|
|
|
|
for (i = 0; i < GSW_PORT6; i++)
|
|
if (mt7530_mdio_r32(gsw, GSW_REG_PORT_STATUS(i)) & 0x1)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
void mt7620_print_link_state(struct mtk_eth *eth, int port, int link,
|
|
int speed, int duplex)
|
|
{
|
|
struct mt7620_gsw *gsw = eth->sw_priv;
|
|
|
|
if (link)
|
|
dev_info(gsw->dev, "port %d link up (%sMbps/%s duplex)\n",
|
|
port, mtk_speed_str(speed),
|
|
(duplex) ? "Full" : "Half");
|
|
else
|
|
dev_info(gsw->dev, "port %d link down\n", port);
|
|
}
|
|
|
|
void mt7620_mdio_link_adjust(struct mtk_eth *eth, int port)
|
|
{
|
|
mt7620_print_link_state(eth, port, eth->link[port],
|
|
eth->phy->speed[port],
|
|
(eth->phy->duplex[port] == DUPLEX_FULL));
|
|
}
|