Commit | Line | Data |
---|---|---|
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 | ||
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 | ||
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 | ||
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 | ||
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 | ||
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 | ||
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 | ||
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 | ||
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 | ||
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 | ||
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 | ||
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 | ||
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 | } |