blob: 838068d2b581830c507fa08bc86085d1383d0a1e [file] [log] [blame]
Vivien Didelot18abed22016-11-04 03:23:26 +01001/*
2 * Marvell 88E6xxx Switch Port Registers support
3 *
4 * Copyright (c) 2008 Marvell Semiconductor
5 *
6 * Copyright (c) 2016 Vivien Didelot <vivien.didelot@savoirfairelinux.com>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 */
13
14#include "mv88e6xxx.h"
15#include "port.h"
16
17int mv88e6xxx_port_read(struct mv88e6xxx_chip *chip, int port, int reg,
18 u16 *val)
19{
20 int addr = chip->info->port_base_addr + port;
21
22 return mv88e6xxx_read(chip, addr, reg, val);
23}
24
25int mv88e6xxx_port_write(struct mv88e6xxx_chip *chip, int port, int reg,
26 u16 val)
27{
28 int addr = chip->info->port_base_addr + port;
29
30 return mv88e6xxx_write(chip, addr, reg, val);
31}
Vivien Didelote28def332016-11-04 03:23:27 +010032
Vivien Didelot08ef7f12016-11-04 03:23:32 +010033/* Offset 0x01: MAC (or PCS or Physical) Control Register
34 *
35 * Link, Duplex and Flow Control have one force bit, one value bit.
36 */
37
Vivien Didelota0a0f622016-11-04 03:23:34 +010038static int mv88e6xxx_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port,
39 phy_interface_t mode)
40{
41 u16 reg;
42 int err;
43
44 err = mv88e6xxx_port_read(chip, port, PORT_PCS_CTRL, &reg);
45 if (err)
46 return err;
47
48 reg &= ~(PORT_PCS_CTRL_RGMII_DELAY_RXCLK |
49 PORT_PCS_CTRL_RGMII_DELAY_TXCLK);
50
51 switch (mode) {
52 case PHY_INTERFACE_MODE_RGMII_RXID:
53 reg |= PORT_PCS_CTRL_RGMII_DELAY_RXCLK;
54 break;
55 case PHY_INTERFACE_MODE_RGMII_TXID:
56 reg |= PORT_PCS_CTRL_RGMII_DELAY_TXCLK;
57 break;
58 case PHY_INTERFACE_MODE_RGMII_ID:
59 reg |= PORT_PCS_CTRL_RGMII_DELAY_RXCLK |
60 PORT_PCS_CTRL_RGMII_DELAY_TXCLK;
61 break;
62 default:
63 /* no delay */
64 break;
65 }
66
67 err = mv88e6xxx_port_write(chip, port, PORT_PCS_CTRL, reg);
68 if (err)
69 return err;
70
71 netdev_dbg(chip->ds->ports[port].netdev, "delay RXCLK %s, TXCLK %s\n",
72 reg & PORT_PCS_CTRL_RGMII_DELAY_RXCLK ? "yes" : "no",
73 reg & PORT_PCS_CTRL_RGMII_DELAY_TXCLK ? "yes" : "no");
74
75 return 0;
76}
77
78int mv88e6352_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port,
79 phy_interface_t mode)
80{
81 if (port < 5)
82 return -EOPNOTSUPP;
83
84 return mv88e6xxx_port_set_rgmii_delay(chip, port, mode);
85}
86
87int mv88e6390_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port,
88 phy_interface_t mode)
89{
90 if (port != 0)
91 return -EOPNOTSUPP;
92
93 return mv88e6xxx_port_set_rgmii_delay(chip, port, mode);
94}
95
Vivien Didelot08ef7f12016-11-04 03:23:32 +010096int mv88e6xxx_port_set_link(struct mv88e6xxx_chip *chip, int port, int link)
97{
98 u16 reg;
99 int err;
100
101 err = mv88e6xxx_port_read(chip, port, PORT_PCS_CTRL, &reg);
102 if (err)
103 return err;
104
105 reg &= ~(PORT_PCS_CTRL_FORCE_LINK | PORT_PCS_CTRL_LINK_UP);
106
107 switch (link) {
108 case LINK_FORCED_DOWN:
109 reg |= PORT_PCS_CTRL_FORCE_LINK;
110 break;
111 case LINK_FORCED_UP:
112 reg |= PORT_PCS_CTRL_FORCE_LINK | PORT_PCS_CTRL_LINK_UP;
113 break;
114 case LINK_UNFORCED:
115 /* normal link detection */
116 break;
117 default:
118 return -EINVAL;
119 }
120
121 err = mv88e6xxx_port_write(chip, port, PORT_PCS_CTRL, reg);
122 if (err)
123 return err;
124
125 netdev_dbg(chip->ds->ports[port].netdev, "%s link %s\n",
126 reg & PORT_PCS_CTRL_FORCE_LINK ? "Force" : "Unforce",
127 reg & PORT_PCS_CTRL_LINK_UP ? "up" : "down");
128
129 return 0;
130}
131
Vivien Didelot7f1ae072016-11-04 03:23:33 +0100132int mv88e6xxx_port_set_duplex(struct mv88e6xxx_chip *chip, int port, int dup)
133{
134 u16 reg;
135 int err;
136
137 err = mv88e6xxx_port_read(chip, port, PORT_PCS_CTRL, &reg);
138 if (err)
139 return err;
140
141 reg &= ~(PORT_PCS_CTRL_FORCE_DUPLEX | PORT_PCS_CTRL_DUPLEX_FULL);
142
143 switch (dup) {
144 case DUPLEX_HALF:
145 reg |= PORT_PCS_CTRL_FORCE_DUPLEX;
146 break;
147 case DUPLEX_FULL:
148 reg |= PORT_PCS_CTRL_FORCE_DUPLEX | PORT_PCS_CTRL_DUPLEX_FULL;
149 break;
150 case DUPLEX_UNFORCED:
151 /* normal duplex detection */
152 break;
153 default:
154 return -EINVAL;
155 }
156
157 err = mv88e6xxx_port_write(chip, port, PORT_PCS_CTRL, reg);
158 if (err)
159 return err;
160
161 netdev_dbg(chip->ds->ports[port].netdev, "%s %s duplex\n",
162 reg & PORT_PCS_CTRL_FORCE_DUPLEX ? "Force" : "Unforce",
163 reg & PORT_PCS_CTRL_DUPLEX_FULL ? "full" : "half");
164
165 return 0;
166}
167
Vivien Didelote28def332016-11-04 03:23:27 +0100168/* Offset 0x04: Port Control Register */
169
170static const char * const mv88e6xxx_port_state_names[] = {
171 [PORT_CONTROL_STATE_DISABLED] = "Disabled",
172 [PORT_CONTROL_STATE_BLOCKING] = "Blocking/Listening",
173 [PORT_CONTROL_STATE_LEARNING] = "Learning",
174 [PORT_CONTROL_STATE_FORWARDING] = "Forwarding",
175};
176
177int mv88e6xxx_port_set_state(struct mv88e6xxx_chip *chip, int port, u8 state)
178{
179 u16 reg;
180 int err;
181
182 err = mv88e6xxx_port_read(chip, port, PORT_CONTROL, &reg);
183 if (err)
184 return err;
185
186 reg &= ~PORT_CONTROL_STATE_MASK;
187 reg |= state;
188
189 err = mv88e6xxx_port_write(chip, port, PORT_CONTROL, reg);
190 if (err)
191 return err;
192
193 netdev_dbg(chip->ds->ports[port].netdev, "PortState set to %s\n",
194 mv88e6xxx_port_state_names[state]);
195
196 return 0;
197}
Vivien Didelot5a7921f2016-11-04 03:23:28 +0100198
Vivien Didelotb4e48c52016-11-04 03:23:29 +0100199/* Offset 0x05: Port Control 1 */
200
Vivien Didelot5a7921f2016-11-04 03:23:28 +0100201/* Offset 0x06: Port Based VLAN Map */
202
203int mv88e6xxx_port_set_vlan_map(struct mv88e6xxx_chip *chip, int port, u16 map)
204{
205 const u16 mask = GENMASK(mv88e6xxx_num_ports(chip) - 1, 0);
206 u16 reg;
207 int err;
208
209 err = mv88e6xxx_port_read(chip, port, PORT_BASE_VLAN, &reg);
210 if (err)
211 return err;
212
213 reg &= ~mask;
214 reg |= map & mask;
215
216 err = mv88e6xxx_port_write(chip, port, PORT_BASE_VLAN, reg);
217 if (err)
218 return err;
219
220 netdev_dbg(chip->ds->ports[port].netdev, "VLANTable set to %.3x\n",
221 map);
222
223 return 0;
224}
Vivien Didelotb4e48c52016-11-04 03:23:29 +0100225
226int mv88e6xxx_port_get_fid(struct mv88e6xxx_chip *chip, int port, u16 *fid)
227{
228 const u16 upper_mask = (mv88e6xxx_num_databases(chip) - 1) >> 4;
229 u16 reg;
230 int err;
231
232 /* Port's default FID lower 4 bits are located in reg 0x06, offset 12 */
233 err = mv88e6xxx_port_read(chip, port, PORT_BASE_VLAN, &reg);
234 if (err)
235 return err;
236
237 *fid = (reg & 0xf000) >> 12;
238
239 /* Port's default FID upper bits are located in reg 0x05, offset 0 */
240 if (upper_mask) {
241 err = mv88e6xxx_port_read(chip, port, PORT_CONTROL_1, &reg);
242 if (err)
243 return err;
244
245 *fid |= (reg & upper_mask) << 4;
246 }
247
248 return 0;
249}
250
251int mv88e6xxx_port_set_fid(struct mv88e6xxx_chip *chip, int port, u16 fid)
252{
253 const u16 upper_mask = (mv88e6xxx_num_databases(chip) - 1) >> 4;
254 u16 reg;
255 int err;
256
257 if (fid >= mv88e6xxx_num_databases(chip))
258 return -EINVAL;
259
260 /* Port's default FID lower 4 bits are located in reg 0x06, offset 12 */
261 err = mv88e6xxx_port_read(chip, port, PORT_BASE_VLAN, &reg);
262 if (err)
263 return err;
264
265 reg &= 0x0fff;
266 reg |= (fid & 0x000f) << 12;
267
268 err = mv88e6xxx_port_write(chip, port, PORT_BASE_VLAN, reg);
269 if (err)
270 return err;
271
272 /* Port's default FID upper bits are located in reg 0x05, offset 0 */
273 if (upper_mask) {
274 err = mv88e6xxx_port_read(chip, port, PORT_CONTROL_1, &reg);
275 if (err)
276 return err;
277
278 reg &= ~upper_mask;
279 reg |= (fid >> 4) & upper_mask;
280
281 err = mv88e6xxx_port_write(chip, port, PORT_CONTROL_1, reg);
282 if (err)
283 return err;
284 }
285
286 netdev_dbg(chip->ds->ports[port].netdev, "FID set to %u\n", fid);
287
288 return 0;
289}
Vivien Didelot77064f32016-11-04 03:23:30 +0100290
291/* Offset 0x07: Default Port VLAN ID & Priority */
292
293int mv88e6xxx_port_get_pvid(struct mv88e6xxx_chip *chip, int port, u16 *pvid)
294{
295 u16 reg;
296 int err;
297
298 err = mv88e6xxx_port_read(chip, port, PORT_DEFAULT_VLAN, &reg);
299 if (err)
300 return err;
301
302 *pvid = reg & PORT_DEFAULT_VLAN_MASK;
303
304 return 0;
305}
306
307int mv88e6xxx_port_set_pvid(struct mv88e6xxx_chip *chip, int port, u16 pvid)
308{
309 u16 reg;
310 int err;
311
312 err = mv88e6xxx_port_read(chip, port, PORT_DEFAULT_VLAN, &reg);
313 if (err)
314 return err;
315
316 reg &= ~PORT_DEFAULT_VLAN_MASK;
317 reg |= pvid & PORT_DEFAULT_VLAN_MASK;
318
319 err = mv88e6xxx_port_write(chip, port, PORT_DEFAULT_VLAN, reg);
320 if (err)
321 return err;
322
323 netdev_dbg(chip->ds->ports[port].netdev, "DefaultVID set to %u\n",
324 pvid);
325
326 return 0;
327}
Vivien Didelot385a0992016-11-04 03:23:31 +0100328
329/* Offset 0x08: Port Control 2 Register */
330
331static const char * const mv88e6xxx_port_8021q_mode_names[] = {
332 [PORT_CONTROL_2_8021Q_DISABLED] = "Disabled",
333 [PORT_CONTROL_2_8021Q_FALLBACK] = "Fallback",
334 [PORT_CONTROL_2_8021Q_CHECK] = "Check",
335 [PORT_CONTROL_2_8021Q_SECURE] = "Secure",
336};
337
338int mv88e6xxx_port_set_8021q_mode(struct mv88e6xxx_chip *chip, int port,
339 u16 mode)
340{
341 u16 reg;
342 int err;
343
344 err = mv88e6xxx_port_read(chip, port, PORT_CONTROL_2, &reg);
345 if (err)
346 return err;
347
348 reg &= ~PORT_CONTROL_2_8021Q_MASK;
349 reg |= mode & PORT_CONTROL_2_8021Q_MASK;
350
351 err = mv88e6xxx_port_write(chip, port, PORT_CONTROL_2, reg);
352 if (err)
353 return err;
354
355 netdev_dbg(chip->ds->ports[port].netdev, "802.1QMode set to %s\n",
356 mv88e6xxx_port_8021q_mode_names[mode]);
357
358 return 0;
359}