ramips: add v4.9 support
[librecmc/librecmc.git] / target / linux / ramips / files-4.9 / drivers / net / ethernet / mtk / mdio_mt7620.c
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-2015 John Crispin <blogic@openwrt.org>
11  *   Copyright (C) 2009-2015 Felix Fietkau <nbd@nbd.name>
12  *   Copyright (C) 2013-2015 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
23 static 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, MT7620A_GSW_REG_PIAC) & GSW_MDIO_ACCESS))
29                         return 0;
30                 if (time_after(jiffies, t_start + GSW_REG_PHY_TIMEOUT))
31                         break;
32         }
33
34         dev_err(gsw->dev, "mdio: MDIO timeout\n");
35         return -1;
36 }
37
38 u32 _mt7620_mii_write(struct mt7620_gsw *gsw, u32 phy_addr,
39                              u32 phy_register, u32 write_data)
40 {
41         if (mt7620_mii_busy_wait(gsw))
42                 return -1;
43
44         write_data &= 0xffff;
45
46         mtk_switch_w32(gsw, GSW_MDIO_ACCESS | GSW_MDIO_START | GSW_MDIO_WRITE |
47                 (phy_register << GSW_MDIO_REG_SHIFT) |
48                 (phy_addr << GSW_MDIO_ADDR_SHIFT) | write_data,
49                 MT7620A_GSW_REG_PIAC);
50
51         if (mt7620_mii_busy_wait(gsw))
52                 return -1;
53
54         return 0;
55 }
56
57 u32 _mt7620_mii_read(struct mt7620_gsw *gsw, int phy_addr, int phy_reg)
58 {
59         u32 d;
60
61         if (mt7620_mii_busy_wait(gsw))
62                 return 0xffff;
63
64         mtk_switch_w32(gsw, GSW_MDIO_ACCESS | GSW_MDIO_START | GSW_MDIO_READ |
65                 (phy_reg << GSW_MDIO_REG_SHIFT) |
66                 (phy_addr << GSW_MDIO_ADDR_SHIFT),
67                 MT7620A_GSW_REG_PIAC);
68
69         if (mt7620_mii_busy_wait(gsw))
70                 return 0xffff;
71
72         d = mtk_switch_r32(gsw, MT7620A_GSW_REG_PIAC) & 0xffff;
73
74         return d;
75 }
76
77 int mt7620_mdio_write(struct mii_bus *bus, int phy_addr, int phy_reg, u16 val)
78 {
79         struct fe_priv *priv = bus->priv;
80         struct mt7620_gsw *gsw = (struct mt7620_gsw *)priv->soc->swpriv;
81
82         return _mt7620_mii_write(gsw, phy_addr, phy_reg, val);
83 }
84
85 int mt7620_mdio_read(struct mii_bus *bus, int phy_addr, int phy_reg)
86 {
87         struct fe_priv *priv = bus->priv;
88         struct mt7620_gsw *gsw = (struct mt7620_gsw *)priv->soc->swpriv;
89
90         return _mt7620_mii_read(gsw, phy_addr, phy_reg);
91 }
92
93 void mt7530_mdio_w32(struct mt7620_gsw *gsw, u32 reg, u32 val)
94 {
95         _mt7620_mii_write(gsw, 0x1f, 0x1f, (reg >> 6) & 0x3ff);
96         _mt7620_mii_write(gsw, 0x1f, (reg >> 2) & 0xf,  val & 0xffff);
97         _mt7620_mii_write(gsw, 0x1f, 0x10, val >> 16);
98 }
99
100 u32 mt7530_mdio_r32(struct mt7620_gsw *gsw, u32 reg)
101 {
102         u16 high, low;
103
104         _mt7620_mii_write(gsw, 0x1f, 0x1f, (reg >> 6) & 0x3ff);
105         low = _mt7620_mii_read(gsw, 0x1f, (reg >> 2) & 0xf);
106         high = _mt7620_mii_read(gsw, 0x1f, 0x10);
107
108         return (high << 16) | (low & 0xffff);
109 }
110
111 static unsigned char *fe_speed_str(int speed)
112 {
113         switch (speed) {
114         case 2:
115         case SPEED_1000:
116                 return "1000";
117         case 1:
118         case SPEED_100:
119                 return "100";
120         case 0:
121         case SPEED_10:
122                 return "10";
123         }
124
125         return "? ";
126 }
127
128 int mt7620_has_carrier(struct fe_priv *priv)
129 {
130         struct mt7620_gsw *gsw = (struct mt7620_gsw *)priv->soc->swpriv;
131         int i;
132
133         for (i = 0; i < GSW_PORT6; i++)
134                 if (mtk_switch_r32(gsw, GSW_REG_PORT_STATUS(i)) & 0x1)
135                         return 1;
136         return 0;
137 }
138
139
140 void mt7620_handle_carrier(struct fe_priv *priv)
141 {
142         if (!priv->phy)
143                 return;
144
145         if (mt7620_has_carrier(priv))
146                 netif_carrier_on(priv->netdev);
147         else
148                 netif_carrier_off(priv->netdev);
149 }
150
151 void mt7620_print_link_state(struct fe_priv *priv, int port, int link,
152                              int speed, int duplex)
153 {
154         if (link)
155                 netdev_info(priv->netdev, "port %d link up (%sMbps/%s duplex)\n",
156                             port, fe_speed_str(speed),
157                             (duplex) ? "Full" : "Half");
158         else
159                 netdev_info(priv->netdev, "port %d link down\n", port);
160 }
161
162 void mt7620_mdio_link_adjust(struct fe_priv *priv, int port)
163 {
164         mt7620_print_link_state(priv, port, priv->link[port],
165                                 priv->phy->speed[port],
166                                 (priv->phy->duplex[port] == DUPLEX_FULL));
167         mt7620_handle_carrier(priv);
168 }