linux/drivers/staging/mt7621-eth/mdio_mt7620.c
<<
>>
Prefs
   1/*   This program is free software; you can redistribute it and/or modify
   2 *   it under the terms of the GNU General Public License as published by
   3 *   the Free Software Foundation; version 2 of the License
   4 *
   5 *   This program is distributed in the hope that it will be useful,
   6 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
   7 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   8 *   GNU General Public License for more details.
   9 *
  10 *   Copyright (C) 2009-2016 John Crispin <blogic@openwrt.org>
  11 *   Copyright (C) 2009-2016 Felix Fietkau <nbd@openwrt.org>
  12 *   Copyright (C) 2013-2016 Michael Lee <igvtee@gmail.com>
  13 */
  14
  15#include <linux/module.h>
  16#include <linux/kernel.h>
  17#include <linux/types.h>
  18
  19#include "mtk_eth_soc.h"
  20#include "gsw_mt7620.h"
  21#include "mdio.h"
  22
  23static int mt7620_mii_busy_wait(struct mt7620_gsw *gsw)
  24{
  25        unsigned long t_start = jiffies;
  26
  27        while (1) {
  28                if (!(mtk_switch_r32(gsw,
  29                                     gsw->piac_offset + MT7620_GSW_REG_PIAC) &
  30                                     GSW_MDIO_ACCESS))
  31                        return 0;
  32                if (time_after(jiffies, t_start + GSW_REG_PHY_TIMEOUT))
  33                        break;
  34        }
  35
  36        dev_err(gsw->dev, "mdio: MDIO timeout\n");
  37        return -1;
  38}
  39
  40u32 _mt7620_mii_write(struct mt7620_gsw *gsw, u32 phy_addr,
  41                      u32 phy_register, u32 write_data)
  42{
  43        if (mt7620_mii_busy_wait(gsw))
  44                return -1;
  45
  46        write_data &= 0xffff;
  47
  48        mtk_switch_w32(gsw, GSW_MDIO_ACCESS | GSW_MDIO_START | GSW_MDIO_WRITE |
  49                (phy_register << GSW_MDIO_REG_SHIFT) |
  50                (phy_addr << GSW_MDIO_ADDR_SHIFT) | write_data,
  51                MT7620_GSW_REG_PIAC);
  52
  53        if (mt7620_mii_busy_wait(gsw))
  54                return -1;
  55
  56        return 0;
  57}
  58EXPORT_SYMBOL_GPL(_mt7620_mii_write);
  59
  60u32 _mt7620_mii_read(struct mt7620_gsw *gsw, int phy_addr, int phy_reg)
  61{
  62        u32 d;
  63
  64        if (mt7620_mii_busy_wait(gsw))
  65                return 0xffff;
  66
  67        mtk_switch_w32(gsw, GSW_MDIO_ACCESS | GSW_MDIO_START | GSW_MDIO_READ |
  68                (phy_reg << GSW_MDIO_REG_SHIFT) |
  69                (phy_addr << GSW_MDIO_ADDR_SHIFT),
  70                MT7620_GSW_REG_PIAC);
  71
  72        if (mt7620_mii_busy_wait(gsw))
  73                return 0xffff;
  74
  75        d = mtk_switch_r32(gsw, MT7620_GSW_REG_PIAC) & 0xffff;
  76
  77        return d;
  78}
  79EXPORT_SYMBOL_GPL(_mt7620_mii_read);
  80
  81int mt7620_mdio_write(struct mii_bus *bus, int phy_addr, int phy_reg, u16 val)
  82{
  83        struct mtk_eth *eth = bus->priv;
  84        struct mt7620_gsw *gsw = (struct mt7620_gsw *)eth->sw_priv;
  85
  86        return _mt7620_mii_write(gsw, phy_addr, phy_reg, val);
  87}
  88
  89int mt7620_mdio_read(struct mii_bus *bus, int phy_addr, int phy_reg)
  90{
  91        struct mtk_eth *eth = bus->priv;
  92        struct mt7620_gsw *gsw = (struct mt7620_gsw *)eth->sw_priv;
  93
  94        return _mt7620_mii_read(gsw, phy_addr, phy_reg);
  95}
  96
  97void mt7530_mdio_w32(struct mt7620_gsw *gsw, u32 reg, u32 val)
  98{
  99        _mt7620_mii_write(gsw, 0x1f, 0x1f, (reg >> 6) & 0x3ff);
 100        _mt7620_mii_write(gsw, 0x1f, (reg >> 2) & 0xf,  val & 0xffff);
 101        _mt7620_mii_write(gsw, 0x1f, 0x10, val >> 16);
 102}
 103EXPORT_SYMBOL_GPL(mt7530_mdio_w32);
 104
 105u32 mt7530_mdio_r32(struct mt7620_gsw *gsw, u32 reg)
 106{
 107        u16 high, low;
 108
 109        _mt7620_mii_write(gsw, 0x1f, 0x1f, (reg >> 6) & 0x3ff);
 110        low = _mt7620_mii_read(gsw, 0x1f, (reg >> 2) & 0xf);
 111        high = _mt7620_mii_read(gsw, 0x1f, 0x10);
 112
 113        return (high << 16) | (low & 0xffff);
 114}
 115EXPORT_SYMBOL_GPL(mt7530_mdio_r32);
 116
 117void mt7530_mdio_m32(struct mt7620_gsw *gsw, u32 mask, u32 set, u32 reg)
 118{
 119        u32 val = mt7530_mdio_r32(gsw, reg);
 120
 121        val &= ~mask;
 122        val |= set;
 123        mt7530_mdio_w32(gsw, reg, val);
 124}
 125EXPORT_SYMBOL_GPL(mt7530_mdio_m32);
 126
 127static unsigned char *mtk_speed_str(int speed)
 128{
 129        switch (speed) {
 130        case 2:
 131        case SPEED_1000:
 132                return "1000";
 133        case 1:
 134        case SPEED_100:
 135                return "100";
 136        case 0:
 137        case SPEED_10:
 138                return "10";
 139        }
 140
 141        return "? ";
 142}
 143
 144int mt7620_has_carrier(struct mtk_eth *eth)
 145{
 146        struct mt7620_gsw *gsw = (struct mt7620_gsw *)eth->sw_priv;
 147        int i;
 148
 149        for (i = 0; i < GSW_PORT6; i++)
 150                if (mt7530_mdio_r32(gsw, GSW_REG_PORT_STATUS(i)) & 0x1)
 151                        return 1;
 152        return 0;
 153}
 154
 155void mt7620_print_link_state(struct mtk_eth *eth, int port, int link,
 156                             int speed, int duplex)
 157{
 158        struct mt7620_gsw *gsw = eth->sw_priv;
 159
 160        if (link)
 161                dev_info(gsw->dev, "port %d link up (%sMbps/%s duplex)\n",
 162                         port, mtk_speed_str(speed),
 163                         (duplex) ? "Full" : "Half");
 164        else
 165                dev_info(gsw->dev, "port %d link down\n", port);
 166}
 167
 168void mt7620_mdio_link_adjust(struct mtk_eth *eth, int port)
 169{
 170        mt7620_print_link_state(eth, port, eth->link[port],
 171                                eth->phy->speed[port],
 172                                (eth->phy->duplex[port] == DUPLEX_FULL));
 173}
 174