Merge tag 'fuse-update-4.20' of git://git.kernel.org/pub/scm/linux/kernel/git/mszered...
[linux-2.6-block.git] / drivers / staging / mt7621-eth / mdio_mt7620.c
CommitLineData
14befc2d
JC
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}