summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/staging/mt7621-eth/TODO2
-rw-r--r--drivers/staging/mt7621-eth/mdio_mt7620.c173
2 files changed, 175 insertions, 0 deletions
diff --git a/drivers/staging/mt7621-eth/TODO b/drivers/staging/mt7621-eth/TODO
index 1ab0530..50fb5a9 100644
--- a/drivers/staging/mt7621-eth/TODO
+++ b/drivers/staging/mt7621-eth/TODO
@@ -4,5 +4,7 @@
- general code review and clean up
- add support for second MAC on mt7621
- convert gsw code to use switchdev interfaces
+- md7620_mmi_write etc should probably be wrapped
+ in a regmap abstraction.
Cc: NeilBrown <neil@brown.name>
diff --git a/drivers/staging/mt7621-eth/mdio_mt7620.c b/drivers/staging/mt7621-eth/mdio_mt7620.c
new file mode 100644
index 0000000..ced605c
--- /dev/null
+++ b/drivers/staging/mt7621-eth/mdio_mt7620.c
@@ -0,0 +1,173 @@
+/* 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));
+}
OpenPOWER on IntegriCloud