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 
mt7620_mii_busy_wait(struct mt7620_gsw * gsw)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,
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 
_mt7620_mii_write(struct mt7620_gsw * gsw,u32 phy_addr,u32 phy_register,u32 write_data)40 u32 _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 }
58 EXPORT_SYMBOL_GPL(_mt7620_mii_write);
59 
_mt7620_mii_read(struct mt7620_gsw * gsw,int phy_addr,int phy_reg)60 u32 _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 }
79 EXPORT_SYMBOL_GPL(_mt7620_mii_read);
80 
mt7620_mdio_write(struct mii_bus * bus,int phy_addr,int phy_reg,u16 val)81 int 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 
mt7620_mdio_read(struct mii_bus * bus,int phy_addr,int phy_reg)89 int 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 
mt7530_mdio_w32(struct mt7620_gsw * gsw,u32 reg,u32 val)97 void 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 }
103 EXPORT_SYMBOL_GPL(mt7530_mdio_w32);
104 
mt7530_mdio_r32(struct mt7620_gsw * gsw,u32 reg)105 u32 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 }
115 EXPORT_SYMBOL_GPL(mt7530_mdio_r32);
116 
mt7530_mdio_m32(struct mt7620_gsw * gsw,u32 mask,u32 set,u32 reg)117 void 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 }
125 EXPORT_SYMBOL_GPL(mt7530_mdio_m32);
126 
mtk_speed_str(int speed)127 static 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 
mt7620_has_carrier(struct mtk_eth * eth)144 int 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 
mt7620_print_link_state(struct mtk_eth * eth,int port,int link,int speed,int duplex)155 void 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 
mt7620_mdio_link_adjust(struct mtk_eth * eth,int port)168 void 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